diff --git a/src/app/admin/clients/[id]/page.tsx b/src/app/admin/clients/[id]/page.tsx
new file mode 100644
index 0000000..6913b1f
--- /dev/null
+++ b/src/app/admin/clients/[id]/page.tsx
@@ -0,0 +1,72 @@
+import { notFound } from "next/navigation";
+import { getClientFullDetail } from "@/lib/admin-queries";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+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 Link from "next/link";
+
+export const revalidate = 0;
+
+export default async function ClientDetailPage({
+ params,
+}: {
+ params: Promise<{ id: string }>;
+}) {
+ const { id } = await params;
+ const detail = await getClientFullDetail(id);
+ if (!detail) notFound();
+
+ const { client, phases, payments, documents, notes, comments } = detail;
+
+ return (
+
+
+
+ ← Clienti
+
+
+
+
+
+
+ Fasi & Task
+ Pagamenti
+ Documenti
+ Commenti
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/admin/tabs/CommentsTab.tsx b/src/components/admin/tabs/CommentsTab.tsx
new file mode 100644
index 0000000..baaa383
--- /dev/null
+++ b/src/components/admin/tabs/CommentsTab.tsx
@@ -0,0 +1,105 @@
+import { postAdminComment } from "@/app/admin/clients/[id]/actions";
+import { Button } from "@/components/ui/button";
+import { Textarea } from "@/components/ui/textarea";
+import type { Comment } from "@/db/schema";
+import type { ClientFullDetail } from "@/lib/admin-queries";
+
+type Props = {
+ comments: Comment[];
+ phases: ClientFullDetail["phases"];
+ clientId: string;
+};
+
+export async function CommentsTab({ comments, phases, clientId }: Props) {
+ // Build entity label map for display
+ const entityLabels: Record = {};
+ for (const phase of phases) {
+ for (const task of phase.tasks) {
+ entityLabels[task.id] = `Task: ${task.title}`;
+ for (const d of task.deliverables) {
+ entityLabels[d.id] = `Deliverable: ${d.title}`;
+ }
+ }
+ }
+
+ // Build list of entities the admin can reply on
+ const entities: Array<{ id: string; type: string; label: string }> = [];
+ for (const phase of phases) {
+ for (const task of phase.tasks) {
+ entities.push({ id: task.id, type: "task", label: `Task: ${task.title}` });
+ for (const d of task.deliverables) {
+ entities.push({
+ id: d.id,
+ type: "deliverable",
+ label: `Deliverable: ${d.title}`,
+ });
+ }
+ }
+ }
+
+ return (
+
+ {/* Comment list */}
+ {comments.length === 0 && (
+
Nessun commento ancora.
+ )}
+
+ {comments.map((c) => (
+
+
+
+ {c.author === "admin" ? "iamcavalli" : "Cliente"} —{" "}
+ {entityLabels[c.entity_id] ?? c.entity_id}
+
+
{c.body}
+
+
+ ))}
+
+
+ {/* Admin reply form */}
+ {entities.length > 0 && (
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/admin/tabs/DocumentsTab.tsx b/src/components/admin/tabs/DocumentsTab.tsx
new file mode 100644
index 0000000..036bdec
--- /dev/null
+++ b/src/components/admin/tabs/DocumentsTab.tsx
@@ -0,0 +1,81 @@
+import { addDocument, deleteDocument } from "@/app/admin/clients/[id]/actions";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import type { Document } from "@/db/schema";
+
+type Props = { documents: Document[]; clientId: string };
+
+export async function DocumentsTab({ documents, clientId }: Props) {
+ return (
+
+
+
+ {documents.length === 0 && (
+
Nessun documento ancora.
+ )}
+
+ {documents.map((doc) => (
+
+
+ {doc.label}
+
+
+
+ ))}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/admin/tabs/PaymentsTab.tsx b/src/components/admin/tabs/PaymentsTab.tsx
new file mode 100644
index 0000000..a20bb1b
--- /dev/null
+++ b/src/components/admin/tabs/PaymentsTab.tsx
@@ -0,0 +1,97 @@
+import {
+ updatePaymentStatus,
+ updateAcceptedTotal,
+} from "@/app/admin/clients/[id]/actions";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import type { Payment } from "@/db/schema";
+
+type Props = {
+ payments: Payment[];
+ acceptedTotal: string;
+ clientId: string;
+};
+
+const statusLabels: Record = {
+ da_saldare: "Da saldare",
+ inviata: "Inviata",
+ saldato: "Saldato",
+};
+
+export async function PaymentsTab({ payments, acceptedTotal, clientId }: Props) {
+ return (
+
+ {/* Accepted total */}
+
+
Totale preventivo
+
+
+ Le rate Acconto e Saldo vengono aggiornate automaticamente al 50% ciascuna.
+
+
+
+ {/* Payment rows */}
+ {payments.map((p) => (
+
+
+
{p.label}
+
+ €{" "}
+ {parseFloat(p.amount).toLocaleString("it-IT", {
+ minimumFractionDigits: 2,
+ })}
+
+
+
+
+ ))}
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/admin/tabs/PhasesTab.tsx b/src/components/admin/tabs/PhasesTab.tsx
new file mode 100644
index 0000000..306e217
--- /dev/null
+++ b/src/components/admin/tabs/PhasesTab.tsx
@@ -0,0 +1,154 @@
+import {
+ addPhase,
+ addTask,
+ updateTaskStatus,
+ updatePhaseStatus,
+} from "@/app/admin/clients/[id]/actions";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import type { ClientFullDetail } from "@/lib/admin-queries";
+
+type Props = {
+ phases: ClientFullDetail["phases"];
+ clientId: string;
+};
+
+const taskStatusOptions = [
+ { value: "todo", label: "Da fare" },
+ { value: "in_progress", label: "In corso" },
+ { value: "done", label: "Fatto" },
+];
+
+const phaseStatusOptions = [
+ { value: "upcoming", label: "In arrivo" },
+ { value: "active", label: "Attiva" },
+ { value: "done", label: "Completata" },
+];
+
+export async function PhasesTab({ phases, clientId }: Props) {
+ return (
+
+ {/* Add phase form */}
+
+
+ {/* Phases list */}
+ {phases.length === 0 && (
+
Nessuna fase ancora.
+ )}
+ {phases.map((phase) => (
+
+
+
{phase.title}
+
+
+
+ {/* Tasks */}
+
+ {phase.tasks.map((task) => (
+
+ {task.title}
+
+
+ ))}
+
+
+ {/* Add task form */}
+
+
+ ))}
+
+ );
+}
\ No newline at end of file