From 7af917fe80af823ca43b7c1d40a39c7b86c51e6a Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Fri, 15 May 2026 23:14:29 +0200 Subject: [PATCH] feat: brand color system + Kanban view (admin + client) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix button contrast: add all missing shadcn tokens (primary-foreground, ring, input, muted, destructive) aligned to iamcavalli brand - NavBar: #1A463C green bar with white text - Login page: clean brand layout with iamcavalli wordmark - Admin pages: brand colors on headings, borders, links - Admin ClientRow: semantic payment badges (green/yellow/red) - Admin phases tab: Lista ↔ Kanban toggle with @dnd-kit drag & drop between Da fare / In corso / Fatto columns (optimistic updates) - Client dashboard: Timeline ↔ Kanban toggle, expandable task cards with approve button + comment form inline Co-Authored-By: Claude Sonnet 4.6 --- package-lock.json | 56 +++++ package.json | 3 + src/app/admin/clients/[id]/page.tsx | 7 +- src/app/admin/login/page.tsx | 28 ++- src/app/admin/page.tsx | 20 +- src/app/globals.css | 62 +++-- src/components/admin/ClientRow.tsx | 12 +- src/components/admin/NavBar.tsx | 8 +- src/components/admin/kanban/KanbanBoard.tsx | 232 ++++++++++++++++++ .../admin/kanban/PhasesViewToggle.tsx | 50 ++++ src/components/client-dashboard.tsx | 11 +- src/components/client/kanban/ClientKanban.tsx | 189 ++++++++++++++ .../client/kanban/PhaseViewToggle.tsx | 56 +++++ 13 files changed, 684 insertions(+), 50 deletions(-) create mode 100644 src/components/admin/kanban/KanbanBoard.tsx create mode 100644 src/components/admin/kanban/PhasesViewToggle.tsx create mode 100644 src/components/client/kanban/ClientKanban.tsx create mode 100644 src/components/client/kanban/PhaseViewToggle.tsx diff --git a/package-lock.json b/package-lock.json index a87cb03..242a7a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "clienthub", "version": "0.1.0", "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-progress": "^1.1.8", @@ -303,6 +306,59 @@ "node": ">=6.9.0" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@drizzle-team/brocli": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", diff --git a/package.json b/package.json index d9eabc6..793a753 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,9 @@ "lint": "eslint" }, "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-progress": "^1.1.8", diff --git a/src/app/admin/clients/[id]/page.tsx b/src/app/admin/clients/[id]/page.tsx index 6913b1f..df32eed 100644 --- a/src/app/admin/clients/[id]/page.tsx +++ b/src/app/admin/clients/[id]/page.tsx @@ -5,6 +5,7 @@ import { PhasesTab } from "@/components/admin/tabs/PhasesTab"; import { PaymentsTab } from "@/components/admin/tabs/PaymentsTab"; import { DocumentsTab } from "@/components/admin/tabs/DocumentsTab"; import { CommentsTab } from "@/components/admin/tabs/CommentsTab"; +import { PhasesViewToggle } from "@/components/admin/kanban/PhasesViewToggle"; import Link from "next/link"; export const revalidate = 0; @@ -51,7 +52,11 @@ export default async function ClientDetailPage({ - + } + phases={phases} + clientId={client.id} + /> - - - Admin — ClientHub - - - Caricamento...}> - - - - +
+
+
+ iamcavalli +

Area riservata

