Files
clienthub/src/app/admin/projects/project-actions.ts
T
simone ef05d647fe feat(04-02): admin projects list + client detail project cards
- NavBar: add Progetti (/admin/projects) and Impostazioni links
- ProjectRow: project table row with €/h calc (accepted_total / tracked hours)
- /admin/projects: table listing all projects with value, payments, timer, €/h
- /admin/projects/new: create form with client select + ?client_id pre-selection
- project-actions: createProject, archiveProject, unarchiveProject, updateProjectAcceptedTotal
- /admin/clients/[id]: rewritten to show project cards grid instead of tabbed workspace
- getAllClientsWithPayments: add projectBrands[] and ltv (sum of project accepted_totals)
- ClientRow: show project brand names under client name, LTV column replaces Totale

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 08:45:10 +02:00

55 lines
1.9 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { db } from "@/db";
import { projects, clients } from "@/db/schema";
import { eq } from "drizzle-orm";
import { nanoid } from "nanoid";
async function requireAdmin() {
const session = await getServerSession(authOptions);
if (!session) throw new Error("Non autorizzato");
}
export async function createProject(fd: FormData): Promise<{ projectId: string }> {
await requireAdmin();
const name = String(fd.get("name") ?? "").trim();
const clientId = String(fd.get("client_id") ?? "").trim();
if (!name) throw new Error("Nome progetto obbligatorio");
if (!clientId) throw new Error("Cliente obbligatorio");
const clientRows = await db
.select({ id: clients.id })
.from(clients)
.where(eq(clients.id, clientId))
.limit(1);
if (clientRows.length === 0) throw new Error("Cliente non trovato");
const id = nanoid();
await db.insert(projects).values({ id, client_id: clientId, name });
revalidatePath("/admin/projects");
revalidatePath(`/admin/clients/${clientId}`);
return { projectId: id };
}
export async function archiveProject(projectId: string): Promise<void> {
await requireAdmin();
await db.update(projects).set({ archived: true }).where(eq(projects.id, projectId));
revalidatePath("/admin/projects");
}
export async function unarchiveProject(projectId: string): Promise<void> {
await requireAdmin();
await db.update(projects).set({ archived: false }).where(eq(projects.id, projectId));
revalidatePath("/admin/projects");
}
export async function updateProjectAcceptedTotal(projectId: string, acceptedTotal: string): Promise<void> {
await requireAdmin();
await db.update(projects).set({ accepted_total: acceptedTotal }).where(eq(projects.id, projectId));
revalidatePath(`/admin/projects/${projectId}`);
}