Files

11 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
14-crm-attio-style-fix 03 ui
react-hook-form
zod
i18n
typescript
shadcn
FollowUpWidget.tsx fully translated to Italian (no English strings)
LeadForm.tsx CreateLeadModal/EditLeadModal typed with CreateLeadInput (no useForm<any>, no as any)
Shared ui/form.tsx FormField component made properly generic (Control<TFieldValues> assignability fix)
SendQuoteModal.tsx onSubmit dead 'new' tab branch removed
14-01
14-02
added patterns
FormField in src/components/ui/form.tsx is now a generic function component (TFieldValues/TName) instead of React.forwardRef<any, ControllerProps<any,any>> — required whenever useForm<T>() uses a concrete (non-any) generic
created modified
.planning/phases/14-crm-attio-style-fix/deferred-items.md
src/components/admin/dashboard/FollowUpWidget.tsx
src/components/admin/leads/LeadForm.tsx
src/components/ui/form.tsx
src/components/admin/leads/SendQuoteModal.tsx
Fixed shared ui/form.tsx FormField generic signature (Rule 3 blocking issue) — required for LeadForm.tsx's useForm<CreateLeadInput>() to type-check against FormField's control prop
Did not retype SendQuoteModal's useForm<any>()/onSubmit(data: any) — attempted fix cascades into a zodResolver/Resolver generic incompatibility from assignQuoteSchema's .optional()/.default() fields; deferred as out-of-scope for CRM-12 (dead-code removal)
When introducing useForm<ConcreteType>() against shadcn's Form/FormField components, verify src/components/ui/form.tsx's FormField generic signature supports concrete Control<T> types (must be a generic function component, not React.forwardRef<any, ControllerProps<any,any>>)
CRM-10
CRM-11
CRM-12
12min 2026-06-14

Phase 14 Plan 03: CRM Residual Bug Fixes (i18n, types, dead code) Summary

Translated FollowUpWidget to Italian, typed LeadForm's useForm with CreateLeadInput (fixing a shared FormField generic bug along the way), and removed SendQuoteModal's unreachable "new" tab onSubmit branch

Performance

  • Duration: ~12 min
  • Started: 2026-06-14T10:38:00Z (approx)
  • Completed: 2026-06-14T10:47:00Z
  • Tasks: 3
  • Files modified: 4 (3 plan-scoped + 1 shared UI component fixed as Rule 3 deviation)

Accomplishments

  • FollowUpWidget.tsx now displays only Italian copy: "Richiedi Follow-up", "{count} lead da contattare" (no pluralization), "Contatta", "Vedi Tutto ({count})", "Tutti i lead sono aggiornati!"
  • LeadForm.tsx's CreateLeadModal and EditLeadModal both use useForm<CreateLeadInput>() with CreateLeadInput imported from lead-validators.ts — no useForm<any>() or as any remains
  • Fixed src/components/ui/form.tsx's FormField component to be properly generic, resolving a Control<T> assignability error that surfaced once LeadForm.tsx adopted a concrete generic
  • SendQuoteModal.tsx's onSubmit no longer contains the unreachable if (tab === "new") { window.location.href = ...; return; } branch — the "new" tab's own onClick-based navigation is unchanged
  • Bonus: fixed react/no-unescaped-entities lint error in SendQuoteModal.tsx (escaped "Proposal Sent"&quot;Proposal Sent&quot;)

Task Commits

Each task was committed atomically:

  1. Task 1: Translate FollowUpWidget.tsx to Italian (CRM-10) - 272e363 (fix)
  2. Task 2: Type LeadForm.tsx's useForm with CreateLeadInput (CRM-11) - ee509cd (fix)
  3. Task 3: Remove dead onSubmit branch in SendQuoteModal.tsx (CRM-12) - a495d84 (fix)

Note: Task 1 verify command (grep ... && npx tsc --noEmit) and overall plan verification both passed with npx tsc --noEmit exiting 0.

Files Created/Modified

  • src/components/admin/dashboard/FollowUpWidget.tsx - All 6 hardcoded English strings translated to Italian; pluralization ternary removed
  • src/components/admin/leads/LeadForm.tsx - useForm<any>()useForm<CreateLeadInput>() in both CreateLeadModal and EditLeadModal; removed local type CreateLeadInput = z.infer<...> redeclaration (now imported from lead-validators.ts); status: lead.status as anystatus: lead.status as CreateLeadInput["status"]
  • src/components/ui/form.tsx - FormField changed from React.forwardRef<any, ControllerProps<any, any>> to a generic function component <TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> (canonical shadcn pattern), fixing Control<T> assignability once form.control is concretely typed
  • src/components/admin/leads/SendQuoteModal.tsx - Removed unreachable if (tab === "new") {...} branch from onSubmit; escaped unescaped " characters in JSX text (react/no-unescaped-entities)
  • .planning/phases/14-crm-attio-style-fix/deferred-items.md - New file logging the 2 pre-existing @typescript-eslint/no-explicit-any lint errors left in SendQuoteModal.tsx (out of scope for CRM-12)

