Here, I have been developing a react web app using Create React App and I haven't used Express Framework and Webpack. Also, I have integrated it into the Spring Boot app. Spring Boot app contains all the Apis which are used by the frontend react web app. Now, after integrating both apps into one, I tested it locally and deployed an app to the Heroku server. After deploying, only the landing page can be viewed. Other Login, Dashboard pages e.t.c. couldn't be open giving following error:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Jun 30 05:18:45 UTC 2020
There was an unexpected error (type=Not Found, status=404).
No message available
I have tested in a local environment, everything works fine, but after deploying nothing works, I have tried so many solutions but none worked for me. Please help me in solving this issue.
Here, I have added code snippet, please review the codes and help me find the solution.
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link href="%PUBLIC_URL%/favicon.ico" rel="icon"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta content="#000000" name="theme-color"/>
<meta
content="Web site created using create-react-app"
name="description"
/>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<title>Zodi</title>
</head>
<body style="margin: 0">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
src/index.js
import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import {store} from './_helpers';
import CssBaseline from '@material-ui/core/CssBaseline';
import {ThemeProvider} from '@material-ui/core/styles';
import {App} from './App';
import theme from './utils/Theme';
import * as serviceWorker from './serviceWorker';
render(
<Provider store={store}>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline/>
<App/>
</ThemeProvider>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
src/main/App.js
import React, {Component} from 'react';
import {Route, Router, Switch} from "react-router-dom";
import {connect} from "react-redux";
import {history} from '../_helpers';
import {alertActions} from '../_actions';
import {Login} from "../login/Login";
import {Dashboard} from "../dashboard/Dashboard";
import {PrivateRoute} from "../_components";
import Welcome from "../main/Welcome";
import './App.css';
import ErrorNotFound from "../error/ErrorNotFound";
import {Alert} from '@material-ui/lab';
class App extends Component {
constructor(props) {
super(props);
const {dispatch} = this.props;
history.listen((location, action) => {
// clear alert on location change
dispatch(alertActions.clear());
});
}
render() {
const {alert} = this.props;
const heading = "Welcome To Zodi";
const quote = "This project includes simple spring boot application with spring security and react js as frontend for authentication with JWT.";
const footer = "Kabindra Shrestha";
return (
<div>
{alert.message &&
<Alert severity="error">{alert.message}</Alert>
}
<Router history={history}>
<Switch className="padding-left">
<Route path="/" exact
component={() => <Welcome heading={heading} quote={quote} footer={footer}/>}/>
<Route path="/login/admin" exact component={Login}/>
<PrivateRoute path="/dashboard" exact component={Dashboard}/>
<Route component={() => <ErrorNotFound/>}/>
</Switch>
</Router>
</div>
);
}
}
function mapStateToProps(state) {
const {alert} = state;
return {
alert
};
}
const connectedApp = connect(mapStateToProps)(App);
export {connectedApp as App};
src/login/Login.js
import React, {Component} from 'react';
import {
Avatar,
Box,
Button,
CircularProgress,
Container,
CssBaseline,
Link,
TextField,
Typography,
withStyles
} from '@material-ui/core';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import {loginActions} from "../_actions";
import {connect} from "react-redux";
import {withRouter} from "react-router-dom";
const useStyles = theme => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
spinner: {
display: 'block',
marginLeft: 'auto',
marginRight: 'auto'
},
});
class Login extends Component {
constructor(props) {
super(props);
// reset login status
this.props.dispatch(loginActions.logout());
this.state = {
username: '',
password: '',
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
const {name, value} = e.target;
this.setState({[name]: value});
}
handleSubmit(e) {
e.preventDefault();
this.setState({submitted: true});
const {username, password} = this.state;
const {dispatch} = this.props;
if (username && password) {
dispatch(loginActions.login(username, password));
}
}
render() {
const {classes} = this.props;
const {loggingIn} = this.props;
const {username, password, submitted} = this.state;
return (
<Container component="main" maxWidth="xs">
<CssBaseline/>
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon/>
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate onSubmit={this.handleSubmit}>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="username"
name="username"
label="Username"
type="text"
value={username}
autoComplete="email"
autoFocus
onChange={this.handleChange}
className={(submitted && !username ? 'has-error' : '')}
helperText={submitted && !username && "Username is required"}/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="password"
name="password"
label="Password"
type="password"
value={password}
autoComplete="current-password"
onChange={this.handleChange}
className={(submitted && !password ? 'has-error' : '')}
helperText={submitted && !password && "Password is required"}/>
{/*<FormControlLabel
control={<Checkbox value="remember" color="primary"/>}
label="Remember me"
/>*/}
<Button
variant="contained"
fullWidth
type="submit"
color="primary"
className={classes.submit}>
Sign In
</Button>
{loggingIn &&
<CircularProgress className={classes.spinner}/>
}
{/*<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>*/}
</form>
</div>
<Box mt={8}>
<Copyright/>
</Box>
</Container>
);
}
}
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '}
<Link color="inherit" href="https://material-ui.com/">
Zodi
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
}
function mapStateToProps(state) {
const {loggingIn} = state.authentication;
return {
loggingIn
};
}
const connectedLoginPage = withStyles(useStyles, {withTheme: true})(withRouter(connect(mapStateToProps)(Login)));
export {connectedLoginPage as Login};
src/dashboard/Dashboard.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {userActions} from '../_actions';
import AppBarNavigation from "../main/AppBarNavigation";
import {withStyles} from "@material-ui/core";
import {withRouter} from "react-router-dom";
import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";
import Card from "@material-ui/core/Card/Card";
const useStyles = theme => ({
root: {
margin: '1.5rem',
borderWidth: '.2rem',
position: 'relative'
},
content: {
padding: '4rem 2rem !important',
backgroundColor: theme.palette.card.background,
borderRadius: '.3rem'
},
title: {
fontSize: '3.5rem',
fontWeight: 300,
lineHeight: 1.2,
marginBottom: '.5rem',
marginTop: 0,
display: 'block',
marginBlockStart: '0.67em',
marginBlockEnd: '0.67em',
marginInlineStart: '0px',
marginInlineEnd: '0px',
color: theme.palette.text,
textAlign: 'left'
},
quote: {
fontSize: '1.25rem',
fontWeight: 300,
marginTop: 0,
marginBottom: '1rem',
display: 'block',
marginBlockStart: '1em',
marginBlockEnd: '1em',
marginInlineStart: '0px',
marginInlineEnd: '0px',
lineHeight: 1.5,
color: theme.palette.text,
textAlign: 'left'
},
footer: {
marginTop: 0,
marginBottom: '1rem',
display: 'block',
marginBlockStart: '1em',
marginBlockEnd: '1em',
marginInlineStart: '0px',
marginInlineEnd: '0px',
fontSize: '1rem',
fontWeight: 400,
lineHeight: 1.5,
color: theme.palette.text,
textAlign: 'left'
},
space: {
marginBottom: '1.5rem!important',
marginTop: '1.5rem!important',
border: 0,
borderTop: '1px solid rgba(0,0,0,.1)',
boxSizing: 'content-box',
height: 0,
},
});
class Dashboard extends Component {
componentDidMount() {
this.props.dispatch(userActions.getAll());
}
render() {
const {classes} = this.props;
const {user, users, usersData} = this.props;
return (<div>
<AppBarNavigation/>
<div>
<Card className={classes.root}>
<CardContent className={classes.content}>
<Typography className={classes.title} gutterBottom>
<p>Hi {user.firstname + " " + user.lastname}! From Authentication Redux</p>
</Typography>
<Typography className={classes.title} gutterBottom>
{usersData &&
<p>Hi {usersData.firstname + " " + usersData.lastname}! From Users Redux</p>}
</Typography>
<Typography className={classes.quote}>
<p>You're logged in with React & JWT!!</p>
</Typography>
<hr className={classes.space}/>
<Typography className={classes.footer}>
{users.loading && <em>Loading users...</em>}
{users.error && <p className="text-danger">ERROR: {users.error}</p>}
</Typography>
<Typography className={classes.footer}>
{users.usersStatus &&
<p className="text-danger">STATUS: {users.usersStatus ? "True" : "False"}</p>}
{users.usersMessage &&
<p className="text-danger">MESSAGE: {users.usersMessage}</p>}
</Typography>
</CardContent>
</Card>
</div>
</div>
);
}
}
function mapStateToProps(state) {
const {users, authentication} = state;
const {user} = authentication;
const {usersData} = users;
return {
user,
users,
usersData
};
}
const connectedDashboard = withStyles(useStyles, {withTheme: true})(withRouter(connect(mapStateToProps)(Dashboard)));
export {connectedDashboard as Dashboard};
package.json
{
"name": "frontend",
"version": "1.0.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.10.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "latest",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"env-cmd": "^10.1.0",
"history": "4.7.2",
"axios": "^0.19.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"react-redux": "5.1.2",
"redux": "4.0.5",
"redux-logger": "3.0.6",
"redux-thunk": "2.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}