---
gsd_state_version: 1.0
milestone: v2.0
milestone_name: Business Operations Suite
status: executing
last_updated: "2026-06-11T07:40:00Z"
last_activity: 2026-06-11
progress:
  total_phases: 5
  completed_phases: 1
  total_plans: 8
  completed_plans: 5
  percent: 62.5
---

# 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: 09-quote-builder-client-acceptance (Plan 3 just completed)
Plans: 07 (complete) | 08 (complete) | 09-01 (complete) | 09-02 (complete) | 09-03 (complete)
Status: Phase 7 executed ✓ | Phase 8 executed ✓ | Phase 9 Plan 1-3 executed ✓
Last activity: 2026-06-11T07:40:00Z — Phase 9 Plan 3 execution complete (public quote page with rate limiting)

## 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:** `services` table 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 `services` table
- **Admin CRUD:** `/admin/catalog` list/add/edit/soft-delete fully operational on `services` table 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 `services` via 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/offers` CRUD — macro, micro, servizi + assegnazione checkbox
- **05-03**: OffersTab nel workspace progetto + `getClientActiveOffers()` + sezione Offerte Attive nel dettaglio cliente
- **05-04**: `OffersSection` nella dashboard cliente (solo `public_name`) + `/admin/forecast` 12 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 UPDATE
- `clients.accepted_total` denormalizzato — client API non tocca mai `quote_items`
- `deliverables.approved_at` immutabile — 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 not` per 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-11T07:40:00Z
Phase 9 Plan 3 execution complete (Public quote page with rate limiting and multistep wizard).
All quote builder infrastructure in place: validators, service layer, admin UI, public page.
Ready for next plan: Phase 9 Plan 4 (Quote Items & Pricing Configuration).

## Operator Next Steps

### Immediate (Phase 9 Remaining Plans)

- Phase 9 Plan 4: Quote items configuration (admin can add/edit line items per phase)
- Phase 9 Plan 5: Service picker and price overrides per line item
- Phase 10: Email confirmation via Resend, quote expiration, sharing links

### 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
