118 lines
4.5 KiB
TypeScript
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 };
|
|
}
|