feat(02-02): add admin client list page and create-client flow

- /admin page: Server Component fetching all clients with payment badges
- ClientRow component with Acconto/Saldo status badges and secret link
- /admin/clients/new: form wired to createClient Server Action
- createClient action: Zod validation, inserts client + 2 payment stubs (Acconto 50%, Saldo 50%)
- Token auto-generated server-side via nanoid $defaultFn
- Redirects to /admin/clients/[id] after creation; revalidates /admin
This commit is contained in:
Simone Cavalli
2026-05-15 18:18:22 +02:00
parent dbcd00ffd6
commit f77051a3fc
4 changed files with 253 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
import Link from "next/link";
import { getAllClientsWithPayments } from "@/lib/admin-queries";
import { ClientRow } from "@/components/admin/ClientRow";
import { Button } from "@/components/ui/button";
export const revalidate = 0; // always fresh — admin needs real-time data
export default async function AdminDashboard() {
const clients = await getAllClientsWithPayments();
return (
<div>
<div className="flex items-center justify-between mb-6">
<h1 className="text-2xl font-bold text-gray-900">Clienti</h1>
<Button asChild>
<Link href="/admin/clients/new">+ Nuovo cliente</Link>
</Button>
</div>
{clients.length === 0 ? (
<div className="text-center py-20 text-gray-400">
<p>Nessun cliente ancora.</p>
<p className="mt-2">
<Link
href="/admin/clients/new"
className="text-blue-600 hover:underline"
>
Crea il primo cliente
</Link>
</p>
</div>
) : (
<div className="bg-white rounded-lg border border-gray-200 overflow-hidden">
<table className="w-full text-sm">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
<th className="text-left py-3 px-4 font-medium text-gray-600">
Cliente
</th>
<th className="text-left py-3 px-4 font-medium text-gray-600">
Totale
</th>
<th className="text-left py-3 px-4 font-medium text-gray-600">
Acconto
</th>
<th className="text-left py-3 px-4 font-medium text-gray-600">
Saldo
</th>
<th className="text-left py-3 px-4 font-medium text-gray-600">
Link
</th>
</tr>
</thead>
<tbody>
{clients.map((client) => (
<ClientRow key={client.id} client={client} />
))}
</tbody>
</table>
</div>
)}
</div>
);
}