Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 KiB
gsd_state_version, milestone, milestone_name, status, last_updated, last_activity, progress
| gsd_state_version | milestone | milestone_name | status | last_updated | last_activity | progress | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1.0 | v2.0 | Business Operations Suite | executing | 2026-06-11T19:00:00Z | 2026-06-11 |
|
Project State
Project Reference
See: .planning/PROJECT.md (updated 2026-05-09)
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.
Current Position
Phase: 10-crm-pipeline-activity-logging (redo complete, deployed) Plans: 07 (complete) | 08 (complete) | 09-01..03 (complete) | 10-01..03 (complete, redeployed 2026-06-11) Status: Phase 7 ✓ | Phase 8 ✓ | Phase 9 ✓ | Phase 10 ✓ (staged redo after rollback) Last activity: 2026-06-11T19:00:00Z — Phase 10 staged redo deployed; production DB migrations 0001/0003/0004/0005 applied
What Was Built (Phase 10 — CRM Pipeline & Activity Logging, redo)
Root cause of the original Phase 10 production crash (resolved)
Production Postgres (Coolify container) had no migrations applied after 0000 — services, leads, offer_phases, offer_phase_services, quotes did not exist. Phase 7/8/9 summaries said "migration pending connectivity" and were never followed up. Any page querying those tables 500'd: catalog (services), quote builder + /quote/[token] (quotes), and after Phase 10 deploy also dashboard (FollowUpWidget → leads). The Phase 10 rollback only masked the dashboard symptom.
Fix (2026-06-11): migrations 0001+0003+0004+0005 applied atomically (single transaction, additive-only, verified zero DROP/TRUNCATE) via SSH → docker exec psql on container xwkk0040w0kk0gsgcgog8owk, db clienthub. Protected rows verified intact post-migration (4 clients, 5 projects, 13 payments, 6 phases). /quote/[token] 500 → 404 (correct behavior) confirmed.
Delivered (redo commits 5aa6614 + 008a434)
- Stage A (
5aa6614): deps@radix-ui/react-dialog,date-fns+ sharedui/dialog.tsx,ui/form.tsx - Stage B: production DB schema aligned (see above) — script
scripts/push-phase10-migration.tsfor future reuse - Stage C (
008a434): schema.ts CRM tables (leads expansion, activities, reminders + relations), lead-service/lead-validators,/admin/leadslist+detail+actions, LeadTable/LeadDetail/LeadForm, LogActivityModal, SendQuoteModal, FollowUpWidget on dashboard, sidebar link
What Was Built (Phase 9 — Quote Builder UI & Public Pages)
09-01: Quote Validators and Service Layer ✓
- Validators: Zod schemas for quote creation (client_id, offer_micro_id, accepted_total)
- Service Layer: Public/admin query separation; token-gated access without exposing line items
- Immutability Guard: isQuoteAccepted() prevents double-accept of quotes
- Migration: 0004_phase-9-quotes-validators.sql (additive-only, backward compatible)
- Status: DONE — Schema and validators tested, ready for UI integration
09-02: Admin Quote Builder UI ✓
- Components: 4 new components in src/components/admin/quotes/
- QuoteBuilderForm.tsx (140 lines): Two-column form (left: inputs, right: preview) with full state management
- OfferSelector.tsx (25 lines): Grouped dropdown for macro/micro selection
- PriceOverrideInput.tsx (45 lines): Price input with visual validation feedback
- QuotePreview.tsx (45 lines): Live preview showing offer details and calculated total
- Server Actions: createQuote() in src/lib/quote-actions.ts
- Validates client and offer existence in DB
- Generates nanoid(21) token (collision-free, ~122-bit entropy)
- Returns public
/quote/[token]link for sharing - Atomic DB transaction: quote header insert only (items follow in Phase 9-04)
- Page: /admin/quotes/new with form rendering and data population
- Query: getAllOfferMacrosWithMicros() for form dropdown population
- Status: DONE — Page builds, form renders, server action tested. Ready for public page (Phase 9-03)
09-03: Public Quote Page with Token-Gated Access & Rate Limiting ✓
- Rate Limiting: Enhanced src/proxy.ts with rate limit check for /quote/[token] routes
- 3 views per minute per IP (via existing rateLimit utility)
- Returns 429 Too Many Requests when limit exceeded
- IP extraction from x-forwarded-for header (Docker-aware)
- Multistep Wizard Components: 4 components totaling 495 lines
- QuoteMultistep.tsx (121 lines): Parent managing step state (1-3) with visual progress indicator
- QuoteStep1Overview.tsx (70 lines): Read-only offer name, total price (EUR), phase summary
- QuoteStep2Selection.tsx (95 lines): Phase/service listing (read-only MVP) with total recap
- QuoteStep3Summary.tsx (210 lines): Summary + optional email/notes form, Accept/Reject buttons
- Page Structure:
- layout.tsx (28 lines): Public layout with gradient background, no auth header
- page.tsx (80 lines): Token validation (21-char nanoid format), quote state detection (draft/accepted/rejected)
- actions.ts (108 lines): acceptQuote() and rejectQuote() server actions with immutability enforcement
- Security: quote_items never exposed; only accepted_total and phase summary visible to client
- Status: DONE — Route visible in build output, 3 commits, all tests passing (npm run build: 0 errors)
What Was Built (Phase 8 — Quote Builder Schema Foundation) ✓
08-01: Offer Phases & Quote Tables Schema ✓
- Schema: 4 new tables (offer_phases, offer_phase_services, quotes, leads) + 7 columns added to existing tables
- offer_phases: Hierarchical breakdown of offer_micros (Discovery → Strategy → Execution phases)
- offer_phase_services: Junction linking phases to unified services table (Phase 7)
- quotes: Separate quote headers from line items with token-based public access
- Extensions: quote_items (quote_id, offer_micro_id, offer_phase_id), projects (offer_id, created_from_lead_id), phases (offer_phase_id)
- TypeScript: OfferPhase, OfferPhaseService, Quote, Lead types exported
- Drizzle Relations: 6 new relations for proper ORM traversal
- Status: Schema complete, migration script ready, validation checks implemented
- Data Safety: Additive-only migration (no drops, no truncates) — fully reversible
08-02: Migration & Validation Scripts ✓
- Migration: src/db/migrations/0003_offer_phases_quote_templates.sql (89 lines, additive-only)
- Indexes: 11 created for performance (micro_id, service_id, token, client_id, status, etc.)
- Validation: scripts/validate-phase8-migration.ts with 10 checks (all 4 tables + 6 columns verified)
- Status: READY FOR DEPLOYMENT when Postgres is reachable
08-03: Query Layer for Quote Builder ✓
- Query Functions: 5 new async functions added to admin-queries.ts
- getOfferPhases(microId) — get all phases for an offer micro
- getOfferPhaseServices(phaseId) — get all services assigned to a phase
- getQuoteById(quoteId) — full quote header by ID
- getQuoteByToken(token) — quote by public token (for /quote/[token])
- getQuotesByClient(clientId) — all quotes for a client
- Build Status: npm run build passes with zero TypeScript errors
- Ready for: Phase 9 quote builder UI, public quote pages
What Was Built (Phase 7 — v2.0)
07-01: Unified Service Catalog (Expand Phase) ✓
- Schema:
servicestable with migrated_from/migrated_id audit trail for rollback safety - TypeScript: Service and NewService types exported
- Backfill: Idempotent migration script (21 service_catalog rows + 35 offer_services rows → services table)
- Collision Resolution: ' (Offer)' suffix prevents silent overwrites of duplicate names
- Validation: 6-check suite proves zero data loss (row counts, FK integrity, orphaned references, net-new services count)
- Status: Code complete; database migration pending connectivity (schema, scripts ready to apply)
- Data Safety: Legacy tables (service_catalog, offer_services) untouched — rollback possible by not using services table
07-02: Admin Catalog CRUD + Quote Builder Rewire ✓
- Query Layer: getAllServices() and activeServices queries updated to read from unified
servicestable - Admin CRUD:
/admin/cataloglist/add/edit/soft-delete fully operational onservicestable with category field - Service Form: Added optional category input field for new/edited services
- Service Table: Category column rendering; net-new services tracked with migrated_from=null
- Quote Builder: QuoteTab service picker reads from
servicesvia activeServices - Backward Compatibility: Historical quote_items → service_catalog join preserved for existing quote item labels
- Type Safety: All components (ServiceTable, ServiceForm, QuoteTab) typed against Service instead of ServiceCatalog
- Status: Code complete, TypeScript verified (npm run build passes), manual testing documented
- ROADMAP Success Criteria: #3 (/admin/catalog operational on services) and #4 (old catalog queries explicit) satisfied
What Was Built (Phase 5)
- 05-01: Schema migration — 5 nuove tabelle offer in DB + tipi TypeScript
- 05-02:
/admin/offersCRUD — macro, micro, servizi + assegnazione checkbox - 05-03: OffersTab nel workspace progetto +
getClientActiveOffers()+ sezione Offerte Attive nel dettaglio cliente - 05-04:
OffersSectionnella dashboard cliente (solopublic_name) +/admin/forecast12 mesi
What Was Built (Phase 4)
- 04-00: Infra — route /c/ → /client/, Dockerfile, deploy Gitea → Coolify
- 04-01: DB schema migration — projects, settings, FK pivot
- 04-02: Admin projects list + client detail project cards
- 04-03: Project workspace + timer analytics + settings page
- 04-04: Slug resolution + multi-project client dashboard + slug edit
- 04-05/06: Post-deploy bug fixes
- 04-07: Debug code removed, public page restored
- security: Full hardening — auth guards, rate limiting, security headers
- payments: Init payments + split payment in project workspace
Production State
- App: hub.iamcavalli.net (Coolify, Hetzner)
- DB: Postgres self-hosted on Coolify
- Auth: Auth.js (admin) + token middleware (clients)
- Routes: /admin/* (admin), /client/[token]/* (clients)
Decisions (All Phases)
clients.tokenè campo separato (non la PK) — rotazionabile via single UPDATEclients.accepted_totaldenormalizzato — client API non tocca maiquote_itemsdeliverables.approved_atimmutabile — audit trail dal giorno uno- Edge middleware (
proxy.ts) usa fetch() a route interna — postgres-js non può girare nell'Edge runtime - Tailwind v4 auto-detection allargata — aggiunto
@source notper escludere.01_projects/e.claude/ - Authelia va solo davanti ad /admin — /client/[token] deve bypassare per accesso via link token
Deferred Items
| Category | Item | Status |
|---|---|---|
| v2 | Claude AI onboarding (CLAUDE-01, CLAUDE-02, CLAUDE-03) | Backlog |
| Infrastructure | Authelia SSO davanti ad /admin | After Phase 4 (now ready) |
| Infrastructure | Higgsfield clone deploy (nuovo progetto Coolify) | Pending |
| SEO | WordPress auto-publish articoli | Mentioned, not planned |
Session Continuity
Last session: 2026-06-11T19:00:00Z
Phase 10 staged redo complete: production DB migrations applied (0001/0003/0004/0005), CRM module deployed.
Dangling work recovered from phase10-wip branch (anchor of reflog commit 8e2752a — branch can be deleted once redo verified in production).
Lesson recorded: migrations are applied manually in this project — every deploy touching schema MUST apply the migration to prod BEFORE pushing code.
Operator Next Steps
Immediate
- Verify in browser (authenticated): /admin (FollowUpWidget), /admin/leads, /admin/catalog, /admin/quotes/new
- Re-run
/gsd-plan-phase 11and/gsd-plan-phase 12(planning folders never committed, must be regenerated) - Optionally cherry-pick sidebar "App shortcuts" dangling commit
5d75752if still wanted
Phase 10 (Concurrent Activities)
- Email integration (Resend) for quote acceptance confirmation
- CRM leads table implementation (currently placeholder)
- Activity logging for quote events (viewed, accepted, rejected)
- Auto-provisioning webhook listener (triggers project creation on acceptance)
Phase 11 (Auto-Provisioning)
- Consume quote acceptance event
- Automatically create project phases from offer_phases structure
- Provision services based on accepted quote items