pelagia-portal/App/app/(portal)/admin/sites/actions.ts
2026-05-18 23:18:58 +05:30

118 lines
4.5 KiB
TypeScript

"use server";
import { auth } from "@/auth";
import { db } from "@/lib/db";
import { hasPermission } from "@/lib/permissions";
import { geocodePincode } from "@/lib/geo";
import { revalidatePath } from "next/cache";
import { z } from "zod";
const siteSchema = z.object({
name: z.string().min(1, "Name is required"),
code: z.string().min(1, "Code is required").max(20).toUpperCase(),
address: z.string().optional(),
pincode: z.string().optional(),
latitude: z.coerce.number().optional(),
longitude: z.coerce.number().optional(),
});
type Result = { ok: true } | { error: string };
export async function createSite(formData: FormData): Promise<Result> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_sites")) return { error: "Forbidden" };
const parsed = siteSchema.safeParse(Object.fromEntries(formData));
if (!parsed.success) return { error: parsed.error.errors[0].message };
const { name, code, address, pincode, latitude, longitude } = parsed.data;
let lat = latitude, lng = longitude;
if (!lat && pincode) {
const geo = await geocodePincode(pincode);
if (geo) { lat = geo.lat; lng = geo.lng; }
}
try {
await db.site.create({ data: { name, code, address: address ?? null, latitude: lat ?? null, longitude: lng ?? null } });
} catch {
return { error: "A site with that code already exists." };
}
revalidatePath("/admin/sites");
return { ok: true };
}
export async function updateSite(id: string, formData: FormData): Promise<Result> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_sites")) return { error: "Forbidden" };
const parsed = siteSchema.safeParse(Object.fromEntries(formData));
if (!parsed.success) return { error: parsed.error.errors[0].message };
const { name, code, address, pincode, latitude, longitude } = parsed.data;
let lat = latitude, lng = longitude;
if (!lat && pincode) {
const geo = await geocodePincode(pincode);
if (geo) { lat = geo.lat; lng = geo.lng; }
}
await db.site.update({ where: { id }, data: { name, code, address: address ?? null, latitude: lat ?? null, longitude: lng ?? null } });
revalidatePath("/admin/sites");
revalidatePath(`/admin/sites/${id}`);
return { ok: true };
}
export async function toggleSiteActive(id: string): Promise<Result> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_sites")) return { error: "Forbidden" };
const site = await db.site.findUnique({ where: { id }, select: { isActive: true } });
if (!site) return { error: "Not found" };
await db.site.update({ where: { id }, data: { isActive: !site.isActive } });
revalidatePath("/admin/sites");
return { ok: true };
}
export async function deleteSite(id: string): Promise<Result> {
const session = await auth();
if (!session?.user || !hasPermission(session.user.role, "manage_sites")) return { error: "Forbidden" };
const blocked = await db.purchaseOrder.findFirst({
where: { siteId: id, status: { not: "DRAFT" } },
});
if (blocked) return { error: "Cannot delete: site is referenced in submitted or active purchase orders." };
await db.$transaction(async (tx) => {
await tx.purchaseOrder.updateMany({ where: { siteId: id, status: "DRAFT" }, data: { siteId: null } });
await tx.itemConsumption.deleteMany({ where: { siteId: id } });
await tx.itemInventory.deleteMany({ where: { siteId: id } });
await tx.vessel.updateMany({ where: { siteId: id }, data: { siteId: null } });
await tx.site.delete({ where: { id } });
});
revalidatePath("/admin/sites");
return { ok: true };
}
export async function recordConsumption(formData: FormData): Promise<Result> {
const session = await auth();
if (!session?.user) return { error: "Unauthorized" };
const schema = z.object({
siteId: z.string().min(1),
productId: z.string().min(1),
date: z.string().min(1),
quantity: z.coerce.number().positive(),
note: z.string().optional(),
});
const parsed = schema.safeParse(Object.fromEntries(formData));
if (!parsed.success) return { error: parsed.error.errors[0].message };
const { siteId, productId, date, quantity, note } = parsed.data;
await db.itemConsumption.upsert({
where: { productId_siteId_date: { productId, siteId, date: new Date(date) } },
update: { quantity, note: note ?? null, recordedById: session.user.id },
create: { productId, siteId, date: new Date(date), quantity, note: note ?? null, recordedById: session.user.id },
});
revalidatePath(`/admin/sites/${siteId}`);
return { ok: true };
}