Skip to content

refactor(components): extract 4 DraftTab hooks (Wave 5.7 Phase A part 1)#54

Merged
saagpatel merged 4 commits into
masterfrom
codex/refactor/wave5-7-draft-tab
Apr 21, 2026
Merged

refactor(components): extract 4 DraftTab hooks (Wave 5.7 Phase A part 1)#54
saagpatel merged 4 commits into
masterfrom
codex/refactor/wave5-7-draft-tab

Conversation

@saagpatel
Copy link
Copy Markdown
Owner

What

Extracts 4 of the 5 planned Phase A hooks from src/components/Draft/DraftTab.tsx into dedicated sibling hook files. DraftTab gains its first-ever direct unit tests via renderHook coverage on each extracted hook.

  • `useDraftApproval` — approval-search sub-feature (7 state, 2 async handlers, auto-reset effect)
  • `useDraftFirstResponse` — first-touch response generator (3 state, 3 handlers)
  • `useDraftChecklist` — checklist generation/update/toggle (5 state, 4 handlers)
  • `useDraftIntake` — case-intake state + preset application + analyze + note-audience sync (1 state, 4 handlers, `INTAKE_PRESETS` constant)

File size: 2573 → 2283 LOC (-290, ~11% reduction).

Why

DraftTab.tsx is the largest file in the codebase (29 useState + 55 useCallback + forwardRef). The Wave 5 plan targets it as the final decomposition pass. Each hook encapsulates one distinct workflow concern (approval search, first response, checklist, intake) that is independent of the others and now testable in isolation.

How

Sequential hook extractions, one commit per hook. Each extraction:

  1. Creates useDraft<Name>.ts in src/components/Draft/.
  2. Moves the relevant state + callbacks out of the shell.
  3. Exposes individual setters where useWorkspaceDraftState needs to hydrate fields on draft load (preserves that hook's API).
  4. Adds a useDraft<Name>.test.ts with 2-3 renderHook tests.
  5. Updates the shell to consume the hook return value.

Deferred to a follow-up PR

  • useDraftGeneration hook (Phase A build(deps): bump numpy from 2.2.6 to 2.4.3 in /search-api #5) — the generation/alternatives pathway cross-touches 8+ pieces of shell state (response, sources, metrics, confidence, grounding, originalResponse, isResponseEdited, alternatives, alternativeIndex, generatingAlternative) that are read across dozens of downstream handlers. Extracting it cleanly requires careful setter plumbing that didn't fit this PR's commit budget.
  • Phase B (sub-component panels)DraftIntakePanel, DraftResponsePanel, DraftChecklistPanel.
  • Phase C (shell reduction to ≤ 700 LOC) — depends on Phase B.
  • Phase D (DraftTab integration tests) — depends on Phase C.

The 4 hook extractions here are independent and commit-revertable. The follow-up PR builds on the current shell.

Testing

  • pnpm tsc --noEmit — clean.
  • pnpm test — 196 tests pass (was 184; +12 new hook tests across 4 files).
  • pnpm ui:gate:static — clean (only pre-existing warnings).
  • Manual walk-through of the draft workflow should still render intake → first response → checklist → approval search → copy / save cleanly.

Risk / Notes

  • DraftTab default export unchanged. DraftTabHandle interface unchanged.
  • useWorkspaceDraftState consumer contract preserved — individual setters (setApprovalQuery, setApprovalSummary, setApprovalSources, setApprovalResults, setApprovalError, setCaseIntake, setFirstResponse, setChecklistItems, setChecklistCompleted, setChecklistError) are still exposed from the hooks and passed into the draft state manager.
  • Each commit individually revertable.

Move the approval-search sub-feature (state + search + summarize +
auto-reset effect) out of DraftTab.tsx into a dedicated hook. The
shell still owns individual setters so useWorkspaceDraftState can
hydrate approval fields when loading a saved draft, but the reset-
on-clear effect and the two async handlers live in the hook.
Move the first-response state (text + tone + generating flag) and
its three callbacks (generate, copy, clear) out of DraftTab.tsx into
a dedicated hook. Shell passes input/ocrText/currentTicket/modelLoaded
in; hook owns state and async flow.
Move checklist state (items, completed ids, generating/updating flags,
local error) and its four callbacks (generate, update, toggle, clear)
out of DraftTab.tsx into a dedicated hook. Shell passes input/ocrText/
diagnosticNotes/treeResult/currentTicket/modelLoaded in; hook owns the
async flows and the completion toggle semantics.
Move caseIntake state plus its four handlers (field change, analyze,
preset apply, note-audience change) and the INTAKE_PRESETS constant
out of DraftTab.tsx into a dedicated hook. Shell passes input,
currentTicket(Id), response, logEvent, and setWorkspacePersonalization
in; hook owns the caseIntake state and returns setCaseIntake so
useWorkspaceDraftState can still hydrate intake on draft load.
@saagpatel saagpatel merged commit 7e47e4d into master Apr 21, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants