docs(03-02): complete service catalog plan — NavBar + page + actions + components
- SUMMARY.md for plan 03-02 with all task commits, decisions, deviations
This commit is contained in:
@@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
phase: 03-service-catalog-quote-builder
|
||||||
|
plan: "02"
|
||||||
|
subsystem: admin-ui
|
||||||
|
tags: [catalog, server-actions, drizzle, zod, nextjs, tailwind]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 03-service-catalog-quote-builder
|
||||||
|
plan: "01"
|
||||||
|
provides: service_catalog table + ServiceCatalog type in schema.ts
|
||||||
|
provides:
|
||||||
|
- /admin/catalog route: fully functional service catalog CRUD page
|
||||||
|
- createService server action (with requireAdmin + Zod validation)
|
||||||
|
- updateService server action (with requireAdmin + Zod validation)
|
||||||
|
- toggleServiceActive server action (with requireAdmin guard)
|
||||||
|
- getAllServices() query in admin-queries.ts
|
||||||
|
- NavBar Catalogo link
|
||||||
|
affects:
|
||||||
|
- 03-03 (quote builder — consumes getAllServices() for active services dropdown)
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Server Action + requireAdmin() guard: getServerSession(authOptions) at top of every action"
|
||||||
|
- "Zod coerce.number for unit_price: z.coerce.number().min(0.01)"
|
||||||
|
- "Inline edit pattern: useTransition + form action + router.refresh() (mirrors DocumentRow.tsx)"
|
||||||
|
- "Soft-delete visibility: opacity-50 on inactive rows; badge Disattivato/Attivo"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- src/app/admin/catalog/actions.ts
|
||||||
|
- src/app/admin/catalog/page.tsx
|
||||||
|
- src/components/admin/catalog/ServiceTable.tsx
|
||||||
|
- src/components/admin/catalog/ServiceForm.tsx
|
||||||
|
modified:
|
||||||
|
- src/lib/admin-queries.ts
|
||||||
|
- src/components/admin/NavBar.tsx
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "requireAdmin() added to all three Server Actions — enforces session check even though /admin/* middleware protects the route (defense in depth for T-03-02-01)"
|
||||||
|
- "unit_price stored as .toFixed(2) string in DB (numeric column) — consistent with existing payments/quote_items pattern"
|
||||||
|
- "Inactive services remain visible in table at opacity-50 — filtering for quote builder dropdown happens in 03-03 query"
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 15min
|
||||||
|
completed: 2026-05-17
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 03 Plan 02: Service Catalog CRUD UI Summary
|
||||||
|
|
||||||
|
**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()**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** ~15 min
|
||||||
|
- **Started:** 2026-05-17T09:40:00Z
|
||||||
|
- **Completed:** 2026-05-17T09:55:00Z
|
||||||
|
- **Tasks:** 2
|
||||||
|
- **Files created:** 4 | **Files modified:** 2
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Creato `src/app/admin/catalog/actions.ts` con tre Server Actions (`createService`, `updateService`, `toggleServiceActive`) — ogni action chiama `requireAdmin()` e valida i dati via Zod
|
||||||
|
- Aggiunto `getAllServices()` a `src/lib/admin-queries.ts` con import di `service_catalog` e tipo `ServiceCatalog`
|
||||||
|
- Creato `src/app/admin/catalog/page.tsx` — Server Component che carica tutti i servizi e renderizza `ServiceForm` + `ServiceTable`
|
||||||
|
- Creato `src/components/admin/catalog/ServiceForm.tsx` — form add-new con toggle open/closed, useTransition, gestione errori
|
||||||
|
- Creato `src/components/admin/catalog/ServiceTable.tsx` — tabella con per-row inline edit (pattern DocumentRow), badge Attivo/Disattivato, opacity-50 per servizi inattivi
|
||||||
|
- Aggiunto link "Catalogo" in `src/components/admin/NavBar.tsx` tra Statistiche e Esci
|
||||||
|
- TypeScript clean (zero errori), `npm run build` compilato con successo
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
| Task | Nome | Commit | File |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 1 | Server Actions + getAllServices query | `efbc235` | src/app/admin/catalog/actions.ts, src/lib/admin-queries.ts |
|
||||||
|
| 2 | Catalog page + components + NavBar link | `4aae2e0` | src/app/admin/catalog/page.tsx, src/components/admin/catalog/ServiceTable.tsx, src/components/admin/catalog/ServiceForm.tsx, src/components/admin/NavBar.tsx |
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
**Creati:**
|
||||||
|
- `src/app/admin/catalog/actions.ts` — tre Server Actions con requireAdmin() + Zod
|
||||||
|
- `src/app/admin/catalog/page.tsx` — Server Component per il catalogo
|
||||||
|
- `src/components/admin/catalog/ServiceTable.tsx` — tabella + inline edit per riga
|
||||||
|
- `src/components/admin/catalog/ServiceForm.tsx` — form aggiunta nuovo servizio
|
||||||
|
|
||||||
|
**Modificati:**
|
||||||
|
- `src/lib/admin-queries.ts` — aggiunto `getAllServices()`, import `service_catalog` e `ServiceCatalog`
|
||||||
|
- `src/components/admin/NavBar.tsx` — aggiunto link Catalogo dopo Statistiche
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- `requireAdmin()` presente in ogni Server Action anche se `/admin/*` è già protetto da middleware — defense in depth per T-03-02-01
|
||||||
|
- `unit_price` salvato come `.toFixed(2)` string in campo `numeric` — coerente con pattern pagamenti e quote_items già presenti
|
||||||
|
- I servizi inattivi rimangono visibili in tabella con opacity-50 — il filtro `active = true` per il dropdown del Quote Builder sarà nella query di 03-03
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 3 - Blocking] Mancanza DATABASE_URL nel worktree per il build**
|
||||||
|
- **Found during:** Task 2 verifica `npm run build`
|
||||||
|
- **Issue:** Il worktree non aveva `.env.local` — il build falliva con "DATABASE_URL env var is required"
|
||||||
|
- **Fix:** Symlink di `/Users/simonecavalli/IAMCAVALLI/.env.local` nel worktree root
|
||||||
|
- **Files modified:** nessun file sorgente — solo symlink di configurazione
|
||||||
|
- **Commit:** nessun commit aggiuntivo (symlink non tracciato in git)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Total deviations:** 1 auto-fixed (1 ambiente/blocking)
|
||||||
|
**Impact on plan:** Fix immediato, nessuno scope creep. Il build finale è compilato con successo.
|
||||||
|
|
||||||
|
## Known Stubs
|
||||||
|
|
||||||
|
Nessuno — tutti i componenti leggono dati reali dal DB via Server Actions e query Drizzle.
|
||||||
|
|
||||||
|
## Threat Surface Scan
|
||||||
|
|
||||||
|
Nessuna nuova superficie di sicurezza introdotta oltre a quanto già coperto dal threat model del piano:
|
||||||
|
- T-03-02-01: `requireAdmin()` implementato in tutti e tre i Server Actions (mitigato)
|
||||||
|
- T-03-02-02: Zod `unit_price: z.coerce.number().min(0.01)` implementato (mitigato)
|
||||||
|
- T-03-02-03: `serviceId` bound a livello di Server Action (mitigato)
|
||||||
|
- T-03-02-04: Rotta sotto middleware Auth.js `/admin/*` (accettato)
|
||||||
|
- T-03-02-05: Nessun `dangerouslySetInnerHTML`, JSX auto-escape (accettato)
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
- FOUND: `src/app/admin/catalog/actions.ts`
|
||||||
|
- FOUND: `src/app/admin/catalog/page.tsx`
|
||||||
|
- FOUND: `src/components/admin/catalog/ServiceTable.tsx`
|
||||||
|
- FOUND: `src/components/admin/catalog/ServiceForm.tsx`
|
||||||
|
- FOUND: `getAllServices` in `src/lib/admin-queries.ts`
|
||||||
|
- FOUND: `/admin/catalog` in `src/components/admin/NavBar.tsx`
|
||||||
|
- FOUND: commit `efbc235` (Task 1)
|
||||||
|
- FOUND: commit `4aae2e0` (Task 2)
|
||||||
|
- OK: TypeScript compila senza errori
|
||||||
|
- OK: `npm run build` — "Compiled successfully"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Phase: 03-service-catalog-quote-builder*
|
||||||
|
*Completed: 2026-05-17*
|
||||||
Reference in New Issue
Block a user