106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
"use server";
|
||
|
||
import { revalidatePath } from "next/cache";
|
||
import { AppwriteException, ID, Permission, Query, Role } from "node-appwrite";
|
||
import { z } from "zod";
|
||
|
||
import { logAudit } from "./audit";
|
||
import { DATABASE_ID, TABLES, type TenantSettings } from "./schema";
|
||
import { createAdminClient } from "./server";
|
||
import { requireRole, requireTenant } from "./tenant-guard";
|
||
import type { WorkspaceSettingsState } from "./workspace-types";
|
||
import { workspaceSettingsSchema } from "@/lib/validation/workspace";
|
||
|
||
function appwriteError(e: unknown): string {
|
||
if (e instanceof AppwriteException) return e.message || "Beklenmeyen hata.";
|
||
return "Bağlantı hatası. Tekrar deneyin.";
|
||
}
|
||
|
||
function flattenErrors(err: z.ZodError): Record<string, string> {
|
||
const out: Record<string, string> = {};
|
||
for (const issue of err.issues) {
|
||
const key = issue.path.join(".");
|
||
if (key && !out[key]) out[key] = issue.message;
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function pickFormFields(formData: FormData) {
|
||
return {
|
||
officeName: String(formData.get("officeName") ?? "").trim(),
|
||
phone: String(formData.get("phone") ?? "").trim() || undefined,
|
||
email: String(formData.get("email") ?? "").trim() || undefined,
|
||
address: String(formData.get("address") ?? "").trim() || undefined,
|
||
defaultCurrency: String(formData.get("defaultCurrency") ?? "TRY").trim(),
|
||
};
|
||
}
|
||
|
||
export async function updateWorkspaceSettingsAction(
|
||
_prev: WorkspaceSettingsState,
|
||
formData: FormData,
|
||
): Promise<WorkspaceSettingsState> {
|
||
let ctx;
|
||
try {
|
||
ctx = await requireTenant();
|
||
requireRole(ctx, ["owner", "admin"]);
|
||
} catch {
|
||
return { ok: false, error: "Düzenleme yetkiniz yok." };
|
||
}
|
||
|
||
const fields = pickFormFields(formData);
|
||
if (!fields.officeName) {
|
||
return { ok: false, error: "Ofis adı zorunlu.", fieldErrors: { officeName: "Ofis adı zorunlu." } };
|
||
}
|
||
const parsed = { data: fields, success: true };
|
||
|
||
try {
|
||
const { tablesDB } = createAdminClient();
|
||
|
||
const existing = await tablesDB.listRows({
|
||
databaseId: DATABASE_ID,
|
||
tableId: TABLES.tenantSettings,
|
||
queries: [Query.equal("tenantId", ctx.tenantId), Query.limit(1)],
|
||
});
|
||
const row = existing.rows[0] as unknown as TenantSettings | undefined;
|
||
|
||
if (row) {
|
||
await tablesDB.updateRow(DATABASE_ID, TABLES.tenantSettings, row.$id, parsed.data);
|
||
await logAudit({
|
||
tenantId: ctx.tenantId,
|
||
userId: ctx.user.id,
|
||
action: "update",
|
||
entityType: "tenant_settings",
|
||
entityId: row.$id,
|
||
changes: parsed.data,
|
||
});
|
||
} else {
|
||
const created = await tablesDB.createRow(
|
||
DATABASE_ID,
|
||
TABLES.tenantSettings,
|
||
ID.unique(),
|
||
{ tenantId: ctx.tenantId, ...parsed.data, createdBy: ctx.user.id },
|
||
[
|
||
Permission.read(Role.team(ctx.tenantId)),
|
||
Permission.update(Role.team(ctx.tenantId, "owner")),
|
||
Permission.update(Role.team(ctx.tenantId, "admin")),
|
||
Permission.delete(Role.team(ctx.tenantId, "owner")),
|
||
],
|
||
);
|
||
await logAudit({
|
||
tenantId: ctx.tenantId,
|
||
userId: ctx.user.id,
|
||
action: "create",
|
||
entityType: "tenant_settings",
|
||
entityId: created.$id,
|
||
changes: parsed.data,
|
||
});
|
||
}
|
||
} catch (e) {
|
||
return { ok: false, error: appwriteError(e) };
|
||
}
|
||
|
||
// Tenant name shows up everywhere — revalidate broadly
|
||
revalidatePath("/", "layout");
|
||
return { ok: true };
|
||
}
|