"use server"; import { revalidatePath } from "next/cache"; import { z } from "zod"; import { getCurrentUser } from "@/data/auth"; import { SubGroupProductDitolakError, countPemakaianSubGroupProduct, deleteSubGroupProduct, insertSubGroupProduct, updateSubGroupProduct, } from "@/data/sub-group-product"; import { logActionError, type DeleteState, type FormState, } from "@/lib/server-action"; type SubGroupProductField = keyof typeof subGroupProductSchema.shape; export type SimpanSubGroupProductState = FormState & { id?: number; }; export type HapusSubGroupProductState = DeleteState; const subGroupProductSchema = z.object({ kodeKedProduk: z .string() .trim() .min(1, "Kode wajib diisi") .max(1, "Kode maksimal 1 karakter"), kodeStrip: z .string() .trim() .min(1, "Group product wajib dipilih") .max(2, "Kode group product maksimal 2 karakter"), kedProduk: z .string() .trim() .min(1, "Nama wajib diisi") .max(50, "Nama maksimal 50 karakter"), }); // Edit hanya mengubah nama; kode dikunci (lihat SubGroupProductUpdate). const updateSchema = subGroupProductSchema.pick({ kedProduk: true }); export async function simpanSubGroupProduct( _prevState: SimpanSubGroupProductState, formData: FormData, ): Promise { const user = await getCurrentUser(); const fields = Object.keys( subGroupProductSchema.shape, ) as SubGroupProductField[]; const values = Object.fromEntries( fields.map((field) => [field, String(formData.get(field) ?? "")]), ) as Record; const idRaw = String(formData.get("id") ?? "").trim(); const id = idRaw === "" ? null : Number.parseInt(idRaw, 10); if (id !== null && !Number.isSafeInteger(id)) { return { ok: false, error: "Sub group product tidak valid", values }; } try { if (id === null) { const parsed = subGroupProductSchema.safeParse(values); if (!parsed.success) { return { ok: false, fieldErrors: z.flattenError(parsed.error).fieldErrors, values, }; } const idBaru = await insertSubGroupProduct(parsed.data); revalidatePath("/master/sub-group-product"); return { ok: true, id: idBaru }; } const parsed = updateSchema.safeParse(values); if (!parsed.success) { return { ok: false, fieldErrors: z.flattenError(parsed.error).fieldErrors, values, }; } const updated = await updateSubGroupProduct(id, parsed.data); if (!updated) { return { ok: false, error: "Sub group product tidak ditemukan", values }; } revalidatePath("/master/sub-group-product"); return { ok: true, id }; } catch (error) { // Penolakan aturan bisnis adalah validasi biasa, bukan error tak terduga, jadi tidak di-log. if (error instanceof SubGroupProductDitolakError) { return { ok: false, error: error.message, values }; } logActionError( "simpanSubGroupProduct", { userId: user.id, id: id ?? "(baru)", ...values }, error, ); return { ok: false, error: "Gagal menyimpan sub group product, coba simpan ulang", values, }; } } export async function hapusSubGroupProduct( _prevState: HapusSubGroupProductState, formData: FormData, ): Promise { const user = await getCurrentUser(); const id = Number.parseInt(String(formData.get("id") ?? ""), 10); if (!Number.isSafeInteger(id)) { return { ok: false, error: "Sub group product tidak valid" }; } try { const dipakai = await countPemakaianSubGroupProduct(id); if (dipakai > 0) { return { ok: false, error: `Sub group product masih dipakai ${dipakai} barang, tidak bisa dihapus`, }; } const deleted = await deleteSubGroupProduct(id); if (!deleted) { return { ok: false, error: "Sub group product tidak ditemukan" }; } revalidatePath("/master/sub-group-product"); return { ok: true }; } catch (error) { logActionError("hapusSubGroupProduct", { userId: user.id, id }, error); return { ok: false, error: "Gagal menghapus sub group product, coba hapus ulang", }; } }