Files
Simone Cavalli a4942d7684 docs(03): plan Phase 3 — Service Catalog & Quote Builder (4 plans, 2 waves)
Wave 1: schema push (service_id nullable + custom_label).
Wave 2 (parallel): catalog CRUD page + quote builder tab.
Wave 3: E2E human verification checkpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:23:15 +02:00

216 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: "03"
plan: "04"
type: execute
wave: 3
depends_on:
- "03-02"
- "03-03"
files_modified: []
autonomous: false
requirements:
- CAT-01
- CAT-02
- ADMIN-03
must_haves:
truths:
- "Admin navigates to /admin/catalog — table shows all services with correct columns and status badges"
- "Admin adds a service, edits it inline, and disattiva/riattiva it — all changes persist on page refresh"
- "Admin opens a client's Preventivo tab — adds a catalog item and a freeform item — both appear in the table with correct subtotals and calculated total"
- "Admin saves an accepted_total — the client dashboard shows that exact amount, not the calculated sum"
- "A curl request to the client API returns NO quote_items field and NO service_id references"
artifacts:
- path: "src/app/admin/catalog/page.tsx"
provides: "Verified: catalog page loads and renders table"
- path: "src/components/admin/tabs/QuoteTab.tsx"
provides: "Verified: three sections render correctly, catalog and freeform items work"
- path: "src/lib/client-view.ts"
provides: "Verified: zero quote_items references"
key_links:
- from: "clients.accepted_total (DB)"
to: "client dashboard display"
via: "client-view.ts query → /c/[token] page"
pattern: "accepted_total"
---
<objective>
End-to-end verification of Phase 3. The admin runs the full workflow — create catalog service, add to quote, set accepted_total — and confirms the client dashboard shows the correct total. Also verifies the security constraint: `quote_items` are never returned by the client API.
Purpose: Confirms Phase 3 is shippable. Catches any integration issue between catalog, quote builder, and client dashboard before the phase is marked complete.
Output: Human verification sign-off + SUMMARY.md.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@/Users/simonecavalli/IAMCAVALLI/.planning/ROADMAP.md
@/Users/simonecavalli/IAMCAVALLI/.planning/phases/03-service-catalog-quote-builder/03-02-SUMMARY.md
@/Users/simonecavalli/IAMCAVALLI/.planning/phases/03-service-catalog-quote-builder/03-03-SUMMARY.md
</context>
<tasks>
<task type="auto">
<name>Task 1: Automated security and integration checks</name>
<read_first>
- /Users/simonecavalli/IAMCAVALLI/src/lib/client-view.ts (must contain zero quote_items references)
- /Users/simonecavalli/IAMCAVALLI/src/app/api (check all client-facing API route files for quote_items leaks)
</read_first>
<files></files>
<action>
Run the following automated checks in sequence. Report results for each.
**Check 1 — TypeScript compiles clean:**
```bash
cd /Users/simonecavalli/IAMCAVALLI && npx tsc --noEmit
```
Expected: zero output (no errors).
**Check 2 — Build succeeds:**
```bash
cd /Users/simonecavalli/IAMCAVALLI && npm run build
```
Expected: "Compiled successfully" with routes listed. No error lines.
**Check 3 — Security: quote_items not in client-facing code:**
```bash
cd /Users/simonecavalli/IAMCAVALLI && grep -rn 'quote_items' src/lib/client-view.ts src/app/api/ src/app/c/ 2>/dev/null || echo "CLEAN"
```
Expected: "CLEAN" or no output. If any match appears, that file must be fixed before the checkpoint.
**Check 4 — Service catalog page references getAllServices:**
```bash
cd /Users/simonecavalli/IAMCAVALLI && grep -c 'getAllServices' src/app/admin/catalog/page.tsx
```
Expected: 1
**Check 5 — NavBar contains Catalogo link:**
```bash
cd /Users/simonecavalli/IAMCAVALLI && grep -c '/admin/catalog' src/components/admin/NavBar.tsx
```
Expected: 1
**Check 6 — Client detail page has Preventivo tab:**
```bash
cd /Users/simonecavalli/IAMCAVALLI && grep -c 'Preventivo' src/app/admin/clients/\[id\]/page.tsx
```
Expected: 2
**Check 7 — quote-actions has requireAdmin in all three actions:**
```bash
cd /Users/simonecavalli/IAMCAVALLI && grep -c 'requireAdmin' src/app/admin/clients/\[id\]/quote-actions.ts
```
Expected: 3 (one per action)
**Check 8 — accepted_total security check (client view does NOT expose quote detail):**
```bash
cd /Users/simonecavalli/IAMCAVALLI && grep 'accepted_total\|quote_items\|service_id' src/lib/client-view.ts
```
Expected: `accepted_total` appears (it's the field clients see), `quote_items` does NOT appear, `service_id` does NOT appear.
If all 8 checks pass, proceed to the human verification checkpoint.
If any check fails, fix the issue before proceeding.
</action>
<verify>
<automated>cd /Users/simonecavalli/IAMCAVALLI && npx tsc --noEmit && npm run build 2>&1 | tail -5</automated>
Expected: build output ends with route list — no "Failed to compile" line.
<automated>cd /Users/simonecavalli/IAMCAVALLI && grep -rn 'quote_items' src/lib/client-view.ts src/app/c/ 2>/dev/null | wc -l | tr -d ' '</automated>
Expected: 0
</verify>
<done>
All 8 automated checks pass. TypeScript clean, build succeeds, quote_items absent from client-facing code.
</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 2: Human end-to-end verification of Phase 3</name>
<what-built>
Service Catalog CRUD at /admin/catalog, Quote Builder tab in client detail, accepted_total round-trip to client dashboard.
</what-built>
<how-to-verify>
Start the dev server: `npm run dev` (port 3000).
**Test A — Catalog page:**
1. Open http://localhost:3000/admin/catalog
2. Confirm the page loads with "Catalogo Servizi" heading and "Aggiungi servizio" button
3. Click "Aggiungi servizio" — fill in Nome: "Test Servizio", Prezzo: "500" — click Aggiungi
4. Confirm "Test Servizio" appears in the table with "Attivo" badge and €500,00 price
5. Click "Modifica" on the row — change price to "750" — click Salva
6. Confirm price updates to €750,00 without page reload
7. Click "Disattiva" — confirm badge changes to "Disattivato" and row becomes dimmed (50% opacity)
8. Click "Riattiva" — confirm badge returns to "Attivo"
**Test B — NavBar:**
1. Confirm "Catalogo" link appears in the admin NavBar between "Statistiche" and "Esci"
2. Click it — confirm it navigates to /admin/catalog
**Test C — Quote Builder tab:**
1. Open any existing client at http://localhost:3000/admin/clients/[id]
2. Confirm "Preventivo" tab appears as 5th tab (after Commenti)
3. Click the Preventivo tab
4. Select "Test Servizio" from the dropdown (if inactive, reactivate first) — set qty 1 — click Aggiungi
5. Confirm item appears in the table with correct unit price and subtotal
6. Click "Oppure aggiungi voce libera →" — enter Nome: "Extra consulenza", Prezzo: "200", Qty: 2 — click Aggiungi voce libera
7. Confirm second item appears with "Extra consulenza" label, subtotal €400,00
8. Confirm "Totale calcolato" shows the sum (e.g., €1.150,00 if service was €750)
9. Click "Rimuovi" on one item — confirm it disappears
**Test D — Accepted total round-trip (critical):**
1. In the Preventivo tab, set "Totale accettato dal cliente" to 1200 — click Salva
2. Open the client dashboard at http://localhost:3000/c/[client-token] in a new tab
3. Confirm the dashboard shows "€1.200,00" (or equivalent) as the accepted total
4. Back in admin, open the Pagamenti tab — confirm "Totale preventivo" input shows 1200
**Test E — Security check (quote_items never exposed):**
1. In the browser DevTools (Network tab), open the client dashboard /c/[token]
2. Find any API calls made by that page — inspect their response bodies
3. Confirm NO response contains "quote_items", "service_id" (from quote context), or individual line item prices
4. Alternative: run `curl http://localhost:3000/api/client/[client-id-or-token]` if a client API route exists — confirm response has only `accepted_total`, not quote item details
</how-to-verify>
<resume-signal>
Type "approved" if all 5 tests pass. Or describe any failures (e.g., "Test C step 5 fails — items not appearing") so they can be fixed.
</resume-signal>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| Client browser → /c/[token] route | Client sees only what the route explicitly returns — verified here that quote_items are absent |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-03-04-01 | Information Disclosure | Client dashboard API response | mitigate | Check 3 + Test E verify that no quote_items appear in any client-facing response; if found, fix before approving |
| T-03-04-02 | Tampering | Phase 3 shipped without DB push | mitigate | 03-01 is a hard dependency of this wave; if drizzle-kit push was skipped, custom_label column absent causes runtime crash caught in Test C |
</threat_model>
<verification>
Phase 3 complete when:
1. All 8 automated checks in Task 1 pass
2. Human verifies Tests AE in Task 2
3. Client dashboard shows correct `accepted_total` after update (Test D)
4. Zero `quote_items` in any client-facing response (Test E)
</verification>
<success_criteria>
- Service catalog is fully operational: add, edit, disable, re-enable services
- Quote builder adds catalog items (with snapshotted price) and freeform items (service_id = null)
- accepted_total write in admin is reflected in client dashboard
- Phase 3 roadmap success criteria 13 are all TRUE:
1. Admin can add/edit/disable catalog services
2. Admin can compose a quote from catalog; system calculates total
3. After saving accepted_total, client dashboard shows correct total; quote_items never exposed
</success_criteria>
<output>
After completion, create `.planning/phases/03-service-catalog-quote-builder/03-04-SUMMARY.md`
</output>