ef05d647fe
- 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>
55 lines
1.9 KiB
TypeScript
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}`);
|
|
} |