Skip to content

Decompose App orchestration into a hook + split Result.tsx #83

Description

@s-annam

Problem

Two surfaces have outgrown the project's own structure rules. App.tsx carries orchestration logic inline (parse state machine, handleFile, JD memo, formatBytes) instead of delegating to a hook — violating CLAUDE.md "Keep business logic in src/lib/… Extract cross-cutting interaction state into src/hooks/." And Result.tsx is ~390 LOC, well past the ~200 LOC decomposition guideline, bundling six distinct concerns in one file.

Current state

  • src/App.tsx (231 LOC): ParseState union (L23-34), handleFile (L56-101), jdMatch memo (L46-54), formatBytes (L36-40), reset (L103). All interaction/orchestration state sits in the component.
  • src/components/Result.tsx (390 LOC): branch + StatusPill + ParsedCard + LimitedParsingCard + AtsScoreReadout (L161-265) + LayoutFlagsList (L133-159) + Dimension (L267-316) + the inline Evidence panel (Source-PDF + extracted-text, L108-128).

Plan

  1. src/hooks/useResumeAnalysis.ts — owns ParseState, handleFile (parse → score → telemetry), reset. Returns { state, handleFile, reset }. App becomes layout-only: drop zone, result region, JD card, footer. formatBytes moves to a small lib/ util (or into the hook).
    • Note: if the edit-loop issue (option A) lands, override state + recompute live here too — coordinate so this hook is the single orchestration owner.
  2. Decompose Result.tsx:
    • src/components/features/EvidencePanel.tsx — the Source-PDF + extracted-text two-pane block (currently L108-128).
    • src/components/features/AtsScoreReadout.tsx — the score readout incl. Dimension (L161-316).
    • src/components/features/LayoutFlagsList.tsx — (L133-159).
    • Result.tsx keeps only the isFontsUnmappable branch + ParsedCard/LimitedParsingCard composition. StatusPill is removed in favor of shared/StatusBadge (primitives issue).
  3. Pure move/extract — no behavior change. Each extracted piece keeps the same props it reads today.

Acceptance criteria

  • App.tsx no longer contains the ParseState machine or handleFile body — it consumes useResumeAnalysis.
  • Result.tsx is under ~200 LOC; EvidencePanel, AtsScoreReadout, LayoutFlagsList are their own files under features/.
  • Rendered output is byte-identical to before (no visual/behavioral change); manual check across parsed / limited / error states.
  • npm run test / npm run typecheck pass with no new warnings.

Notes

Coordinates with the shared-primitives issue (StatusBadge removal) and the edit-loop issue (hook ownership). Order: primitives → this → edit-loop, or land together.

Labels

refactor

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

refactorCode restructuring without behavior change

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions