Files
clienthub/design-reference/DESIGN-SYSTEM.md
T
simone 08aadc1d97 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>
2026-07-11 11:57:10 +02:00

12 KiB
Raw Blame History

Design System: iamcavalli Admin & Client Portal (v1.0)

"Minimalist Premium / Quiet Luxury" — the aesthetic that replaces the old mixed token/hardcoded styling across ClientHub's admin area. First applied to the Lead Pipeline page (Phase: Pipeline redesign); the tokens and primitives documented here are the base for restyling every other section.

Philosophy

  • Quiet, not loud. Elevation is nearly invisible (shadow opacity ≤ 0.02), borders are hairline, color is used sparingly and only to carry meaning (status, semantics) — never for decoration.
  • Density with air. Generous padding (py-4 px-6 in tables, p-4 in kanban columns) paired with small type (text-xs, text-[11px]) reads as premium rather than cramped.
  • Numbers are monospace. Prices, phone numbers, dates, counts — anything tabular/numeric — use font-mono and right-alignment so columns of digits line up.
  • Dual-theme by construction. Every surface is built from semantic tokens (bg-card, text-muted-foreground, border-border, …), never raw Tailwind palette classes (bg-white, text-slate-900, …) or hex literals. This is what makes dark mode "just work" without a parallel dark stylesheet.
  • Motion is functional. Transitions exist to explain state change (sidebar collapse, hover, drag) — duration-200duration-350, ease-[cubic-bezier(0.4,0,0.2,1)] for the sidebar specifically. No decorative animation.

Typography

  • Sans: Plus Jakarta Sans (weights 300700), loaded via next/font/google as --font-plus-jakarta-sans, mapped to Tailwind's --font-sans in @theme. Used for all UI text.
  • Mono: Geist Mono, --font-geist-mono--font-mono. Used for numeric data cells only (prices, phone numbers, counts, dates in tables).
  • Scale conventions:
    • Page title: text-2xl font-semibold tracking-tight
    • Page subtitle: text-xs text-muted-foreground
    • Table header cells: text-[11px] font-semibold uppercase tracking-wider text-muted-foreground
    • Table body: text-sm
    • Badge/pill label: text-[10px]text-xs font-semibold uppercase tracking-wide

Color Tokens

Raw values live in :root (light) / .dark (dark) in src/app/globals.css and are mapped to Tailwind utilities via @theme inline. Components must consume the mapped utility, never the raw hex or a Tailwind palette shade.

Utility Light Dark Usage
bg-background / text-foreground #ffffff / #1a1a1a #0e1512 / #f2f4f3 Page canvas
bg-card / text-card-foreground #ffffff #131a16 Cards, table, kanban cards, header bar
bg-muted / text-muted-foreground #f9f9f9 / #71717a #171f1b / #9aa39e Subtle backgrounds, secondary text, kanban columns
bg-primary / text-primary-foreground #1A463C #3FA88C Primary actions, sidebar, active nav state
bg-accent #DEF168 #DEF168 Rare highlight accent (lime)
border-border / border-input #e5e7eb #26302b All hairline borders
ring-ring #1A463C #3FA88C Focus rings
bg-destructive #ef4444 #f87171 Destructive actions

