feat: Dashboard admin redesign to Quiet Luxury + client profitability widget

Replica il mock design-reference/pagina-dashboard: layout condensato a
schermata singola (4 KPI + griglia 2/3-1/3), token semantici per dual-theme.

- Nuova query getClientProfitability(year): valore orario reale per cliente
  (contrattualizzato / ore tracciate) con badge margine, target 100 €/h
- Nuovo widget ClientProfitability "Analisi Oraria & Redditività Clienti"
- Rewrite /admin: KPI strip + Cashflow + Redditività | Follow-up + Offerte
  Più Richieste. Rimossi chart mensile, card extra, barre ore/cliente
- Tokenizzati ForecastChart, OffersSoldChart, FollowUpWidget, YearSelector
  (ora pill interattiva); rimosso MonthlyChart e prop availableYears inutile
- DESIGN-SYSTEM.md: inventario + note dashboard (applied 2026-07-11)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 11:57:10 +02:00
parent e5fa07bba3
commit 08aadc1d97
9 changed files with 622 additions and 254 deletions
@@ -25,11 +25,13 @@ export async function FollowUpWidget() {
if (leads.length === 0) {
return (
<div className="bg-white rounded-xl border border-[#e5e7eb] px-5 py-4">
<div className="bg-card rounded-xl border border-border shadow-card px-5 py-4">
<div className="flex items-center gap-2">
<h2 className="text-sm font-bold text-[#1a1a1a]">Follow-up</h2>
<h2 className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
Follow-up Attivi
</h2>
</div>
<p className="text-sm text-[#71717a] mt-2">Tutti i lead sono aggiornati </p>
<p className="text-sm text-muted-foreground mt-2">Tutti i lead sono aggiornati </p>
</div>
);
}
@@ -37,37 +39,39 @@ export async function FollowUpWidget() {
const visible = leads.slice(0, MAX_ROWS);
return (
<div className="bg-white rounded-xl border border-[#e5e7eb] overflow-hidden">
<div className="bg-card rounded-xl border border-border shadow-card overflow-hidden">
{/* Header */}
<div className="flex items-center justify-between px-5 py-3.5 border-b border-[#f4f4f5]">
<div className="flex items-center justify-between px-5 py-3.5 border-b border-border">
<div className="flex items-center gap-2.5">
<h2 className="text-sm font-bold text-[#1a1a1a]">Follow-up</h2>
<span className="inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 rounded-full bg-[#1A463C] text-white text-[11px] font-bold">
<h2 className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
Follow-up Attivi
</h2>
<span className="inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 rounded-full bg-amber-50 text-amber-700 border border-amber-100 text-[11px] font-bold dark:bg-amber-500/10 dark:text-amber-400 dark:border-amber-500/20">
{leads.length}
</span>
</div>
<Link
href="/admin/pipeline"
className="text-xs font-medium text-[#71717a] hover:text-[#1A463C] transition-colors"
className="text-xs font-medium text-muted-foreground hover:text-primary transition-colors"
>
Vedi tutti
</Link>
</div>
{/* Lead rows */}
<ul className="divide-y divide-[#f4f4f5]">
<ul className="divide-y divide-border">
{visible.map((lead) => (
<li key={lead.id}>
<Link
href={`/admin/pipeline/${lead.id}`}
className="flex items-center gap-3 px-5 py-3 hover:bg-[#f9f9f9] transition-colors group"
className="flex items-center gap-3 px-5 py-3 hover:bg-muted/40 transition-colors group"
>
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[#1A463C]/10 text-[#1A463C] text-xs font-bold">
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary text-xs font-bold">
{initials(lead.name)}
</span>
<div className="min-w-0 flex-1">
<p className="text-sm font-semibold text-[#1a1a1a] truncate">{lead.name}</p>
<p className="text-xs text-[#71717a] truncate">
<p className="text-sm font-semibold text-foreground truncate">{lead.name}</p>
<p className="text-xs text-muted-foreground truncate">
{lead.next_action
? lead.next_action
: [lead.company, relativeDays(lead.last_contact_date)]
@@ -75,7 +79,7 @@ export async function FollowUpWidget() {
.join(" · ")}
</p>
</div>
<span className="text-xs font-medium text-[#71717a] group-hover:text-[#1A463C] transition-colors shrink-0">
<span className="text-xs font-medium text-muted-foreground group-hover:text-primary transition-colors shrink-0">
Contatta
</span>
</Link>
@@ -86,7 +90,7 @@ export async function FollowUpWidget() {
{leads.length > MAX_ROWS && (
<Link
href="/admin/pipeline"
className="block px-5 py-2.5 text-center text-xs font-medium text-[#71717a] hover:text-[#1A463C] border-t border-[#f4f4f5] transition-colors"
className="block px-5 py-2.5 text-center text-xs font-medium text-muted-foreground hover:text-primary border-t border-border transition-colors"
>
Vedi tutti i {leads.length} lead
</Link>