import { useState, type ReactNode } from "react"; import { Alert, Avatar, Badge, Button, Card, Group, Stack, Text, } from "@mantine/core"; import { BadgeCheck, Clock, Mail, MapPin, Phone, ShieldCheck, XCircle, } from "lucide-react"; import { stashPendingVerification, verifaydaService, type IdentitySubject, type IdentityVerificationState, } from "@/services/verifayda.service"; import { isBypassEnv } from "@/utils/dev-bypass"; /** Sentinel code that skips the real eSignet exchange in dev/staging (see api's DEV_BYPASS_FAYDA_CODE). */ const DEV_BYPASS_FAYDA_CODE = "DEV_BYPASS"; interface FaydaVerifyPanelProps { subject: IdentitySubject; /** Heading — "General Manager" / "Power of Attorney". */ title: string; /** What this person's verification is currently known to be. */ state?: IdentityVerificationState; /** * False for a foreign company: verification is offered but nothing is gated * on it, so the panel says so rather than nagging. */ required: boolean; disabled?: boolean; /** * True when a fresh verification for this person is already staged in a * pending change request. On an active company a re-verification never * touches the live record — it's staged for review — so `state` alone * would keep showing the OLD verified data with no sign anything happened. */ pendingReview?: boolean; } function getInitials(name: string | null): string { if (!name) return "?"; const parts = name.trim().split(/\s+/); const first = parts[0]?.[0] ?? ""; const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? "") : ""; return (first + last).toUpperCase(); } /** * Verify one of the company's people through Fayda and show what came back. * * The identity is proved on eSignet, which the whole tab navigates to — no * popup, because a popup opened after the /start round-trip has lost its user * activation and iOS Safari blocks it outright. eSignet redirects back to * /fayda/callback, which completes the exchange and returns the user here; the API * writes the person's name, phone, email and address from the verified * payload. Nothing on this panel is typed. */ export default function FaydaVerifyPanel({ subject, title, state, required, disabled, pendingReview, }: FaydaVerifyPanelProps) { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const startVerification = async () => { setError(null); setLoading(true); try { // Dev/staging only: send the tab straight to /fayda/callback with the // sentinel code instead of round-tripping through eSignet — that page's // existing completeIdentity()/navigate-back logic runs unchanged. if (isBypassEnv()) { stashPendingVerification({ subject, returnTo: window.location.pathname + window.location.search + window.location.hash, }); window.location.assign( `/fayda/callback?code=${DEV_BYPASS_FAYDA_CODE}&state=bypass`, ); return; } const authorizationUrl = await verifaydaService.start(); // Record who is being verified and where to come back to before the tab // leaves — /fayda/callback has no other way to know either. stashPendingVerification({ subject, returnTo: window.location.pathname + window.location.search + window.location.hash, }); window.location.assign(authorizationUrl); } catch (err) { setLoading(false); setError( (err as { response?: { data?: { message?: string } } })?.response?.data ?.message ?? (err instanceof Error ? err.message : "Could not start verification"), ); } }; const verified = state?.verified ?? false; return ( {title} {verified ? ( } > Fayda verified ) : ( required && ( Verification required ) )} {pendingReview && ( } > Re-verification pending review )} {!verified && ( {required ? "Verify this person with Fayda. Their name, phone and address come from the verification — there is nothing to fill in by hand." : "Optional for a foreign company. If this person holds a Fayda ID, verifying it fills in their details."} )} {verified && state && ( {getInitials(state.name)} {state.name} } value={state.phone} /> } value={state.email} /> } value={state.address} /> )} {error && ( }> {error} )} ); } function DataRow({ icon, value }: { icon: ReactNode; value: string | null }) { if (!value) return null; return ( {icon} {value} ); }