diff --git a/apps/backend/src/auth/auth.service.ts b/apps/backend/src/auth/auth.service.ts index ccc4b6d4..4994cac5 100644 --- a/apps/backend/src/auth/auth.service.ts +++ b/apps/backend/src/auth/auth.service.ts @@ -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 { diff --git a/apps/backend/src/dtos/sign-up.dto.ts b/apps/backend/src/dtos/sign-up.dto.ts index a900249a..03d99463 100644 --- a/apps/backend/src/dtos/sign-up.dto.ts +++ b/apps/backend/src/dtos/sign-up.dto.ts @@ -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; } diff --git a/apps/frontend/src/pages/registerPage/formPage.tsx b/apps/frontend/src/pages/registerPage/formPage.tsx index 0f4f4b2d..7be37eb3 100644 --- a/apps/frontend/src/pages/registerPage/formPage.tsx +++ b/apps/frontend/src/pages/registerPage/formPage.tsx @@ -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 = ({ 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); @@ -30,7 +47,7 @@ const FormPage: React.FC = ({ 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() @@ -50,15 +67,43 @@ const FormPage: React.FC = ({ 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(`${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(`${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 ( @@ -85,11 +130,7 @@ const FormPage: React.FC = ({ setIsSubmitted }) => { Volunteer Registration
- + = ({ setIsSubmitted }) => { {formik.touched.userId && formik.errors.userId && ( - {formik.errors.userId} @@ -150,39 +189,37 @@ const FormPage: React.FC = ({ setIsSubmitted }) => { fontFamily="Montserrat" fontSize="20px" fontWeight="600" - lineHeight="24x" + lineHeight="24px" marginBottom={1} marginTop={2} > Email {formik.touched.email && formik.errors.email && ( - {formik.errors.email} @@ -193,49 +230,47 @@ const FormPage: React.FC = ({ setIsSubmitted }) => { fontFamily="Montserrat" fontSize="20px" fontWeight="600" - lineHeight="24x" + lineHeight="24px" marginBottom={1} marginTop={2} > Password - + {showPassword ? : } ), }} - sx={{ - '& .MuiFilledInput-root': { fontFamily: 'Montserrat' }, - }} + sx={{ '& .MuiFilledInput-root': { fontFamily: 'Montserrat' } }} /> {formik.touched.password && formik.errors.password && ( - {formik.errors.password} @@ -246,49 +281,47 @@ const FormPage: React.FC = ({ setIsSubmitted }) => { fontFamily="Montserrat" fontSize="20px" fontWeight="600" - lineHeight="24x" + lineHeight="24px" marginBottom={1} marginTop={2} > Re-enter Password - + {showRePassword ? : } ), }} - sx={{ - '& .MuiFilledInput-root': { fontFamily: 'Montserrat' }, - }} + sx={{ '& .MuiFilledInput-root': { fontFamily: 'Montserrat' } }} /> {formik.touched.rePassword && formik.errors.rePassword && ( - {formik.errors.rePassword} @@ -319,6 +352,15 @@ const FormPage: React.FC = ({ setIsSubmitted }) => { Create Account + setSnackbar({ ...snackbar, open: false })} + > + setSnackbar({ ...snackbar, open: false })}> + {snackbar.message} + + ); }; diff --git a/apps/frontend/src/types/SignUpModel.ts b/apps/frontend/src/types/SignUpModel.ts new file mode 100644 index 00000000..161ed54a --- /dev/null +++ b/apps/frontend/src/types/SignUpModel.ts @@ -0,0 +1,4 @@ +export type SignUpDto = { + email: string; + password: string; +} diff --git a/apps/frontend/src/types/UserModel.ts b/apps/frontend/src/types/UserModel.ts new file mode 100644 index 00000000..a018fae7 --- /dev/null +++ b/apps/frontend/src/types/UserModel.ts @@ -0,0 +1,33 @@ +export type UserModel = { + userId: number, + firstName: string, + lastName: string, + phoneNumber: number, + email: string, + siteIds: number[], + zipCode: number, + birthDate: Date, + role: Role, + status: UserStatus +}; + + +export enum Role { + VOLUNTEER = "Volunteer", + ADMIN = "Admin", +}; + +export enum UserStatus { + APPROVED = "Approved", + PENDING = "Pending", + DENIED = "Denied", +}; + +export type NewUserInput = { + firstName: string, + lastName: string, + phoneNumber: string, + email: string, + zipCode: string, + birthDate: string, +}; \ No newline at end of file