mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
195 lines
6.3 KiB
TypeScript
195 lines
6.3 KiB
TypeScript
import ForwardIcon from '@mui/icons-material/Forward';
|
|
import { Box, Paper, Typography, MenuItem, TextField, Button } from '@mui/material';
|
|
import { useRequest } from 'alova';
|
|
import { useContext, useState } from 'react';
|
|
import { toast } from 'react-toastify';
|
|
import type { ValidateFieldsError } from 'async-validator';
|
|
|
|
import type { Locales } from 'i18n/i18n-types';
|
|
import type { ChangeEventHandler, FC } from 'react';
|
|
import type { SignInRequest } from 'types';
|
|
import * as AuthenticationApi from 'api/authentication';
|
|
import { PROJECT_NAME } from 'api/env';
|
|
|
|
import { ValidatedPasswordField, ValidatedTextField } from 'components';
|
|
import { AuthenticationContext } from 'contexts/authentication';
|
|
|
|
import DEflag from 'i18n/DE.svg';
|
|
import FRflag from 'i18n/FR.svg';
|
|
import GBflag from 'i18n/GB.svg';
|
|
import ITflag from 'i18n/IT.svg';
|
|
import NLflag from 'i18n/NL.svg';
|
|
import NOflag from 'i18n/NO.svg';
|
|
import PLflag from 'i18n/PL.svg';
|
|
import SKflag from 'i18n/SK.svg';
|
|
import SVflag from 'i18n/SV.svg';
|
|
import TRflag from 'i18n/TR.svg';
|
|
import { I18nContext } from 'i18n/i18n-react';
|
|
import { loadLocaleAsync } from 'i18n/i18n-util.async';
|
|
import { onEnterCallback, updateValue } from 'utils';
|
|
import { SIGN_IN_REQUEST_VALIDATOR, validate } from 'validators';
|
|
|
|
const SignIn: FC = () => {
|
|
const authenticationContext = useContext(AuthenticationContext);
|
|
|
|
const { LL, setLocale, locale } = useContext(I18nContext);
|
|
|
|
const [signInRequest, setSignInRequest] = useState<SignInRequest>({
|
|
username: '',
|
|
password: ''
|
|
});
|
|
const [processing, setProcessing] = useState<boolean>(false);
|
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
|
|
|
const { send: callSignIn, onSuccess } = useRequest((request: SignInRequest) => AuthenticationApi.signIn(request), {
|
|
immediate: false
|
|
});
|
|
|
|
onSuccess((response) => {
|
|
if (response.data) {
|
|
authenticationContext.signIn(response.data.access_token);
|
|
}
|
|
});
|
|
|
|
const updateLoginRequestValue = updateValue(setSignInRequest);
|
|
|
|
const signIn = async () => {
|
|
await callSignIn(signInRequest).catch((event) => {
|
|
if (event.message === 'Unauthorized') {
|
|
toast.warning(LL.INVALID_LOGIN());
|
|
} else {
|
|
toast.error(LL.ERROR() + ' ' + event.message);
|
|
}
|
|
setProcessing(false);
|
|
});
|
|
};
|
|
|
|
const validateAndSignIn = async () => {
|
|
setProcessing(true);
|
|
SIGN_IN_REQUEST_VALIDATOR.messages({
|
|
required: LL.IS_REQUIRED('%s')
|
|
});
|
|
try {
|
|
await validate(SIGN_IN_REQUEST_VALIDATOR, signInRequest);
|
|
await signIn();
|
|
} catch (errors: any) {
|
|
setFieldErrors(errors);
|
|
setProcessing(false);
|
|
}
|
|
};
|
|
|
|
const submitOnEnter = onEnterCallback(signIn);
|
|
|
|
const onLocaleSelected: ChangeEventHandler<HTMLInputElement> = async ({ target }) => {
|
|
const loc = target.value as Locales;
|
|
localStorage.setItem('lang', loc);
|
|
await loadLocaleAsync(loc);
|
|
setLocale(loc);
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
display="flex"
|
|
height="100vh"
|
|
margin="auto"
|
|
padding={2}
|
|
justifyContent="center"
|
|
flexDirection="column"
|
|
maxWidth={(theme) => theme.breakpoints.values.sm}
|
|
>
|
|
<Paper
|
|
sx={(theme) => ({
|
|
textAlign: 'center',
|
|
padding: theme.spacing(2),
|
|
paddingTop: '172px',
|
|
backgroundImage: 'url("/app/icon.png")',
|
|
backgroundRepeat: 'no-repeat',
|
|
backgroundPosition: '50% ' + theme.spacing(2),
|
|
width: '100%'
|
|
})}
|
|
>
|
|
<Typography variant="h4">{PROJECT_NAME}</Typography>
|
|
|
|
<TextField name="locale" variant="outlined" value={locale} onChange={onLocaleSelected} size="small" select>
|
|
<MenuItem key="de" value="de">
|
|
<img src={DEflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
DE
|
|
</MenuItem>
|
|
<MenuItem key="en" value="en">
|
|
<img src={GBflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
EN
|
|
</MenuItem>
|
|
<MenuItem key="fr" value="fr">
|
|
<img src={FRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
FR
|
|
</MenuItem>
|
|
<MenuItem key="it" value="it">
|
|
<img src={ITflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
IT
|
|
</MenuItem>
|
|
<MenuItem key="nl" value="nl">
|
|
<img src={NLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
NL
|
|
</MenuItem>
|
|
<MenuItem key="no" value="no">
|
|
<img src={NOflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
NO
|
|
</MenuItem>
|
|
<MenuItem key="pl" value="pl">
|
|
<img src={PLflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
PL
|
|
</MenuItem>
|
|
<MenuItem key="sk" value="sk">
|
|
<img src={SKflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
SK
|
|
</MenuItem>
|
|
<MenuItem key="sv" value="sv">
|
|
<img src={SVflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
SV
|
|
</MenuItem>
|
|
<MenuItem key="tr" value="tr">
|
|
<img src={TRflag} style={{ width: 16, verticalAlign: 'middle' }} />
|
|
TR
|
|
</MenuItem>
|
|
</TextField>
|
|
|
|
<Box display="flex" flexDirection="column" alignItems="center">
|
|
<ValidatedTextField
|
|
fieldErrors={fieldErrors}
|
|
disabled={processing}
|
|
sx={{
|
|
width: 240
|
|
}}
|
|
name="username"
|
|
label={LL.USERNAME(0)}
|
|
value={signInRequest.username}
|
|
onChange={updateLoginRequestValue}
|
|
margin="normal"
|
|
variant="outlined"
|
|
/>
|
|
<ValidatedPasswordField
|
|
fieldErrors={fieldErrors}
|
|
disabled={processing}
|
|
sx={{
|
|
width: 240
|
|
}}
|
|
name="password"
|
|
label={LL.PASSWORD()}
|
|
value={signInRequest.password}
|
|
onChange={updateLoginRequestValue}
|
|
onKeyDown={submitOnEnter}
|
|
variant="outlined"
|
|
/>
|
|
</Box>
|
|
|
|
<Button variant="contained" color="primary" sx={{ mt: 2 }} onClick={validateAndSignIn} disabled={processing}>
|
|
<ForwardIcon sx={{ mr: 1 }} />
|
|
{LL.SIGN_IN()}
|
|
</Button>
|
|
</Paper>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default SignIn;
|