From dd2d148457d465e4ebf58a152d39241b634d4675 Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Sun, 12 Jul 2026 12:14:52 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20pagina=20Impostazioni=20redesign=20Quie?= =?UTF-8?q?t=20Luxury=20=E2=80=94=20sezioni=20tokenizzate=20+=20PoolManage?= =?UTF-8?q?r=20tassonomie=20dual-mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - impostazioni/page.tsx: sezione Analytics con input tariffa bordato (€/h, font-mono) + header uppercase - TaxonomyManager: card sezione tokenizzata, griglie Offerte (2col) / Catalogo (3col) - PoolManager: box bg-muted, badge conteggio mono, chip valori, add su Enter, delete cascade confermata - DESIGN-SYSTEM.md: note pagina Impostazioni Co-Authored-By: Claude Opus 4.8 --- design-reference/DESIGN-SYSTEM.md | 25 ++ design-reference/pagina-impostazioni | 377 ++++++++++++++++++ src/app/admin/impostazioni/page.tsx | 67 ++-- .../admin/impostazioni/PoolManager.tsx | 107 +++-- .../admin/impostazioni/TaxonomyManager.tsx | 26 +- 5 files changed, 502 insertions(+), 100 deletions(-) create mode 100644 design-reference/pagina-impostazioni diff --git a/design-reference/DESIGN-SYSTEM.md b/design-reference/DESIGN-SYSTEM.md index 85db563..fa71949 100644 --- a/design-reference/DESIGN-SYSTEM.md +++ b/design-reference/DESIGN-SYSTEM.md @@ -303,6 +303,31 @@ header `bg-muted/50` uppercase muted, righe `hover:bg-muted/40` + `divide-y divi - **Redditività**: `null` (0h tracciate) → `—` muted; `>0` → emerald bold con dot `bg-emerald-500`; `=0` → mono muted. Footer "Mostrando N di N progetti attivi". +### Impostazioni notes + +`/admin/impostazioni` migrata a "Quiet Luxury" (replica `design-reference/pagina-impostazioni`). +Pagina a sezioni impilate (`space-y-8`), ogni sezione è una card +`rounded-xl border-border-light bg-card shadow-card p-6`. + +- **Header**: `PageHeader` "Impostazioni" + sottotitolo ("Configura le tariffe target e gestisci + le tassonomie globali del sistema"). +- **Titoli sezione**: `text-xs font-bold uppercase tracking-wider text-foreground`, sottotitolo + `text-xs text-tertiary`. Le sezioni tassonomia hanno un'icona (`Tag`/`BookOpen`) in un box + `h-9 w-9 rounded-lg border-border-light bg-muted text-muted-foreground`. +- **Analytics Profittabilità**: form server-action con input `type=number` in un gruppo bordato + `focus-within:border-primary focus-within:ring-1 ring-primary`, prefisso `€`/suffisso `/h` + muted assoluti, valore `font-mono`; bottone "Salva" `bg-primary text-primary-foreground`. +- **Griglie tassonomia** (`TaxonomyManager`): `border-t border-border-light pt-4` + grid + `gap-6` — Offerte `md:grid-cols-2`, Catalogo `md:grid-cols-2 lg:grid-cols-3`. +- **PoolManager** (box campo): `rounded-xl border-border-light bg-muted/40 p-5 flex flex-col + justify-between`. Testata = label uppercase muted + badge conteggio mono + (`bg-card border-border-light text-tertiary`, sempre visibile anche a 0). Valori = chip + `bg-card border-border/60 text-foreground shadow-sm` con `X` (hover destructive → cascade + delete confermata via `window.confirm`). Vuoto → "Nessun valore inserito" italic muted. + Aggiunta = input full-width `bg-card border-border` con Enter (no bottone `+`); duplicati + bloccati con errore inline `text-destructive`. +- **Footer**: nota `text-xs text-tertiary` sulla sincronizzazione bidirezionale dei valori. + ### Client Portal restyle notes The `/client/[token]` portal was migrated to the same "Quiet Luxury" tokens diff --git a/design-reference/pagina-impostazioni b/design-reference/pagina-impostazioni new file mode 100644 index 0000000..0240226 --- /dev/null +++ b/design-reference/pagina-impostazioni @@ -0,0 +1,377 @@ + + + + + + Impostazioni — Luxury Admin CRM + + + + + + + + + + + + + + + +
+ + +
+
+ + + Configurazione Sistema +
+
+ Admin Panel +
+
+ + +
+ + +
+

Impostazioni

+

Configura le tariffe target e gestisci le tassonomie globali del sistema

+
+ + +
+
+

Analytics Profittabilità

+

Tariffa oraria target (€/h). Usata per calcolare il costo ideale e il delta profitto/perdita per ogni progetto.

+ +
+
+ + € + + + + /h + +
+ + + +
+
+
+ + +
+
+
+ +
+
+

Tassonomie Offerte

+

Valori dei campi Categoria, Ticket, Tipo e Obiettivo dell'editor offerte.

+
+
+ +
+ + +
+
+
+ Categoria + 3 +
+
+ + Entry Offer + + + Retainer Offer + + + Signature Offer + +
+
+ +
+ + +
+
+
+ Ticket + 3 +
+
+ + High Ticket + + + Low Ticket + + + Mid Ticket + +
+
+ +
+ + +
+
+
+ Tipo + 3 +
+
+ + Audit + + + Consulenza + + + Done For You + +
+
+ +
+ + +
+
+
+ Obiettivo + 7 +
+
+ + Aumento LTV + + + Caso Studio + + + Fatturato + + + Lead Generation + +
+
+ +
+ +
+
+ + +
+
+
+ +
+
+

Tassonomie Catalogo

+

Valori dei campi Fase, Offerta e Pacchetto del catalogo servizi.

+
+
+ +
+ + +
+
+
+ Fase + 5 +
+
+ + Fase 1 ➔ Onboarding + + + Fase 2 ➔ Analisi + + + Fase 3 ➔ Esecuzione + +
+
+ +
+ + +
+
+
+ Offerta + 3 +
+
+ + Entry Offer + + + Retainer Offer + + + Signature Offer + +
+
+ +
+ + +
+
+
+ Pacchetto + 0 +
+
+ Nessun valore inserito +
+
+ +
+ +
+
+ + +
+ I valori si sincronizzano ovunque: creandone uno dall'editor offerte o dal catalogo comparirà qui automaticamente. Eliminando un valore da qui viene rimosso ovunque sia usato. +
+ +
+
+ + + + + \ No newline at end of file diff --git a/src/app/admin/impostazioni/page.tsx b/src/app/admin/impostazioni/page.tsx index 44b5ee4..dbaa2e4 100644 --- a/src/app/admin/impostazioni/page.tsx +++ b/src/app/admin/impostazioni/page.tsx @@ -17,50 +17,53 @@ export default async function ImpostazioniPage() { } return ( -
- +
+ -
-
-

Analytics Profittabilità

+
+
+

+ Analytics Profittabilità +

+

+ Tariffa oraria target (€/h). Usata per calcolare il costo ideale e il delta + profitto/perdita per ogni progetto. +

-
-
- -

- Usata per calcolare il costo ideale e il delta profitto/perdita per ogni progetto. -

-
- - - /h -
+ +
+ + € + + + + /h +
+
- -
+
); } diff --git a/src/components/admin/impostazioni/PoolManager.tsx b/src/components/admin/impostazioni/PoolManager.tsx index d38f712..b57b552 100644 --- a/src/components/admin/impostazioni/PoolManager.tsx +++ b/src/components/admin/impostazioni/PoolManager.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useTransition } from "react"; -import { X, Plus } from "lucide-react"; +import { X } from "lucide-react"; export function PoolManager({ label, @@ -18,8 +18,6 @@ export function PoolManager({ const [isPending, startTransition] = useTransition(); const [error, setError] = useState(null); - const canAdd = input.trim().length > 0; - function handleAdd() { const trimmed = input.trim(); if (!trimmed) return; @@ -51,67 +49,62 @@ export function PoolManager({ return (
-
-

{label}

- {pool.length > 0 && ( - {pool.length} - )} -
+
+
+ + {label} + + + {pool.length} + +
-
- {pool.length === 0 ? ( - Nessun valore - ) : ( - pool.map((v) => ( - - {v} - - - )) - )} + {v} + + + )) + )} +
-
- { - setInput(e.target.value); - setError(null); - }} - onKeyDown={onKeyDown} - placeholder="Aggiungi valore" - disabled={isPending} - aria-label={`Aggiungi valore a ${label}`} - className="h-8 w-full rounded-md border border-[#e5e7eb] bg-white pl-3 pr-9 text-sm text-[#1a1a1a] placeholder:text-[#a1a1aa] transition-shadow focus:border-[#1A463C]/30 focus:outline-none focus:ring-2 focus:ring-[#1A463C]/15 disabled:opacity-50" - /> - -
- {error &&

{error}

} + { + setInput(e.target.value); + setError(null); + }} + onKeyDown={onKeyDown} + placeholder="Aggiungi valore..." + disabled={isPending} + aria-label={`Aggiungi valore a ${label}`} + className="w-full rounded-lg border border-border bg-card px-3 py-2 text-xs text-foreground transition-colors placeholder:text-tertiary focus:border-primary focus:outline-none disabled:opacity-50" + /> + {error &&

{error}

}
); } diff --git a/src/components/admin/impostazioni/TaxonomyManager.tsx b/src/components/admin/impostazioni/TaxonomyManager.tsx index 1937e4b..8d9cbbb 100644 --- a/src/components/admin/impostazioni/TaxonomyManager.tsx +++ b/src/components/admin/impostazioni/TaxonomyManager.tsx @@ -25,26 +25,28 @@ function Section({ subtitle, fields, pools, + gridClass, }: { icon: React.ReactNode; title: string; subtitle: string; fields: FieldDef[]; pools: Record; + gridClass: string; }) { return ( -
-
-
+
+
+
{icon}
-

{title}

-

{subtitle}

+

{title}

+

{subtitle}

-
+
{fields.map((f) => ( ))}
-
+
); } export function TaxonomyManager({ pools }: { pools: Record }) { return ( -
+
} + icon={} title="Tassonomie Offerte" subtitle="Valori dei campi Categoria, Ticket, Tipo e Obiettivo dell'editor offerte." fields={OFFER_FIELDS} pools={pools} + gridClass="grid-cols-1 md:grid-cols-2" />
} + icon={} title="Tassonomie Catalogo" subtitle="Valori dei campi Fase, Offerta e Pacchetto del catalogo servizi." fields={CATALOG_FIELDS} pools={pools} + gridClass="grid-cols-1 md:grid-cols-2 lg:grid-cols-3" /> -

+

I valori si sincronizzano ovunque: creandone uno dall'editor offerte o dal catalogo comparirà qui automaticamente. Eliminando un valore da qui viene rimosso ovunque sia usato.