diff --git a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx index b28a615..b0c18fa 100644 --- a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx +++ b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx @@ -25,6 +25,7 @@ export function DocumentPageClient({ const [previewToken, setPreviewToken] = useState(null); const [selectedFieldId, setSelectedFieldId] = useState(null); const [textFillData, setTextFillData] = useState>({}); + const [aiPlacementKey, setAiPlacementKey] = useState(0); const handleFieldsChanged = useCallback(() => { setPreviewToken(null); @@ -40,6 +41,24 @@ export function DocumentPageClient({ setPreviewToken(null); // TXTF-03: reset staleness on quick fill }, []); + const handleAiAutoPlace = useCallback(async () => { + const res = await fetch(`/api/documents/${docId}/ai-prepare`, { method: 'POST' }); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: 'AI placement failed' })); + throw new Error(err.error ?? err.message ?? 'AI placement failed'); + } + const { textFillData: aiTextFill } = await res.json() as { + fields: unknown[]; + textFillData: Record; + }; + // Merge AI pre-fill into existing textFillData (AI values take precedence) + setTextFillData(prev => ({ ...prev, ...aiTextFill })); + // Trigger FieldPlacer to re-fetch from DB (fields were written server-side) + setAiPlacementKey(k => k + 1); + // Reset preview staleness — fields changed + setPreviewToken(null); + }, [docId]); + return (
@@ -51,6 +70,7 @@ export function DocumentPageClient({ textFillData={textFillData} onFieldSelect={setSelectedFieldId} onFieldValueChange={handleFieldValueChange} + aiPlacementKey={aiPlacementKey} />
@@ -67,6 +87,7 @@ export function DocumentPageClient({ textFillData={textFillData} selectedFieldId={selectedFieldId} onQuickFill={handleQuickFill} + onAiAutoPlace={handleAiAutoPlace} />
diff --git a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx index 1e484a4..99629b8 100644 --- a/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx +++ b/teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx @@ -16,6 +16,7 @@ interface PreparePanelProps { textFillData: Record; selectedFieldId: string | null; onQuickFill: (fieldId: string, value: string) => void; + onAiAutoPlace: () => Promise; } function parseEmails(raw: string | undefined): string[] { @@ -31,6 +32,7 @@ export function PreparePanel({ agentDownloadUrl, signedAt, clientPropertyAddress, previewToken, onPreviewTokenChange, textFillData, selectedFieldId, onQuickFill, + onAiAutoPlace, }: PreparePanelProps) { const router = useRouter(); const [recipients, setRecipients] = useState(defaultEmail ?? ''); @@ -39,6 +41,7 @@ export function PreparePanel({ if (defaultEmail) setRecipients(defaultEmail); }, [defaultEmail]); const [loading, setLoading] = useState(false); + const [aiLoading, setAiLoading] = useState(false); const [result, setResult] = useState<{ ok: boolean; message: string } | null>(null); const [previewBytes, setPreviewBytes] = useState(null); const [showPreview, setShowPreview] = useState(false); @@ -93,6 +96,18 @@ export function PreparePanel({ ); } + async function handleAiAutoPlaceClick() { + setAiLoading(true); + setResult(null); + try { + await onAiAutoPlace(); + } catch (e) { + setResult({ ok: false, message: String(e) }); + } finally { + setAiLoading(false); + } + } + async function handlePreview() { setLoading(true); setResult(null); @@ -227,6 +242,15 @@ export function PreparePanel({ )} + +