diff --git a/client/components/CustomFormField.tsx b/client/components/CustomFormField.tsx index 0128c963..2042dc38 100644 --- a/client/components/CustomFormField.tsx +++ b/client/components/CustomFormField.tsx @@ -16,9 +16,18 @@ import PhoneInput from "react-phone-number-input"; import ReactDatePicker from "react-datepicker"; import { Select } from "@radix-ui/react-select"; import { SelectContent, SelectTrigger, SelectValue } from "./ui/select"; +import { Checkbox } from "./ui/checkbox"; const RenderField = ({ field, props }: { field: any; props: CustomProps }) => { - const { fieldType, iconSrc, iconAlt, placeholder, renderSkeleton } = props; + const { + fieldType, + iconSrc, + iconAlt, + placeholder, + renderSkeleton, + label, + name, + } = props; switch (fieldType) { case FormFieldType.INPUT: @@ -80,8 +89,8 @@ const RenderField = ({ field, props }: { field: any; props: CustomProps }) => { ); - - case FormFieldType.PHONE_INPUT: + + case FormFieldType.PHONE_INPUT: return ( { ); - case FormFieldType.SKELETON: - return renderSkeleton ? renderSkeleton(field) : null; + case FormFieldType.SKELETON: + return renderSkeleton ? renderSkeleton(field) : null; + case FormFieldType.CHECKBOX: + return ( + +
+ + +
+
+ ); default: return null; } diff --git a/client/components/forms/RegistrationForm.tsx b/client/components/forms/RegistrationForm.tsx index e18d86d2..0c4d387d 100644 --- a/client/components/forms/RegistrationForm.tsx +++ b/client/components/forms/RegistrationForm.tsx @@ -7,7 +7,7 @@ import { z } from "zod"; import { Form, FormControl } from "@/components/ui/form"; import CustomFormField from "../CustomFormField"; import SubmitButton from "../SubmitButton"; -import { loginFormValidation } from "@/lib/validation"; +import { registerFormValidation } from "@/lib/validation"; // import { useLoginMutation } from "@/lib/features/api/authApi"; import { FormFieldType } from "@/types/fromTypes"; import Link from "next/link"; @@ -20,15 +20,15 @@ import Image from "next/image"; const RegistrationForm = () => { const [isLoading, setIsLoading] = useState(false); // const [login] = useLoginMutation(); - const form = useForm>({ - resolver: zodResolver(loginFormValidation), + const form = useForm>({ + resolver: zodResolver(registerFormValidation), defaultValues: { phone: "", password: "", }, }); - const onSubmit = async (values: z.infer) => { + const onSubmit = async (values: z.infer) => { setIsLoading(true); setTimeout(() => { console.log("done"); @@ -102,7 +102,7 @@ const RegistrationForm = () => { fieldType={FormFieldType.DATE_PICKER} control={form.control} name="birthDate" - label="Date of birth" + label="Date of birth *" /> { label="Confirm Password" placeholder="Re-enter your password" /> +
+
+

Consent and Privacy

+
+
+ + + Sign In diff --git a/client/components/ui/checkbox.tsx b/client/components/ui/checkbox.tsx new file mode 100644 index 00000000..df61a138 --- /dev/null +++ b/client/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } diff --git a/client/lib/validation.ts b/client/lib/validation.ts index ed03872d..e268c80e 100644 --- a/client/lib/validation.ts +++ b/client/lib/validation.ts @@ -1,13 +1,63 @@ +import { BloodTypes, GenderOptions } from "@/constants"; import { z } from "zod"; export const loginFormValidation = z.object({ - phone: z.string().refine(phone => /^\+?[1-9]\d{1,14}$/.test(phone), "Invalid phone number"), - password: z.string().min(4, "Password must be at least 6 characters long"), - // password: z.string() - // .min(8, "Password must be at least 8 characters long") - // .regex(/[A-Z]/, "Password must contain at least one uppercase letter") - // .regex(/[a-z]/, "Password must contain at least one lowercase letter") - // .regex(/[0-9]/, "Password must contain at least one number") - // .regex(/[@$!%*?&#]/, "Password must contain at least one special character"), + phone: z + .string() + .refine( + (phone) => /^\+?[1-9]\d{1,14}$/.test(phone), + "Invalid phone number" + ), + password: z.string().min(4, "Password must be at least 4 characters long"), }); +export const registerFormValidation = z + .object({ + name: z + .string() + .min(1, "Full Name is required") + .nonempty("Full Name cannot be empty"), + email: z + .string() + .email("Invalid email address") + .min(1, "Email address is required"), + phone: z + .string() + .min(10, "Phone number must be at least 10 digits") + .max(15, "Phone number must be at most 15 digits"), + birthDate: z.string().min(1, "Date of birth is required"), + gender: z.enum(["Male", "Female", "Other"]), + bloodType: z.enum(["A+", "A-", "B+", "B-", "O+", "O-", "AB+", "AB-"]), + disease: z.string().min(1, "Primary Disease is required"), + password: z.string().min(4, "Password must be at least 4 characters long"), + // password: z.string() + // .min(8, "Password must be at least 8 characters long") + // .regex(/[A-Z]/, "Password must contain at least one uppercase letter") + // .regex(/[a-z]/, "Password must contain at least one lowercase letter") + // .regex(/[0-9]/, "Password must contain at least one number") + // .regex(/[@$!%*?&#]/, "Password must contain at least one special character"), + confirmPassword: z + .string() + .min(4, "Password must be at least 4 characters long"), + privacyPolicy: z + .boolean() + .refine( + (val) => val === true, + "You must agree to the Privacy Policy" + ), + concent: z + .boolean() + .refine((val) => val === true, "You must Consent to Treatment"), + disclosureConsent: z + .boolean() + .refine((val) => val === true, "You must Consent to Privacy Policy"), + }) + .superRefine(({ confirmPassword, password }, ctx) => { + if (confirmPassword !== password) { + ctx.addIssue({ + code: "custom", + message: "The Password did not match", + path: ["confirmPassword"], + }); + } + }); diff --git a/client/package-lock.json b/client/package-lock.json index e7d31f5b..5718eb8d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^3.9.0", + "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-select": "^2.1.1", @@ -505,6 +506,35 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.1.tgz", + "integrity": "sha512-0i/EKJ222Afa1FE0C6pNJxDq1itzcl3HChE9DwskA4th4KRse8ojx8a1nVcOjwJdbpDLcz7uol77yYnQNMHdKw==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", diff --git a/client/package.json b/client/package.json index a1d1a031..292ae89a 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.0", + "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-select": "^2.1.1",