diff --git a/webui/package-lock.json b/webui/package-lock.json index 2270b2a..5956179 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -21,13 +21,15 @@ "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.3.1", "axios": "^1.9.0", + "formik": "^2.4.6", "i18next": "^25.2.1", "lucide-react": "^0.516.0", "next": "15.3.3", "next-i18next": "^15.4.2", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-i18next": "^15.5.3" + "react-i18next": "^15.5.3", + "yup": "^1.6.1" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -1954,6 +1956,15 @@ } } }, + "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/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2134,6 +2145,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", @@ -2634,6 +2670,18 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "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/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2991,6 +3039,12 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "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", @@ -3018,6 +3072,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-i18next": { "version": "15.5.3", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.3.tgz", @@ -3287,12 +3347,42 @@ "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/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/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "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/use-sync-external-store": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", @@ -3329,6 +3419,18 @@ "engines": { "node": ">= 6" } + }, + "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/webui/package.json b/webui/package.json index d361ade..6159669 100644 --- a/webui/package.json +++ b/webui/package.json @@ -22,13 +22,15 @@ "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.3.1", "axios": "^1.9.0", + "formik": "^2.4.6", "i18next": "^25.2.1", "lucide-react": "^0.516.0", "next": "15.3.3", "next-i18next": "^15.4.2", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-i18next": "^15.5.3" + "react-i18next": "^15.5.3", + "yup": "^1.6.1" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/webui/src/app/form/page.js b/webui/src/app/form/page.js new file mode 100644 index 0000000..b87a321 --- /dev/null +++ b/webui/src/app/form/page.js @@ -0,0 +1,376 @@ +'use client'; + +import React from 'react'; +import { + Box, + Card, + CardContent, + TextField, + Button, + Typography, + Grid, + MenuItem, + FormControl, + InputLabel, + Select, + FormHelperText, + Chip, + Avatar, + Container, +} from '@mui/material'; +import { useFormik } 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'; + +const validationSchema = yup.object({ + firstName: yup + .string('Enter your first name') + .min(2, 'First name should be at least 2 characters') + .required('First name is required'), + lastName: yup + .string('Enter your last name') + .min(2, 'Last name should be at least 2 characters') + .required('Last name is required'), + email: yup + .string('Enter your email') + .email('Enter a valid email') + .required('Email is required'), + phone: yup + .string('Enter your phone number') + .matches(/^[\+]?[1-9][\d]{0,15}$/, 'Enter a valid phone number') + .required('Phone number is required'), + age: yup + .number('Enter your age') + .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('Select a department') + .required('Department is required'), + skills: yup + .array() + .min(1, 'Select at least one skill') + .required('Skills are required'), + bio: yup + .string('Enter your bio') + .min(10, 'Bio should be at least 10 characters') + .max(500, 'Bio should not exceed 500 characters') + .required('Bio is required'), +}); + +const departments = [ + 'Engineering', + 'Marketing', + 'Sales', + 'HR', + 'Finance', + 'Operations', + 'Design', +]; + +const skillOptions = [ + 'JavaScript', + 'React', + 'Node.js', + 'Python', + 'UI/UX Design', + 'Project Management', + 'Data Analysis', + 'Marketing', + 'Sales', + 'Communication', +]; + +export default function FormPage() { + const formik = useFormik({ + initialValues: { + firstName: '', + lastName: '', + email: '', + phone: '', + age: '', + department: '', + skills: [], + bio: '', + }, + validationSchema: validationSchema, + onSubmit: (values, { setSubmitting, resetForm }) => { + setTimeout(() => { + console.log('Form submitted:', values); + alert('Form submitted successfully!'); + setSubmitting(false); + resetForm(); + }, 1000); + }, + }); + + const handleSkillChange = (event) => { + 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} + )} + + + + + + +
+
+
+
+
+ ); +} \ No newline at end of file