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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+
+
+
+
+
+ Salva
+
+
+
+
+
+
+
+
+
+
+
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.
+
-
+
);
}
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}
- handleRemove(v)}
- disabled={isPending}
- className="rounded p-0.5 text-[#c4c4c8] transition-colors hover:bg-red-50 hover:text-red-500 disabled:opacity-50"
- aria-label={`Elimina ${v}`}
- title={`Elimina "${v}" ovunque`}
+
+ {pool.length === 0 ? (
+
+ Nessun valore inserito
+
+ ) : (
+ pool.map((v) => (
+
-
-
-
- ))
- )}
+ {v}
+
handleRemove(v)}
+ disabled={isPending}
+ className="rounded p-0.5 text-tertiary transition-colors hover:bg-destructive/10 hover:text-destructive disabled:opacity-50"
+ aria-label={`Elimina ${v}`}
+ title={`Elimina "${v}" ovunque`}
+ >
+
+
+
+ ))
+ )}
+
-
-
{
- 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}
-
+
);
}
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.