docs(v2.0): finalize roadmap + requirements — phases 7-11 approved for planning

- Upgrade PROJECT.md: v2.0 Business Operations Suite (unified catalog, offers with phases, public quotes, CRM pipeline, auto-provisioning)
- Create REQUIREMENTS.md sections: CAT-U, OFFER-B, QUOTE, CRM, WIN requirements for phases 7-11
- Extend ROADMAP.md: 5-phase structure (7-11), 14-19 days total, risk/flag registry
- Archive v1.0 milestone (phases 1-6, complete)
- Switch STATE.md to v2.0, status=planning
- Create research documents: STACK.md, FEATURES_v2.0.md, ARCHITECTURE.md, PITFALLS.md, SUMMARY.md

Ready for: /gsd-plan-phase 7

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 06:01:59 +02:00
parent 6239eed6e6
commit 3b1f49d059
8 changed files with 2085 additions and 84 deletions
+32
View File
@@ -0,0 +1,32 @@
# Milestones
## v1.0 Client Portal & Offer System (Shipped: 2026-06-10)
**Phases completed:** 6 phases, 24 plans, 29 tasks
**Key accomplishments:**
- Next.js 16.2.6 App Router con TypeScript strict, Tailwind v4, Drizzle ORM + postgres-js per Coolify Postgres, e 10 componenti shadcn/ui installati e pronti.
- Schema Drizzle ORM completo con 10 tabelle, migration SQL generata e schema live sul database Postgres 16 (Hetzner/Coolify). TypeScript strict compila senza errori.
- Edge proxy con validazione token, ClientView type system che esclude quote_items, e Server Component che fetcha tutti i dati cliente senza esporre segreti admin. Build Next.js 16 senza errori TypeScript.
- Tutti i componenti UI della dashboard cliente renderizzati come Server Components con design light & clean in Tailwind v4: header con logo iamcavalli + brand name cliente, progress bar globale, timeline laterale delle fasi con barre per fase e task list, sezione pagamenti con badge stato (zero importi singoli), link documenti esterni e log note read-only.
- Task 1: scripts/seed.ts
- 1. [Rule 1 - Bug] Next.js 16 proxy export name is 'proxy', not 'middleware'
- Admin home shows all clients with Acconto/Saldo payment badges; new client form inserts client row + two payment stubs via Zod-validated Server Action with nanoid token auto-generation
- Full-featured admin client workspace with Radix Tabs covering phases/tasks, payments, documents, and comments — all mutations via inline Server Actions with server-side validation.
- Client-facing approval and comment API routes with token validation, ownership verification, approved_at immutability enforcement, and inline ApproveButton/CommentForm/CommentList wired into the Phase 1 dashboard via PhaseTimeline.
- Added `custom_label text` column and made `service_id` nullable in `quote_items` via Drizzle schema edit + Neon DDL push — unblocking Wave 2 free-form quote items
- Vertical slice completo `/admin/catalog`: NavBar link + pagina catalogo + tabella con edit inline + form aggiunta servizio + soft-delete toggle, con Server Actions protetti da Zod e requireAdmin()
- Admin quote builder tab with catalog dropdown, freeform toggle, items table with calculated total, and accepted_total editor — all backed by Zod-validated Server Actions with requireAdmin guard.
- Verifica umana completa: catalogo servizi → preventivo → accepted_total → dashboard cliente, con conferma security constraint quote_items mai esposti
- NavBar
- `/admin/projects/[id]`
- `/api/internal/validate-slug/route.ts`
- Five Offer System tables added to schema.ts and production DB via direct SQL migration, with Phase 4 projects table bootstrapped from existing client data.
- Full admin CRUD for macro-offers, micro-offers, and offer services at /admin/offers with a client-side checkbox list for service-to-micro assignments and NavBar updated with Forecast and Offerte links.
- OffersTab client component for assigning micro-offers to projects with inline accepted_total editing, plus active-offers summary section on the client detail page showing public_name per project.
- Client dashboard now shows active offers per project (public_name + cumulative service price + accepted_total), and /admin/forecast displays a 12-month server-side revenue projection spread from project_offers.accepted_total across duration_months.
- AdminSidebar.tsx
- dashboard-queries.ts
---
+57 -41
View File
@@ -2,45 +2,56 @@
## What This Is ## What This Is
Strumento personale in due parti per gestire i clienti di consulenza: una dashboard web (Vercel) dove ogni cliente accede con un link segreto per vedere il suo progetto, e un flusso Claude per aggiungere clienti step-by-step e generare piani + preventivi. Fatto per un professionista del personal branding con clienti attivi da gestire subito. Suite operativa per un consulente di personal branding, live su hub.iamcavalli.net (Coolify/Hetzner): un'area admin (`/admin/*`) per gestire clienti, progetti, offerte e pagamenti, e una dashboard cliente via link segreto (`/client/[token]`) dove ogni cliente vede lo stato del suo progetto. Con la v2.0 si espande in suite completa: catalogo servizi unificato, builder offerte con fasi, preventivi pubblici multistep e CRM con pipeline lead.
## Core Value ## Core Value
Il cliente apre il link e vede esattamente a che punto è il suo progetto, cosa deve ancora succedere e cosa ha già approvato — senza dover scrivere email per chiedere aggiornamenti. Il cliente apre il link e vede esattamente a che punto è il suo progetto, cosa deve ancora succedere e cosa ha già approvato — senza dover scrivere email per chiedere aggiornamenti.
## Current Milestone: v2.0 Business Operations Suite
**Goal:** Trasformare ClientHub da portale clienti a suite operativa completa: catalogo servizi unificato + builder offerte con fasi, generazione preventivi pubblici, e CRM con pipeline lead che al "Vinto" crea automaticamente cliente e progetto nell'hub.
**Target features:**
- **Catalogo & Offerte** — tabella `services` unificata (sostituisce `service_catalog` + `offer_services`); builder offerte con fasi e drag&drop dei servizi tra fasi; tag tipo offerta (Entry, Signature, Retainer, custom); tier indipendenti (es. Signature A/B/C)
- **Preventivi** — pagina pubblica multistep HTML/CSS generata da cliente + offerte + prezzi scelti di volta in volta; obiettivo delivery entro 2 ore dalla call
- **CRM** — pipeline lead (→ Vinto/Perso, preventivo prerequisito); reminder follow-up in dashboard; al "Vinto" auto-crea cliente + progetto con fasi copiate dall'offerta scelta (modificabili, visibili nella pagina pubblica cliente), importo accettato, 1-4 pagamenti configurabili caso per caso
## Requirements ## Requirements
### Validated ### Validated
(None yet — ship to validate) Shipped in v1.0 (Phases 16, in produzione su hub.iamcavalli.net):
- ✓ Ogni cliente ha un URL segreto univoco, nessun login (token rotatable + slug opzionale) — Phase 1/4
- ✓ Dashboard cliente: brand, brief, fasi/task con stato, progress, multi-progetto — Phase 1/4
- ✓ Il cliente approva deliverable (approved_at immutabile) e lascia commenti — Phase 2
- ✓ Il cliente vede solo il totale accettato, mai i prezzi dei singoli servizi — Phase 2/3
- ✓ Stato pagamenti acconto/saldo visibile al cliente — Phase 1
- ✓ Link a documenti esterni + storico note/decisioni — Phase 1/2
- ✓ Area admin completa: clienti, progetti, fasi, task, pagamenti, documenti — Phase 2/4
- ✓ Catalogo servizi + quote builder admin (quote_items mai esposti al cliente) — Phase 3
- ✓ Sistema offerte: macro/micro/servizi, assegnazione a progetti, forecast 12 mesi — Phase 5
- ✓ Offerte attive visibili nella dashboard cliente (solo public_name) — Phase 5
- ✓ UX admin: sidebar + dashboard operativa con KPI e activity feed — Phase 6
### Active ### Active
**Dashboard cliente (priorità v1):** **Catalogo & Offerte (priorità 1):**
- [ ] Ogni cliente ha un URL segreto univoco (nessun login richiesto) - [ ] Catalogo servizi unificato (nome + prezzo unitario) usato da offerte e preventivi
- [ ] La dashboard mostra nome cliente, nome brand, brief del progetto e stato attuale - [ ] Offerta = nome + tag tipo (Entry/Signature/Retainer/custom) + fasi ordinate
- [ ] Il piano è strutturato per fasi con milestone e task all'interno di ogni fase - [ ] Ogni fase di un'offerta contiene servizi assegnati, spostabili via drag&drop tra fasi
- [ ] I task hanno stato visibile (da fare / in corso / fatto) - [ ] Migrazione dati: consolidare `service_catalog` + `offer_services` senza perdita
- [ ] Il cliente può approvare i deliverable dalla sua area
- [ ] Il cliente può lasciare commenti su task e deliverable
- [ ] Il cliente vede solo il totale del preventivo accettato (non i prezzi dei singoli servizi)
- [ ] Il cliente vede lo stato dei pagamenti: acconto 50% (da saldare / inviata / saldato) e saldo 50% (da saldare / inviata / saldato)
- [ ] Link a documenti e file (Google Drive, PDF, deliverable)
- [ ] Storico note e decisioni prese nel tempo
**Area amministratore (tu):** **Preventivi (priorità 2):**
- [ ] Vista di tutti i clienti con stato sintetico - [ ] Generazione preventivo: cliente + 1-3 offerte + prezzi impostati di volta in volta
- [ ] Gestione completa di ogni cliente: fasi, task, documenti, pagamenti - [ ] Pagina pubblica multistep (HTML/CSS) consultabile dal lead via link
- [ ] Preventivo completo con dettaglio servizi (non visibile al cliente) - [ ] Delivery del preventivo entro 2 ore dalla call (flusso assistito)
**Catalogo servizi:** **CRM (priorità 3):**
- [ ] File/database dei servizi con prezzi e cosa è incluso - [ ] Pipeline lead con stati fino a Vinto/Perso (preventivo inviato come prerequisito del Vinto)
- [ ] Usato come base per la generazione assistita dei preventivi - [ ] Reminder follow-up in dashboard: ultima interazione, chi contattare oggi
- [ ] Al "Vinto": auto-creazione cliente + progetto con fasi copiate dall'offerta scelta, importo accettato, 1-4 pagamenti scelti caso per caso
**Flusso Claude (v2):**
- [ ] Onboarding guidato step-by-step via chat per aggiungere un nuovo cliente
- [ ] Generazione del piano a fasi basato sul brief
- [ ] Generazione preventivo assistita (Claude suggerisce, tu approvi prima di finalizzare)
### Out of Scope ### Out of Scope
@@ -48,32 +59,37 @@ Il cliente apre il link e vede esattamente a che punto è il suo progetto, cosa
- App mobile nativa — solo web responsive - App mobile nativa — solo web responsive
- Multi-utente con team — solo tu come admin per ora - Multi-utente con team — solo tu come admin per ora
- Prezzi singoli visibili al cliente — vede solo il totale accettato - Prezzi singoli visibili al cliente — vede solo il totale accettato
- Email OTP per accesso cliente — design pronto ma deferito a batch successivo su richiesta utente
- File hosting — documenti solo come URL esterni (v1 constraint, ancora valido)
## Context ## Context
- Il professionista lavora nel personal branding e content creation (cfr. SparklingOrbit) - Produzione: hub.iamcavalli.net su Coolify (Hetzner), Postgres self-hosted, deploy via webhook Gitea
- Ha clienti attivi ora — la dashboard è la priorità immediata prima del flusso Claude - Stack: Next.js 16 App Router, Drizzle ORM, Auth.js v4 (admin), token middleware (clienti), Tailwind v4, shadcn/ui
- I clienti accedono via link segreto fisso (no account, no password) per semplicità massima - Tutto sotto la stessa app: `/admin/*` (sessione Auth.js) + `/client/[token]/*` (token) + future pagine pubbliche preventivo
- Il preventivo ha sempre struttura acconto 50% + saldo 50% - La sidebar admin (Phase 6) è l'hub di navigazione per le nuove sezioni (CRM, Preventivi, Offerte)
- Il catalogo servizi va costruito da zero durante il progetto - `project_offers` (Phase 5) già copre l'attribuzione di offerte a progetti esistenti — da rifinire, non ricostruire
- Piattaforma di deploy: Vercel - Il flusso commerciale reale: call con lead → preventivo 3 tier (es. Signature A/B/C) in giornata → accettazione → onboarding automatico nell'hub
## Constraints ## Constraints
- **Urgenza**: Clienti attivi da gestire subito — la dashboard cliente deve arrivare per prima - **Data Safety (LOCKED)**: migration solo additive su `clients`, `projects`, `payments`, `phases` — la migrazione del catalogo servizi va pianificata per consolidare senza drop/truncate
- **Semplicità accesso cliente**: Link segreto senza login — nessuna friction per il cliente - **Architettura (LOCKED)**: `clients.token` separato e rotatable; `quote_items` mai esposti via client API; `deliverables.approved_at` immutabile; no file hosting
- **Privacy preventivo**: Il cliente vede solo il totale, mai il dettaglio dei servizi - **Stesso DB**: tutte le nuove aree (CRM, preventivi, offerte) leggono/scrivono lo stesso Postgres — nessun servizio separato
- **Deploy**: Vercel su sottodominio `welcomeclient.iamcavalli.net` - **Numerazione fasi**: v2.0 parte da Phase 7 (v1.0 ha chiuso alla 6)
- **Dominio**: sottodominio di iamcavalli.net — richiede configurazione DNS su dominio esistente
## Key Decisions ## Key Decisions
| Decision | Rationale | Outcome | | Decision | Rationale | Outcome |
|----------|-----------|---------| |----------|-----------|---------|
| Link segreto senza login per i clienti | Massima semplicità — nessun account da creare, zero friction | — Pending | | Link segreto senza login per i clienti | Massima semplicità — nessun account da creare, zero friction | ✓ Good |
| Dashboard prima del flusso Claude | Clienti attivi ora, la visibilità al cliente è il valore immediato | — Pending | | Dashboard prima del flusso Claude | Clienti attivi ora, la visibilità al cliente è il valore immediato | ✓ Good |
| Preventivo: cliente vede solo il totale | Il dettaglio dei prezzi è informazione commerciale riservata | — Pending | | Preventivo: cliente vede solo il totale | Il dettaglio dei prezzi è informazione commerciale riservata | ✓ Good |
| Catalogo servizi da costruire da zero | Nessun listino esistente — parte del progetto stesso | — Pending | | Suite unica sotto /admin/* invece di 3 app separate | Stesso DB, stesso deploy, auth unica — overhead di 3 app ingiustificato per singolo admin | — Pending |
| Catalogo servizi unificato (una tabella `services`) | Due cataloghi paralleli (service_catalog + offer_services) duplicano manutenzione prezzi | — Pending |
| Tier offerte indipendenti (A/B/C separati, stesso tag) | Più semplice di un meccanismo di ereditarietà; ogni tier configurato a sé | — Pending |
| Prezzi pacchetti per-preventivo, non da catalogo | Permette di alzare i prezzi nel tempo senza toccare il catalogo | — Pending |
| Al "Vinto" le fasi dell'offerta sono COPIATE nel progetto | Il progetto resta modificabile senza toccare il template offerta | — Pending |
## Evolution ## Evolution
@@ -93,4 +109,4 @@ This document evolves at phase transitions and milestone boundaries.
4. Update Context with current state 4. Update Context with current state
--- ---
*Last updated: 2026-05-15Phase 2 complete (admin area + client interactions)* *Last updated: 2026-06-10Milestone v2.0 started (Business Operations Suite)*
+80 -8
View File
@@ -1,4 +1,4 @@
# Requirements — ClientHub # Requirements — ClientHub v2.0 Business Operations Suite
## Dashboard Cliente (v1) ## Dashboard Cliente (v1)
@@ -51,20 +51,69 @@
| OFFER-05 | La dashboard cliente mostra le offerte attive con nome pubblico, prezzo cumulativo dei servizi inclusi e prezzo finale accettato in evidenza; se ha più offerte le vede tutte | Pending | | OFFER-05 | La dashboard cliente mostra le offerte attive con nome pubblico, prezzo cumulativo dei servizi inclusi e prezzo finale accettato in evidenza; se ha più offerte le vede tutte | Pending |
| OFFER-06 | Admin ha una vista revenue forecast dei 12 mesi successivi basata su offerte attive, durata e accepted_total; breakdown mensile con totale fatturato per mese | Pending | | OFFER-06 | Admin ha una vista revenue forecast dei 12 mesi successivi basata su offerte attive, durata e accepted_total; breakdown mensile con totale fatturato per mese | Pending |
## Flusso Claude (v2 — deferred to Phase 6) ## Catalogo Unificato & Offer Builder (Phase 78)
| ID | Requirement | Status |
|----|-------------|--------|
| CAT-U-01 | Un'unica tabella `services` unifica `service_catalog` + `offer_services`; migrazione zero data loss con audit trail (`migrated_from`) | Phase 7 |
| CAT-U-02 | Admin `/admin/catalog` list/add/edit/soft-delete per i servizi singoli (nome, prezzo_unitario, attivo) | Phase 7 |
| OFFER-B-01 | Offer = nome + tag tipo (Entry/Signature/Retainer/custom) + fasi ordinate; fasi indipendenti l'una dall'altra (es. Signature A/B/C non ereditate) | Phase 8 |
| OFFER-B-02 | `/admin/offers/[id]/edit` two-column builder: left colonna servizi (ricerca), right colonna fasi con drag&drop tra fasi e dentro fase (reorder) | Phase 8 |
| OFFER-B-03 | Drag&drop salva per singolo drop con optimistic update client-side + validazione server; versioning previene race condition da multi-tab edit | Phase 8 |
## Quote Builder & Public Proposal Pages (Phase 9)
| ID | Requirement | Status |
|----|-------------|--------|
| QUOTE-01 | `/admin/quotes/new`: select cliente + 1-3 offerte + override prezzo per ciascuna offerta + genera link pubblico `/quote/[token]` | Phase 9 |
| QUOTE-02 | Public `/quote/[token]` pagina multistep (Zod validazione): Step 1 (overview) → Step 2 (tier A/B/C selection + prezzi + fasi) → Step 3 (summary + accept/decline) | Phase 9 |
| QUOTE-03 | Accept CTA → POST `/api/public/quote/accept?token=X` con cattura email (opzionale) + note opzionali; registra timestamp accepted_at immutabile | Phase 9 |
| QUOTE-04 | Quote token: nanoid 21 char (~122 bits), unico, validato in proxy come `/client/[token]` (nessun login sessione); rate limit 3 views/min per IP | Phase 9 |
| QUOTE-05 | Mai esporre `quote_items` (prezzi per servizio) via public API; client API vede solo `accepted_total` (server-side ClientView type enforces) | Phase 9 |
## CRM Pipeline & Activity Logging (Phase 10)
| ID | Requirement | Status |
|----|-------------|--------|
| CRM-01 | Lead CRUD: `/admin/leads` list table (name, email, company, status, last_contact_date, next_action) con create/edit/delete | Phase 10 |
| CRM-02 | Lead detail page `/admin/leads/[id]`: full profilo, activity log (reverse chronological), quote(s) sent, actions menu (log activity, send quote, mark won) | Phase 10 |
| CRM-03 | Pipeline stages: Contacted → Qualified → Proposal Sent → Negotiating → Won / Lost; transizioni via dropdown (manual di default) | Phase 10 |
| CRM-04 | Activity logging: tipi (call, email, meeting, note); ogni attività: type, date, durata (opz), notes; auto-aggiorna lead.last_contact_date | Phase 10 |
| CRM-05 | "Log activity" modal: <10 sec UX (type dropdown, date picker, notes textarea, save) — bassa tolleranza per data entry lunghi | Phase 10 |
| CRM-06 | Follow-up reminders widget in dashboard: "Follow up today" (leads dove last_contact_date < oggi - 7 giorni); click → jump lead detail | Phase 10 |
| CRM-07 | "Send Quote" button in lead detail: select existing quote o crea nuovo; update lead.last_quote_sent + status → "Proposal Sent" | Phase 10 |
## Auto-Onboarding on Win (Phase 11)
| ID | Requirement | Status |
|----|-------------|--------|
| WIN-01 | "Mark as Won" button in lead detail (o auto-triggered da public quote accept); idempotency: call 2 volte = idempotent, stesso client token | Phase 11 |
| WIN-02 | winLead(leadId, quoteId): crea client (name da lead, token = nanoid), crea project (name = company/offer name), copia offer fasi → project fasi | Phase 11 |
| WIN-03 | 1-4 pagamenti: user sceglie split template (50/50, 3-part, 4-part, custom); registra importo accepted_total da quote | Phase 11 |
| WIN-04 | Generate client dashboard link (`/client/[token]`), invia via email al lead (copy-paste OK, email automation deferred Phase 12+) | Phase 11 |
| WIN-05 | Quote accept da public link `/quote/[token]` auto-aggiorna lead.status → "Proposal Sent" + trigga win (auto-onboarding) | Phase 11 |
| WIN-06 | Copy offer phases → project phases: tutte le fasi copiate con status = "upcoming", le fasi modificabili dopo (template immutabile, instance editable) | Phase 11 |
## Flusso Claude (v3 — deferred to Phase 13+)
| ID | Requirement | Status | | ID | Requirement | Status |
|----|-------------|--------| |----|-------------|--------|
| CLAUDE-01 | Onboarding guidato step-by-step via chat per aggiungere un nuovo cliente | Deferred | | CLAUDE-01 | Onboarding guidato step-by-step via chat per aggiungere un nuovo cliente | Deferred |
| CLAUDE-02 | Generazione del piano a fasi basato sul brief | Deferred | | CLAUDE-02 | Generazione del piano a fasi basato dal brief | Deferred |
| CLAUDE-03 | Generazione preventivo assistita (Claude suggerisce, tu approvi prima di finalizzare) | Deferred | | CLAUDE-03 | Generazione preventivo assistita (Claude suggerisce struttura offerta, tu approvi) | Deferred |
## Out of Scope ## Out of Scope (v2.0)
- Fatturazione e invio fatture (solo stato pagamenti) - Fatturazione e invio fatture (solo stato pagamenti)
- App mobile nativa (solo web responsive) - App mobile nativa (solo web responsive)
- Multi-utente con team (solo admin singolo) - Multi-utente con team (solo admin singolo)
- Prezzi singoli visibili al cliente (solo totale accettato) - Prezzi singoli visibili al cliente (solo totale accettato)
- Email automation (manual copy-paste OK per MVP)
- Lead scoring / predictive analytics
- Team collaboration
- Lead de-duplication (manual link on Win)
- Proposal expiry logic (open indefinitely)
- BullMQ persistent reminders (in-app computed, persistent deferred Phase 12+)
## Traceability ## Traceability
@@ -96,6 +145,29 @@
| OFFER-04 | Phase 5 | Pending | | OFFER-04 | Phase 5 | Pending |
| OFFER-05 | Phase 5 | Pending | | OFFER-05 | Phase 5 | Pending |
| OFFER-06 | Phase 5 | Pending | | OFFER-06 | Phase 5 | Pending |
| CLAUDE-01 | Phase 6 (v2) | Deferred | | CAT-U-01 | Phase 7 | Phase 7 |
| CLAUDE-02 | Phase 6 (v2) | Deferred | | CAT-U-02 | Phase 7 | Phase 7 |
| CLAUDE-03 | Phase 6 (v2) | Deferred | | OFFER-B-01 | Phase 8 | Phase 8 |
| OFFER-B-02 | Phase 8 | Phase 8 |
| OFFER-B-03 | Phase 8 | Phase 8 |
| QUOTE-01 | Phase 9 | Phase 9 |
| QUOTE-02 | Phase 9 | Phase 9 |
| QUOTE-03 | Phase 9 | Phase 9 |
| QUOTE-04 | Phase 9 | Phase 9 |
| QUOTE-05 | Phase 9 | Phase 9 |
| CRM-01 | Phase 10 | Phase 10 |
| CRM-02 | Phase 10 | Phase 10 |
| CRM-03 | Phase 10 | Phase 10 |
| CRM-04 | Phase 10 | Phase 10 |
| CRM-05 | Phase 10 | Phase 10 |
| CRM-06 | Phase 10 | Phase 10 |
| CRM-07 | Phase 10 | Phase 10 |
| WIN-01 | Phase 11 | Phase 11 |
| WIN-02 | Phase 11 | Phase 11 |
| WIN-03 | Phase 11 | Phase 11 |
| WIN-04 | Phase 11 | Phase 11 |
| WIN-05 | Phase 11 | Phase 11 |
| WIN-06 | Phase 11 | Phase 11 |
| CLAUDE-01 | Phase 13 (v3) | Deferred |
| CLAUDE-02 | Phase 13 (v3) | Deferred |
| CLAUDE-03 | Phase 13 (v3) | Deferred |
+117 -21
View File
@@ -1,8 +1,10 @@
# Roadmap: ClientHub # Roadmap: ClientHub v2.0 Business Operations Suite
## Overview ## Overview
ClientHub si costruisce dall'esterno verso l'interno: prima la dashboard che un cliente reale può aprire su mobile, poi l'area admin per creare e gestire i dati, poi il catalogo servizi e il preventivo interno. Il flusso Claude AI è v2 e dipende da CRUD stabile e catalogo completo. Ogni fase consegna una capacità end-to-end verificabile. **v1.0 (Phases 15, Complete)**: ClientHub si costruisce dall'esterno verso l'interno: prima la dashboard cliente, poi l'area admin CRUD, poi catalogo servizi, progetti multi-progetto e sistema offerte.
**v2.0 (Phases 711, Planning)**: Trasformazione da portale clienti a suite operativa completa. Catalogo servizi unificato → template offerte con fasi → generazione preventivi pubblici in 2 ore → CRM pipeline lead → auto-onboarding Won leads con copiat fasi e pagamenti. Obiettivo: delivery completa di proposta, accettazione, e provisioning in workflow continuo.
## Phases ## Phases
@@ -12,12 +14,20 @@ ClientHub si costruisce dall'esterno verso l'interno: prima la dashboard che un
Decimal phases appear between their surrounding integers in numeric order. Decimal phases appear between their surrounding integers in numeric order.
**v1.0 (Completed):**
- [x] **Phase 1: Foundation & Client Dashboard** - DB schema, token API, dashboard read-only per il cliente con link segreto condivisibile - [x] **Phase 1: Foundation & Client Dashboard** - DB schema, token API, dashboard read-only per il cliente con link segreto condivisibile
- [ ] **Phase 2: Admin Area & Interactive Features** - Auth admin, CRUD completo clienti/fasi/task/deliverable/pagamenti, approvazioni e commenti - [x] **Phase 2: Admin Area & Interactive Features** - Auth admin, CRUD completo clienti/fasi/task/deliverable/pagamenti, approvazioni e commenti
- [ ] **Phase 3: Service Catalog & Quote Builder** - Catalogo servizi riutilizzabile e costruttore preventivi (admin-only, cliente vede solo il totale) - [x] **Phase 3: Service Catalog & Quote Builder** - Catalogo servizi riutilizzabile e costruttore preventivi (admin-only, cliente vede solo il totale)
- [ ] **Phase 4: Progetti — Multi-Project per Cliente** - Modello dati multi-progetto per cliente; dashboard con tabs; /admin/projects; slug link; analytics €/h - [x] **Phase 4: Progetti — Multi-Project per Cliente** - Modello dati multi-progetto per cliente; dashboard con tabs; /admin/projects; slug link; analytics €/h
- [x] **Phase 5: Offer System** - Catalogo macro/micro offerte con servizi assegnabili, assegnazione offerte a progetti, dashboard cliente con offerte attive e revenue forecast 12 mesi per l'admin - [x] **Phase 5: Offer System** - Catalogo macro/micro offerte con servizi assegnabili, assegnazione offerte a progetti, dashboard cliente con offerte attive e revenue forecast 12 mesi per l'admin
- [ ] **Phase 6: Claude AI Onboarding (v2)** - Flusso guidato step-by-step per onboarding cliente e generazione assistita del piano/preventivo - [x] **Phase 6: UX Overhaul** - Admin sidebar + dashboard operativa con KPI e activity feed
**v2.0 (Planning):**
- [ ] **Phase 7: Unified Service Catalog** - Migrazione `service_catalog` + `offer_services` → unica tabella `services`; zero data loss, audit trail per rollback
- [ ] **Phase 8: Offer Phases & Quote Templates** - Tabelle `offer_phases`, `quotes`, `offer_phase_services`; struttura hierarchica offer (macro → micro → phase → services)
- [ ] **Phase 9: Quote Builder & Public Routes** - Builder preventivi (select offer + override prezzi); `/quote/[token]` pagina pubblica multistep; accettazione
- [ ] **Phase 10: CRM Pipeline** - Lead CRUD, attività, follow-up reminders; pipeline stages (5 step); activity log per lead
- [ ] **Phase 11: Auto-Provisioning on Win** - Lead → quote accept → auto-crea client + progetto (copia fasi) + 1-4 pagamenti; launch client dashboard
## Phase Details ## Phase Details
@@ -138,23 +148,94 @@ Decimal phases appear between their surrounding integers in numeric order.
**UI hint**: yes **UI hint**: yes
**Status**: Planning complete **Status**: Planning complete
### Phase 7: Claude AI Onboarding (v2) ### Phase 7: Unified Service Catalog
**Goal**: L'admin può usare un flusso chat guidato per onboardare un nuovo cliente e generare un piano a fasi e un preventivo assistito da Claude **Goal**: Consolidare `service_catalog` + `offer_services` in un'unica tabella `services`; fondazione per offer builder e quote generator
**Mode:** mvp **Mode:** migration
**Depends on**: Phase 6 **Depends on**: Phase 5 (offer_services exists)
**Requirements**: CLAUDE-01, CLAUDE-02, CLAUDE-03 **Requirements**: CAT-U-01, CAT-U-02
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. L'admin avvia il flusso Claude inserendo il brief del cliente; Claude guida step-by-step la raccolta delle informazioni necessarie 1. Una sola tabella `services` con nome, prezzo_unitario, attivo, created_at
2. Al termine del flusso, Claude propone un piano strutturato per fasi che l'admin può accettare o modificare prima di salvarlo 2. Migrazione zero data loss: campi audit (`migrated_from`, `migrated_id`) abilitano rollback sicuro
3. Claude suggerisce un preventivo basato sul catalogo servizi; l'admin approva o modifica le voci prima della finalizzazione 3. `/admin/catalog` list/add/edit/soft-delete funzionante con nuova tabella
**Plans**: TBD 4. Query vecchie servizi falliscono esplicitamente (no silent fallback)
**UI hint**: yes **Plans**: 1 (schema migration + admin page)
**Status**: Pending planning (v2 — may defer indefinitely) **Durata**: 34 giorni
**Status**: Pending planning
**Risk**: SQL deduplication logic; validate on staging before prod
### Phase 8: Offer Phases & Quote Templates
**Goal**: Struttura hierarchica offer (macro → micro → phase → services) con drag&drop builder
**Mode:** schema + ui
**Depends on**: Phase 7
**Requirements**: OFFER-B-01, OFFER-B-02, OFFER-B-03
**Success Criteria** (what must be TRUE):
1. Nuove tabelle: `offer_phases`, `quotes`, `offer_phase_services`
2. `/admin/offers/[id]/edit` builder two-column: left servizi (ricerca), right fasi con drag&drop
3. Drag&drop tra fasi e dentro fase (reorder); salva per singolo drop con optimistic update
4. Versioning previene race condition da multi-tab edit (409 Conflict su version mismatch)
**Plans**: 2 (schema + builder UI)
**Durata**: 23 giorni
**Status**: Pending planning
### Phase 9: Quote Builder & Public Routes
**Goal**: Generazione preventivi 2-click (select offer → override prezzi → link pubblico); pagina multistep pubblico per lead
**Mode:** UI + routes
**Depends on**: Phase 8
**Requirements**: QUOTE-01, QUOTE-02, QUOTE-03, QUOTE-04, QUOTE-05
**Success Criteria** (what must be TRUE):
1. `/admin/quotes/new`: select cliente + 1-3 offerte + override prezzi → genera `/quote/[token]` link pubblico
2. Pagina pubblica `/quote/[token]` multistep (Step 1 overview → Step 2 tier selection → Step 3 summary + accept)
3. Accept CTA → `/api/public/quote/accept?token=X` con cattura email + note
4. Token: nanoid 21 char, unico, rate limit 3 views/min per IP
5. Mai esporre `quote_items` (prezzi per servizio) via public API
**Plans**: 3 (quote builder + public routes + security)
**Durata**: 45 giorni
**Status**: Pending planning
**Risk**: RHF + React 19 compatibility (dev test prima del merge); PDF generation approach TBD
### Phase 10: CRM Pipeline & Activity Logging
**Goal**: Lead CRUD, activity log, follow-up reminders, quote assignment; pipeline stages
**Mode:** UI + CRUD
**Depends on**: Phase 5 (project_offers) + Phase 9 (quotes)
**Requirements**: CRM-01 through CRM-07
**Success Criteria** (what must be TRUE):
1. `/admin/leads` list (name, email, company, status, last_contact_date, next_action)
2. `/admin/leads/[id]` detail: profilo, activity log reverse-chronological, quote(s) sent, action menu
3. Activity types: call, email, meeting, note; auto-aggiorna lead.last_contact_date
4. Pipeline stages: Contacted → Qualified → Proposal Sent → Negotiating → Won / Lost
5. Follow-up widget in dashboard: "Follow up today" (leads dove last_contact_date < oggi - 7 giorni)
6. "Log activity" modal: <10 sec UX (type dropdown, date picker, notes, save)
7. "Send Quote" button: select/create quote → update lead.status → "Proposal Sent"
**Plans**: 3 (lead CRUD + activity logging + follow-up widget)
**Durata**: 34 giorni
**Status**: Pending planning
### Phase 11: Auto-Provisioning on Win
**Goal**: Lead → quote accept → auto-crea client + progetto (copia fasi) + 1-4 pagamenti; lancia dashboard cliente
**Mode:** automation + integration
**Depends on**: Phase 9 (quotes) + Phase 10 (CRM)
**Requirements**: WIN-01 through WIN-06
**Success Criteria** (what must be TRUE):
1. "Mark as Won" button in lead detail; idempotency: call 2x = stesso client token
2. `winLead(leadId, quoteId)`: create client (token=nanoid) + project (name=company) + copy offer phases → project.phases
3. 1-4 pagamenti: user sceglie split template (50/50, 3-part, 4-part, custom); registra accepted_total
4. Generate client link + email (copy-paste MVP; email automation deferred Phase 12+)
5. Quote accept da public `/quote/[token]` auto-trigga win (auto-onboarding)
6. Copy offer phases: status="upcoming", phases modificabili dopo (template immutabile, instance editable)
**Plans**: 2 (win action + email + BullMQ optional)
**Durata**: 23 giorni
**Status**: Pending planning
**Risk**: Idempotency key implementation; Redis + BullMQ ops (persistent reminders optional MVP)
### Phase 12+: Deferred Features (Future Milestones)
- **Phase 12: Email Automation & Polish** — Send quote link via email, email reminders, lead de-duplication UI
- **Phase 13: Claude AI Onboarding (v3)** — Chat-guided client onboarding, assisted quote generation, plan suggestion
## Progress ## Progress
**Execution Order:** **v1.0 Execution (Complete):**
Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 Phases 1 → 2 → 3 → 4 → 5 executed in order. Phase 6 (UX Overhaul) executed May 31.
| Phase | Plans | Status | Completed | | Phase | Plans | Status | Completed |
|-------|-------|--------|-----------| |-------|-------|--------|-----------|
@@ -163,5 +244,20 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5
| 3. Service Catalog & Quote Builder | 4/4 | ✅ Done | 2026-05-19 | | 3. Service Catalog & Quote Builder | 4/4 | ✅ Done | 2026-05-19 |
| 4. Progetti — Multi-Project per Cliente | 5/5 | ✅ Done | 2026-05-23 | | 4. Progetti — Multi-Project per Cliente | 5/5 | ✅ Done | 2026-05-23 |
| 5. Offer System | 4/4 | ✅ Done | 2026-05-30 | | 5. Offer System | 4/4 | ✅ Done | 2026-05-30 |
| 6. UX Overhaul — Sidebar + Dashboard | 0/2 | Pending | - | | 6. UX Overhaul — Sidebar + Dashboard | 2/2 | ✅ Done | 2026-05-31 |
| 7. Claude AI Onboarding (v2) | 0/TBD | Pending | - |
**v2.0 Execution (Planning):**
Phases 7 → 8 → 9 → 10 → 11 planned for sequential execution. Research complete (2026-06-11), requirements/roadmap finalized. Ready for phase-by-phase planning + execution.
| Phase | Plans | Status | Estimated Duration | Blocking? |
|-------|-------|--------|-------------------|-----------|
| 7. Unified Service Catalog | 1 | Pending planning | 34 giorni | Foundation |
| 8. Offer Phases & Quote Templates | 2 | Pending planning | 23 giorni | Foundation |
| 9. Quote Builder & Public Routes | 3 | Pending planning | 45 giorni | RHF compat test |
| 10. CRM Pipeline & Activity Logging | 3 | Pending planning | 34 giorni | No |
| 11. Auto-Provisioning on Win | 2 | Pending planning | 23 giorni | BullMQ ops (optional) |
| **Total v2.0 MVP** | **11** | Pending | **1419 giorni** | — |
**v2.0 Deferred (Phase 12+):**
- Phase 12: Email Automation & Polish
- Phase 13: Claude AI Onboarding (v3)
+18 -14
View File
@@ -1,17 +1,16 @@
--- ---
gsd_state_version: 1.0 gsd_state_version: 1.0
milestone: v1.0 milestone: v2.0
milestone_name: milestone milestone_name: Business Operations Suite
status: completed status: planning
stopped_at: Phase 5 fully executed — Offer System live on hub.iamcavalli.net last_updated: "2026-06-10T20:03:26.545Z"
last_updated: "2026-05-30" last_activity: 2026-06-10
last_activity: 2026-05-30 -- Phase 5 confirmed complete (05-01 through 05-04)
progress: progress:
total_phases: 5 total_phases: 0
completed_phases: 5 completed_phases: 0
total_plans: 21 total_plans: 0
completed_plans: 21 completed_plans: 0
percent: 100 percent: 0
--- ---
# Project State # Project State
@@ -24,9 +23,10 @@ See: .planning/PROJECT.md (updated 2026-05-09)
## Current Position ## Current Position
Phase: 5 — COMPLETE Phase: Not started (defining requirements)
Plan: All plans done (05-01 through 05-04) Plan:
Status: Production ready — deploy pending Status: Defining requirements
Last activity: 2026-06-10 — Milestone v2.0 started
## What Was Built (Phase 5) ## What Was Built (Phase 5)
@@ -76,3 +76,7 @@ Status: Production ready — deploy pending
Last session: 2026-05-27 Last session: 2026-05-27
All Phase 4 plans complete. Next: Authelia infra or new feature scope. All Phase 4 plans complete. Next: Authelia infra or new feature scope.
## Operator Next Steps
- Start the next milestone with /gsd-new-milestone
+210
View File
@@ -0,0 +1,210 @@
# Pitfalls Checklist: Business Operations Suite v2.0
Quick reference for preventing the 9 major pitfalls during Phase 79 implementation.
---
## Critical Pitfalls (Before Code)
### 1. Catalog Consolidation Breaks Quotes
- [ ] Migration strategy documented (Expand-Contract, not drop)
- [ ] Mapping layer between old/new tables designed
- [ ] Backfill script tested on production backup
- [ ] Referential integrity test query written
- [ ] Dual-write period defined (2-3 weeks)
- [ ] Rollback plan documented (drop new table, revert code)
### 2. Offer Template Mutation
- [ ] Offer/project hierarchy mapping documented (1 page)
- [ ] Copy function uses `db.transaction()` (all-or-nothing)
- [ ] Uses `structuredClone()` for deep copy, not `{...spread}`
- [ ] Idempotency key added to CRM leads table
- [ ] Integration test: copy offer, verify structure consistent across instances
- [ ] Test: edit project phase, verify offer template unchanged
### 3. Public Quote Token Leakage
- [ ] Token length ≥ 32 chars (nanoid(32) or nanoid(64))
- [ ] Expiration field added (default 7 days)
- [ ] Email validation on public page (token + email required)
- [ ] Rate limiting implemented (max 3 views/token/minute)
- [ ] Code audit: quote_items NOT in public API response
- [ ] Test: brute-force 1000 guesses, verify rate limit activates
- [ ] Test: enumerate tokens with different emails, verify 401
---
## Moderate Pitfalls (During Implementation)
### 4. Drag-and-Drop Race Condition
- [ ] `version` field added to offer_phases
- [ ] Update mutation checks version (returns 409 if mismatch)
- [ ] Server recomputes all sort_orders atomically
- [ ] Frontend handles 409 Conflict (refresh UI)
- [ ] Test: concurrent drag in 2 tabs, verify final state correct
### 5. CRM "Win" Double-Click
- [ ] Idempotency key added to crm_leads (unique)
- [ ] "Win" mutation checks if key already processed
- [ ] All sub-steps in single `db.transaction()`
- [ ] Button disabled until success
- [ ] Idempotency key stored in localStorage
- [ ] Test: double-click "Win", verify only 1 client created
### 6. Offer Copy Missing Tasks/Deliverables
- [ ] Mapping documented: offer_micro → project_phase → project_task → deliverable
- [ ] Copy function recursive (phases → tasks → deliverables)
- [ ] Validation test: assert counts match expected
- [ ] Integration test exists (copy, verify structure)
- [ ] Query test: no orphaned deliverables (task_id IS NULL)
---
## Minor Pitfalls (Polish)
### 7. Offer State Not Synced Between Tabs
- [ ] `version` field on offer_micros
- [ ] Update returns 409 on version mismatch
- [ ] UI shows "Offer changed, reload?" dialog
### 8. CRM Schema Backward Incompatibility
- [ ] New columns added as NULLABLE
- [ ] Code handles NULL defensively (e.g., `lead.budget ?? 0`)
- [ ] Backfill script written (sets defaults for existing rows)
- [ ] NOT NULL constraint added only after backfill + validation
- [ ] Test: production data copied to test db, code runs without errors
### 9. CRM Scope Creep
- [ ] Must-have features defined for v2.0 (lead pipeline, quote, auto-onboard)
- [ ] Should-have features listed for v2.1+ (email, calls, source)
- [ ] Won't-have features documented (team, multi-user, integrations)
- [ ] Scope document signed off by product
- [ ] Success metric defined: "Can go from lead to won project in <30 min"
---
## Phase 7 Checklist (Catalog & Offers)
**Before code:**
- [ ] Catalog consolidation migration plan reviewed
- [ ] Offer/project hierarchy documented
- [ ] Drag-drop version/locking strategy designed
**During implementation:**
- [ ] Migration backfill tested on production backup
- [ ] Drag-drop version field added
- [ ] Offer phase copy function atomic (transaction)
- [ ] Idempotency key on CRM leads
- [ ] Integration tests for copy consistency
**Before go-live:**
- [ ] Dry-run migration on production DB
- [ ] Verify referential integrity (no orphaned quote_items)
- [ ] Concurrent drag-drop test passes
- [ ] Offer copy count validation passes
---
## Phase 8 Checklist (Public Quote Pages)
**Before code:**
- [ ] Token security requirements finalized
- [ ] API response schema designed (no quote_items)
- [ ] Rate limiting strategy defined
**During implementation:**
- [ ] Token generation (nanoid(32))
- [ ] Expiration + email validation
- [ ] Rate limiting middleware
- [ ] Code audit for quote_items exposure
- [ ] Brute-force test (>1000 guesses/sec)
**Before go-live:**
- [ ] Brute-force test activates rate limit
- [ ] Enumeration test returns 401 for wrong email
- [ ] Expiration test returns 401 after expiration
- [ ] Code review: quote_items not in response
---
## Phase 9 Checklist (CRM Won Automation)
**Before code:**
- [ ] Scope document (Must/Should/Could/Won't) reviewed
- [ ] Idempotency strategy designed
- [ ] Payment plan algorithm defined
- [ ] Offer snapshot structure (JSONB) designed
**During implementation:**
- [ ] Idempotency key on crm_leads
- [ ] "Win" mutation atomic (client + project + phases + payments)
- [ ] Offer snapshot stored on win
- [ ] Button disabled until success
- [ ] Payment consistency tests
**Before go-live:**
- [ ] Double-click test: only 1 client created
- [ ] Network timeout test: retry returns same project
- [ ] Partial failure test: rollback on error
- [ ] Payment consistency test: correct number created
- [ ] Idempotency test: 10x "Win" with same key = 1 client
---
## Key Questions Before Each Phase
**Phase 7:**
- ✓ Is catalog consolidation migration safe (Expand-Contract, dual-write)?
- ✓ Does offer/project hierarchy mapping make sense (1 micro = 1 phase)?
- ✓ Is copy function atomic (all-or-nothing)?
- ✓ Do we have integration tests for offer phase structure consistency?
**Phase 8:**
- ✓ Is token length ≥ 32 chars and properly random?
- ✓ Do we validate both token AND email on public page?
- ✓ Is rate limiting active (max 3 views/token/min)?
- ✓ Can we prove quote_items never appears in public response?
**Phase 9:**
- ✓ Is "Win" action atomic (transaction)?
- ✓ Does idempotency key prevent duplicates on retry?
- ✓ Is button disabled until success?
- ✓ Have we defined Must/Should/Won't scope with user?
---
## Testing Validation Matrix
| Pitfall | Test Case | Expected Outcome | Status |
|---------|-----------|------------------|--------|
| 1. Catalog consolidation | Backfill migration on prod backup | Zero orphaned quote_items | [ ] |
| 2. Offer template mutation | Edit project phase, check offer unchanged | Offer template immutable | [ ] |
| 3. Token leakage | Brute-force 1000 guesses/sec | Rate limit activates (429) | [ ] |
| 3. Token leakage | Enumerate with wrong email | Returns 401 Unauthorized | [ ] |
| 4. Drag-drop race | Concurrent drag in 2 tabs | Final sort_order is coherent | [ ] |
| 5. Double-click clients | Click "Win" twice | Only 1 client created | [ ] |
| 5. Double-click clients | Network timeout + retry | Same project returned | [ ] |
| 6. Missing deliverables | Copy offer to project | All tasks and deliverables copied | [ ] |
| 8. Schema backward compat | Copy production data to test DB | No crashes, NULL handled | [ ] |
| 9. Payment consistency | "Win" with partial failure | Rollback, no orphaned payments | [ ] |
---
## Red Flags During Development
🚨 **Stop and review if:**
- Offer/project hierarchy mapping is unclear (should be 1 page, explicit)
- Drag-drop sort_order updates without version check
- "Win" mutation has any non-transactional steps (client created outside txn)
- Copy function uses shallow spread (`{...obj}`) instead of `structuredClone()`
- Token length < 32 chars
- Public API response includes quote_items or per_service_price
- CRM leads table has no idempotency_key column
- Schema changes add NOT NULL without backfill test
- "Win" button not disabled until response received
- No rate limiting on public quote page
---
**Owner:** Development team
**Review:** Before each phase kickoff
**Update:** As testing results come in
+302
View File
@@ -0,0 +1,302 @@
# Pitfalls Research Summary: Business Operations Suite (v2.0)
**Project:** ClientHub v2.0 — Adding Business Operations Suite to Production
**Research Date:** 2026-06-10
**Confidence:** HIGH (domain-specific research backed by schema review + security analysis)
---
## Overview
Adding a complex Business Operations Suite (catalog consolidation + offer builder + public quotes + CRM automation) to a production app with real client data creates integration risks not present when building from scratch. This research documents the most dangerous pitfalls (rewrites/data loss) and operational friction points, with concrete prevention strategies for each.
**Key insight:** Most pitfalls cluster in four areas of high integration complexity:
1. **Schema consolidation** — breaking existing quote_items referential integrity
2. **Template-to-instance copy semantics** — offer phases mutating templates
3. **Optimistic UI + concurrent edits** — sort order conflicts
4. **Multi-step workflow atomicity** — CRM automation creating duplicates or partial state
---
## Critical Pitfalls (Require Design Changes Before Code)
### Pitfall 1: Catalog Consolidation Breaks Quotes (Data Loss Risk)
**Severity:** CRITICAL — Can orphan quote_items, break billing calculations
**Problem:**
Two parallel service tables (`service_catalog` for costs, `offer_services` for marketing pricing) need consolidation. Naive merge breaks `quote_items` referential integrity or loses price history.
**Prevention Strategy:**
- **Expand-Contract migration:** Add new unified `services` table alongside old ones, backfill in phases, only drop old tables after validation
- **Immutable snapshot:** Store `quote_snapshot: JSONB` in project_offers (freeze prices at time of win)
- **Referential integrity test:** Query for orphaned quote_items before/after migration
- **Backward compatibility:** Old quotes read from old tables, new quotes use new table, transition over 2-3 weeks
**Phase:** 7 (Catalog & Offers) — Plan migration design BEFORE any code changes
**Validation checklist:**
- [ ] Migration mapping layer documented (catalog ↔ services ↔ offer_services)
- [ ] Backfill strategy written (small batches, checksum validation)
- [ ] Dual-write period defined (how long to run old+new in parallel)
- [ ] Referential integrity test query exists
---
### Pitfall 2: Offer Template Mutation on Copy (Data Corruption Risk)
**Severity:** CRITICAL — Templates silently mutate when project phases edited
**Problem:**
Copying offer phases to project via shallow copy = shared references. Admin edits project phase, template phase mutates too. Next deal uses corrupted template. Also: partial copies on retry create duplicate phases.
**Prevention Strategy:**
- **Deep copy with atomic transaction:** Use `db.transaction()` for entire copy operation (all-or-nothing); copy at database level, not in JS
- **Immutable template flag:** `offer_micros.is_template = true`, prevent UPDATE on templates
- **Idempotency key:** "Win" request includes idempotency key; retry returns same project (no duplicates)
- **Explicit hierarchy mapping:** Document what offer_micro → project_phase, what offer_service → project_task/deliverable
**Phase:** 7 (Offers) for design, 9 (CRM) for "Win" automation
**Validation checklist:**
- [ ] Hierarchy mapping documented (offer ↔ project structures)
- [ ] Atomic copy function implemented (single transaction, all-or-nothing)
- [ ] Idempotency key added to CRM leads
- [ ] Integration tests verify copy structure consistency across multiple instances
---
### Pitfall 3: Public Quote Token Leakage (Security Risk)
**Severity:** CRITICAL — Token enumeration exposes all pricing
**Problem:**
Public quote page with nanoid 21-char token can be brute-forced or enumerated. Attacker builds pricing database, breaches commercial confidentiality.
**Prevention Strategy:**
- **Longer token:** nanoid(32) instead of 21 (~190 bits vs ~122 bits entropy)
- **Access control:** Require both token + email (validate recipient match)
- **Expiration:** token_expires_at (default 7 days)
- **Rate limiting:** Max 3 views/token/minute (blocks enumeration)
- **Never expose quote_items:** Public API response excludes line items, shows TOTAL PRICE ONLY
- **Activation state:** Token valid only AFTER admin sends it (not auto-generated on create)
**Phase:** 8 (Public Quote Pages) — Implement security controls IN PARALLEL with feature
**Validation checklist:**
- [ ] Token length ≥ 32 chars, rate limiting ≥ 3/min
- [ ] Email validation on public page (token + email both required)
- [ ] Expiration enforced (test expired token returns 401)
- [ ] Code audit: quote_items never in public API response
- [ ] Brute-force test: verify rate limit activates
---
## Moderate Pitfalls (Major Refactoring / Data Inconsistency)
### Pitfall 4: Drag-and-Drop Sort Order Race Condition
**Severity:** MODERATE — Phases display in wrong order, user frustration
**Problem:**
Concurrent drag-and-drop edits in multiple tabs cause sort_order conflicts. Last write wins, earlier update is lost. No version conflict detection.
**Prevention Strategy:**
- **Optimistic locking:** Add `version` field to offer_phases, update only if version matches (return 409 Conflict if mismatch)
- **Server-side recomputation:** Don't trust client's sort_order, recompute all orders atomically based on actual position
- **Optimistic UI with reconciliation:** Update frontend instantly, reconcile with server result (409 = refresh)
**Phase:** 7 (Offer Builder — Drag & Drop)
**Validation checklist:**
- [ ] `version` field added to offer_phases
- [ ] Update mutation includes version check
- [ ] Server recomputes all sort_orders atomically
- [ ] Concurrent edit test exists (open 2 tabs, drag in both)
---
### Pitfall 5: CRM "Win" Double-Click Creates Duplicate Clients
**Severity:** MODERATE — Duplicate clients, broken billing, audit confusion
**Problem:**
Multi-step "Win" automation (create client → project → phases → payments) has no idempotency. Double-click creates two clients, two projects. Network timeouts are invisible to user.
**Prevention Strategy:**
- **Idempotency key:** Every lead has unique idempotency_key; "Win" request checks if key already processed (if yes, return existing client_id)
- **Atomic transaction:** All steps (client, project, phases, payments) in single transaction — all succeed or all rollback
- **UI feedback:** Button disabled until success (prevents accidental double-click)
- **Browser persistence:** Store idempotency_key in localStorage, preserve across refresh
**Phase:** 9 (CRM Won Automation)
**Validation checklist:**
- [ ] CRM leads table has idempotency_key column (unique)
- [ ] "Win" mutation checks for existing key before creating
- [ ] All substeps in single db.transaction()
- [ ] Button disabled until response received
- [ ] Idempotency key stored in localStorage
- [ ] Test: double-click "Win", verify only 1 client created
---
### Pitfall 6: Offer Phase Copy Misses Tasks/Deliverables
**Severity:** MODERATE — Client dashboard shows incomplete phases (no tasks to approve)
**Problem:**
Copy offer → project phases but forget to copy tasks or deliverables. Client sees phase with no work items.
**Prevention Strategy:**
- **Explicit mapping:** Document offer_micro → project_phase, offer_service → project_task/deliverable mapping
- **Full-tree copy function:** Copy phases, then tasks for each phase, then deliverables for each task (all in one transaction)
- **Validation test:** Assert phase_count, task_count, deliverable_count match expected values after copy
**Phase:** 7 (Offers structure design), 9 (Copy implementation)
**Validation checklist:**
- [ ] Mapping documented (offer vs project hierarchies)
- [ ] Copy function handles entire tree (phases → tasks → deliverables)
- [ ] Integration test: copy offer, verify counts match expected
- [ ] Query test: check no deliverables are orphaned (task_id IS NULL)
---
## Minor Pitfalls (Operational Friction)
### Pitfall 7: Offer State Not Synced Between Tabs
**Problem:** Admin edits offer in tab 1, tab 2 doesn't know, last save wins (first admin's work lost)
**Prevention:** Add `version` field, return 409 on version mismatch, show "Offer changed, reload?"
---
### Pitfall 8: CRM Schema Backward Incompatibility
**Problem:** Add new fields (budget, source) as NULL, code assumes populated, crashes
**Prevention:** Expand-Contract pattern — add as NULL, handle NULL defensively in code, backfill, then add NOT NULL constraint
---
### Pitfall 9: CRM Scope Creep (Feature Bloat)
**Problem:** "Just add email templates" → 30 hours. "Add calls" → 40 hours. CRM never ships.
**Prevention:** Explicit Must/Should/Could/Won't scope document. Must-haves only for v2.0: lead pipeline, quote attachment, auto-onboarding. Defer rest.
---
## Key Design Decisions for Phase 79
| Decision | Why | Validation |
|----------|-----|-----------|
| Expand-Contract migration for catalog | Zero-downtime, safe rollback, no data loss | Dry-run on prod backup before go-live |
| Deep copy + atomic transaction for offer phases | Prevents template mutation, partial copies | Integration test, structure consistency query |
| Idempotency key on "Win" action | Safe retry, prevents duplicate clients | Double-click test, query for duplicates |
| Public quote token security layers (length, expiration, email, rate limit) | Blocks enumeration, limits leakage | Brute-force test, code audit for quote_items |
| Offer snapshot (immutable JSONB) | Preserves what was promised vs. what's executing | Store at win time, display in client dashboard |
| Version field on offer_phases + optimistic locking | Detects concurrent edit conflicts | Concurrent drag test, 409 Conflict handling |
---
## Roadmap Implications
**Phase 7 (Catalog & Offers):**
- Design catalog consolidation migration before any code
- Implement drag-drop with version fields from start
- Document offer/project hierarchy mapping explicitly
- Write copy function atomically (no partial copies)
**Phase 8 (Public Quote Pages):**
- Implement token security controls in parallel (not after)
- Rate limiting + email validation + expiration
- Code audit: quote_items never in response
- Test brute-force + enumeration
**Phase 9 (CRM — Won Automation):**
- Add idempotency_key to leads table
- Implement atomic "Win" transaction
- Store offer snapshot at win time
- Define Must/Should/Won't scope before design
- Integration tests for payment consistency
**Phase 10+ (Future):**
- Defer: email templates, call logging, team features, integrations
- Solo consultant doesn't need multi-user or GoHighLevel-scale features
---
## Testing Strategy for Confidence
### Phase 7 (Catalog & Offers)
1. Dry-run consolidation migration on production database backup
2. Verify all quote_items still resolve to services (no orphans)
3. Regenerate quotes from old projects, compare prices to originals
4. Concurrent drag-drop test: open offer in 2 tabs, drag in both, verify final state
5. Copy offer phases to test project, verify phase/task/deliverable counts
### Phase 8 (Public Quote Pages)
1. Brute-force token space: 1000 guesses/sec, verify rate limit activates
2. Enumerate tokens: generate 100 quotes, try to access one meant for different email, verify 401
3. Expiration test: set token_expires_at to past, verify access fails
4. Code audit: search response JSON for price, quote_items, per_service_price (should be zero matches)
5. Authorized access test: correct email + token, verify success
### Phase 9 (CRM — Won Automation)
1. Double-click "Win" button, verify only 1 client created
2. Network failure during "Win": simulate timeout after client creation, retry, verify same project returned
3. Partial failure: mock project creation failure, verify client not created (rollback)
4. Payment consistency: verify 24 payments created based on payment plan
5. Idempotency: call "Win" 10x with same idempotency_key, verify only 1 project, 1 set of payments
---
## Confidence Assessment
| Area | Level | Reason |
|------|-------|--------|
| Catalog consolidation risks | HIGH | Schema review shows existing quote_items dependencies; migration pattern verified via Drizzle docs |
| Offer copy semantics | HIGH | JavaScript shallow copy / deep copy distinction well-understood; transaction guarantees verified |
| Token security | HIGH | Brute-force math straightforward; nanoid 32 vs 21 entropy difference verified |
| CRM idempotency | HIGH | Idempotency pattern researched across multiple sources; double-click problem well-documented |
| Scope creep prevention | MEDIUM | CRM feature bloat is common, but solo consultant constraint makes Must/Should/Won't achievable |
| Concurrent edit handling | MEDIUM | Optimistic locking pattern standard, but requires careful implementation in Next.js + Drizzle |
---
## Next Steps for Phase Planning
1. **Before Phase 7 design starts:**
- Finalize catalog consolidation migration plan (Expand-Contract timeline)
- Document offer/project hierarchy mapping (1 page, code-level documentation)
- Design offer copy function signature and transaction strategy
2. **Before Phase 7 code starts:**
- Implement drag-drop versioning (version field + optimistic locking)
- Write integration tests for offer phase copy
3. **Before Phase 8 design starts:**
- Token security requirements: length (32), expiration (7d), email validation, rate limit (3/min)
- API response schema: exclude quote_items, include total_price only
4. **Before Phase 9 design starts:**
- Finalize Must/Should/Won't scope (document with user/consultant)
- Design "Win" workflow: idempotency key + atomic transaction
- Define payment plan algorithm (24 payments based on offer tier)
---
## References for Implementation
- **Drizzle migrations:** https://dev.to/whoffagents/zero-downtime-postgres-migrations-with-drizzle-orm-22ga
- **Idempotency pattern:** https://codefarm0.medium.com/the-double-click-problem-how-idempotency-saved-our-checkout-system-a704be65d207
- **Optimistic updates:** https://www.nirtamir.com/articles/optimistic-updates-state-vs-render/
- **Schema evolution:** https://www.dataexpert.io/blog/backward-compatibility-schema-evolution-guide
- **Token security:** https://workos.com/blog/oauth-common-attacks-and-how-to-prevent-them/
---
**Document location:** `/Users/simonecavalli/Vault/IAMCAVALLI/.planning/research/PITFALLS_V2.md` (detailed pitfall reference)
File diff suppressed because it is too large Load Diff