diff --git a/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx b/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx index 3f07d7059..09c632d56 100644 --- a/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx +++ b/apps/edr-freight-web/portal/src/pages/accounts/CompanyProfileForm.tsx @@ -9,6 +9,7 @@ import { Stack, Text, TextInput, + UnstyledButton, } from "@mantine/core"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery } from "@tanstack/react-query"; @@ -16,9 +17,9 @@ import { AlertCircle, ArrowLeft, ArrowRight, + Check, CheckCircle2, RotateCw, - ShieldCheck, Smartphone, UserCheck, } from "lucide-react"; @@ -282,6 +283,53 @@ function toFormValues(p: ProfileResponse): FormData { }; } +/** + * A card styled as a large checkbox: clicking it toggles `checked`, which the + * caller uses to prefill + lock a set of fields (and clear them on uncheck). + */ +function LinkCheckboxCard({ + checked, + onToggle, + title, + description, +}: { + checked: boolean; + onToggle: (checked: boolean) => void; + title: string; + description: string; +}) { + return ( + onToggle(!checked)} + role="checkbox" + aria-checked={checked} + className={`w-full rounded-lg border! p-3! text-left transition-colors! ${checked + ? "border-[var(--mantine-color-edr-green-6)]! bg-[var(--mantine-color-edr-green-0)]!" + : "border-[var(--mantine-color-gray-3)]! hover:border-[var(--mantine-color-edr-green-4)]! hover:bg-[var(--mantine-color-edr-green-0)]!" + }`} + > + +
+ {checked && } +
+
+ + {title} + + + {description} + +
+
+
+ ); +} + /** A single read-only registration value rendered as a label/value pair. */ function ReadOnlyField({ label, value }: { label: string; value?: string }) { return ( @@ -352,7 +400,9 @@ export default function CompanyProfileForm({ * "Continue" action). Resolves to an error message string on failure so the * step can surface it and hold the user in place. */ - onUploadDocuments?: () => Promise<{ ok: true } | { ok: false; error: string }>; + onUploadDocuments?: () => Promise< + { ok: true } | { ok: false; error: string } + >; }) { const [step, setStep] = useState(initialStep ?? "company"); const [saving, setSaving] = useState(false); @@ -542,22 +592,54 @@ export default function CompanyProfileForm({ }); }; - /** Copy the General Manager into the Contact Person fields (still editable). */ - const useGmAsContact = () => { - setValue("contactPersonName", watch("generalManagerName"), { - shouldValidate: true, - }); - setValue("contactPersonEmail", watch("generalManagerEmail")); - setValue("contactPersonPhone", watch("generalManagerPhone"), { - shouldValidate: true, - }); + // "Same as …" links. A checked card prefills the target step's fields from the + // source step and disables them (kept mirrored while linked); unchecking clears + // them and re-enables editing. + const [contactSameAsGm, setContactSameAsGm] = useState(false); + const [poaSameAsContact, setPoaSameAsContact] = useState(false); + + const gmName = watch("generalManagerName"); + const gmEmail = watch("generalManagerEmail"); + const gmPhone = watch("generalManagerPhone"); + const contactName = watch("contactPersonName"); + const contactEmail = watch("contactPersonEmail"); + const contactPhone = watch("contactPersonPhone"); + + // While linked, mirror the source values into the (disabled) target fields so + // the copy stays current even if the user goes back and edits the source. + useEffect(() => { + if (!contactSameAsGm) return; + setValue("contactPersonName", gmName ?? "", { shouldValidate: true }); + setValue("contactPersonEmail", gmEmail ?? ""); + setValue("contactPersonPhone", gmPhone ?? "", { shouldValidate: true }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [contactSameAsGm, gmName, gmEmail, gmPhone]); + + useEffect(() => { + if (!poaSameAsContact) return; + setValue("poaName", contactName ?? ""); + setValue("poaEmail", contactEmail ?? ""); + setValue("poaPhone", contactPhone ?? ""); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [poaSameAsContact, contactName, contactEmail, contactPhone]); + + const toggleContactSameAsGm = (checked: boolean) => { + setContactSameAsGm(checked); + // Checked → the mirror effect fills the fields; unchecked → reset them. + if (!checked) { + setValue("contactPersonName", ""); + setValue("contactPersonEmail", ""); + setValue("contactPersonPhone", ""); + } }; - /** Copy the Contact Person into the PoA fields (still editable). */ - const useContactAsPoa = () => { - setValue("poaName", watch("contactPersonName")); - setValue("poaEmail", watch("contactPersonEmail")); - setValue("poaPhone", watch("contactPersonPhone")); + const togglePoaSameAsContact = (checked: boolean) => { + setPoaSameAsContact(checked); + if (!checked) { + setValue("poaName", ""); + setValue("poaEmail", ""); + setValue("poaPhone", ""); + } }; // --- Contact-phone SMS OTP verification ----------------------------------- @@ -950,24 +1032,17 @@ export default function CompanyProfileForm({ {step === "contact" && ( <> - - - Contact Person - - - {watch("generalManagerName") && ( - - )} - - + + Contact Person + + {watch("generalManagerName") && ( + + )} - - - Power of Attorney details are optional. Fill them in if you - have them, or skip to continue. - - {watch("contactPersonName") && ( - - )} - + + Power of Attorney details are optional. Fill them in if you have + them, or skip to continue. + + {watch("contactPersonName") && ( + + )}