+
+ + + Accedi + + + Caricamento...
}> + + + + +
); } diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 4ce8a1a..6576a70 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -11,42 +11,42 @@ export default async function AdminDashboard() { return (
-

Clienti

+

Clienti

{clients.length === 0 ? ( -
+

Nessun cliente ancora.

Crea il primo cliente

) : ( -
+
- + - - - - - diff --git a/src/app/globals.css b/src/app/globals.css index af66a74..e54c3b3 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -4,28 +4,60 @@ @source not "../../scripts/**"; /* ========================================================= - Design tokens — light & clean palette (Tailwind v4 @theme) + Design tokens — iamcavalli brand palette (Tailwind v4 @theme) ========================================================= */ @theme inline { - /* Colori base */ - --color-background: #ffffff; - --color-foreground: #171717; - /* Font */ --font-sans: var(--font-geist-sans), system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; --font-mono: var(--font-geist-mono); - /* Palette brand iamcavalli — light & clean */ - --color-primary: #1a1a1a; /* charcoal per testo principale */ - --color-secondary: #666666; /* grigio medio per testo secondario */ - --color-tertiary: #999999; /* grigio chiaro per hint e timestamp */ - --color-bg-subtle: #f9f9f9; /* grigio molto chiaro per sfondi sezione */ - --color-border-light: #e5e5e5; /* bordo sottile */ - --color-accent: #0066cc; /* blu accent */ - --color-success: #16a34a; /* verde per done/saldato */ - --color-warning: #ca8a04; /* giallo per in_progress/inviata */ - --color-info: #2563eb; /* blu per pending/da_saldare */ + /* Base */ + --color-background: #ffffff; + --color-foreground: #1a1a1a; + + /* Brand primary — verde scuro iamcavalli */ + --color-primary: #1A463C; + --color-primary-foreground: #ffffff; + + /* Brand accent — giallo lime */ + --color-accent: #DEF168; + --color-accent-foreground: #1A463C; + + /* Secondary — grigio neutro */ + --color-secondary: #f4f4f5; + --color-secondary-foreground: #1a1a1a; + + /* Muted — per testi e sfondi secondari */ + --color-muted: #f9f9f9; + --color-muted-foreground: #71717a; + + /* Destructive */ + --color-destructive: #ef4444; + --color-destructive-foreground: #ffffff; + + /* Card */ + --color-card: #ffffff; + --color-card-foreground: #1a1a1a; + + /* Popover */ + --color-popover: #ffffff; + --color-popover-foreground: #1a1a1a; + + /* Border / Input / Ring */ + --color-border: #e5e7eb; + --color-input: #e5e7eb; + --color-ring: #1A463C; + + /* Semantic — stato task/pagamenti */ + --color-success: #16a34a; + --color-warning: #ca8a04; + --color-info: #2563eb; + + /* Legacy — usati inline nei componenti esistenti */ + --color-tertiary: #999999; + --color-bg-subtle: #f9f9f9; + --color-border-light: #e5e5e5; } /* ========================================================= diff --git a/src/components/admin/ClientRow.tsx b/src/components/admin/ClientRow.tsx index 3182b33..41fb0d1 100644 --- a/src/components/admin/ClientRow.tsx +++ b/src/components/admin/ClientRow.tsx @@ -4,11 +4,11 @@ import type { ClientWithPayments } from "@/lib/admin-queries"; const statusConfig: Record< string, - { label: string; variant: "default" | "secondary" | "destructive" | "outline" } + { label: string; className: string } > = { - da_saldare: { label: "Da saldare", variant: "destructive" }, - inviata: { label: "Inviata", variant: "secondary" }, - saldato: { label: "Saldato", variant: "default" }, + da_saldare: { label: "Da saldare", className: "bg-red-100 text-red-700 border-transparent" }, + inviata: { label: "Inviata", className: "bg-[#DEF168]/30 text-[#1A463C] border-transparent" }, + saldato: { label: "Saldato", className: "bg-[#1A463C]/10 text-[#1A463C] border-transparent font-medium" }, }; export function ClientRow({ client }: { client: ClientWithPayments }) { @@ -34,14 +34,14 @@ export function ClientRow({ client }: { client: ClientWithPayments }) {
+ Cliente + Totale + Acconto + Saldo + Link
{acconto && ( - + Acconto: {statusConfig[acconto.status]?.label ?? acconto.status} )} {saldo && ( - + Saldo: {statusConfig[saldo.status]?.label ?? saldo.status} )} diff --git a/src/components/admin/NavBar.tsx b/src/components/admin/NavBar.tsx index 1a4305f..5035761 100644 --- a/src/components/admin/NavBar.tsx +++ b/src/components/admin/NavBar.tsx @@ -6,12 +6,12 @@ import { Button } from "@/components/ui/button"; export function NavBar() { return ( -