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
+10 -1
View File
@@ -1,9 +1,10 @@
import { db } from "@/db";
import { clients, projects, payments } from "@/db/schema";
import { eq, sum, count } from "drizzle-orm";
import { eq, sum, count, sql } from "drizzle-orm";
export type KpiStats = {
clientiAttivi: number;
clientiNuoviMese: number; // clients created in the current calendar month
revenueTotale: string; // sum of projects.accepted_total (non-archived)
pagamentiInSospeso: string; // sum of payments.amount where status = 'da_saldare' or 'inviata'
progettiInCorso: number; // projects not archived
@@ -20,6 +21,13 @@ export async function getDashboardStats(): Promise<DashboardStats> {
.from(clients)
.where(eq(clients.archived, false));
const [clientiNuoviMeseRow] = await db
.select({ count: count() })
.from(clients)
.where(
sql`date_trunc('month', ${clients.created_at}) = date_trunc('month', now())`
);
const [revenueRow] = await db
.select({ total: sum(projects.accepted_total) })
.from(projects)
@@ -46,6 +54,7 @@ export async function getDashboardStats(): Promise<DashboardStats> {
const kpi: KpiStats = {
clientiAttivi: clientiAttiviRow?.count ?? 0,
clientiNuoviMese: clientiNuoviMeseRow?.count ?? 0,
revenueTotale: revenueRow?.total ?? "0",
pagamentiInSospeso: pagamentiSospeso.toFixed(2),
progettiInCorso: progettiRow?.count ?? 0,