Files
dta_webapp/ditch-the-agent/src/pages/authentication/SignUp.tsx
Ryan Westfall 89493853e9 closes #6
closes #7

Updates for site tracking and support agent portal
2025-12-10 13:02:45 -06:00

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;