---
phase: 13-ai-field-placement-and-pre-fill
plan: 03
type: execute
wave: 3
depends_on:
  - 13-01
  - 13-02
files_modified:
  - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
  - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx
  - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx
  - teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx
autonomous: true
requirements:
  - AI-01
  - AI-02

must_haves:
  truths:
    - "PreparePanel has an 'AI Auto-place' button that calls POST /api/documents/[id]/ai-prepare and shows loading state"
    - "After AI placement succeeds, FieldPlacer re-fetches fields from DB and displays the AI-placed field overlays"
    - "After AI placement, DocumentPageClient's textFillData is updated with the returned pre-fill map and previewToken is reset to null"
    - "Agent can review, move, resize, or delete any AI-placed field after placement — fields are editable, not locked"
  artifacts:
    - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx"
      provides: "FieldPlacer with aiPlacementKey prop that triggers loadFields re-fetch"
      contains: "aiPlacementKey"
    - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx"
      provides: "PdfViewerWrapper threads aiPlacementKey and onAiPlacementKeyChange to PdfViewer/FieldPlacer"
      contains: "aiPlacementKey"
    - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx"
      provides: "AI Auto-place button with loading state, calls onAiAutoPlace callback"
      contains: "AI Auto-place"
    - path: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx"
      provides: "handleAiAutoPlace that calls route, updates textFillData, increments aiPlacementKey, resets previewToken"
      contains: "handleAiAutoPlace"
  key_links:
    - from: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx"
      to: "/api/documents/[id]/ai-prepare"
      via: "onAiAutoPlace callback → DocumentPageClient.handleAiAutoPlace → fetch POST"
      pattern: "onAiAutoPlace|handleAiAutoPlace"
    - from: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx"
      to: "teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx"
      via: "aiPlacementKey prop threaded through PdfViewerWrapper"
      pattern: "aiPlacementKey"
---

<objective>
Wire the "AI Auto-place" button into the PreparePanel and connect it to the FieldPlacer reload mechanism via DocumentPageClient state.

Purpose: The AI utilities (Plan 01) and route (Plan 02) are server-side. This plan adds the client-side interaction: the button, the loading state, the post-success state updates (fields reload + textFillData update + preview reset), and the FieldPlacer re-fetch trigger.

Output: Four files modified — FieldPlacer gets `aiPlacementKey` prop, PdfViewerWrapper threads it, PreparePanel gets "AI Auto-place" button, DocumentPageClient orchestrates the handler.
</objective>

<execution_context>
@/Users/ccopeland/.claude/get-shit-done/workflows/execute-plan.md
@/Users/ccopeland/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/13-ai-field-placement-and-pre-fill/13-RESEARCH.md
@.planning/phases/13-ai-field-placement-and-pre-fill/13-01-SUMMARY.md
@.planning/phases/13-ai-field-placement-and-pre-fill/13-02-SUMMARY.md

Read next.config.ts and check for Next.js guide in node_modules/next/dist/docs/ before writing component code.
</context>

<interfaces>
<!-- Current component interfaces. Extracted from codebase — no exploration needed. -->

Current FieldPlacer props (FieldPlacer.tsx line 155-166):
```typescript
interface FieldPlacerProps {
  docId: string;
  pageInfo: PageInfo | null;
  currentPage: number;
  children: React.ReactNode;
  readOnly?: boolean;
  onFieldsChanged?: () => void;
  selectedFieldId?: string | null;
  textFillData?: Record<string, string>;
  onFieldSelect?: (fieldId: string | null) => void;
  onFieldValueChange?: (fieldId: string, value: string) => void;
}
// FieldPlacer loads fields in useEffect([docId]) — adding aiPlacementKey to dependency array triggers re-fetch
```

Current PdfViewerWrapper props:
```typescript
// PdfViewerWrapper({ docId, docStatus, onFieldsChanged, selectedFieldId, textFillData, onFieldSelect, onFieldValueChange })
// It wraps PdfViewer (dynamic import, ssr: false) which renders FieldPlacer
// PdfViewer is at src/app/portal/(protected)/documents/[docId]/_components/PdfViewer.tsx
// It passes all props through to FieldPlacer
```

