diff --git a/ui/package-lock.json b/ui/package-lock.json index e7222cf..983eb78 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,9 +17,11 @@ "@mui/material-nextjs": "^7.1.1", "@mui/system": "^7.1.1", "axios": "^1.10.0", + "formik": "^2.4.6", "next": "15.3.4", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "yup": "^1.6.1" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -1918,6 +1920,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3210,6 +3222,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4141,6 +4162,31 @@ "node": ">= 6" } }, + "node_modules/formik": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", + "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -5319,6 +5365,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5936,6 +5994,12 @@ "react-is": "^16.13.1" } }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -5994,6 +6058,12 @@ "react": "^19.1.0" } }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==", + "license": "MIT" + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -6693,6 +6763,18 @@ "node": ">=18" } }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "license": "MIT" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", @@ -6751,6 +6833,12 @@ "node": ">=8.0" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -6796,6 +6884,18 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -7114,6 +7214,18 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yup": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", + "license": "MIT", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } } } } diff --git a/ui/package.json b/ui/package.json index bc1a8a5..3cfb0a1 100644 --- a/ui/package.json +++ b/ui/package.json @@ -18,9 +18,11 @@ "@mui/material-nextjs": "^7.1.1", "@mui/system": "^7.1.1", "axios": "^1.10.0", + "formik": "^2.4.6", "next": "15.3.4", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "yup": "^1.6.1" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/ui/src/app/form/page.tsx b/ui/src/app/form/page.tsx index e69de29..312df13 100644 --- a/ui/src/app/form/page.tsx +++ b/ui/src/app/form/page.tsx @@ -0,0 +1,392 @@ +'use client'; + +import React from 'react'; +import { + Box, + Card, + CardContent, + TextField, + Button, + Typography, + Grid, + MenuItem, + FormControl, + InputLabel, + Select, + FormHelperText, + Chip, + Avatar, + Container, + SelectChangeEvent, +} from '@mui/material'; +import { useFormik, FormikHelpers } from 'formik'; +import * as yup from 'yup'; +import PersonIcon from '@mui/icons-material/Person'; +import EmailIcon from '@mui/icons-material/Email'; +import PhoneIcon from '@mui/icons-material/Phone'; +import WorkIcon from '@mui/icons-material/Work'; +import SendIcon from '@mui/icons-material/Send'; + +interface FormValues { + firstName: string; + lastName: string; + email: string; + phone: string; + age: string | number; + department: string; + skills: string[]; + bio: string; +} + +const validationSchema = yup.object({ + firstName: yup + .string() + .min(2, 'First name should be at least 2 characters') + .required('First name is required'), + lastName: yup + .string() + .min(2, 'Last name should be at least 2 characters') + .required('Last name is required'), + email: yup + .string() + .email('Enter a valid email') + .required('Email is required'), + phone: yup + .string() + .matches(/^[\+]?[1-9][\d]{0,15}$/, 'Enter a valid phone number') + .required('Phone number is required'), + age: yup + .number() + .min(18, 'Must be at least 18 years old') + .max(120, 'Must be less than 120 years old') + .required('Age is required'), + department: yup + .string() + .required('Department is required'), + skills: yup + .array() + .of(yup.string()) + .min(1, 'Select at least one skill') + .required('Skills are required'), + bio: yup + .string() + .min(10, 'Bio should be at least 10 characters') + .max(500, 'Bio should not exceed 500 characters') + .required('Bio is required'), +}); + + +const departments: string[] = [ + 'Engineering', + 'Marketing', + 'Sales', + 'HR', + 'Finance', + 'Operations', + 'Design', +]; + +const skillOptions: string[] = [ + 'JavaScript', + 'React', + 'Node.js', + 'Python', + 'UI/UX Design', + 'Project Management', + 'Data Analysis', + 'Marketing', + 'Sales', + 'Communication', +]; + +const FormPage: React.FC = () => { + const formik = useFormik({ + initialValues: { + firstName: '', + lastName: '', + email: '', + phone: '', + age: '', + department: '', + skills: [], + bio: '', + }, + validationSchema: validationSchema, + onSubmit: (values: FormValues, { setSubmitting, resetForm }: FormikHelpers) => { + setTimeout(() => { + console.log('Form submitted:', values); + alert('Form submitted successfully!'); + setSubmitting(false); + resetForm(); + }, 1000); + }, + }); + + const handleSkillChange = (event: SelectChangeEvent): void => { + const value = event.target.value; + formik.setFieldValue('skills', typeof value === 'string' ? value.split(',') : value); + }; + + return ( + + theme.palette.mode === 'dark' + ? 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%)' + : 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + py: 4, + }} + > + + + theme.palette.mode === 'dark' + ? 'rgba(26, 26, 46, 0.95)' + : 'rgba(255, 255, 255, 0.95)', + backdropFilter: 'blur(10px)', + border: (theme) => + theme.palette.mode === 'dark' + ? '1px solid rgba(99, 102, 241, 0.2)' + : '1px solid rgba(255, 255, 255, 0.3)', + }} + > + + + + + + + User Registration + + + Please fill out all required fields + + + +
+ + + Personal Information + + + + + + + + + + + + , + }} + /> + + , + }} + /> + + + + + Professional Information + + + + Department + + {formik.touched.department && formik.errors.department && ( + {formik.errors.department} + )} + + + + Skills + + {formik.touched.skills && formik.errors.skills && ( + {formik.errors.skills} + )} + + + + + + +
+
+
+
+
+ ); +}; + +export default FormPage; \ No newline at end of file