Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {
"@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.58",
"@material-ui/styles": "^4.11.3",
"@reduxjs/toolkit": "^1.5.0",
"@testing-library/jest-dom": "^5.11.4",
Expand Down
21 changes: 1 addition & 20 deletions frontend-react/src/App.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import React, {
useState,
useEffect,
useLayoutEffect,
} from 'react';
import {
useDispatch,
useSelector,
} from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { createBrowserHistory } from 'history';
import rtl from 'jss-rtl';
import { create } from 'jss';
import CssBaseline from '@material-ui/core/CssBaseline';
Expand All @@ -24,14 +21,11 @@ import {

import themeObject from './theme';
import routes, { renderRoutes } from './routes';
import { getMe, login } from './store/slices/auth';
import { getDirectionSelector } from './store/selectors/ui';
import { DIRECTIONS } from './utils/constants';
import Sidebar from './components/Sidebar';
import logo from './assets/logo_pah_en.svg';

const history = createBrowserHistory();

// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });

Expand All @@ -51,31 +45,18 @@ const useStyles = makeStyles({

const App = () => {
const classes = useStyles();
const dispatch = useDispatch();
const isRtl = useSelector(getDirectionSelector);
const [open, setOpen] = useState(false);

useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? DIRECTIONS.RTL : DIRECTIONS.LTR);
}, [isRtl]);

useEffect(() => {
const authenticate = async () => {
await dispatch(login({
username: 'driver@codeforpoznan.pl',
password: 'pass123',
}));
dispatch(getMe());
}

authenticate();
}, [dispatch]);

return (
<StylesProvider jss={jss}>
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
<CssBaseline />
<BrowserRouter history={history}>
<BrowserRouter>
<Grid container className={classes.root}>
<Grid container wrap="nowrap" direction="row">
<Grid item xs>
Expand Down
5 changes: 2 additions & 3 deletions frontend-react/src/components/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import PropTypes from 'prop-types';
const Page = ({
children,
title = '',
...rest
}) => (
<div {...rest}>
<>
{children}
</div>
</>
);

Page.propTypes = {
Expand Down
8 changes: 5 additions & 3 deletions frontend-react/src/store/slices/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ const GET_ME = 'get me';

export const login = createAsyncThunk(
`${PREFIX}/${LOGIN}`,
async (values) => {
async (values, { rejectWithValue, dispatch }) => {
try {
const response = await request.post('/api-token-auth/', values);
const token = response.data.token;

request.setAuthToken(token);
setToken(token);


await dispatch(getMe());

return null;
} catch (error) {
return error.response.message;
return rejectWithValue(error.response.data);
}
}
)
Expand Down
113 changes: 113 additions & 0 deletions frontend-react/src/views/Login/components/LoginForm/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, { useState } from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import { useHistory } from 'react-router-dom';
import {
Box,
Button,
TextField,
} from '@material-ui/core';
import Alert from '@material-ui/lab/Alert';

import useT from '../../../../utils/translation';

const MAX_USERNAME = 150;
const MIN_PASSWORD = 7;
const MAX_PASSWORD = 128;

const LoginForm = ({ submitAction }) => {
const [notFieldError, setNonFieldError] = useState(null);
const login = useT("Login");
const username = useT("Username");
const password = useT("Password");
const passwordRequired = useT("Password is required");
const usernameRequired = useT("Username is required");
const usernameMax = useT("Should not be longer than {max} characters", { max: MAX_USERNAME });
const passwordMin = useT("Should not be shorter than {min} characters", { min: MIN_PASSWORD });
const passwordMax = useT("Should not be longer than {max} characters", { max: MAX_PASSWORD });

const history = useHistory();

const validationSchema = Yup.object().shape({
username: Yup.string()
.required(usernameRequired)
.max(MAX_USERNAME, usernameMax),
password: Yup.string()
.required(passwordRequired)
.min(MIN_PASSWORD, passwordMin)
.max(MAX_PASSWORD, passwordMax),
});

const formik = useFormik({
initialValues: {
username: '',
password: '',
},
onSubmit: async (values, formikBag) => {
const response = await submitAction(values);

if (response?.payload) {
setNonFieldError(response.payload.detail || response.payload.nonFieldErrors[0]);
} else {
history.push('/')
}
},
validationSchema,
});

return (
<form onSubmit={formik.handleSubmit}>
<Box
display="flex"
flexDirection="column"
pt={3}
>
<Box mb={2}>
<TextField
id="username"
name="username"
fullWidth
label={username}
placeholder={username}
value={formik.values.username}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.username && Boolean(formik.errors.username)}
helperText={formik.touched.username && formik.errors.username}
/>
</Box>
<Box mb={4}>
<TextField
fullWidth
label={password}
id="password"
name="password"
type="password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.password && Boolean(formik.errors.password)}
helperText={formik.touched.password && formik.errors.password}
/>
</Box>
{notFieldError && (
<Box mb={2}>
<Alert severity="error">
{notFieldError}
</Alert>
</Box>
)}
<Button
variant="contained"
color="primary"
type="submit"
disabled={!(formik.isValid && formik.dirty)}
>
{login}
</Button>
</Box>
</form>
);
};

export default LoginForm;
53 changes: 12 additions & 41 deletions frontend-react/src/views/Login/index.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,28 @@
import React from 'react';

import { useDispatch } from 'react-redux';
import {
Box,
Container,
Button,
TextField,
Typography,
} from '@material-ui/core';

import Page from '../../components/Page';
import LoginForm from './components/LoginForm';

import { login as loginAction } from '../../store/slices/auth';

import useT from '../../utils/translation';

const LoginView = () => {
const login = useT("Login");
const username = useT("Username");
const password = useT("Password");
const dispatch = useDispatch();

const formSubmit = values => dispatch(loginAction(values));

return (
<Page
title="Login"
>
<Container>
<Typography variant="h2" component="h2"> {login}</Typography>
<form>
<Box
display="flex"
flexDirection="column"
>
<Box mb={2}>
<TextField
fullWidth
label={username}
type="email"
placeholder={username}
/>
</Box>
<Box mb={2} width="100%">
<TextField
fullWidth
label={password}
type="email"
placeholder={password}
/>
</Box>
<Button
variant="contained"
color="primary"
type="submit"
>
{login}
</Button>
</Box>
</form>
<Page title={login}>
<Container maxWidth="sm">
<Typography variant="h2" component="h2">{login}</Typography>
<LoginForm submitAction={formSubmit} />
</Container>
</Page>
);
Expand Down
13 changes: 12 additions & 1 deletion frontend-react/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,7 @@
dependencies:
regenerator-runtime "^0.13.4"

"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
Expand Down Expand Up @@ -1479,6 +1479,17 @@
dependencies:
"@babel/runtime" "^7.4.4"

"@material-ui/lab@^4.0.0-alpha.58":
version "4.0.0-alpha.58"
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.58.tgz#c7ebb66f49863c5acbb20817163737caa299fafc"
integrity sha512-GKHlJqLxUeHH3L3dGQ48ZavYrqGOTXkFkiEiuYMAnAvXAZP4rhMIqeHOPXSUQan4Bd8QnafDcpovOSLnadDmKw==
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/utils" "^4.11.2"
clsx "^1.0.4"
prop-types "^15.7.2"
react-is "^16.8.0 || ^17.0.0"

"@material-ui/styles@^4.11.3":
version "4.11.3"
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.3.tgz#1b8d97775a4a643b53478c895e3f2a464e8916f2"
Expand Down