Current PreparePanel props (PreparePanel.tsx lines 6-18):
```typescript
interface PreparePanelProps {
  docId: string;
  defaultEmail: string;
  clientName: string;
  currentStatus: string;
  agentDownloadUrl?: string | null;
  signedAt?: Date | null;
  clientPropertyAddress?: string | null;
  previewToken: string | null;
  onPreviewTokenChange: (token: string | null) => void;
  textFillData: Record<string, string>;
  selectedFieldId: string | null;
  onQuickFill: (fieldId: string, value: string) => void;
}
```

Current DocumentPageClient state (DocumentPageClient.tsx):
```typescript
const [previewToken, setPreviewToken] = useState<string | null>(null);
const [selectedFieldId, setSelectedFieldId] = useState<string | null>(null);
const [textFillData, setTextFillData] = useState<Record<string, string>>({});
// handleFieldsChanged, handleFieldValueChange, handleQuickFill already exist
```
</interfaces>

<tasks>

<task type="auto">
  <name>Task 1: Add aiPlacementKey prop to FieldPlacer and thread through PdfViewerWrapper</name>
  <files>
    teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/FieldPlacer.tsx
    teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PdfViewerWrapper.tsx
  </files>
  <action>
**FieldPlacer.tsx changes:**

1. Add `aiPlacementKey?: number` to `FieldPlacerProps` interface.

2. Add `aiPlacementKey = 0` to the destructured props in the function signature.

3. Update the `loadFields` useEffect dependency array to include `aiPlacementKey`:
   ```typescript
   // Change from:
   useEffect(() => { ... loadFields(); }, [docId]);
   // Change to:
   useEffect(() => { ... loadFields(); }, [docId, aiPlacementKey]);
   ```
   This causes FieldPlacer to re-fetch from DB whenever `aiPlacementKey` increments after AI placement.

No other changes to FieldPlacer.tsx.

**PdfViewerWrapper.tsx changes:**

Read PdfViewer.tsx first to understand how it passes props to FieldPlacer — the thread needs to go: `DocumentPageClient → PdfViewerWrapper → PdfViewer → FieldPlacer`.

Add `aiPlacementKey?: number` to PdfViewerWrapper's props interface and pass it through to PdfViewer:
```typescript
export function PdfViewerWrapper({
  docId, docStatus, onFieldsChanged, selectedFieldId, textFillData,
  onFieldSelect, onFieldValueChange,
  aiPlacementKey,         // NEW
}: {
  docId: string;
  docStatus?: string;
  onFieldsChanged?: () => void;
  selectedFieldId?: string | null;
  textFillData?: Record<string, string>;
  onFieldSelect?: (fieldId: string | null) => void;
  onFieldValueChange?: (fieldId: string, value: string) => void;
  aiPlacementKey?: number;  // NEW
}) {
  return (
    <PdfViewer
      docId={docId}
      docStatus={docStatus}
      onFieldsChanged={onFieldsChanged}
      selectedFieldId={selectedFieldId}
      textFillData={textFillData}
      onFieldSelect={onFieldSelect}
      onFieldValueChange={onFieldValueChange}
      aiPlacementKey={aiPlacementKey}   // NEW
    />
  );
}
```

Also read PdfViewer.tsx and add `aiPlacementKey?: number` to its props + pass through to FieldPlacer. PdfViewer is the component that actually renders FieldPlacer wrapping the PDF page canvas.
  </action>
  <verify>
    <automated>cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | grep -E "FieldPlacer|PdfViewer|error TS" | head -20</automated>
  </verify>
  <done>
    - FieldPlacer accepts and uses `aiPlacementKey` in the loadFields useEffect dependency array
    - PdfViewerWrapper and PdfViewer thread `aiPlacementKey` through to FieldPlacer
    - TypeScript compiles without errors for these files
  </done>
</task>

<task type="auto">
  <name>Task 2: Add AI Auto-place button to PreparePanel and wire DocumentPageClient handler</name>
  <files>
    teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/PreparePanel.tsx
    teressa-copeland-homes/src/app/portal/(protected)/documents/[docId]/_components/DocumentPageClient.tsx
  </files>
  <action>
**PreparePanel.tsx changes:**

1. Add `onAiAutoPlace: () => Promise<void>` to `PreparePanelProps` interface.

2. Destructure `onAiAutoPlace` from props.

3. Add a local `aiLoading` state: `const [aiLoading, setAiLoading] = useState(false)`.