Sidebar exception: the sidebar stays brand green bg-[#1A463C] in both themes (it is not a themed surface — it's the constant brand anchor).

Status/semantic colors (lead stages, badges) use Tailwind's default palette (blue/purple/amber/orange/emerald/red) directly, each paired with an explicit dark: variant for legibility on dark surfaces — see StatusBadge.

Elevation & Radius

  • --radius: 0.5rem (base) is wired into @theme as --radius-lg (= --radius) and --radius-xl (= --radius + 0.25rem), so rounded-lg / rounded-xl utilities resolve consistently off one token.
  • --shadow-card: 0 4px 20px rgba(0,0,0,0.01)shadow-card utility. Default resting elevation for cards, table containers, kanban cards.
  • --shadow-card-hover: 0 8px 30px rgba(0,0,0,0.02)shadow-card-hover utility. Applied on hover for interactive cards.

Layout Conventions

  • Table container: bg-card rounded-xl border border-border shadow-card overflow-hidden.
  • Table header row: bg-muted/50 border-b border-border text-[11px] uppercase tracking-wider text-muted-foreground.
  • Table body rows: py-4 px-6, divide-y divide-border, hover:bg-muted/40 transition-colors.
  • Kanban column: bg-muted/60 rounded-xl border border-border p-4.
  • Kanban card: bg-card rounded-lg border border-border shadow-sm hover:border-primary/30 hover:shadow-card-hover transition-all.
  • Page header: title text-2xl font-semibold tracking-tight text-foreground, subtitle text-xs text-muted-foreground mt-1, primary action button bg-primary text-primary-foreground hover:bg-primary/90.

Color/Class Migration Map (dual-theme principle)

When restyling any surface, replace hardcoded classes with token utilities:

Old (hardcoded) New (token)
bg-white bg-card
bg-slate-50 / bg-gray-50 bg-muted
border-slate-100 / border-slate-200 border-border
text-slate-900 / text-[#1a1a1a] text-foreground
text-slate-500 / text-slate-400 / text-[#71717a] text-muted-foreground
bg-brand-dark / text-[#1A463C] (as action color) bg-primary / text-primary

Motion

  • Sidebar collapse: transition-[width] duration-350 ease-[cubic-bezier(0.4,0,0.2,1)] on the <aside>; label/logo text use whitespace-nowrap transition-opacity duration-200 and fade to opacity-0 immediately on collapse, but only fade back in ~150ms after the width transition starts on expand (avoids text reflow/wrap during the animation).
  • Hover states: transition-colors duration-150duration-200 on rows, buttons, nav links.
  • Kanban drag: dragged card renders in a DragOverlay with rotate-1 shadow-xl; drop target column highlights via border-primary bg-primary/5.

UX Rules

  1. Numeric/tabular data is always monospace and right-aligned in tables.
  2. Every interactive control has a visible focus state (focus:ring-1 focus:ring-ring focus:border-primary pattern) for keyboard accessibility.
  3. Status is always communicated redundantly — color + text label — never color alone.
  4. Destructive actions use destructive tokens, never ad-hoc red.
  5. Empty states are muted, centered, and brief ("Nessun lead trovato").
  6. Sidebar collapse state persists across page loads via localStorage (iamcavalli:sidebar) and is restored before paint where possible to avoid layout jump.

Component Inventory

Primitives extracted from the Lead Pipeline redesign, intended for reuse across all future admin pages.

Component Path Purpose
AdminShell src/components/admin/AdminShell.tsx Client shell holding sidebar-collapsed state (persisted to localStorage), renders sidebar + top header + <main>. Wraps all /admin/* pages.
AdminSidebar src/components/admin/AdminSidebar.tsx Collapsible brand sidebar (w-64w-20), controlled by AdminShell. Nav items, theme toggle, sign-out.
StatusBadge src/components/ui/StatusBadge.tsx Rounded-full status pill with a color map per lead stage, each with a dark: variant. Replaces the old scattered STAGE_COLOR maps.
SearchInput src/components/ui/SearchInput.tsx Search-icon input, rounded-lg border-border focus:ring-primary.
SegmentedToggle src/components/ui/SegmentedToggle.tsx Generic Lista/Kanban-style segmented control (bg-muted p-1 rounded-lg, active option bg-card shadow-sm).
PageHeader src/components/admin/PageHeader.tsx Page title (text-2xl tracking-tight) + subtitle + action slot, token-based.
LeadTable src/components/admin/leads/LeadTable.tsx Luxury table restyle: bg-card rounded-xl shadow-card, uppercase muted headers, hover:bg-muted/40 rows, uses StatusBadge.
LeadsKanbanBoard src/components/admin/leads/LeadsKanbanBoard.tsx Kanban restyle: bg-muted/60 columns with count pills, bg-card cards with hover:border-primary/30. Uses @dnd-kit, keeps all 6 lead stages.
LeadsViewToggle src/components/admin/leads/LeadsViewToggle.tsx Wires SearchInput + SegmentedToggle together with the table/kanban views.
MilestoneStepper src/components/client/MilestoneStepper.tsx Client-portal horizontal milestone stepper: one node per real phase, connecting line filled by global_progress_pct. Done = filled bg-primary ✓, active = border-primary outline, upcoming = bg-muted.
LeadDetail src/components/admin/leads/LeadDetail.tsx Lead detail page (/admin/pipeline/[id]): bespoke header (name + inline StatusBadge + action bar), asymmetric lg:grid-cols-3 layout (1/3 profile card w/ icon-chip rows + tags, 2/3 Note/Follow-up + unified timeline).
ClientProfitability src/components/admin/dashboard/ClientProfitability.tsx Dashboard widget: valore orario reale per cliente (getClientProfitability → contrattualizzato ÷ ore tracciate) con badge margine (Ottimo bg-primary/10 text-primary / In Linea bg-muted).
YearSelector src/components/admin/YearSelector.tsx Selettore anno restilizzato a pill (rounded-full border-border bg-card shadow-card), frecce ←/→, valore font-mono. Guida i dati year-scoped della dashboard via ?year=.

Unified timeline pattern: activities + transcripts merged into one reverse-chronological "Storico delle Interazioni". Vertical connector = absolute w-px bg-border at left-[19px] behind w-10 h-10 rounded-full nodes. Transcript node is highlighted (bg-primary text-primary-foreground) and expandable (line-clamp-2 → "Mostra tutto/meno"); activity nodes are muted (bg-muted border-border). Dates are font-mono — activities relative (formatDistanceToNow), transcripts absolute (d MMMM yyyy).

Admin Dashboard restyle notes

The /admin dashboard was migrated to "Quiet Luxury" (applied 2026-07-11), replicating design-reference/pagina-dashboard. It is a condensed single-screen layout: no in-<main> PageHeader (sidebar + shell header carry the chrome).

  • KPI strip: 4 bg-card metric cards (Clienti Attivi · Contrattualizzato · Incassato Reale · Ore Tracciate). Value text-2xl font-bold font-mono, label text-[10px] uppercase text-muted-foreground. Positive delta uses text-emerald-600 dark:text-emerald-400.
  • Griglia lg:grid-cols-3: 2/3 col = ForecastChart (istogramma flussi 12 mesi, mese prossimo evidenziato bg-primary) + ClientProfitability; 1/3 col = FollowUpWidget (count pill amber) + OffersSoldChart ("Offerte Più Richieste").
  • Year selector interattivo (YearSelector) in una toolbar top-right; governa i KPI year-scoped + redditività. Il forecast è rolling-12-mesi (non year-scoped).
  • Widget rimossi dal legacy: chart incassi mensili, card "Da incassare"/"Clienti acquisiti", barre "Ore per cliente" (sostituite dalla redditività oraria).

Client Portal restyle notes

The /client/[token] portal was migrated to the same "Quiet Luxury" tokens (applied 2026-07-10). Key conventions specific to the client-facing surface:

  • Header: bg-card sticky bar, three zones — iamcavalli | Client Portal (left), brand name centered, "Area Riservata Protetta" emerald pill (right).
  • Status pills (phases, payments) use soft tints, not solid fills: emerald-50/700 (done/saldato), amber-50/700 (in corso/da saldare), bg-muted/text-muted-foreground (upcoming) — each with a dark: variant.
  • Phase cards (PhaseCard): rounded-xl border-border-light bg-card shadow-card, progress bar colored per status (emerald/amber/bg-border).
  • Sidebar cards (OffersSection, PaymentStatus, DocumentsSection, NotesSection, TranscriptsSection) all follow rounded-xl border-border-light bg-card shadow-card.
  • 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.