Files
simone 5d785a1c1c feat(04-04): slug resolution + multi-project client dashboard + slug edit
- validate-slug API route: resolves clients.slug for Edge middleware
- proxy.ts: slug-first resolution (D-06) — tries slug then falls back to token
- client-view.ts: complete rewrite — getClientWithProjectsByToken + getProjectView
  - No quote_items, no payment amounts in client API (CLAUDE.md security invariants)
- client/[token]/page.tsx: multi-project dashboard — 1 project = direct view,
  2+ projects = shadcn Tabs with project names (D-09/D-10)
- edit/page.tsx: slug field with link preview + unique constraint error handling
- actions.ts: updateClient now persists slug, redirects on success/slug_taken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 11:31:32 +02:00

3.5 KiB

plan, phase, status, completed_at
plan phase status completed_at
04-02 04-progetti-multi-project complete 2026-05-22

Summary: 04-02 — Admin Projects List + Client Detail Project Cards

What Was Built

NavBar — added Progetti (/admin/projects) and Impostazioni (/admin/impostazioni) links in the correct order: Clienti → Progetti → Statistiche → Catalogo → Impostazioni.

ProjectRow (src/components/admin/ProjectRow.tsx) — new table row component cloned from ClientRow, adapted for projects. Columns: Nome+Cliente, Valore, Acconto badge, Saldo badge, TimerCell, €/h. The €/h is computed inline: accepted_total / (totalTrackedSeconds / 3600), showing when no hours tracked.

/admin/projects (src/app/admin/projects/page.tsx) — table listing all projects via getAllProjectsWithPayments(). Empty state shows helpful message.

/admin/projects/new (src/app/admin/projects/new/page.tsx) — creation form with client <select>. Accepts ?client_id query param to pre-select the client (used when clicking "+ Nuovo Progetto" from a client's detail page). On submit calls createProject server action and redirects to the new project's workspace.

project-actions (src/app/admin/projects/project-actions.ts) — server actions: createProject, archiveProject, unarchiveProject, updateProjectAcceptedTotal. Each guards with local requireAdmin() (same pattern as other actions files — not imported from @/lib/auth which doesn't export it).

/admin/clients/[id] — fully rewritten from tabbed workspace to project cards grid. Active projects shown as clickable cards linking to /admin/projects/[id]. Archived projects shown below with reduced opacity. Header retains "Link cliente →" and ClientActions component.

getAllClientsWithPayments — extended to also fetch project names/amounts per client, computing:

  • projectBrands: string[] — non-archived project names, used as secondary labels under client name
  • ltv: string — sum of all projects' accepted_total (including archived), replaces single accepted_total in the LTV column

ClientRow — shows projectBrands.join(" | ") under client name (falls back to brand_name if no projects). LTV column now uses client.ltv.

/admin/page.tsx — column header renamed from "Totale" to "LTV".

Key Implementation Decisions

  • ?client_id pre-selection in new project form: used defaultValue={client_id ?? ""} on the <select> — React controlled default, works with server components.
  • No requireAdmin import: all server actions files define it locally with the same getServerSession(authOptions) pattern — consistent with existing codebase.
  • clientProjects query extended: instead of a separate parallel query for projectBrands/ltv, the existing clientProjects fetch was extended to also select name, accepted_total, archived — avoids an extra DB round-trip.

Files Modified/Created

File Action
src/components/admin/NavBar.tsx Updated — added 2 links
src/components/admin/ProjectRow.tsx Created
src/app/admin/projects/page.tsx Created
src/app/admin/projects/new/page.tsx Created
src/app/admin/projects/project-actions.ts Created
src/app/admin/clients/[id]/page.tsx Rewritten
src/app/admin/page.tsx Updated — column header
src/lib/admin-queries.ts Updated — projectBrands + ltv
src/components/admin/ClientRow.tsx Updated — projectBrands + ltv