diff --git a/src/app/admin/catalog/actions.ts b/src/app/admin/catalog/actions.ts index 6f9f7e5..ca792de 100644 --- a/src/app/admin/catalog/actions.ts +++ b/src/app/admin/catalog/actions.ts @@ -1,9 +1,9 @@ "use server"; import { db } from "@/db"; -import { services } from "@/db/schema"; +import { services, tags } from "@/db/schema"; import { revalidatePath } from "next/cache"; -import { eq } from "drizzle-orm"; +import { eq, and } from "drizzle-orm"; import { z } from "zod"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/auth"; @@ -65,3 +65,88 @@ export async function toggleServiceActive(serviceId: string, active: boolean) { await db.update(services).set({ active }).where(eq(services.id, serviceId)); revalidatePath("/admin/catalog"); } + +// ── Inline-edit, tag, and quick-add actions (Phase 11 database-view) ──────── + +const EDITABLE_FIELDS = ["name", "description", "category", "unit_price", "active"] as const; +type EditableField = (typeof EDITABLE_FIELDS)[number]; + +export async function updateServiceField( + serviceId: string, + fieldName: EditableField, + value: string | boolean +) { + await requireAdmin(); + + if (!EDITABLE_FIELDS.includes(fieldName)) { + throw new Error(`Campo non editabile: ${fieldName}`); + } + + if (fieldName === "name") { + const s = String(value).trim(); + if (s.length === 0) throw new Error("Nome richiesto"); + await db.update(services).set({ name: s }).where(eq(services.id, serviceId)); + } else if (fieldName === "description") { + const s = String(value).trim(); + await db.update(services).set({ description: s || null }).where(eq(services.id, serviceId)); + } else if (fieldName === "category") { + const s = String(value).trim(); + await db.update(services).set({ category: s || null }).where(eq(services.id, serviceId)); + } else if (fieldName === "unit_price") { + const num = parseFloat(String(value)); + if (isNaN(num) || num < 0) throw new Error("Prezzo invalido"); + await db.update(services).set({ unit_price: num.toFixed(2) }).where(eq(services.id, serviceId)); + } else if (fieldName === "active") { + const boolValue = typeof value === "boolean" ? value : value === "true"; + await db.update(services).set({ active: boolValue }).where(eq(services.id, serviceId)); + } + + revalidatePath("/admin/catalog"); +} + +export async function addTagToService(serviceId: string, tagName: string) { + await requireAdmin(); + const trimmed = tagName.trim(); + if (trimmed.length === 0) throw new Error("Nome tag richiesto"); + + await db + .insert(tags) + .values({ + entity_type: "services", + entity_id: serviceId, + name: trimmed, + }) + .onConflictDoNothing(); + + revalidatePath("/admin/catalog"); +} + +export async function removeTagFromService(serviceId: string, tagName: string) { + await requireAdmin(); + + await db + .delete(tags) + .where( + and( + eq(tags.entity_type, "services"), + eq(tags.entity_id, serviceId), + eq(tags.name, tagName) + ) + ); + + revalidatePath("/admin/catalog"); +} + +export async function quickAddService(name: string) { + await requireAdmin(); + const trimmed = name.trim(); + if (trimmed.length === 0) throw new Error("Nome richiesto"); + + await db.insert(services).values({ + name: trimmed, + unit_price: "0.00", + active: true, + }); + + revalidatePath("/admin/catalog"); +}