244 lines
7.8 KiB
TypeScript
244 lines
7.8 KiB
TypeScript
import { Done } from '@mui/icons-material';
|
|
import {
|
|
Button,
|
|
CircularProgress,
|
|
Dialog,
|
|
DialogActions,
|
|
DialogContent,
|
|
DialogTitle,
|
|
Grid,
|
|
TextField,
|
|
Typography,
|
|
useMediaQuery,
|
|
useTheme,
|
|
} from '@mui/material';
|
|
import { useForm } from '@tanstack/react-form';
|
|
import { useMutation } from '@tanstack/react-query';
|
|
import { FC, FormEvent, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import Api from '../../../api/Api';
|
|
import { UserCreate } from '../../../types/User';
|
|
import ErrorComponent from '../../Error/ErrorComponent';
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
const RegisterDialog: FC<Props> = ({ open, onClose }) => {
|
|
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const [error, setError] = useState<any>();
|
|
|
|
const createMutation = useMutation({
|
|
mutationFn: ({ data }: { data: UserCreate }) => {
|
|
return Api.createUser(data);
|
|
},
|
|
});
|
|
|
|
const { t } = useTranslation();
|
|
const theme = useTheme();
|
|
const fullScreen = useMediaQuery(theme.breakpoints.only('xs'), { noSsr: true });
|
|
|
|
const form = useForm<UserCreate>({
|
|
defaultValues: {
|
|
username: '',
|
|
email: '',
|
|
password: '',
|
|
},
|
|
onSubmit: async ({ value }) => {
|
|
try {
|
|
createMutation.mutate(
|
|
{ data: value },
|
|
{
|
|
onSuccess: () => setError(undefined),
|
|
onError: setError,
|
|
}
|
|
);
|
|
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
} catch (error: any) {
|
|
setError(error);
|
|
}
|
|
},
|
|
});
|
|
|
|
const handleClose = () => {
|
|
form.reset();
|
|
setError(undefined);
|
|
onClose();
|
|
};
|
|
|
|
if (createMutation.isSuccess)
|
|
return (
|
|
<Dialog open={open} onClose={handleClose} fullWidth fullScreen={fullScreen}>
|
|
<DialogContent>
|
|
<Grid container spacing={2}>
|
|
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center' }}>
|
|
<Done color="action" sx={{ fontSize: '200px' }} />
|
|
</Grid>
|
|
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center' }}>
|
|
<Typography variant="h5">{t('Confirm header')}</Typography>
|
|
</Grid>
|
|
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center' }}>
|
|
<Typography>{t('Confirm mail')}</Typography>
|
|
</Grid>
|
|
</Grid>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={handleClose}>{t('Close')}</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
);
|
|
|
|
return (
|
|
<Dialog
|
|
open={open}
|
|
onClose={handleClose}
|
|
fullWidth
|
|
fullScreen={fullScreen}
|
|
PaperProps={{
|
|
component: 'form',
|
|
encType: 'multipart/form-data',
|
|
onSubmit: (e: FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
form.handleSubmit();
|
|
},
|
|
onKeyDown: (event: React.KeyboardEvent<HTMLFormElement>) => {
|
|
if (event.key === 'Tab') {
|
|
event.stopPropagation();
|
|
}
|
|
},
|
|
noValidate: true,
|
|
}}
|
|
>
|
|
<DialogTitle>{t('Register')}</DialogTitle>
|
|
<DialogContent sx={{ gap: 2 }}>
|
|
<Grid container spacing={2}>
|
|
<Grid item xs={12}>
|
|
<form.Field
|
|
name="username"
|
|
validators={{
|
|
onChange: ({ value }) => (!value ? t('Username required') : undefined),
|
|
onChangeAsyncDebounceMs: 250,
|
|
onChangeAsync: async ({ value }) => {
|
|
return !value && t('Username required');
|
|
},
|
|
}}
|
|
children={(field) => {
|
|
return (
|
|
<>
|
|
<TextField
|
|
variant="outlined"
|
|
name={field.name}
|
|
value={field.state.value}
|
|
onBlur={field.handleBlur}
|
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
size="small"
|
|
label={t('Username')}
|
|
required
|
|
error={field.state.meta.isTouched && field.state.meta.errors.length > 0}
|
|
helperText={field.state.meta.isTouched ? field.state.meta.errors.join(',') : ''}
|
|
autoComplete="new-username"
|
|
fullWidth
|
|
margin="dense"
|
|
/>
|
|
</>
|
|
);
|
|
}}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<form.Field
|
|
name="email"
|
|
validators={{
|
|
onChange: ({ value }) => (!value ? t('Email required') : undefined),
|
|
onChangeAsyncDebounceMs: 250,
|
|
onChangeAsync: async ({ value }) => {
|
|
return !value && t('Email required');
|
|
},
|
|
}}
|
|
children={(field) => {
|
|
return (
|
|
<>
|
|
<TextField
|
|
variant="outlined"
|
|
name={field.name}
|
|
value={field.state.value}
|
|
onBlur={field.handleBlur}
|
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
size="small"
|
|
label={t('Email')}
|
|
required
|
|
error={field.state.meta.isTouched && field.state.meta.errors.length > 0}
|
|
helperText={field.state.meta.isTouched ? field.state.meta.errors.join(',') : ''}
|
|
type="email"
|
|
autoComplete="new-email"
|
|
inputMode="email"
|
|
fullWidth
|
|
/>
|
|
</>
|
|
);
|
|
}}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<form.Field
|
|
name="password"
|
|
validators={{
|
|
onChange: ({ value }) => (!value ? t('Password required') : undefined),
|
|
onChangeAsyncDebounceMs: 250,
|
|
onChangeAsync: async ({ value }) => {
|
|
return !value && t('Password required');
|
|
},
|
|
}}
|
|
children={(field) => {
|
|
return (
|
|
<>
|
|
<TextField
|
|
variant="outlined"
|
|
name={field.name}
|
|
value={field.state.value}
|
|
onBlur={field.handleBlur}
|
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
size="small"
|
|
label={t('Password')}
|
|
required
|
|
error={field.state.meta.isTouched && field.state.meta.errors.length > 0}
|
|
helperText={field.state.meta.isTouched ? field.state.meta.errors.join(',') : ''}
|
|
type="password"
|
|
autoComplete="new-password"
|
|
fullWidth
|
|
/>
|
|
</>
|
|
);
|
|
}}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<form.Subscribe
|
|
selector={(state) => [state.canSubmit, state.isSubmitting]}
|
|
children={([canSubmit]) => (
|
|
<>
|
|
<Button
|
|
type="submit"
|
|
disabled={!canSubmit || createMutation.isPending}
|
|
variant="contained"
|
|
endIcon={createMutation.isPending && <CircularProgress color="inherit" size="20px" />}
|
|
>
|
|
{t('Register')}
|
|
</Button>
|
|
</>
|
|
)}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
{error && <ErrorComponent error={error} context="register" />}
|
|
</Grid>
|
|
</Grid>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
export default RegisterDialog;
|