style: rifinitura Quiet Luxury dashboard — bordi soft, badge, colori

Allinea la dashboard alla rifinitura del mock (finezza bordi/colori/badge):

- Card su border-border-light (slate-100) per il bordo sottile
- KPI: valore verde su Incassato Reale, delta verde "+N questo mese" su
  Clienti Attivi (nuova metrica getDashboardStats.clientiNuoviMese)
- Redditività: badge "Ottimo" con tint emerald soft (non brand primary)
- Follow-up: righe come box bordati (no avatar), link "Apri →" primary
- Forecast: selettore anno (pill interattiva) spostato nell'header della card,
  barre non-picco grigie (bg-border), rimosso il numero "mese prossimo"
- Offerte Più Richieste: rimossa la pill totale nell'header

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 12:18:10 +02:00
parent 08aadc1d97
commit a20a9de2d7
7 changed files with 95 additions and 129 deletions
+26 -28
View File
@@ -17,32 +17,34 @@ export const revalidate = 0;
function MetricCard({
label,
value,
valueAccent,
delta,
sub,
positive,
}: {
label: string;
value: string;
valueAccent?: boolean;
delta?: string;
sub?: string;
positive?: boolean;
}) {
return (
<div className="bg-card p-5 rounded-xl border border-border shadow-card">
<div className="bg-card p-5 rounded-xl border border-border-light shadow-card">
<span className="text-[10px] uppercase font-bold text-muted-foreground tracking-wider">
{label}
</span>
<div className="flex items-baseline gap-2 mt-1">
<span className="text-2xl font-bold text-foreground font-mono">{value}</span>
{sub && (
<span
className={`text-xs ${
positive
? "font-medium text-emerald-600 dark:text-emerald-400"
: "text-muted-foreground"
}`}
>
{sub}
</span>
)}
<span
className={`text-2xl font-bold font-mono ${
valueAccent ? "text-emerald-700 dark:text-emerald-400" : "text-foreground"
}`}
>
{value}
</span>
{delta ? (
<span className="text-xs font-medium text-emerald-600 dark:text-emerald-400">{delta}</span>
) : sub ? (
<span className="text-xs text-muted-foreground">{sub}</span>
) : null}
</div>
</div>
);
@@ -80,14 +82,14 @@ export default async function AdminDashboard({
return (
<div className="flex flex-col gap-8">
{/* Toolbar: selettore anno (governa i KPI year-scoped + redditività) */}
<div className="flex items-center justify-end">
<YearSelector currentYear={year} />
</div>
{/* KPI strip */}
<section className="grid grid-cols-2 lg:grid-cols-4 gap-6">
<MetricCard label="Clienti Attivi" value={String(kpi.clientiAttivi)} sub="Totale corrente" />
<MetricCard
label="Clienti Attivi"
value={String(kpi.clientiAttivi)}
delta={kpi.clientiNuoviMese > 0 ? `+${kpi.clientiNuoviMese} questo mese` : undefined}
sub="Totale corrente"
/>
<MetricCard
label="Contrattualizzato"
value={fmtEur0(data.contracted)}
@@ -96,21 +98,17 @@ export default async function AdminDashboard({
<MetricCard
label="Incassato Reale"
value={fmtEur0(data.collected)}
valueAccent={data.collected > 0}
sub={`${collectedPct}% del totale`}
positive={data.collected > 0}
/>
<MetricCard
label="Ore Tracciate"
value={`${totalHours}h`}
sub="Su progetti attivi"
/>
<MetricCard label="Ore Tracciate" value={`${totalHours}h`} sub="Su progetti attivi" />
</section>
{/* Griglia contenuti: 2/3 + 1/3 */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Colonna 2/3 */}
<div className="lg:col-span-2 flex flex-col gap-8">
<ForecastChart data={forecast} />
<ForecastChart data={forecast} headerAction={<YearSelector currentYear={year} />} />
<ClientProfitability data={profitability} />
</div>