'use client'; import { useMemo, useState } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Label } from '@/components/ui/label'; import { apiFetch } from '@/lib/api/client'; /** Required for the EOI's top paragraph (Section 2) — without these the * document is unsignable, so generation is blocked. Yacht and berth fields * belong to Section 3 and may be left blank. */ interface EoiPrerequisites { hasName: boolean; hasEmail: boolean; hasAddress: boolean; /** Optional — info-only checks. Generation proceeds without them. */ hasYacht: boolean; hasBerth: boolean; } interface EoiGenerateDialogProps { interestId: string; open: boolean; onOpenChange: (open: boolean) => void; prerequisites: EoiPrerequisites; } const REQUIRED_LABELS: { key: keyof EoiPrerequisites; label: string }[] = [ { key: 'hasName', label: 'Client name' }, { key: 'hasAddress', label: 'Client address' }, { key: 'hasEmail', label: 'Client email' }, ]; const OPTIONAL_LABELS: { key: keyof EoiPrerequisites; label: string }[] = [ { key: 'hasYacht', label: 'Yacht linked (name + dimensions)' }, { key: 'hasBerth', label: 'Berth linked (mooring number)' }, ]; const DOCUMENSO_TEMPLATE_VALUE = 'documenso-template'; interface InAppTemplate { id: string; name: string; description?: string | null; templateType: string; } interface ListResponse { data: InAppTemplate[]; } export function EoiGenerateDialog({ interestId, open, onOpenChange, prerequisites, }: EoiGenerateDialogProps) { const queryClient = useQueryClient(); const [isGenerating, setIsGenerating] = useState(false); const [error, setError] = useState(null); const [selectedTemplate, setSelectedTemplate] = useState(DOCUMENSO_TEMPLATE_VALUE); const requiredMet = REQUIRED_LABELS.every(({ key }) => prerequisites[key]); // Load in-app EOI templates so the operator can pick one as an alternative // to the Documenso external-signing flow. const { data: templatesRes } = useQuery({ queryKey: ['document-templates', { templateType: 'eoi', isActive: true }], queryFn: () => apiFetch('/api/v1/document-templates?templateType=eoi&isActive=true'), enabled: open, }); const inAppTemplates = useMemo(() => templatesRes?.data ?? [], [templatesRes]); const handleGenerate = async () => { if (!requiredMet) return; setIsGenerating(true); setError(null); try { const isDocumensoPath = selectedTemplate === DOCUMENSO_TEMPLATE_VALUE; const url = `/api/v1/document-templates/${encodeURIComponent(selectedTemplate)}/generate-and-sign`; await apiFetch(url, { method: 'POST', body: { interestId, pathway: isDocumensoPath ? 'documenso-template' : 'inapp', // Signers are derived server-side from EOI context for both pathways // when the template type is EOI, so the dialog doesn't collect them. signers: [], }, }); // Invalidate all document list queries (hub counts + per-interest lists). // The DocumentList component uses ['documents', { interestId, clientId }] // and the hub uses ['documents', 'hub', ...] / ['documents', 'hub-counts']. // Using a predicate avoids key-shape drift between callers. queryClient.invalidateQueries({ predicate: (q) => q.queryKey[0] === 'documents', }); onOpenChange(false); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to generate EOI'); } finally { setIsGenerating(false); } }; return ( Generate Expression of Interest Pick how to render the EOI. Documenso is the primary path; in-app templates use the same source PDF but render and store the PDF locally before sending for signing.

Required (Section 2 of the EOI)

{REQUIRED_LABELS.map(({ key, label }) => (
{prerequisites[key] ? '✓' : '✗'} {label}
))}

Optional (Section 3 — left blank if absent)

{OPTIONAL_LABELS.map(({ key, label }) => (
{prerequisites[key] ? '✓' : '–'} {label}
))}
{!requiredMet ? (

Add the missing required details on the client's record before generating the EOI.

) : null}
{error &&

{error}

}
); }