Skip to content

Commit a53457e

Browse files
author
Emile Frey
committed
converted redux to redux toolkit and overall improvements to react router and tsconfig/webpack
1 parent 6ccb5f1 commit a53457e

40 files changed

+2241
-10155
lines changed

backend/mainapp/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181

8282
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
8383

84+
DJANGO_REST_PASSWORDRESET_NO_INFORMATION_LEAKAGE = True
8485

8586
# Database
8687
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

backend/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Django==3.1.12
1+
Django==3.1.13
22
djangorestframework==3.11.2
33
django-rest-auth==0.9.5
44
django-cors-headers==3.5.0

frontend/Dockerfile.local

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,4 @@ COPY package.json .
88
RUN npm install
99

1010
# Add rest of the client code
11-
COPY . .
12-
13-
EXPOSE 3000
14-
15-
CMD npm start
11+
COPY . .

frontend/package-lock.json

Lines changed: 1564 additions & 9267 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
"@testing-library/react": "^9.3.2",
1919
"@testing-library/user-event": "^7.1.2",
2020
"axios": "^0.21.1",
21+
"framer-motion": "^2.0.1",
2122
"prop-types": "15.7.2",
2223
"react": "^17.0.1",
2324
"react-dom": "^17.0.1",
24-
"react-redux": "^7.2.1",
25+
"react-redux": "^7.2.4",
26+
"@reduxjs/toolkit": "^1.6.0",
2527
"react-router-dom": "^5.2.0",
28+
"redux-persist": "^6.0.0",
2629
"redux": "^4.0.5",
2730
"redux-thunk": "^2.3.0"
2831
},
@@ -39,12 +42,12 @@
3942
"@types/react-router-dom": "^5.1.7",
4043
"babel-loader": "^8.2.2",
4144
"css-loader": "^5.0.1",
45+
"file-loader": "^6.2.0",
4246
"html-webpack-plugin": "^5.3.2",
43-
"react-hot-loader": "^4.13.0",
4447
"style-loader": "^2.0.0",
4548
"ts-loader": "^8.2.0",
4649
"typescript": "^4.1.3",
47-
"webpack": "^5.42.0",
50+
"webpack": "^5.45.1",
4851
"webpack-cli": "^4.7.2",
4952
"webpack-dev-server": "^3.11.2"
5053
}

frontend/src/App.tsx

Lines changed: 96 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,109 @@
1-
import React, { Dispatch, useContext, useEffect } from 'react';
2-
import Router from './routes/Router';
3-
import Layout from './components/Layout/Layout';
4-
import { connect } from 'react-redux';
5-
import * as actions from './auth/authActions';
6-
import { PrivateRouteProps } from './routes/PrivateRoute';
7-
import { Dialog, DialogActions, DialogContent, DialogTitle, Snackbar, ThemeProvider } from '@material-ui/core';
8-
import { theme } from './Theme'
9-
import { AlertContext } from './contexts/AlertContext';
10-
import Alert from '@material-ui/lab/Alert';
11-
import { AxiosError } from './interfaces/axios/AxiosError'
12-
import { ThemeContext } from './contexts/ThemeContext';
13-
import { useLocation } from "react-router-dom";
14-
import { DialogContext } from './contexts/DialogContext';
1+
import React, { useContext, useEffect, useMemo, useState } from "react";
2+
import {
3+
BrowserRouter as Router,
4+
Switch,
5+
Route,
6+
useLocation,
7+
Redirect,
8+
useHistory,
9+
} from "react-router-dom";
10+
import axios from 'axios';
11+
import { ThemeProvider, CssBaseline, Snackbar, Dialog, DialogTitle, DialogContent, DialogActions } from "@material-ui/core";
1512

13+
import { useAppSelector } from './redux/hooks';
14+
import { Login } from './components/Login/Login';
15+
import { appArray } from "./routes/Routes";
16+
import PrivateRoute from "./routes/PrivateRoute";
17+
import Layout from "./components/Layout/Layout";
18+
import Footer from "./components/Layout/Footer";
19+
import { theme } from "./Theme";
20+
import { DialogContext } from "./contexts/DialogContext";
21+
import { Alert } from "@material-ui/lab";
22+
import { AlertContext } from "./contexts/AlertContext";
23+
import PasswordUpdate from "./components/Login/PasswordUpdate";
24+
import PasswordReset from "./components/Login/PasswordReset";
1625

17-
type Error = {
18-
message: string
19-
response: {
20-
data: Record<string, string[]>
21-
}
22-
}
23-
export interface AuthProps {
24-
logout: Function
25-
setAuthenticatedIfRequired: Function
26-
onAuth: Function
27-
token: string
28-
error: Error
29-
}
30-
31-
export interface AppProps extends AuthProps, PrivateRouteProps { }
32-
33-
function App(props: AppProps) {
34-
35-
const { alertType, openAlert, alertMessage, handleAlertClose } = useContext(AlertContext);
26+
export function App() {
27+
const location = useLocation();
28+
const { authenticated } = useAppSelector(state => state.auth)
29+
const { darkMode } = useAppSelector(state => state.darkMode)
30+
const history = useHistory()
3631
const { showDialog, dialogTitle, dialogBody, dialogActions, handleDialogClose } = useContext(DialogContext);
37-
const { darkMode } = useContext(ThemeContext);
38-
const palletType = darkMode ? "dark" : "light"
39-
const location = useLocation().pathname
32+
const { alertType, openAlert, alertMessage, handleAlertClose } = useContext(AlertContext);
4033

4134
useEffect(() => {
42-
props.setAuthenticatedIfRequired();
43-
}, [props]);
35+
if (authenticated) {
36+
axios.get('/api/auth/loggedin/')
37+
}
38+
}, [])
4439

