feat: Client Portal redesign to Quiet Luxury + milestone stepper

- New MilestoneStepper primitive (horizontal per-phase progress)
- Token-migrate portal shell, phase cards, sidebar cards, kanban to dual-theme
- Soft-tint status pills (emerald/amber/muted) replacing solid badges
- embedded prop on ClientDashboard fixes double-header in multi-project tabs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 22:54:58 +02:00
parent 6d5e04bf9c
commit 11870e15d3
13 changed files with 333 additions and 306 deletions
+21 -23
View File
@@ -106,10 +106,10 @@ export default async function ClientPage({
if (projects.length === 0) {
return (
<div className="min-h-screen bg-[#f9f9f9] flex items-center justify-center">
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center">
<h1 className="text-xl font-bold text-[#1a1a1a]">{client.name}</h1>
<p className="text-sm text-[#71717a] mt-2">Nessun progetto disponibile al momento.</p>
<h1 className="text-xl font-bold text-foreground">{client.name}</h1>
<p className="text-sm text-muted-foreground mt-2">Nessun progetto disponibile al momento.</p>
</div>
</div>
);
@@ -132,22 +132,23 @@ export default async function ClientPage({
const projectViews = await Promise.all(projects.map((p) => getProjectView(p.id)));
return (
<div className="min-h-screen bg-white">
<header className="bg-white border-b border-[#e5e5e5] sticky top-0 z-10">
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-5">
<div className="flex items-center">
<span className="text-xs font-semibold tracking-widest text-[#999999] uppercase w-28 shrink-0">
iamcavalli
</span>
<h1 className="flex-1 text-center text-2xl sm:text-3xl font-bold text-[#1a1a1a] tracking-tight">
{client.brand_name}
</h1>
<div className="w-28 shrink-0" />
</div>
<div className="min-h-screen bg-background">
<header className="sticky top-0 z-50 flex flex-col items-center gap-4 border-b border-border-light bg-card px-6 py-5 shadow-card md:flex-row md:justify-between md:px-8">
<div className="flex w-full items-center gap-3 md:w-auto">
<span className="text-xs font-bold uppercase tracking-widest text-muted-foreground">iamcavalli</span>
<span className="text-border">|</span>
<span className="text-xs font-medium text-muted-foreground">Client Portal</span>
</div>
<div className="text-center">
<h1 className="text-xl font-bold tracking-tight text-foreground">{client.brand_name}</h1>
</div>
<div className="hidden items-center gap-2 rounded-full border border-emerald-100 bg-emerald-50 px-3 py-1 text-[11px] text-emerald-700 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-400 md:flex">
<span className="h-1.5 w-1.5 animate-pulse rounded-full bg-emerald-500" />
Area Riservata Protetta
</div>
</header>
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-8">
<div className="max-w-[1400px] mx-auto px-4 sm:px-6 py-8">
<Tabs defaultValue={projects[0].id} className="w-full">
<TabsList className="mb-6">
{projects.map((p) => (
@@ -166,9 +167,10 @@ export default async function ClientPage({
view={projectViewToClientView(client, view)}
token={client.token}
comments={view.comments as unknown as Comment[]}
embedded
/>
) : (
<p className="text-sm text-[#71717a]">Progetto non disponibile.</p>
<p className="text-sm text-muted-foreground">Progetto non disponibile.</p>
)}
</TabsContent>
);
@@ -176,12 +178,8 @@ export default async function ClientPage({
</Tabs>
</div>
<footer className="border-t border-[#e5e5e5] bg-[#f9f9f9] mt-10">
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-6">
<p className="text-xs text-[#999999] text-center">
Questa è la tua dashboard privata non condividere il link.
</p>
</div>
<footer className="mt-10 py-10 text-center text-xs text-muted-foreground">
Questa è la tua dashboard privata non condividere il link.
</footer>
</div>
);