fix: the use person x button to checkbox

This commit is contained in:
Nathnael
2026-06-26 12:41:52 +00:00
parent 552e6bcd16
commit f51488e989

View File

@@ -9,6 +9,7 @@ import {
Stack, Stack,
Text, Text,
TextInput, TextInput,
UnstyledButton,
} from "@mantine/core"; } from "@mantine/core";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
@@ -16,9 +17,9 @@ import {
AlertCircle, AlertCircle,
ArrowLeft, ArrowLeft,
ArrowRight, ArrowRight,
Check,
CheckCircle2, CheckCircle2,
RotateCw, RotateCw,
ShieldCheck,
Smartphone, Smartphone,
UserCheck, UserCheck,
} from "lucide-react"; } 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 (
<UnstyledButton
onClick={() => 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)]!"
}`}
>
<Group gap="sm" wrap="nowrap" align="flex-start">
<div
className={`mt-px flex h-5 w-5 shrink-0 items-center justify-center rounded-[6px] border transition-colors ${checked
? "border-[var(--mantine-color-edr-green-6)] bg-[var(--mantine-color-edr-green-6)] text-white"
: "border-[var(--mantine-color-gray-4)] bg-white"
}`}
>
{checked && <Check size={14} strokeWidth={3} />}
</div>
<div>
<Text fw={600} size="sm" c="edr-text" lh={1.25}>
{title}
</Text>
<Text size="xs" c="dimmed" mt={2}>
{description}
</Text>
</div>
</Group>
</UnstyledButton>
);
}
/** A single read-only registration value rendered as a label/value pair. */ /** A single read-only registration value rendered as a label/value pair. */
function ReadOnlyField({ label, value }: { label: string; value?: string }) { function ReadOnlyField({ label, value }: { label: string; value?: string }) {
return ( return (
@@ -352,7 +400,9 @@ export default function CompanyProfileForm({
* "Continue" action). Resolves to an error message string on failure so the * "Continue" action). Resolves to an error message string on failure so the
* step can surface it and hold the user in place. * 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<CompanyStep>(initialStep ?? "company"); const [step, setStep] = useState<CompanyStep>(initialStep ?? "company");
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
@@ -542,22 +592,54 @@ export default function CompanyProfileForm({
}); });
}; };
/** Copy the General Manager into the Contact Person fields (still editable). */ // "Same as …" links. A checked card prefills the target step's fields from the
const useGmAsContact = () => { // source step and disables them (kept mirrored while linked); unchecking clears
setValue("contactPersonName", watch("generalManagerName"), { // them and re-enables editing.
shouldValidate: true, const [contactSameAsGm, setContactSameAsGm] = useState(false);
}); const [poaSameAsContact, setPoaSameAsContact] = useState(false);
setValue("contactPersonEmail", watch("generalManagerEmail"));
setValue("contactPersonPhone", watch("generalManagerPhone"), { const gmName = watch("generalManagerName");
shouldValidate: true, 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 togglePoaSameAsContact = (checked: boolean) => {
const useContactAsPoa = () => { setPoaSameAsContact(checked);
setValue("poaName", watch("contactPersonName")); if (!checked) {
setValue("poaEmail", watch("contactPersonEmail")); setValue("poaName", "");
setValue("poaPhone", watch("contactPersonPhone")); setValue("poaEmail", "");
setValue("poaPhone", "");
}
}; };
// --- Contact-phone SMS OTP verification ----------------------------------- // --- Contact-phone SMS OTP verification -----------------------------------
@@ -950,24 +1032,17 @@ export default function CompanyProfileForm({
{step === "contact" && ( {step === "contact" && (
<> <>
<Group justify="space-between" align="center" wrap="nowrap"> <Text fw={600} size="sm" c="edr-text">
<Text fw={600} size="sm" c="edr-text"> Contact Person
Contact Person </Text>
</Text> {watch("generalManagerName") && (
<Group gap="xs" wrap="nowrap" style={{ flexShrink: 0 }}> <LinkCheckboxCard
{watch("generalManagerName") && ( checked={contactSameAsGm}
<Button onToggle={toggleContactSameAsGm}
variant="light" title="Same as General Manager"
color="edr-green" description="Reuse the general manager's name, email and phone. Uncheck to enter different details."
size="xs" />
leftSection={<UserCheck size={14} />} )}
onClick={useGmAsContact}
>
Use General Manager
</Button>
)}
</Group>
</Group>
<SimpleGrid cols={2} spacing="md"> <SimpleGrid cols={2} spacing="md">
<TextInput <TextInput
label="Name" label="Name"
@@ -1103,24 +1178,18 @@ export default function CompanyProfileForm({
{step === "poa" && ( {step === "poa" && (
<> <>
<Group justify="space-between" align="center" wrap="nowrap"> <Text size="sm" c="edr-muted">
<Text size="sm" c="edr-muted"> Power of Attorney details are optional. Fill them in if you have
Power of Attorney details are optional. Fill them in if you them, or skip to continue.
have them, or skip to continue. </Text>
</Text> {watch("contactPersonName") && (
{watch("contactPersonName") && ( <LinkCheckboxCard
<Button checked={poaSameAsContact}
variant="light" onToggle={togglePoaSameAsContact}
color="edr-green" title="Same as contact person"
size="xs" description="Reuse the contact person's name, email and phone. Uncheck to enter different details."
leftSection={<UserCheck size={14} />} />
onClick={useContactAsPoa} )}
style={{ flexShrink: 0 }}
>
Use contact person
</Button>
)}
</Group>
<TextInput <TextInput
label="PoA Name" label="PoA Name"
placeholder="Authorized Representative Name" placeholder="Authorized Representative Name"