import React from "react"; import { Navigate, useNavigate } from "react-router"; import { useSignUpContext } from "./signUpContext"; import { yupResolver } from "@hookform/resolvers/yup"; import { useForm } from "react-hook-form"; import * as yup from "yup"; import { AuthContext } from "@/authContext"; import MkdSDK from "@/utils/MkdSDK"; import { Link } from "react-router-dom"; import { callCustomAPI } from "@/utils/callCustomAPI"; import { useRef } from "react"; import { isSameDay } from "@/utils/date-time-utils"; import moment from "moment/moment"; import TermsAndConditionsModal from "./TermsAndConditionsModal"; import DatePickerV2 from "@/components/frontend/DatePickerV2"; import { LoadingButton } from "@/components/frontend"; import PrivacyAndPolicyModal from "./PrivacyAndPolicyModal"; import commonPasswords from "@/assets/json/common-passwords.json"; export default function SignUpDetailsForm() { const navigate = useNavigate(); const { signUpData } = useSignUpContext(); const role = signUpData.role; const { dispatch: authDispatch } = React.useContext(AuthContext); const [showPassword, setShowPassword] = React.useState(false); const [loading, setLoading] = React.useState(false); const sdk = new MkdSDK(); const [modalOpen, setModalOpen] = React.useState(false); const [privacyOpen, setPrivacyModalOpen] = React.useState(false); const initialDate = useRef(new Date()); const [agreedToTerms, setAgreedToTerms] = React.useState(false); const [privacyRead, setPrivacyRead] = React.useState(false); const [passwordErrors, setPasswordErrors] = React.useState([]); function closeModal() { setModalOpen(false); } function closePrivacyModal() { setPrivacyModalOpen(false); } // --- DOB must be at least 18 years --- function isAtLeast18YearsOld(date) { if (!date) return false; const now = new Date(); const dob = new Date(date); let age = now.getFullYear() - dob.getFullYear(); const monthDiff = now.getMonth() - dob.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < dob.getDate())) { age--; } return age >= 18; } // --- Password validation --- function validatePassword(password, firstName, lastName, dob) { const errors = []; if (!password) errors.push("Password is required."); if (password && password.length < 10) errors.push("Password must be at least 10 characters long."); if (password && !/[a-z]/.test(password)) errors.push("Password must contain at least one lowercase letter."); if (password && !/[A-Z]/.test(password)) errors.push("Password must contain at least one uppercase letter."); if (password && !/[0-9]/.test(password)) errors.push("Password must contain at least one digit."); if (password && !/[^A-Za-z0-9]/.test(password)) errors.push("Password must contain at least one symbol."); if ( password && firstName && password.toLowerCase().includes(firstName.toLowerCase()) ) errors.push("Password must not contain your first name."); if ( password && lastName && password.toLowerCase().includes(lastName.toLowerCase()) ) errors.push("Password must not contain your last name."); if (password && dob) { const dobStr = new Date(dob).toISOString().slice(0, 10).replace(/-/g, ""); if (dobStr && password.includes(dobStr)) errors.push("Password must not contain your date of birth."); } if ( password && commonPasswords.some((w) => password.toLowerCase().includes(w)) ) errors.push("Password must not contain common password words."); return errors; } // --- Validation schema --- const schema = yup.object({ firstName: yup.string().required("First name is required."), lastName: yup.string().required("Last name is required."), dob: yup .date() .required("Date of birth is required.") .test("age", "You must be at least 18 years old.", isAtLeast18YearsOld), password: yup.string().required("Password is required."), }); const { register, setError, handleSubmit, trigger, watch, setValue, control, formState: { errors, dirtyFields }, } = useForm({ resolver: yupResolver(schema), defaultValues: { firstName: signUpData.firstName, lastName: signUpData.lastName, dob: initialDate.current, password: signUpData.password, }, criteriaMode: "all", }); const data = watch(); // --- Password error display --- React.useEffect(() => { setPasswordErrors( validatePassword(data.password, data.firstName, data.lastName, data.dob) ); }, [data.password, data.firstName, data.lastName, data.dob]); // --- Trigger DOB validation when date changes --- React.useEffect(() => { if (data.dob && !isSameDay(data.dob, initialDate.current)) { trigger("dob"); } }, [data.dob, trigger]); // --- Terms and Privacy Modal handlers --- function handleAgreeTerms() { setAgreedToTerms(true); setModalOpen(false); } function handlePrivacyRead() { setPrivacyRead(true); setPrivacyModalOpen(false); } // --- Disable submit logic --- const canSubmit = data.firstName && data.lastName && data.dob && isAtLeast18YearsOld(data.dob) && data.password && passwordErrors.length === 0 && agreedToTerms && privacyRead; async function onSubmit() { setLoading(true); try { const result = await sdk.register(signUpData.email, data.password, role); if (!result.error) { localStorage.setItem("token", result.token); // register device sdk.setTable("device"); await sdk.callRestAPI( { active: 1, user_id: result.user_id, last_login_time: new Date().toISOString().split("T")[0], uid: localStorage.getItem("device-uid"), }, "POST" ); await callCustomAPI( "edit-self", "post", { user: { first_name: data.firstName, last_name: data.lastName, }, profile: { dob: isSameDay(data.dob, initialDate.current) ? undefined : moment(data.dob).format("yyyy-MM-DD"), }, }, "", result.token ); localStorage.removeItem("token"); authDispatch({ type: "ALLOW_CHECK_VERIFICATION" }); navigate("/check-verification"); localStorage.setItem("first_login", result.user_id); setLoading(false); } else { setLoading(false); if (result.validation) { const keys = Object.keys(result.validation); for (let i = 0; i < keys.length; i++) { const field = keys[i]; setError(field, { type: "manual", message: result.validation[field], }); } } } } catch (err) { setLoading(false); setError("firstName", { type: "manual", message: err.message, }); } } if (!signUpData.email) return ; return ( <> Finish Signing Up {errors.firstName?.message} {errors.lastName?.message} { setValue("dob", v); trigger("dob"); }} /> {errors.dob && ( {errors.dob.message} )} 0 && dirtyFields.password ? "rounded-md border border-[#C42945]" : "borde" } relative mb-4 flex flex-col rounded-md bg-transparent`} > { trigger("password"); }, })} className='flex-grow rounded-md border p-2 px-4 focus:outline-none active:outline-none ' placeholder='Password' /> setShowPassword((prev) => !prev)} className='absolute right-1 top-[20%]' > {showPassword ? ( ) : ( )} {dirtyFields.password && passwordErrors.length > 0 && ( {passwordErrors.map((err, idx) => ( {err} ))} )} Select and agree to{" "} setModalOpen(true)} className='underline' // target={"_blank"} > {" "} Terms and Conditions {" "} to continue.{" "} setPrivacyModalOpen(true)} className='underline' > Privacy Policy {" "} {!agreedToTerms && ( You must agree to the Terms and Conditions. )} {!privacyRead && ( You must read the Privacy Policy to the end. )} Continue setModalOpen(false)} setIsAgreed={handleAgreeTerms} /> setPrivacyModalOpen(false)} onReadToEnd={handlePrivacyRead} /> > ); }
{errors.firstName?.message}
{errors.lastName?.message}
{errors.dob.message}
Select and agree to{" "} setModalOpen(true)} className='underline' // target={"_blank"} > {" "} Terms and Conditions {" "} to continue.{" "} setPrivacyModalOpen(true)} className='underline' > Privacy Policy