"use server"; import { revalidatePath } from "next/cache"; import { AppwriteException, ID, Permission, Role } from "node-appwrite"; import { z } from "zod"; import { logAudit } from "./audit"; import { isPlanLimitError, planLimitMessage, requirePlanCapacity, } from "./plan-limits"; import { DATABASE_ID, TABLES, type Customer } from "./schema"; import { createAdminClient } from "./server"; import { requireTenant } from "./tenant-guard"; import type { CustomerActionState } from "./customer-types"; import { customerSchema } from "@/lib/validation/customers"; function appwriteError(e: unknown): string { if (e instanceof AppwriteException) { return e.message || "Beklenmeyen bir hata oluştu."; } return "Bağlantı hatası. Tekrar deneyin."; } function pickFormFields(formData: FormData) { return { name: String(formData.get("name") ?? "").trim(), email: String(formData.get("email") ?? "").trim(), phone: String(formData.get("phone") ?? "").trim(), taxId: String(formData.get("taxId") ?? "").trim(), taxOffice: String(formData.get("taxOffice") ?? "").trim(), website: String(formData.get("website") ?? "").trim(), address: String(formData.get("address") ?? "").trim(), notes: String(formData.get("notes") ?? "").trim(), status: (formData.get("status") as "active" | "passive" | null) ?? "active", }; } function flattenErrors(err: z.ZodError): Record { const out: Record = {}; for (const issue of err.issues) { const key = issue.path.join("."); if (key && !out[key]) out[key] = issue.message; } return out; } function teamRowPermissions(tenantId: string) { return [ Permission.read(Role.team(tenantId)), Permission.update(Role.team(tenantId)), Permission.delete(Role.team(tenantId, "owner")), Permission.delete(Role.team(tenantId, "admin")), ]; } export async function createCustomerAction( _prev: CustomerActionState, formData: FormData, ): Promise { let ctx; try { ctx = await requireTenant(); } catch { return { ok: false, error: "Yetkiniz yok." }; } const parsed = customerSchema.safeParse(pickFormFields(formData)); if (!parsed.success) { return { ok: false, error: "Form geçersiz.", fieldErrors: flattenErrors(parsed.error) }; } try { await requirePlanCapacity(ctx, "customers"); } catch (e) { if (isPlanLimitError(e)) { return { ok: false, error: planLimitMessage(e.resource, e.limit), code: "PLAN_LIMIT_EXCEEDED", }; } throw e; } try { const { tablesDB } = createAdminClient(); const row = await tablesDB.createRow( DATABASE_ID, TABLES.customers, ID.unique(), { tenantId: ctx.tenantId, createdBy: ctx.user.id, ...parsed.data, }, teamRowPermissions(ctx.tenantId), ); await logAudit({ tenantId: ctx.tenantId, userId: ctx.user.id, action: "create", entityType: "customer", entityId: row.$id, changes: parsed.data, }); } catch (e) { return { ok: false, error: appwriteError(e) }; } revalidatePath("/customers"); return { ok: true }; } export async function updateCustomerAction( _prev: CustomerActionState, formData: FormData, ): Promise { const id = String(formData.get("id") ?? ""); if (!id) return { ok: false, error: "ID eksik." }; let ctx; try { ctx = await requireTenant(); } catch { return { ok: false, error: "Yetkiniz yok." }; } const parsed = customerSchema.safeParse(pickFormFields(formData)); if (!parsed.success) { return { ok: false, error: "Form geçersiz.", fieldErrors: flattenErrors(parsed.error) }; } try { const { tablesDB } = createAdminClient(); const existing = (await tablesDB.getRow( DATABASE_ID, TABLES.customers, id, )) as unknown as Customer; if (existing.tenantId !== ctx.tenantId) { return { ok: false, error: "Erişim engellendi." }; } await tablesDB.updateRow(DATABASE_ID, TABLES.customers, id, parsed.data); await logAudit({ tenantId: ctx.tenantId, userId: ctx.user.id, action: "update", entityType: "customer", entityId: id, changes: parsed.data, }); } catch (e) { return { ok: false, error: appwriteError(e) }; } revalidatePath("/customers"); return { ok: true }; } export async function deleteCustomerAction(formData: FormData): Promise { const id = String(formData.get("id") ?? ""); if (!id) return { ok: false, error: "ID eksik." }; let ctx; try { ctx = await requireTenant(); } catch { return { ok: false, error: "Yetkiniz yok." }; } try { const { tablesDB } = createAdminClient(); const existing = (await tablesDB.getRow( DATABASE_ID, TABLES.customers, id, )) as unknown as Customer; if (existing.tenantId !== ctx.tenantId) { return { ok: false, error: "Erişim engellendi." }; } await tablesDB.deleteRow(DATABASE_ID, TABLES.customers, id); await logAudit({ tenantId: ctx.tenantId, userId: ctx.user.id, action: "delete", entityType: "customer", entityId: id, changes: { name: existing.name }, }); } catch (e) { return { ok: false, error: appwriteError(e) }; } revalidatePath("/customers"); return { ok: true }; }