"use server"; import { revalidatePath } from "next/cache"; import { z } from "zod"; import { getCurrentUser } from "@/data/auth"; import { deletePelanggan, insertPelanggan, updatePelanggan, type PelangganInput, } from "@/data/pelanggan"; import { logActionError, type DeleteState, type FormState, } from "@/lib/server-action"; type PelangganField = keyof typeof pelangganSchema.shape; export type SimpanPelangganState = FormState & { kdCustomer?: string; }; export type HapusPelangganState = DeleteState; const pelangganSchema = z.object({ nama: z .string() .trim() .min(1, "Nama wajib diisi") .max(100, "Nama maksimal 100 karakter"), alias: z.string().trim().max(11, "Alias maksimal 11 karakter"), ktp: z.string().trim().max(16, "No. KTP maksimal 16 karakter"), alamat: z .string() .trim() .min(1, "Alamat wajib diisi") .max(100, "Alamat maksimal 100 karakter"), wilayah: z.string().trim().max(200, "Wilayah maksimal 200 karakter"), telp: z.string().trim().max(15, "Telepon maksimal 15 karakter"), ponsel: z.string().trim().max(15, "Ponsel maksimal 15 karakter"), email: z.string().trim().max(30, "Email maksimal 30 karakter"), jenisCustomer: z.string().trim().max(1, "Jenis customer maksimal 1 karakter"), upline: z.string().trim().max(11, "Upline maksimal 11 karakter"), btsBon: z.coerce .number({ error: "Harus berupa angka" }) .int("Harus bilangan bulat") .min(0, "Tidak boleh negatif") .max(2_000_000_000, "Maksimal 2.000.000.000"), }); function emptyStringToNull(value: string): string | null { return value === "" ? null : value; } export async function simpanPelanggan( _prevState: SimpanPelangganState, formData: FormData, ): Promise { const user = await getCurrentUser(); const fields = Object.keys(pelangganSchema.shape) as PelangganField[]; const values = Object.fromEntries( fields.map((field) => [field, String(formData.get(field) ?? "")]), ) as Record; const parsed = pelangganSchema.safeParse(values); if (!parsed.success) { return { ok: false, fieldErrors: z.flattenError(parsed.error).fieldErrors, values, }; } const input: PelangganInput = { ...parsed.data, wilayah: emptyStringToNull(parsed.data.wilayah), telp: emptyStringToNull(parsed.data.telp), ponsel: emptyStringToNull(parsed.data.ponsel), email: emptyStringToNull(parsed.data.email), jenisCustomer: emptyStringToNull(parsed.data.jenisCustomer), upline: emptyStringToNull(parsed.data.upline), }; const kdCustomer = String(formData.get("kdCustomer") ?? "").trim(); try { if (kdCustomer === "") { const kdBaru = await insertPelanggan(input); revalidatePath("/master/pelanggan"); return { ok: true, kdCustomer: kdBaru }; } const updated = await updatePelanggan(kdCustomer, input); if (!updated) { return { ok: false, error: "Pelanggan tidak ditemukan", values }; } revalidatePath("/master/pelanggan"); return { ok: true, kdCustomer }; } catch (error) { logActionError( "simpanPelanggan", { userId: user.id, kdCustomer: kdCustomer || "(baru)", nama: input.nama }, error, ); return { ok: false, error: "Gagal menyimpan pelanggan, coba simpan ulang", values, }; } } export async function hapusPelanggan( _prevState: HapusPelangganState, formData: FormData, ): Promise { const user = await getCurrentUser(); const kdCustomer = String(formData.get("kdCustomer") ?? "").trim(); if (kdCustomer === "") { return { ok: false, error: "Kode pelanggan tidak ada" }; } try { const deleted = await deletePelanggan(kdCustomer); if (!deleted) { return { ok: false, error: "Pelanggan tidak ditemukan" }; } revalidatePath("/master/pelanggan"); return { ok: true }; } catch (error) { logActionError("hapusPelanggan", { userId: user.id, kdCustomer }, error); return { ok: false, error: "Gagal menghapus pelanggan, coba hapus ulang", }; } }