356 lines
13 KiB
TypeScript
356 lines
13 KiB
TypeScript
import { ReactElement, Suspense, useState } from 'react';
|
|
import { ErrorMessage, Form, Formik } from 'formik';
|
|
import {
|
|
Alert,
|
|
Autocomplete,
|
|
Button,
|
|
FormControl,
|
|
IconButton,
|
|
InputAdornment,
|
|
InputLabel,
|
|
Link,
|
|
OutlinedInput,
|
|
Paper,
|
|
Skeleton,
|
|
Stack,
|
|
TextField,
|
|
Typography,
|
|
} from '@mui/material';
|
|
import { CATEGORY_NAMES } from '../Vendors/Vendors';
|
|
import signupBanner from 'assets/authentication-banners/green.png';
|
|
import IconifyIcon from 'components/base/IconifyIcon';
|
|
import logo from 'assets/logo/favicon-logo.png';
|
|
import Image from 'components/base/Image';
|
|
import { axiosInstance } from '../../axiosApi.js';
|
|
import PasswordStrengthChecker from '../../components/PasswordStrengthChecker';
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
type SignUpValues = {
|
|
email: string;
|
|
first_name: string;
|
|
last_name: string;
|
|
password: string;
|
|
password2: string;
|
|
ownerType: string;
|
|
vendorType?: string;
|
|
};
|
|
|
|
const SignUp = (): ReactElement => {
|
|
const [showPassword, setShowPassword] = useState(false);
|
|
const [showPassword2, setShowPassword2] = useState(false);
|
|
const [password, setPassword] = useState('');
|
|
|
|
const [errorMessage, setErrorMessage] = useState<any | null>(null);
|
|
|
|
const handleClickShowPassword = () => setShowPassword(!showPassword);
|
|
const handleClickShowPassword2 = () => setShowPassword2(!showPassword2);
|
|
const navigate = useNavigate();
|
|
|
|
const handleSignUp = async ({
|
|
email,
|
|
first_name,
|
|
last_name,
|
|
ownerType,
|
|
password,
|
|
password2,
|
|
vendorType,
|
|
}: SignUpValues): Promise<void> => {
|
|
try {
|
|
const response = await axiosInstance.post('register/', {
|
|
email: email,
|
|
first_name: first_name,
|
|
last_name: last_name,
|
|
user_type: ownerType,
|
|
vendor_type: ownerType === 'vendor' ? vendorType : undefined,
|
|
password: password,
|
|
password2: password2,
|
|
});
|
|
console.log(response);
|
|
if (response.status == 201) {
|
|
navigate('/authentication/login');
|
|
|
|
console.log('Good response');
|
|
} else {
|
|
console.log(`No good: ${response}`);
|
|
}
|
|
} catch (error) {
|
|
const hasErrors = Object.keys(error.response.data).length > 0;
|
|
if (hasErrors) {
|
|
setErrorMessage(error.response.data);
|
|
} else {
|
|
setErrorMessage(null);
|
|
}
|
|
}
|
|
};
|
|
return (
|
|
<Stack
|
|
direction="row"
|
|
bgcolor="background.paper"
|
|
boxShadow={(theme) => theme.shadows[3]}
|
|
width={{ md: 960 }}
|
|
>
|
|
<Stack width={{ md: 0.5 }} m={2.5} gap={10}>
|
|
<Link href="/" width="fit-content">
|
|
<Image src={logo} width={82.6} />
|
|
</Link>
|
|
<Stack alignItems="center" gap={2.5} mx="auto">
|
|
<Typography variant="h3">Signup</Typography>
|
|
<Formik
|
|
initialValues={{
|
|
first_name: '',
|
|
email: '',
|
|
last_name: '',
|
|
password: '',
|
|
password2: '',
|
|
ownerType: 'property_owner',
|
|
vendorType: '',
|
|
}}
|
|
onSubmit={handleSignUp}
|
|
>
|
|
{({ setFieldValue, values }) => (
|
|
<Form>
|
|
<FormControl variant="standard" fullWidth>
|
|
{errorMessage ? (
|
|
<Alert severity="error">
|
|
<ul>
|
|
{Object.entries(errorMessage).map(([fieldName, errorMessages]) => (
|
|
<li key={fieldName}>
|
|
<strong>{fieldName}</strong>
|
|
{errorMessages.length > 0 ? (
|
|
<ul>
|
|
{errorMessages.map((message, index) => (
|
|
<li key={`${fieldName}-${index}`}>{message}</li> // Key for each message
|
|
))}
|
|
</ul>
|
|
) : (
|
|
<span> No specific errors for this field.</span>
|
|
)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</Alert>
|
|
) : null}
|
|
<InputLabel shrink htmlFor="name">
|
|
First Name
|
|
</InputLabel>
|
|
<TextField
|
|
variant="filled"
|
|
onChange={(event) => setFieldValue('first_name', event.target.value)}
|
|
placeholder="Enter your first name"
|
|
id="first_name"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end" sx={{ width: 16, height: 16 }}>
|
|
<IconifyIcon icon="mdi:user" width={1} height={1} />
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<FormControl variant="standard" fullWidth>
|
|
<InputLabel shrink htmlFor="name">
|
|
Last Name
|
|
</InputLabel>
|
|
<TextField
|
|
variant="filled"
|
|
onChange={(event) => setFieldValue('last_name', event.target.value)}
|
|
placeholder="Enter your last name"
|
|
id="last_name"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end" sx={{ width: 16, height: 16 }}>
|
|
<IconifyIcon icon="mdi:user" width={1} height={1} />
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
|
|
<FormControl variant="standard" fullWidth>
|
|
<InputLabel shrink htmlFor="email">
|
|
Email
|
|
</InputLabel>
|
|
<OutlinedInput
|
|
placeholder="Enter your email"
|
|
id="email"
|
|
endAdornment={
|
|
<InputAdornment position="end" sx={{ width: 16, height: 16 }}>
|
|
<IconifyIcon icon="ic:baseline-email" width={1} height={1} />
|
|
</InputAdornment>
|
|
}
|
|
sx={{
|
|
width: 1,
|
|
backgroundColor: 'action.focus',
|
|
}}
|
|
onChange={(event) => setFieldValue('email', event.target.value)}
|
|
/>
|
|
</FormControl>
|
|
<FormControl variant="standard" fullWidth sx={{ mt: 2, mb: 1 }}>
|
|
<InputLabel shrink htmlFor="account-type" sx={{ position: 'relative', transform: 'none', mb: 1, fontSize: '0.875rem' }}>
|
|
I am a...
|
|
</InputLabel>
|
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
|
{[
|
|
{ value: 'property_owner', label: 'Home Buyer/Seller', icon: 'mdi:home-account' },
|
|
{ value: 'attorney', label: 'Attorney', icon: 'mdi:gavel' },
|
|
{ value: 'vendor', label: 'Vendor', icon: 'mdi:briefcase' },
|
|
].map((type) => (
|
|
<Paper
|
|
key={type.value}
|
|
variant="outlined"
|
|
onClick={() => setFieldValue('ownerType', type.value)}
|
|
sx={{
|
|
p: 2,
|
|
flex: 1,
|
|
cursor: 'pointer',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
alignItems: 'center',
|
|
gap: 1,
|
|
borderColor: values.ownerType === type.value ? 'primary.main' : 'divider',
|
|
bgcolor: values.ownerType === type.value ? 'action.selected' : 'background.paper',
|
|
transition: 'all 0.2s',
|
|
'&:hover': {
|
|
borderColor: 'primary.main',
|
|
bgcolor: 'action.hover',
|
|
},
|
|
}}
|
|
>
|
|
<IconifyIcon icon={type.icon} width={24} height={24} color={values.ownerType === type.value ? 'primary.main' : 'text.secondary'} />
|
|
<Typography
|
|
variant="body2"
|
|
fontWeight={600}
|
|
color={values.ownerType === type.value ? 'primary.main' : 'text.primary'}
|
|
>
|
|
{type.label}
|
|
</Typography>
|
|
</Paper>
|
|
))}
|
|
</Stack>
|
|
</FormControl>
|
|
|
|
{/* Vendor Category Selection */}
|
|
{values.ownerType === 'vendor' && (
|
|
<FormControl variant="standard" fullWidth sx={{ mb: 2 }}>
|
|
<Autocomplete
|
|
options={CATEGORY_NAMES}
|
|
onChange={(_, value) => setFieldValue('vendorType', value)}
|
|
renderInput={(params) => (
|
|
<TextField
|
|
{...params}
|
|
label="Vendor Category"
|
|
variant="filled"
|
|
placeholder="Select your service type"
|
|
fullWidth
|
|
/>
|
|
)}
|
|
/>
|
|
</FormControl>
|
|
)}
|
|
<FormControl variant="standard" fullWidth>
|
|
<InputLabel shrink htmlFor="password">
|
|
Password
|
|
</InputLabel>
|
|
<TextField
|
|
variant="filled"
|
|
placeholder="********"
|
|
onChange={(event) => {
|
|
setFieldValue('password', event.target.value);
|
|
setPassword(event.target.value);
|
|
}}
|
|
type={showPassword ? 'text' : 'password'}
|
|
id="password"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<IconButton
|
|
aria-label="toggle password visibility"
|
|
onClick={handleClickShowPassword}
|
|
edge="end"
|
|
sx={{
|
|
color: 'text.secondary',
|
|
}}
|
|
>
|
|
{showPassword ? (
|
|
<IconifyIcon icon="ic:baseline-key-off" />
|
|
) : (
|
|
<IconifyIcon icon="ic:baseline-key" />
|
|
)}
|
|
</IconButton>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
{/*<PasswordStrengthChecker password={password} />*/}
|
|
</FormControl>
|
|
<FormControl variant="standard" fullWidth>
|
|
<InputLabel shrink htmlFor="password">
|
|
Confirm Password
|
|
</InputLabel>
|
|
<TextField
|
|
variant="filled"
|
|
placeholder="********"
|
|
onChange={(event) => setFieldValue('password2', event.target.value)}
|
|
type={showPassword2 ? 'text' : 'password'}
|
|
id="password"
|
|
InputProps={{
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<IconButton
|
|
aria-label="toggle password visibility"
|
|
onClick={handleClickShowPassword2}
|
|
edge="end"
|
|
sx={{
|
|
color: 'text.secondary',
|
|
}}
|
|
>
|
|
{showPassword ? (
|
|
<IconifyIcon icon="ic:baseline-key-off" />
|
|
) : (
|
|
<IconifyIcon icon="ic:baseline-key" />
|
|
)}
|
|
</IconButton>
|
|
</InputAdornment>
|
|
),
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<Button variant="contained" type={'submit'} fullWidth>
|
|
Sign up
|
|
</Button>
|
|
</Form>
|
|
)}
|
|
</Formik>
|
|
|
|
<Typography variant="body2" color="text.secondary">
|
|
Already have an account ?{' '}
|
|
<Link
|
|
href="/authentication/login"
|
|
underline="hover"
|
|
fontSize={(theme) => theme.typography.body1.fontSize}
|
|
>
|
|
Log in
|
|
</Link>
|
|
</Typography>
|
|
</Stack>
|
|
</Stack>
|
|
<Suspense
|
|
fallback={
|
|
<Skeleton variant="rectangular" height={1} width={1} sx={{ bgcolor: 'primary.main' }} />
|
|
}
|
|
>
|
|
<Image
|
|
alt="Signup banner"
|
|
src={signupBanner}
|
|
sx={{
|
|
width: 0.5,
|
|
display: { xs: 'none', md: 'block' },
|
|
}}
|
|
/>
|
|
</Suspense>
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export default SignUp;
|