8641253e85
- SUMMARY.md for plan 03-03 (2/2 tasks complete) - Covers quote-actions.ts, QuoteTab.tsx, admin-queries extension, page.tsx wiring - Security verification: requireAdmin on all actions, 0 quote_items in client-view
6.0 KiB
6.0 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03 | 03 | quote-builder |
|
|
|
|
|
|
Phase 03 Plan 03: Quote Builder Tab Summary
One-liner: 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.
Tasks Completed
| Task | Name | Commit | Files |
|---|---|---|---|
| 1 | Server Actions + extend getClientFullDetail | db81829 |
quote-actions.ts, admin-queries.ts |
| 2 | QuoteTab component + wire into client detail page | 48f81e7 |
QuoteTab.tsx, page.tsx |
What Was Built
Task 1 — Server Actions + Query Layer
src/app/admin/clients/[id]/quote-actions.ts: Three Server Actions exported:addQuoteItem(clientId, formData)— Zod validates service_id/custom_label, quantity, unit_price; computes subtotal; inserts intoquote_itemsremoveQuoteItem(quoteItemId, clientId)— deletes by item IDupdateAcceptedTotal(clientId, formData)— writes toclients.accepted_totalonly (no payment row splitting — that stays inactions.ts)- All three call
requireAdmin()(getServerSession check) before any DB operation
src/lib/admin-queries.ts:- Added
QuoteItemWithLabeltype (COALESCE resolved label, snapshotted unit_price) - Extended
ClientFullDetailwithquoteItems: QuoteItemWithLabel[]andactiveServices: ServiceCatalog[] - Added two queries in
getClientFullDetail(): leftJoin for quote items with COALESCE label; active services ordered by name - Security comment enforces that
client-view.tsmust never queryquote_items(verified: 0 functional references)
- Added
Task 2 — UI Component + Page Wiring
src/components/admin/tabs/QuoteTab.tsx("use client") — three sections:- Add items: catalog dropdown (pre-fills unit_price on selection, editable) + freeform toggle (custom_label + price + qty)
- Items table: label, qty, unit_price, subtotal columns; "Rimuovi" button per row; "Totale calcolato" in bold footer
- Accepted total: editable numeric input with "Salva" button + helper text clarifying client sees only this value
src/app/admin/clients/[id]/page.tsx:- Import
QuoteTab - Destructure
quoteItems,activeServicesfromgetClientFullDetailresult - Added 5th
TabsTrigger value="quote"with label "Preventivo" - Added 5th
TabsContent value="quote"rendering<QuoteTab>
- Import
Security Verification
| Constraint | Status |
|---|---|
| T-03-03-01: requireAdmin on all Server Actions | Done — all three actions call await requireAdmin() first |
| T-03-03-02: Zod validation on formData numbers | Done — quoteItemSchema validates quantity + unit_price as z.coerce.number().min(0.01) |
| T-03-03-03: quote_items not in client-facing routes | Done — client-view.ts has 0 functional references to quote_items (only comments) |
| T-03-03-04: IDOR on removeQuoteItem | Mitigated by requireAdmin; future multi-admin scenario noted for future hardening |
| T-03-03-05: XSS in custom_label | Accepted — React JSX auto-escapes, no dangerouslySetInnerHTML used |
| T-03-03-06: calculated_total vs accepted_total confusion | Accepted — visual design enforces separation |
Deviations from Plan
1. [Rule 3 - Blocking] Build required DATABASE_URL env var not present in worktree
- Found during: Task 2 build verification
- Issue: Worktree has no
.env.local; build fails with "DATABASE_URL env var is required" at runtime collection phase - Fix: Ran build with
DATABASE_URL=$(grep DATABASE_URL /path/.env.local ...)from main repo — build passed clean - Impact: None on code quality; worktree environment limitation only
2. [Rule 1 - Architecture] updateAcceptedTotal in quote-actions.ts does NOT update payment rows
- Found during: Task 1 implementation
- Rationale: The existing
updateAcceptedTotalinactions.tssplits the total 50/50 between payment rows. The quote tab version intentionally only writes toclients.accepted_total— this is the quote builder's domain. Payment row updates remain in the payments tab action. This preserves clean separation of concerns.
Known Stubs
None — all three sections are fully wired to real Server Actions and real DB queries.
Threat Flags
None — all new surface is admin-only, guarded by requireAdmin(), and consistent with the plan's threat model.
Self-Check: PASSED
src/app/admin/clients/[id]/quote-actions.tsexists and exports 3 Server Actionssrc/components/admin/tabs/QuoteTab.tsxexists and exports QuoteTabsrc/lib/admin-queries.tsmodified with QuoteItemWithLabel type + quoteItems/activeServices in returnsrc/app/admin/clients/[id]/page.tsxmodified with Preventivo tab- Commits
db81829and48f81e7verified in git log - TypeScript: no errors
- Build: passes (with DATABASE_URL)
- client-view.ts: 0 functional references to quote_items