Files

8.3 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 02 frontend
react
nextjs
leads
crm
database-view
inline-edit
tags
phase provides
14-01-leads-data-layer LeadWithTags/LeadFieldOptions types, getLeadsWithTags()/getLeadFieldOptions() queries, updateLeadField/addLeadTag/removeLeadTag/renameLeadTag server actions
LeadTable.tsx — raw <table> database-view component (8 columns: Nome/Email/Telefono/Azienda/Stato/Prossima Azione/Tag/Azioni), EditableCell + StatusCell + OptionMultiSelect
LeadsSearch.tsx — client-side instant filter across name/email/company/status/tags
LeadDetail.tsx Profilo card — OptionMultiSelect for lead tags, wired to addLeadTag/removeLeadTag/renameLeadTag
added patterns
Local StatusCell component: closed click-to-open dropdown over a fixed enum (LEAD_STAGES), preserving a semantic color map — used instead of OptionSelect when a shared component's props contract can't express semantic per-value badge colors without modification
run() transition+error helper pattern (from ServiceRow) replicated in LeadRow and LeadDetail for inline-edit/tag mutations with router.refresh()
created modified
src/app/admin/leads/LeadsSearch.tsx
src/components/admin/leads/LeadTable.tsx
src/app/admin/leads/page.tsx
src/app/admin/leads/[id]/page.tsx
src/components/admin/leads/LeadDetail.tsx
[id]/page.tsx now sources lead+tags via getLeadsWithTags().find(id) + getLeadFieldOptions() (plan's approach (a)) instead of getLeadById, removing one query function call while reusing Plan 14-01's left-join
Two plan acceptance_criteria greps are stale relative to their own analog files (CatalogSearch.tsx) and were treated as documentation inaccuracies, not implementation defects — see Deviations
StatusCell-style local closed-dropdown for fixed-enum fields needing semantic per-value badge colors, as an alternative to OptionSelect
CRM-08
CRM-09
~25min 2026-06-14

Phase 14 Plan 02: LeadTable Attio Rewrite Summary

Rewrote LeadTable.tsx as a raw-table Attio-style database view with inline editing, a closed status dropdown preserving semantic colors, and lead tags via OptionMultiSelect; added LeadsSearch.tsx for client-side instant filtering; rewired /admin/leads and /admin/leads/[id] to the new data layer; surfaced lead tags in LeadDetail's "Profilo" card.

Performance

  • Duration: ~25 min
  • Completed: 2026-06-14T13:22:00+02:00
  • Tasks: 2
  • Files modified: 4 (+1 created)

Accomplishments

  • LeadTable.tsx fully rewritten: raw <table> (no shadcn Table wrapper), 8 columns (Nome/Email/Telefono/Azienda/Stato/Prossima Azione/Tag/Azioni)
  • EditableCell wired for name (required), email, phone, company, next_action → updateLeadField
  • New local StatusCell: click-to-open closed dropdown over the 6 LEAD_STAGES values only (no free-text/create option), preserving semantic STAGE_COLOR badges (won=green, lost=red, etc.) per UI-SPEC Decision 1 — implemented without modifying shared option-select.tsx
  • OptionMultiSelect for lead tags, wired to addLeadTag/removeLeadTag/renameLeadTag
  • Per-row error <tr> and "Nessun lead trovato" empty state
  • New LeadsSearch.tsx: instant client-side filter over name/email/company/status/tags (mirrors CatalogSearch.tsx)
  • page.tsx rewired to fetch getLeadsWithTags() + getLeadFieldOptions() in parallel, render LeadsSearch + CreateLeadModal, revalidate = 0
  • [id]/page.tsx rewired to fetch getLeadsWithTags()/getLeadFieldOptions(), locate the lead by id, pass tags/tagOptions to LeadDetail
  • LeadDetail.tsx "Profilo" card now includes an OptionMultiSelect for lead tags with its own run()/error-display helper, identical mutation pattern to LeadTable

Task Commits

Each task was committed atomically:

  1. Task 1: Rewrite LeadTable.tsx as raw-table database-view (CRM-08, CRM-09) - 4887a31 (feat)
  2. Task 2: Create LeadsSearch.tsx, rewire page.tsx, surface tags in LeadDetail.tsx - ab7fa62 (feat)

Files Created/Modified

  • src/components/admin/leads/LeadTable.tsx - Full rewrite: raw-table LeadRow/LeadTable, new StatusCell, EditableCell/OptionMultiSelect wiring
  • src/app/admin/leads/LeadsSearch.tsx - New file: client-side instant filter wrapper around LeadTable
  • src/app/admin/leads/page.tsx - Rewired to getLeadsWithTags()/getLeadFieldOptions() + LeadsSearch, revalidate = 0, dropped Suspense/LeadsList
  • src/app/admin/leads/[id]/page.tsx - Rewired to getLeadsWithTags()/getLeadFieldOptions() (dropped getLeadById), passes tags/tagOptions to LeadDetail
  • src/components/admin/leads/LeadDetail.tsx - Added tags/tagOptions props, OptionMultiSelect + run() helper in "Profilo" card, imports for useState/useTransition/useRouter/addLeadTag/removeLeadTag/renameLeadTag/OptionMultiSelect

Decisions Made

  • Followed the plan's <action> blocks essentially verbatim, including the plan's own correction to use a local StatusCell instead of OptionSelect (the plan pre-emptively documents and resolves this — not a Claude-discretion deviation, just executing the plan as written)
  • For [id]/page.tsx, used plan's approach (a): getLeadsWithTags().find(l => l.id === id) + getLeadFieldOptions(), calling notFound() if no match — removes the getLeadById import entirely

Deviations from Plan

Documentation-only (plan acceptance_criteria inaccuracies, not implementation defects)

1. grep -c '"Dettagli"' LeadTable.tsx expects 1, returns 0

  • The plan's own <action> code renders <Link href={...}>Dettagli</Link> — "Dettagli" as JSX text content, not a quoted string literal "Dettagli". The implementation matches the <action> block exactly; the acceptance grep (which looks for literal "Dettagli") was simply never going to match this code shape.
  • No fix needed — <behavior> and <action> blocks both specify this exact JSX form.

2. grep -c "useMemo" LeadsSearch.tsx expects 1, returns 2

  • LeadsSearch.tsx is a 1:1 structural port of CatalogSearch.tsx, which itself returns 2 for this same grep (1 import line + 1 usage line). Verified: grep -c "useMemo" src/app/admin/catalog/CatalogSearch.tsx also returns 2. The acceptance criterion is stale relative to its own reference implementation.
  • No fix needed — implementation matches the established pattern exactly.

Pre-existing, unrelated (not introduced by this plan)

3. Unused Button import in LeadDetail.tsx

  • npx eslint src/components/admin/leads/LeadDetail.tsx reports 1 warning: @typescript-eslint/no-unused-vars for the Button import.
  • Confirmed pre-existing: git show main:src/components/admin/leads/LeadDetail.tsx | grep -c '\bButton\b' returns 1 (import-only) on main as well, before this plan's edits. Not touched — out of scope per "Do not modify any other part of LeadDetail.tsx" instruction.

Total deviations: 0 implementation deviations. 2 stale plan-doc acceptance criteria (documented above, both verified against their reference analogs). 1 pre-existing lint warning (unrelated, untouched). Impact on plan: None — npx tsc --noEmit exits 0 across the whole project; npx eslint exits 0 (errors) with only the 1 pre-existing warning noted above.

Issues Encountered

None beyond the documentation-only items above.

User Setup Required

None — no new dependencies, no schema/migration changes (this plan is pure frontend wiring on top of Plan 14-01's already-applied data layer).

Next Phase Readiness

  • /admin/leads and /admin/leads/[id] now fully deliver CRM-08 (inline-edit database view) and CRM-09 (lead tags)
  • This was the only plan in Wave 2 and the last plan in Phase 14 — ready for phase-level verification (CRM-08 through CRM-12 across all 3 plans)

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

Self-Check: PASSED

  • FOUND: src/components/admin/leads/LeadTable.tsx
  • FOUND: src/app/admin/leads/LeadsSearch.tsx
  • FOUND: src/app/admin/leads/page.tsx
  • FOUND: src/app/admin/leads/[id]/page.tsx
  • FOUND: src/components/admin/leads/LeadDetail.tsx
  • FOUND: .planning/phases/14-crm-attio-style-fix/14-02-SUMMARY.md
  • FOUND commit: 4887a31
  • FOUND commit: ab7fa62