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:
@@ -0,0 +1,61 @@
|
||||
import Link from "next/link";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import type { ClientWithPayments } from "@/lib/admin-queries";
|
||||
|
||||
const statusConfig: Record<
|
||||
string,
|
||||
{ label: string; variant: "default" | "secondary" | "destructive" | "outline" }
|
||||
> = {
|
||||
da_saldare: { label: "Da saldare", variant: "destructive" },
|
||||
inviata: { label: "Inviata", variant: "secondary" },
|
||||
saldato: { label: "Saldato", variant: "default" },
|
||||
};
|
||||
|
||||
export function ClientRow({ client }: { client: ClientWithPayments }) {
|
||||
const acconto = client.payments.find((p) => p.label.includes("Acconto"));
|
||||
const saldo = client.payments.find((p) => p.label.includes("Saldo"));
|
||||
|
||||
return (
|
||||
<tr className="border-b border-gray-100 hover:bg-gray-50 transition-colors">
|
||||
<td className="py-3 px-4">
|
||||
<Link
|
||||
href={`/admin/clients/${client.id}`}
|
||||
className="font-medium text-gray-900 hover:underline"
|
||||
>
|
||||
{client.name}
|
||||
</Link>
|
||||
<p className="text-xs text-gray-400">{client.brand_name}</p>
|
||||
</td>
|
||||
<td className="py-3 px-4 text-sm text-gray-600">
|
||||
€{" "}
|
||||
{parseFloat(client.accepted_total).toLocaleString("it-IT", {
|
||||
minimumFractionDigits: 2,
|
||||
})}
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
{acconto && (
|
||||
<Badge variant={statusConfig[acconto.status]?.variant ?? "outline"}>
|
||||
Acconto: {statusConfig[acconto.status]?.label ?? acconto.status}
|
||||
</Badge>
|
||||
)}
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
{saldo && (
|
||||
<Badge variant={statusConfig[saldo.status]?.variant ?? "outline"}>
|
||||
Saldo: {statusConfig[saldo.status]?.label ?? saldo.status}
|
||||
</Badge>
|
||||
)}
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<a
|
||||
href={`/c/${client.token}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs text-blue-600 hover:underline font-mono"
|
||||
>
|
||||
/c/{client.token.slice(0, 10)}…
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user