"use server"; import { auth } from "@/auth"; import { db } from "@/lib/db"; import { hasPermission } from "@/lib/permissions"; import { geocodePincode } from "@/lib/geo"; import { z } from "zod"; import { revalidatePath } from "next/cache"; type ActionResult = { ok: true } | { error: string }; const contactSchema = z.object({ name: z.string().min(1), role: z.string().optional(), mobile: z.string().optional(), email: z.string().optional(), isPrimary: z.boolean().default(false), }); const vendorSchema = z.object({ name: z.string().min(1, "Vendor name is required"), vendorId: z.string().optional(), address: z.string().optional(), pincode: z.string().optional(), gstin: z.string().optional(), }); function parseContacts(formData: FormData) { const contacts: z.infer[] = []; let i = 0; while (formData.has(`contacts[${i}].name`)) { const name = (formData.get(`contacts[${i}].name`) as string).trim(); if (name) { contacts.push({ name, role: (formData.get(`contacts[${i}].role`) as string) || undefined, mobile: (formData.get(`contacts[${i}].mobile`) as string) || undefined, email: (formData.get(`contacts[${i}].email`) as string) || undefined, isPrimary: formData.get(`contacts[${i}].isPrimary`) === "true", }); } i++; } return contacts; } async function resolveLatLng(pincode?: string) { if (!pincode) return { latitude: null, longitude: null }; const coords = await geocodePincode(pincode); return { latitude: coords?.lat ?? null, longitude: coords?.lng ?? null }; } export async function createVendor(formData: FormData): Promise { const session = await auth(); if (!session?.user || !hasPermission(session.user.role, "manage_vendors")) { return { error: "Unauthorized" }; } const parsed = vendorSchema.safeParse({ name: formData.get("name"), vendorId: formData.get("vendorId") || undefined, address: formData.get("address") || undefined, pincode: formData.get("pincode") || undefined, gstin: formData.get("gstin") || undefined, }); if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" }; const data = parsed.data; if (data.vendorId) { const exists = await db.vendor.findUnique({ where: { vendorId: data.vendorId } }); if (exists) return { error: "A vendor with that Vendor ID already exists" }; } const { latitude, longitude } = await resolveLatLng(data.pincode); const contacts = parseContacts(formData); await db.vendor.create({ data: { name: data.name, vendorId: data.vendorId ?? null, address: data.address ?? null, pincode: data.pincode ?? null, gstin: data.gstin ?? null, latitude, longitude, isVerified: !!data.vendorId, contacts: contacts.length > 0 ? { create: contacts } : undefined, }, }); revalidatePath("/admin/vendors"); return { ok: true }; } export async function updateVendor(formData: FormData): Promise { const session = await auth(); if (!session?.user || !hasPermission(session.user.role, "manage_vendors")) { return { error: "Unauthorized" }; } const id = formData.get("id") as string; if (!id) return { error: "Vendor ID is required" }; const parsed = vendorSchema.safeParse({ name: formData.get("name"), vendorId: formData.get("vendorId") || undefined, address: formData.get("address") || undefined, pincode: formData.get("pincode") || undefined, gstin: formData.get("gstin") || undefined, }); if (!parsed.success) return { error: parsed.error.errors[0]?.message ?? "Validation failed" }; const data = parsed.data; if (data.vendorId) { const conflict = await db.vendor.findFirst({ where: { vendorId: data.vendorId, id: { not: id } } }); if (conflict) return { error: "Another vendor already has that Vendor ID" }; } const existing = await db.vendor.findUnique({ where: { id }, select: { pincode: true, latitude: true, longitude: true } }); const pincodeChanged = data.pincode !== (existing?.pincode ?? undefined); const { latitude, longitude } = pincodeChanged ? await resolveLatLng(data.pincode) : { latitude: existing?.latitude ?? null, longitude: existing?.longitude ?? null }; const contacts = parseContacts(formData); await db.$transaction(async (tx) => { await tx.vendor.update({ where: { id }, data: { name: data.name, vendorId: data.vendorId ?? null, address: data.address ?? null, pincode: data.pincode ?? null, gstin: data.gstin ?? null, latitude, longitude, isVerified: !!data.vendorId, }, }); // Replace contacts wholesale await tx.vendorContact.deleteMany({ where: { vendorId: id } }); if (contacts.length > 0) { await tx.vendorContact.createMany({ data: contacts.map((c) => ({ ...c, vendorId: id })) }); } }); revalidatePath("/admin/vendors"); revalidatePath(`/admin/vendors/${id}`); return { ok: true }; } export async function toggleVendorActive(vendorId: string): Promise { const session = await auth(); if (!session?.user || !hasPermission(session.user.role, "manage_vendors")) { return { error: "Unauthorized" }; } const vendor = await db.vendor.findUnique({ where: { id: vendorId }, select: { isActive: true } }); if (!vendor) return { error: "Vendor not found" }; await db.vendor.update({ where: { id: vendorId }, data: { isActive: !vendor.isActive } }); revalidatePath("/admin/vendors"); return { ok: true }; } export async function deleteVendor(id: string): Promise { const session = await auth(); if (!session?.user || !hasPermission(session.user.role, "manage_vendors")) return { error: "Unauthorized" }; const blocked = await db.purchaseOrder.findFirst({ where: { vendorId: id, status: { not: "DRAFT" } }, }); if (blocked) return { error: "Cannot delete: vendor is referenced in submitted or active purchase orders." }; await db.$transaction(async (tx) => { await tx.purchaseOrder.updateMany({ where: { vendorId: id, status: "DRAFT" }, data: { vendorId: null } }); await tx.product.updateMany({ where: { lastVendorId: id }, data: { lastVendorId: null } }); await tx.productVendorPrice.deleteMany({ where: { vendorId: id } }); await tx.vendor.delete({ where: { id } }); }); revalidatePath("/admin/vendors"); return { ok: true }; }