Decisions Made

  • Fixed src/components/ui/form.tsx's FormField generic signature as a Rule 3 (blocking issue) auto-fix during Task 2 — without it, npx tsc --noEmit failed with 12 Control<CreateLeadInput,...> not assignable to Control<any,any,any> errors once useForm<CreateLeadInput>() was introduced. This is the first file in the codebase to use a concrete useForm<T>() generic alongside the shared Form/FormField components, so the bug was previously masked by useForm<any>() everywhere.
  • Attempted (then reverted) typing SendQuoteModal.tsx's useForm<any>()/onSubmit(data: any) with a local AssignQuoteInput = z.infer<typeof assignQuoteSchema> type to fully satisfy npx eslint's no-explicit-any rule. This cascaded into 3 new tsc errors (Resolver<...> generic incompatibility) caused by assignQuoteSchema's .optional()/.default() fields producing divergent Zod input/output types. Reverted to keep tsc clean (CRM-12's actual acceptance criteria); logged to deferred-items.md as out of scope for this isolated dead-code-removal task.

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] Fixed FormField generic signature in shared ui/form.tsx

  • Found during: Task 2 (LeadForm.tsx useForm typing)
  • Issue: FormField was declared as React.forwardRef<any, ControllerProps<any, any>>. Once LeadForm.tsx's useForm<any>() became useForm<CreateLeadInput>(), form.control became Control<CreateLeadInput, any, {...}>, which TypeScript reported as not assignable to FormField's control={form.control} prop typed via ControllerProps<any, any> — 12 tsc errors across both modals (one per FormField usage).
  • Fix: Rewrote FormField as a generic function component <TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>>(props: ControllerProps<TFieldValues, TName>), matching the canonical (current) shadcn/ui pattern. FormFieldContext and Controller usage unchanged.
  • Files modified: src/components/ui/form.tsx
  • Verification: npx tsc --noEmit went from 12 errors to 0; npx eslint src/components/ui/form.tsx clean
  • Committed in: ee509cd (Task 2 commit)

2. [Rule 1 - Lint cleanup] Escaped unescaped double quotes in SendQuoteModal.tsx JSX text

  • Found during: Task 3 (SendQuoteModal.tsx dead-branch removal verification)
  • Issue: react/no-unescaped-entities ESLint error on the line "Proposal Sent". inside a <p> element (pre-existing, in the same file being edited)
  • Fix: Replaced literal " with &quot;&quot;Proposal Sent&quot;.
  • Files modified: src/components/admin/leads/SendQuoteModal.tsx
  • Verification: npx eslint error count for this file dropped from 4 to 2 (remaining 2 are unrelated no-explicit-any, see below)
  • Committed in: a495d84 (Task 3 commit)

Total deviations: 2 auto-fixed (1 blocking, 1 lint cleanup) Impact on plan: Both fixes were necessary to meet the plan's stated npx tsc --noEmit/npx eslint exit-0 verification for the modified files. No scope creep beyond what was required to make CRM-10/11/12 type-check and lint cleanly. One additional lint issue (pre-existing no-explicit-any in SendQuoteModal.tsx, unrelated to CRM-12) was investigated, found to require an out-of-scope schema/resolver restructuring, and deferred — documented in deferred-items.md.

Issues Encountered

  • npx eslint src/components/admin/leads/SendQuoteModal.tsx still reports 2 @typescript-eslint/no-explicit-any errors (lines ~39 useForm<any>() and ~48 onSubmit(data: any)), both pre-existing and unrelated to the CRM-12 dead-branch removal. A fix attempt (typing both with a local AssignQuoteInput derived from assignQuoteSchema) caused 3 new tsc errors due to a zodResolver/Resolver<...> generic mismatch stemming from assignQuoteSchema's .optional()/.default() fields (Zod input vs output type divergence). Reverted; logged to .planning/phases/14-crm-attio-style-fix/deferred-items.md for future cleanup. This does not block CRM-10/11/12 — all three requirements' acceptance criteria (dead branch removed, Italian translation complete, LeadForm fully typed) are satisfied, and npx tsc --noEmit exits 0 across the whole project.

Known Stubs

None - no stubs introduced. All three fixes are direct edits to existing, fully-wired components.

Threat Flags

None - all three fixes are display-copy translation, compile-time type-tightening, and dead-code removal. No new network endpoints, auth paths, file access, or schema changes introduced. Matches the plan's threat_model dispositions (T-14-11 mitigate via stronger typing — done; T-14-12 and T-14-13 accept — no security-relevant change).

User Setup Required

None - no external service configuration required.

Next Phase Readiness

  • CRM-10, CRM-11, CRM-12 all satisfied — these were the last residual non-UI bugs from Phase 14's scope (the Attio-style table redesign itself is covered by Plans 14-01/14-02)
  • npx tsc --noEmit exits 0 across the whole project after this plan
  • Remaining open item: 2 pre-existing no-explicit-any lint errors in SendQuoteModal.tsx (deferred-items.md) — not blocking, can be addressed in a future cleanup pass alongside other assignQuoteSchema/zodResolver typing work

Phase: 14-crm-attio-style-fix Completed: 2026-06-14

Self-Check: PASSED

  • FOUND: src/components/admin/dashboard/FollowUpWidget.tsx
  • FOUND: src/components/admin/leads/LeadForm.tsx
  • FOUND: src/components/admin/leads/SendQuoteModal.tsx
  • FOUND: src/components/ui/form.tsx
  • FOUND: .planning/phases/14-crm-attio-style-fix/14-03-SUMMARY.md
  • FOUND: .planning/phases/14-crm-attio-style-fix/deferred-items.md
  • FOUND commit: 272e363 (Task 1)
  • FOUND commit: ee509cd (Task 2)
  • FOUND commit: a495d84 (Task 3)
  • FOUND commit: 6b51403 (plan metadata)