feat(portale): barra full-width, card offerta senza accordion, valore override

Barra di avanzamento a tutta larghezza (via il max-w-[1200px]).

Card offerta: rimosso l'accordion "Cosa è compreso". La lista servizi non
arriva più al client — client-view.ts legge dai servizi i soli prezzi — e
OffersSection perde il suo unico useState, quindi anche il "use client".

"Valore incluso" diventa "Valore dell'offerta" e accetta un override manuale
(migration 0020, project_offers.offer_value_override). Prima era sempre la
somma dei prezzi di catalogo del tier: in produzione mostrava €20.250 su
offerte vendute a 7.000 e 5.500. NULL torna al calcolo, 0 nasconde la riga.
Si gestisce dal tab Offerte del progetto, che affianca la cifra calcolata
per far vedere cosa si sta sostituendo.

La somma calcolata vive ora in src/lib/offer-value.ts, letta sia dal portale
sia dall'admin: duplicarla avrebbe fatto divergere le due viste.

"Prezzo finale" diventa "Investimento finale" (il ricorrente resta "Canone
mensile").

Migration 0020 applicata a prod prima del push. Override tutti NULL, quindi
il comportamento resta invariato finché non se ne imposta uno.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 07:54:55 +02:00
parent 7e58031528
commit 44be190631
12 changed files with 270 additions and 133 deletions
+4
View File
@@ -343,6 +343,10 @@ The `/client/[token]` portal was migrated to the same "Quiet Luxury" tokens
- Sidebar cards (`OffersSection`, `PaymentStatus`, `DocumentsSection`,
`NotesSection`, `TranscriptsSection`) all follow `rounded-xl border-border-light
bg-card shadow-card`.
- **Offer card** (`OffersSection`): due sole righe di importo — «Valore
dell'offerta» (override admin o somma dei servizi; nascosta a 0) e «Canone
mensile» / «Investimento finale». Niente accordion «Cosa è compreso»: la lista
servizi non arriva più al client, e il componente non è più `"use client"`.
- `ClientDashboard` takes an `embedded` prop: when rendered inside the
multi-project tabs wrapper (`page.tsx`), it drops its own header/footer/canvas
so the page-level chrome isn't duplicated.