2024-06-30 18:55:39 -04:00
|
|
|
import {
|
|
|
|
Box,
|
|
|
|
Button,
|
|
|
|
Container,
|
|
|
|
Divider,
|
|
|
|
FormControl,
|
|
|
|
FormHelperText,
|
|
|
|
Input,
|
|
|
|
Sheet,
|
|
|
|
Typography,
|
|
|
|
} from '@mui/joy'
|
|
|
|
import React from 'react'
|
|
|
|
import { useNavigate } from 'react-router-dom'
|
|
|
|
import Logo from '../../Logo'
|
|
|
|
import { login, signUp } from '../../utils/Fetcher'
|
|
|
|
|
|
|
|
const SignupView = () => {
|
|
|
|
const [username, setUsername] = React.useState('')
|
|
|
|
const [password, setPassword] = React.useState('')
|
|
|
|
const Navigate = useNavigate()
|
|
|
|
const [displayName, setDisplayName] = React.useState('')
|
|
|
|
const [email, setEmail] = React.useState('')
|
|
|
|
const [usernameError, setUsernameError] = React.useState('')
|
|
|
|
const [passwordError, setPasswordError] = React.useState('')
|
|
|
|
const [emailError, setEmailError] = React.useState('')
|
|
|
|
const [displayNameError, setDisplayNameError] = React.useState('')
|
|
|
|
const [error, setError] = React.useState(null)
|
|
|
|
const handleLogin = (username, password) => {
|
|
|
|
login(username, password).then(response => {
|
|
|
|
if (response.status === 200) {
|
|
|
|
response.json().then(res => {
|
|
|
|
localStorage.setItem('ca_token', res.token)
|
|
|
|
localStorage.setItem('ca_expiration', res.expire)
|
|
|
|
setTimeout(() => {
|
|
|
|
// TODO: not sure if there is a race condition here
|
|
|
|
// but on first sign up it renavigates to login.
|
|
|
|
Navigate('/my/chores')
|
|
|
|
}, 500)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
console.log('Login failed', response)
|
|
|
|
// Navigate('/login')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
const handleSignUpValidation = () => {
|
|
|
|
// Reset errors before validation
|
|
|
|
setUsernameError(null)
|
|
|
|
setPasswordError(null)
|
|
|
|
setDisplayNameError(null)
|
|
|
|
setEmailError(null)
|
|
|
|
|
|
|
|
let isValid = true
|
|
|
|
|
|
|
|
if (!username.trim()) {
|
|
|
|
setUsernameError('Username is required')
|
|
|
|
isValid = false
|
|
|
|
}
|
|
|
|
if (username.length < 4) {
|
|
|
|
setUsernameError('Username must be at least 4 characters')
|
|
|
|
isValid = false
|
|
|
|
}
|
2024-07-05 17:28:47 -04:00
|
|
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
|
|
setEmailError('Invalid email address')
|
|
|
|
isValid = false
|
|
|
|
}
|
2024-06-30 18:55:39 -04:00
|
|
|
|
|
|
|
if (password.length < 8) {
|
|
|
|
setPasswordError('Password must be at least 8 characters')
|
|
|
|
isValid = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!displayName.trim()) {
|
|
|
|
setDisplayNameError('Display name is required')
|
|
|
|
isValid = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// display name should only contain letters and spaces and numbers:
|
|
|
|
if (!/^[a-zA-Z0-9 ]+$/.test(displayName)) {
|
|
|
|
setDisplayNameError('Display name can only contain letters and numbers')
|
|
|
|
isValid = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// username should only contain letters , numbers , dot and dash:
|
|
|
|
if (!/^[a-zA-Z0-9.-]+$/.test(username)) {
|
|
|
|
setUsernameError(
|
|
|
|
'Username can only contain letters, numbers, dot and dash',
|
|
|
|
)
|
|
|
|
isValid = false
|
|
|
|
}
|
|
|
|
|
|
|
|
return isValid
|
|
|
|
}
|
|
|
|
const handleSubmit = async e => {
|
|
|
|
e.preventDefault()
|
|
|
|
if (!handleSignUpValidation()) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
signUp(username, password, displayName, email).then(response => {
|
|
|
|
if (response.status === 201) {
|
|
|
|
handleLogin(username, password)
|
|
|
|
} else {
|
|
|
|
console.log('Signup failed')
|
|
|
|
setError('Signup failed')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Container component='main' maxWidth='xs'>
|
|
|
|
<Box
|
|
|
|
sx={{
|
|
|
|
display: 'flex',
|
|
|
|
flexDirection: 'column',
|
|
|
|
alignItems: 'center',
|
|
|
|
marginTop: 4,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Sheet
|
|
|
|
component='form'
|
|
|
|
sx={{
|
|
|
|
mt: 1,
|
|
|
|
width: '100%',
|
|
|
|
display: 'flex',
|
|
|
|
flexDirection: 'column',
|
|
|
|
// alignItems: 'center',
|
|
|
|
padding: 2,
|
|
|
|
borderRadius: '8px',
|
|
|
|
boxShadow: 'md',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Box
|
|
|
|
sx={{
|
|
|
|
display: 'flex',
|
|
|
|
alignItems: 'center',
|
|
|
|
flexDirection: 'column',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Logo />
|
|
|
|
<Typography level='h2'>
|
|
|
|
Done
|
|
|
|
<span
|
|
|
|
style={{
|
|
|
|
color: '#06b6d4',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
tick
|
|
|
|
</span>
|
|
|
|
</Typography>
|
|
|
|
<Typography level='body2'>
|
|
|
|
Create an account to get started!
|
|
|
|
</Typography>
|
|
|
|
</Box>
|
|
|
|
<Typography level='body2' alignSelf={'start'} mt={4}>
|
|
|
|
Username
|
|
|
|
</Typography>
|
|
|
|
<Input
|
|
|
|
margin='normal'
|
|
|
|
required
|
|
|
|
fullWidth
|
2024-07-05 17:28:47 -04:00
|
|
|
id='username'
|
|
|
|
label='Username'
|
|
|
|
name='username'
|
|
|
|
autoComplete='username'
|
2024-06-30 18:55:39 -04:00
|
|
|
autoFocus
|
|
|
|
value={username}
|
|
|
|
onChange={e => {
|
|
|
|
setUsernameError(null)
|
|
|
|
setUsername(e.target.value.trim())
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<FormControl error={usernameError}>
|
|
|
|
<FormHelperText c>{usernameError}</FormHelperText>
|
|
|
|
</FormControl>
|
|
|
|
{/* Error message display */}
|
2024-07-05 17:28:47 -04:00
|
|
|
<Typography level='body2' alignSelf={'start'}>
|
|
|
|
Email
|
|
|
|
</Typography>
|
|
|
|
<Input
|
|
|
|
margin='normal'
|
|
|
|
required
|
|
|
|
fullWidth
|
|
|
|
id='email'
|
|
|
|
label='email'
|
|
|
|
name='email'
|
|
|
|
autoComplete='email'
|
|
|
|
value={email}
|
|
|
|
onChange={e => {
|
|
|
|
setEmailError(null)
|
|
|
|
setEmail(e.target.value.trim())
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<FormControl error={emailError}>
|
|
|
|
<FormHelperText c>{emailError}</FormHelperText>
|
|
|
|
</FormControl>
|
2024-06-30 18:55:39 -04:00
|
|
|
<Typography level='body2' alignSelf={'start'}>
|
|
|
|
Password:
|
|
|
|
</Typography>
|
|
|
|
<Input
|
|
|
|
margin='normal'
|
|
|
|
required
|
|
|
|
fullWidth
|
|
|
|
name='password'
|
|
|
|
label='Password'
|
|
|
|
type='password'
|
|
|
|
id='password'
|
|
|
|
value={password}
|
|
|
|
onChange={e => {
|
|
|
|
setPasswordError(null)
|
|
|
|
setPassword(e.target.value)
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<FormControl error={passwordError}>
|
|
|
|
<FormHelperText>{passwordError}</FormHelperText>
|
|
|
|
</FormControl>
|
|
|
|
<Typography level='body2' alignSelf={'start'}>
|
|
|
|
Display Name:
|
|
|
|
</Typography>
|
|
|
|
<Input
|
|
|
|
margin='normal'
|
|
|
|
required
|
|
|
|
fullWidth
|
|
|
|
name='displayName'
|
|
|
|
label='Display Name'
|
|
|
|
id='displayName'
|
|
|
|
value={displayName}
|
|
|
|
onChange={e => {
|
|
|
|
setDisplayNameError(null)
|
|
|
|
setDisplayName(e.target.value)
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
<FormControl error={displayNameError}>
|
|
|
|
<FormHelperText>{displayNameError}</FormHelperText>
|
|
|
|
</FormControl>
|
|
|
|
<Button
|
|
|
|
// type='submit'
|
|
|
|
size='lg'
|
|
|
|
fullWidth
|
|
|
|
variant='solid'
|
|
|
|
sx={{ mt: 3, mb: 1 }}
|
|
|
|
onClick={handleSubmit}
|
|
|
|
>
|
|
|
|
Sign Up
|
|
|
|
</Button>
|
|
|
|
<Divider> or </Divider>
|
|
|
|
<Button
|
|
|
|
size='lg'
|
|
|
|
onClick={() => {
|
|
|
|
Navigate('/login')
|
|
|
|
}}
|
|
|
|
fullWidth
|
|
|
|
variant='soft'
|
|
|
|
// sx={{ mt: 3, mb: 2 }}
|
|
|
|
>
|
|
|
|
Login
|
|
|
|
</Button>
|
|
|
|
</Sheet>
|
|
|
|
</Box>
|
|
|
|
</Container>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default SignupView
|