4. Add an `handleAiAutoPlaceClick` function in the component:
   ```typescript
   async function handleAiAutoPlaceClick() {
     setAiLoading(true);
     setResult(null);
     try {
       await onAiAutoPlace();
     } catch (e) {
       setResult({ ok: false, message: String(e) });
     } finally {
       setAiLoading(false);
     }
   }
   ```

5. Add the "AI Auto-place" button to the JSX. Position it ABOVE the Preview button, at the top of the Draft panel (after the recipients textarea section, before the quick-fill section). Only show it when `currentStatus === 'Draft'`:
   ```tsx
   <button
     onClick={handleAiAutoPlaceClick}
     disabled={aiLoading || loading}
     className="w-full py-2 px-4 bg-violet-600 text-white rounded hover:bg-violet-700 disabled:opacity-50 disabled:cursor-not-allowed text-sm font-medium"
     type="button"
   >
     {aiLoading ? 'AI placing fields...' : 'AI Auto-place Fields'}
   </button>
   ```
   Use violet (#7c3aed) to visually distinguish from the gray Preview button and blue Prepare and Send button.

**DocumentPageClient.tsx changes:**

1. Add `aiPlacementKey` state: `const [aiPlacementKey, setAiPlacementKey] = useState(0)`.

2. Add `handleAiAutoPlace` callback:
   ```typescript
   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<string, string>;
     };
     // 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]);
   ```

3. Pass `aiPlacementKey` to `PdfViewerWrapper`:
   ```tsx
   <PdfViewerWrapper
     docId={docId}
     docStatus={docStatus}
     onFieldsChanged={handleFieldsChanged}
     selectedFieldId={selectedFieldId}
     textFillData={textFillData}
     onFieldSelect={setSelectedFieldId}
     onFieldValueChange={handleFieldValueChange}
     aiPlacementKey={aiPlacementKey}   // NEW
   />
   ```

4. Pass `onAiAutoPlace={handleAiAutoPlace}` to `PreparePanel`.

Key design decisions:
- AI placement REPLACES all existing fields in the DB (the route's `db.update` overwrites `signatureFields`)
- FieldPlacer re-fetches from DB via the aiPlacementKey increment — this is the single source of truth
- textFillData is MERGED (not replaced) so any manually typed values are preserved alongside AI pre-fills
- previewToken is reset to null — agent must re-preview after AI placement
- Error from the route is thrown and caught in PreparePanel's handleAiAutoPlaceClick, displayed via the existing `result` state
  </action>
  <verify>
    <automated>cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | grep -E "PreparePanel|DocumentPageClient|error TS" | head -20</automated>
  </verify>
  <done>
    - PreparePanel has "AI Auto-place Fields" button with violet color, loading state, error display
    - DocumentPageClient has handleAiAutoPlace calling /api/documents/[id]/ai-prepare
    - After success: textFillData merges AI pre-fills, aiPlacementKey increments, previewToken resets to null
    - Error from route is surfaced to user via PreparePanel result state
    - TypeScript compiles without errors across all 4 modified files
  </done>
</task>

</tasks>

<verification>
- `npx tsc --noEmit` passes with no errors
- FieldPlacer.tsx: `aiPlacementKey` in loadFields useEffect dependency array
- PdfViewerWrapper.tsx + PdfViewer.tsx: `aiPlacementKey` threaded through
- PreparePanel.tsx: "AI Auto-place Fields" button renders (violet, above Preview button) for Draft docs
- DocumentPageClient.tsx: `handleAiAutoPlace` callback, `aiPlacementKey` state, wired to both components
- Agent can still drag, move, resize, delete fields after AI placement (existing FieldPlacer behavior unchanged)
</verification>

<success_criteria>
- TypeScript compiles clean for all 4 files
- "AI Auto-place Fields" button is visible in PreparePanel for Draft documents
- Clicking the button calls POST /api/documents/[id]/ai-prepare
- On success: FieldPlacer shows AI-placed fields (via DB re-fetch), textFillData merges pre-fills, previewToken is null
- On error: error message displayed in PreparePanel result area
- Existing functionality unchanged: drag-and-drop, click-to-edit, quick-fill, preview, prepare-and-send
</success_criteria>

<output>
After completion, create `.planning/phases/13-ai-field-placement-and-pre-fill/13-03-SUMMARY.md`
</output>
