Skip to content

Commit

Permalink
Merge pull request #66 from Code-4-Community/adopt-form-validation
Browse files Browse the repository at this point in the history
Adopt form validation
  • Loading branch information
denniwang authored Feb 6, 2025
2 parents fd34c81 + 79e87d9 commit 5b3fe11
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 118 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ To start the development server run `nx serve frontend`. Open your browser and n
To run just the frontend (port 4200):

```
nx serve frontend
nx serve green-infrastructure-frontend
```

To run just the backend (port 3000):
Expand Down
246 changes: 130 additions & 116 deletions apps/frontend/src/components/volunteer/signup/SignUpPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ import {
VStack,
Button,
IconButton,
FormLabel,
FormControl,
FormErrorMessage,
SimpleGrid,
Center,
} from '@chakra-ui/react';
import { Checkbox } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import CircleIcon from '@mui/icons-material/Circle';
import CircleOutlinedIcon from '@mui/icons-material/CircleOutlined';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';

interface InputField {
label: string;
Expand All @@ -30,40 +37,14 @@ interface InputFieldGroup {
height: string;
width: string;
}
interface FormField {
label: string;
type: string;
}

const personalInfoCheckboxesMap: CheckboxField[] = [
{
label: 'Signing up as a group representative?',
},
];

const personalInfoInputFieldsMap: InputFieldGroup[] = [
{
fields: [{ label: 'First Name', width: '250px'}, { label: 'Last Name', width: '350px' }],
type: 'double',
height: '40px',
width: '810px',
},
{
fields: [{ label: 'Email Address' }],
type: 'single',
height: '40px',
width: '380px',
},
{
fields: [{ label: 'Phone Number' }],
type: 'single',
height: '40px',
width: '380px',
},
{
fields: [{ label: 'Birth Year' }],
type: 'single',
height: '40px',
width: '100px',
},
];

interface FormFields {
[key: string]: FormField;
}
const termsAndConditionsCheckboxesMap: CheckboxField[] = [
{
label: 'I have reviewed the General Safety Guidelines',
Expand All @@ -75,82 +56,101 @@ const termsAndConditionsCheckboxesMap: CheckboxField[] = [
label: 'I have read and agree to the Release of Liability',
},
];
const validationSchema = Yup.object({
firstname: Yup.string().required('First name is required'),
lastname: Yup.string().required('Last name is required'),
email: Yup.string()
.email('Invalid email address')
.required('Email is required'),
phone: Yup.string()
.matches(/^[0-9]{10}$/, 'Phone number must be 10 digits')
.required('Phone number is required'),
birthyear: Yup.number()
.min(1900, 'Invalid birth year')
.max(new Date().getFullYear(), 'Invalid birth year')
.required('Birth year is required'),
groupRepresentative: Yup.boolean(),
});
const formFields: FormFields = {
firstname: { label: 'First Name', type: 'text' },
lastname: { label: 'Last Name', type: 'text' },
email: { label: 'Email', type: 'email' },
phone: { label: 'Phone Number', type: 'text' },
birthyear: { label: 'Birth Year', type: 'number' },
groupRepresentative: {
label: 'Group representative?',
type: 'checkbox',
},
};

function PersonalInfo() {
return (
<Box
className="personal-info-box"
>
<VStack spacing={0} marginBottom={'20px'} borderBottom="2px solid #000000" paddingBottom="20px">
{personalInfoCheckboxesMap.map((field, i) => (
<HStack key={i} width="100%" height="100%" marginBottom={'20px'} alignItems="flex-start">
<Text fontSize="18px" fontWeight={600} fontFamily="Montserrat">
{field.label}
</Text>
<Checkbox
sx={{
color: '#808080', // Grey color for the checkbox when not checked
'&.Mui-checked': {
color: '#808080', // Grey color for the checkbox when checked
},
'& .MuiSvgIcon-root': {
fontSize: 23,
},
padding: '2px',
marginLeft: '20px',
}}
/>
</HStack>
))}
{personalInfoInputFieldsMap.map((group, i) => (
<VStack key={i} width="100%" spacing={0} align="flex-start">
{group.type === 'double' ? (
<HStack width="100%" justifyContent="left" spacing="20%">
{group.fields.map((field, j) => (
<VStack key={j} width={field.width}>
<Text
className="label"
alignSelf="flex-start"
fontSize="18px"
fontWeight={600}
marginBottom={-10}
fontFamily="Montserrat"
>
{field.label}
</Text>
<Input
variant="filled"
height={group.height}
placeholder={field.placeholder || 'example'}
width="100%"
<Box className="personal-info-box">
<Formik
initialValues={{
firstname: '',
lastname: '',
email: '',
phone: '',
birthyear: '',
}}
validationSchema={validationSchema}
onSubmit={(values) => {
console.log(values);
}}
>
{({
errors,
touched,
isValid,
dirty,
}: {
errors: { [key: string]: string };
touched: { [key: string]: boolean };
isValid: boolean;
dirty: boolean;
}) => (
<Form>
<Center flexDir={'column'}>
<SimpleGrid
columns={2}
spacing={4}
w={'80%'}
alignContent={'center'}
justifyContent={'center'}
>
{Object.keys(formFields).map((field) => (
<FormControl
key={field}
isInvalid={!!errors[field] && touched[field]}
>
<FormLabel htmlFor={field}>
{formFields[field].label}
</FormLabel>
<Field
as={Input}
id={field}
name={field}
type={formFields[field].type}
/>
</VStack>
<FormErrorMessage color='red'>
{errors[field]}
</FormErrorMessage>
</FormControl>
))}
</HStack>
) : (
<VStack width="100%" align="flex-start">
<Text
className="label"
fontSize="18px"
fontWeight={600}
fontFamily="Montserrat"
alignSelf="flex-start"
marginBottom={-10}
marginTop="30px"
>
{group.fields[0].label}
</Text>
<Input
variant="filled"
height={group.height}
placeholder={group.fields[0].placeholder || 'example'}
width={group.width}
/>
</VStack>
)}
</VStack>
))}
</VStack>
</SimpleGrid>
<Button
mt={4}
colorScheme="teal"
type="submit"
isDisabled={!(isValid && dirty)}
>
Submit
</Button>
</Center>
</Form>
)}
</Formik>
<HStack
className="circle-progress"
display="flex"
Expand All @@ -163,19 +163,35 @@ function PersonalInfo() {
<CircleOutlinedIcon />
</HStack>
</Box>
)
);
}

function TermsAndConditions() {
return (
<Box
className="terms-and-conditions-box"
>
<VStack spacing={102} marginTop={'20px'} marginBottom={'20px'} borderBottom="2px solid #000000" paddingBottom="20px">
<Box className="terms-and-conditions-box">
<VStack
spacing={102}
marginTop={'20px'}
marginBottom={'20px'}
borderBottom="2px solid #000000"
paddingBottom="20px"
>
{termsAndConditionsCheckboxesMap.map((field, i) => (
<HStack key={i} width="100%" height="100%" marginTop={'20px'} alignItems="flex-start">
<Text textDecoration="underline" fontSize="18px" fontWeight={600} fontFamily="Montserrat" marginTop={'4px'} >
{field.label}
<HStack
key={i}
width="100%"
height="100%"
marginTop={'20px'}
alignItems="flex-start"
>
<Text
textDecoration="underline"
fontSize="18px"
fontWeight={600}
fontFamily="Montserrat"
marginTop={'4px'}
>
{field.label}
</Text>
<Checkbox
sx={{
Expand Down Expand Up @@ -220,7 +236,6 @@ export default function SignUpPage({ setShowSignUp }: Props) {
setShowSignUp(false);
};


const handleSubmit = () => {
// You can add form validation logic here if needed
setIsSubmitted(true);
Expand All @@ -238,7 +253,7 @@ export default function SignUpPage({ setShowSignUp }: Props) {
justifyContent="center"
bg="#D9D9D9"
width="80%"
height="140%"
height="100%"
zIndex={'200'}
>
<IconButton
Expand Down Expand Up @@ -305,8 +320,7 @@ export default function SignUpPage({ setShowSignUp }: Props) {
You can add additional content for the success page
</Box>
)} */}

</Box>
</Box>
);
}
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"class-validator": "^0.14.0",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"formik": "^2.4.6",
"framer-motion": "^10.12.4",
"fuse.js": "^6.6.2",
"google-maps": "^4.3.3",
Expand All @@ -70,6 +71,7 @@
"typed.js": "^2.0.15",
"typeorm": "^0.3.17",
"vite-plugin-svgr": "^2.4.0",
"yup": "^1.6.1",
"zod": "^3.21.4"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 5b3fe11

Please sign in to comment.