Skip to content
This repository was archived by the owner on Jan 12, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,14 @@ export class AuthService {
Password: password,
});

const response = await this.providerClient.send(signUpCommand);
return response.UserConfirmed;
try {
const response = await this.providerClient.send(signUpCommand);
return response.UserConfirmed;
} catch (e) {
console.log(e)
throw new Error(e.message);
}

}

async signin({ email, password }: SignInDto): Promise<SignInResponseDto> {
Expand Down
3 changes: 3 additions & 0 deletions apps/backend/src/dtos/sign-up.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { IsEmail, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class SignUpDto {
@ApiProperty({description: "The user's email address", example: "test@gmail.com"})
@IsEmail()
email: string;

@ApiProperty({description: "The user's password", example: "password123"})
@IsString()
password: string;
}
172 changes: 107 additions & 65 deletions apps/frontend/src/pages/registerPage/formPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,31 @@ import {
import { useState } from 'react';
import InfoIcon from '@mui/icons-material/Info';
import { Visibility, VisibilityOff } from '@mui/icons-material';
import { Snackbar, Alert } from '@mui/material';
import { useFormik } from 'formik';
import * as Yup from 'yup';

import { UserModel, NewUserInput } from '../../types/UserModel';
import { SignUpDto } from '../../types/SignUpModel';
import axios from 'axios';




interface FormPageProps {
setIsSubmitted: (value: boolean) => void;
}

const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
const [snackbar, setSnackbar] = useState<{
open: boolean;
message: string;
severity: 'error' | 'info' | 'success' | 'warning'; // 👈 enforce correct type
}>({
open: false,
message: '',
severity: 'info',
});
const [showPassword, setShowPassword] = useState(false);
const [showRePassword, setShowRePassword] = useState(false);

Expand All @@ -30,7 +47,7 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
setShowRePassword((prev) => !prev);
};

// Validation schema using Yup
// Define the validation schema using Yup
const validationSchema = Yup.object({
userId: Yup.string().required('User ID is required'),
email: Yup.string()
Expand All @@ -50,15 +67,43 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
password: '',
rePassword: '',
},
validationSchema: validationSchema,
onSubmit: (values) => {
// Handle form submission
console.log('Form submitted:', values);
setIsSubmitted(true);
validationSchema,
onSubmit: async (values) => {
try {

const matchingResponse = await axios.get<UserModel>(`${import.meta.env.VITE_API_BASE_URL}/users/${values.userId}`);
const user: UserModel = matchingResponse.data;
if (user.email !== values.email) {
console.log('User ID and email do not match');
setSnackbar({ open: true, message: 'User ID and email do not match', severity: 'error' });
return;
}

if (values.password !== values.rePassword) {
setSnackbar({ open: true, message: 'Passwords do not match. Please try again.', severity: 'error' });
return;
}

const inputUser: SignUpDto = { email: values.email, password: values.password };

await axios.post<NewUserInput>(`${import.meta.env.VITE_API_BASE_URL}/auth/signup`, inputUser);
setIsSubmitted(true);
} catch (error) {
if (axios.isAxiosError(error)) {
const msg = error.response?.data.message || '';
if (msg.includes("Password did not conform with policy: ")) {
setSnackbar({ open: true, message: msg.split(': ')[1], severity: 'error' });
} else if (msg.includes("User already exists")) {
setSnackbar({ open: true, message: 'User already exists', severity: 'error' });
}
} else {
setSnackbar({ open: true, message: 'An unexpected error occurred', severity: 'error' });
}
}
},
});

// Check if all fields are valid
// Check if the form is valid and all fields have been touched
const isFormValid = formik.isValid && Object.keys(formik.touched).length === 4;

return (
Expand All @@ -85,11 +130,7 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
Volunteer Registration
</h1>
<form onSubmit={formik.handleSubmit}>
<Box
display="flex"
alignItems="center"
marginTop={5}
>
<Box display="flex" alignItems="center" marginTop={5}>
<Text
fontFamily="Montserrat"
fontSize="20px"
Expand All @@ -114,32 +155,30 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
</Tooltip>
</Box>
<TextField
color="success"
variant="outlined"
fullWidth
margin="none"
size="small"
name="userId"
value={formik.values.userId}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.userId && Boolean(formik.errors.userId)}
color="success"
variant="outlined"
fullWidth
margin="none"
size="small"
InputLabelProps={{
style: { fontFamily: 'Montserrat', fontWeight: 600 },
}}
sx={{
'& .MuiFilledInput-root': { fontFamily: 'Montserrat' },
}}
sx={{ '& .MuiFilledInput-root': { fontFamily: 'Montserrat' } }}
/>
{formik.touched.userId && formik.errors.userId && (
<FormHelperText
error
sx={{
<FormHelperText
error
sx={{
fontFamily: 'Montserrat',
fontSize: '14px',
fontWeight: '500',
marginLeft: '2px',
marginTop: '4px'
marginTop: '4px',
}}
>
{formik.errors.userId}
Expand All @@ -150,39 +189,37 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
fontFamily="Montserrat"
fontSize="20px"
fontWeight="600"
lineHeight="24x"
lineHeight="24px"
marginBottom={1}
marginTop={2}
>
Email
</Text>
<TextField
color="success"
variant="outlined"
fullWidth
margin="none"
size="small"
name="email"
value={formik.values.email}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.email && Boolean(formik.errors.email)}
color="success"
variant="outlined"
fullWidth
margin="none"
size="small"
InputLabelProps={{
style: { fontFamily: 'Montserrat', fontWeight: 600 },
}}
sx={{
'& .MuiFilledInput-root': { fontFamily: 'Montserrat' },
}}
sx={{ '& .MuiFilledInput-root': { fontFamily: 'Montserrat' } }}
/>
{formik.touched.email && formik.errors.email && (
<FormHelperText
error
sx={{
<FormHelperText
error
sx={{
fontFamily: 'Montserrat',
fontSize: '14px',
fontWeight: '500',
marginLeft: '2px',
marginTop: '4px'
marginTop: '4px',
}}
>
{formik.errors.email}
Expand All @@ -193,49 +230,47 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
fontFamily="Montserrat"
fontSize="20px"
fontWeight="600"
lineHeight="24x"
lineHeight="24px"
marginBottom={1}
marginTop={2}
>
Password
</Text>
<TextField
name="password"
type={showPassword ? 'text' : 'password'}
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.password && Boolean(formik.errors.password)}
color="success"
variant="outlined"
fullWidth
margin="none"
size="small"
name="password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.password && Boolean(formik.errors.password)}
InputLabelProps={{
style: { fontFamily: 'Montserrat', fontWeight: 600 },
}}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleTogglePassword} edge="end">
<IconButton type="button" onClick={handleTogglePassword} edge="end">
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
sx={{
'& .MuiFilledInput-root': { fontFamily: 'Montserrat' },
}}
sx={{ '& .MuiFilledInput-root': { fontFamily: 'Montserrat' } }}
/>
{formik.touched.password && formik.errors.password && (
<FormHelperText
error
sx={{
<FormHelperText
error
sx={{
fontFamily: 'Montserrat',
fontSize: '14px',
fontWeight: '500',
marginLeft: '2px',
marginTop: '4px'
marginTop: '4px',
}}
>
{formik.errors.password}
Expand All @@ -246,49 +281,47 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
fontFamily="Montserrat"
fontSize="20px"
fontWeight="600"
lineHeight="24x"
lineHeight="24px"
marginBottom={1}
marginTop={2}
>
Re-enter Password
</Text>
<TextField
name="rePassword"
type={showRePassword ? 'text' : 'password'}
value={formik.values.rePassword}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.rePassword && Boolean(formik.errors.rePassword)}
color="success"
variant="outlined"
fullWidth
margin="none"
size="small"
name="rePassword"
value={formik.values.rePassword}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.rePassword && Boolean(formik.errors.rePassword)}
InputLabelProps={{
style: { fontFamily: 'Montserrat', fontWeight: 600 },
}}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleToggleRePassword} edge="end">
<IconButton type="button" onClick={handleToggleRePassword} edge="end">
{showRePassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
sx={{
'& .MuiFilledInput-root': { fontFamily: 'Montserrat' },
}}
sx={{ '& .MuiFilledInput-root': { fontFamily: 'Montserrat' } }}
/>
{formik.touched.rePassword && formik.errors.rePassword && (
<FormHelperText
error
sx={{
<FormHelperText
error
sx={{
fontFamily: 'Montserrat',
fontSize: '14px',
fontWeight: '500',
marginLeft: '2px',
marginTop: '4px'
marginTop: '4px',
}}
>
{formik.errors.rePassword}
Expand Down Expand Up @@ -319,6 +352,15 @@ const FormPage: React.FC<FormPageProps> = ({ setIsSubmitted }) => {
Create Account
</Button>
</form>
<Snackbar
open={snackbar.open}
autoHideDuration={5000}
onClose={() => setSnackbar({ ...snackbar, open: false })}
>
<Alert severity={snackbar.severity} onClose={() => setSnackbar({ ...snackbar, open: false })}>
{snackbar.message}
</Alert>
</Snackbar>
</>
);
};
Expand Down
4 changes: 4 additions & 0 deletions apps/frontend/src/types/SignUpModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type SignUpDto = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be a good idea to create a new folder at the apps directory which will house anything that both the frontend and backend need to share like DTOs. This is so that we can avoid repeated code.

Again, definitely don't need to do this in this PR. Just leaving this so we know for the future!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll probably wait on this one just so I can pull before I rewrite the imports on all the files!

email: string;
password: string;
}
Loading
Loading