45-
useEffect(() => {
46-
handleAlertClose()
47-
}, [location])
40+
const publicRoutes = [
41+
{ path: "login", component: Login, exact: true },
42+
{ path: "password_reset", component: PasswordReset, exact: true }
43+
]
4844

49-
return (
50-
<div className="App">
51-
<ThemeProvider theme={theme(palletType)}>
52-
<Layout {...props} >
53-
<Router {...props} />
54-
</Layout>
55-
<Snackbar id="appAlertSnackbar" open={openAlert} autoHideDuration={6000} onClose={handleAlertClose}>
56-
<Alert variant="filled" onClose={handleAlertClose} severity={alertType}>
57-
{alertMessage}
58-
</Alert>
59-
</Snackbar>
60-
<Dialog maxWidth="md" fullWidth open={showDialog} onClose={handleDialogClose} aria-labelledby="alert-dialog-title">
61-
<DialogTitle id="alert-dialog-title">{dialogTitle}</DialogTitle>
62-
<DialogContent>
63-
{dialogBody}
64-
</DialogContent>
65-
<DialogActions>
66-
{dialogActions}
67-
</DialogActions>
68-
</Dialog>
69-
</ThemeProvider>
70-
</div>
71-
);
72-
}
45+
const atPublicRoute = publicRoutes.findIndex(route => location.pathname.includes(route.path)) !== -1
7346

74-
interface MapStateToPropsInterface {
75-
auth: {
76-
token: string,
77-
error: AxiosError
78-
}
79-
}
47+
useEffect(() => {
48+
// when un-authenticated, redirect to login (only for non-public routes)
49+
if (!authenticated && !atPublicRoute) {
50+
history.push({ pathname: "/login", state: { from: location } })
51+
}
52+
}, [authenticated, location.pathname])
8053

81-
//This means that one or more of the redux states in the store are available as props
82-
const mapStateToProps = (state: MapStateToPropsInterface) => {
83-
return {
84-
isAuthenticated: state.auth.token !== null && typeof state.auth.token !== 'undefined',
85-
token: state.auth.token,
86-
error: state.auth.error
54+
const generateAppRoutes = () => {
55+
return appArray
56+
.map((app, index1) => {
57+
let result = app.routes?.map((route, index2) => {
58+
const key = `${index1}_${index2}`
59+
return <PrivateRoute key={key} exact={route.exact} path={route.path} component={route.component} />;
60+
})
61+
return result
62+
})
8763
}
88-
}
8964

90-
//This means that one or more of the redux actions in the form of dispatch(action) combinations are available as props
91-
const mapDispatchToProps = (dispatch: Dispatch<any>) => {
92-
return {
93-
setAuthenticatedIfRequired: () => dispatch(actions.authCheckState()),
94-
logout: () => dispatch(actions.authLogout())
95-
}
65+
const privateRoutes = useMemo(() => generateAppRoutes(), []);
66+
67+
return (
68+
<ThemeProvider theme={theme(darkMode ? "dark" : "light")}>
69+
<CssBaseline />
70+
<Snackbar id="appAlertSnackbar" open={openAlert} autoHideDuration={6000} onClose={handleAlertClose}>
71+
<Alert variant="filled" onClose={handleAlertClose} severity={alertType}>
72+
{alertMessage}
73+
</Alert>
74+
</Snackbar>
75+
<Dialog maxWidth="md" fullWidth open={showDialog} onClose={handleDialogClose} aria-labelledby="alert-dialog-title">
76+
<DialogTitle id="alert-dialog-title">{dialogTitle}</DialogTitle>
77+
<DialogContent>
78+
{dialogBody}
79+
</DialogContent>
80+
<DialogActions>
81+
{dialogActions}
82+
</DialogActions>
83+
</Dialog>
84+
<Layout>
85+
<div className={atPublicRoute ? "" : "content-wrap"}>
86+
<div className={atPublicRoute ? "" : "container-fluid body main_container"}>
87+
<Switch>
88+
{publicRoutes.map((route, index) =>
89+
<Route key={index} component={route.component} path={"/" + route.path} exact={route.exact} />
90+
)}
91+
{privateRoutes}
92+
<PrivateRoute component={PasswordUpdate} path={"/change_password"}/>
93+
<Redirect from="/" to={"/home"} />
94+
</Switch>
95+
</div>
96+
</div>
97+
</Layout>
98+
<Footer />
99+
</ThemeProvider>
100+
)
96101
}
97102

98-
export default connect(mapStateToProps, mapDispatchToProps)(App);
103+
export function MainPage() {
104+
return (
105+
<Router>
106+
<App />
107+
</Router>
108+
)
109+
}

frontend/src/Theme.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@ export const theme = (type: "dark" | "light") => createMuiTheme({
44
palette: {
55
type: type,
66
primary: {
7-
main: '#1976d2',
8-
light: '#63a4ff',
9-
dark: '#004ba0'
7+
main: "#3498db",
8+
contrastText: "#fff"
109
},
1110
secondary: {
12-
main: '#ffa000',
13-
light: '#ffd149',
14-
dark: '#c67100'
11+
main: "#ffaa00",
12+
contrastText: "#fff"
1513
},
1614
},
1715
});

frontend/src/auth/authActionTypes.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

frontend/src/auth/authActions.js

Lines changed: 0 additions & 115 deletions
This file was deleted.

0 commit comments

Comments
 (0)