feat(settings): /settings/workspace — edit company info + invoice defaults
Owner/admin edit, member read-only.
Schema/validation:
- lib/validation/workspace.ts (workspaceSettingsSchema)
- lib/appwrite/workspace-actions.ts:
* updateWorkspaceSettingsAction — requireRole owner|admin, upserts the
tenant_settings row (creates one with team-scoped perms if absent,
e.g. for tenants created before tenant_settings was a table; just
defense). Audit-logged.
* Forces invoicePrefix to uppercase. defaultVatRate clamped to [0, 100].
* revalidatePath('/', 'layout') so the new company name updates in
sidebar header and dashboard greeting on next render.
UI:
- /settings/workspace page (server) — pulls active tenant settings
via requireTenant, shows form pre-filled.
- WorkspaceSettingsForm: 2 cards
* Şirket — name (required), tax id, phone, email, address
* Faturalama — invoicePrefix, defaultVatRate, read-only invoiceCounter
- All inputs disabled if user is a member (canEdit=false). Submit button
hidden in that case. Description on the page changes accordingly.
- Toast feedback for success/error.
Skipped: logo upload (storage bucket pending). Will revisit.
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const workspaceSettingsSchema = z.object({
|
||||
companyName: z.string().trim().min(1, "Şirket adı zorunlu.").max(255),
|
||||
companyTaxId: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(50)
|
||||
.optional()
|
||||
.transform((v) => (v ? v : undefined)),
|
||||
companyAddress: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(500)
|
||||
.optional()
|
||||
.transform((v) => (v ? v : undefined)),
|
||||
companyEmail: z
|
||||
.union([z.string().email("Geçerli bir email girin."), z.literal("")])
|
||||
.optional()
|
||||
.transform((v) => (v ? v : undefined)),
|
||||
companyPhone: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(30)
|
||||
.optional()
|
||||
.transform((v) => (v ? v : undefined)),
|
||||
defaultVatRate: z
|
||||
.union([z.number(), z.string()])
|
||||
.optional()
|
||||
.transform((v) => {
|
||||
if (v === undefined || v === "") return 20;
|
||||
const n = typeof v === "string" ? Number(v.replace(",", ".")) : v;
|
||||
return Number.isFinite(n) ? n : 20;
|
||||
})
|
||||
.pipe(z.number().min(0, "Negatif olamaz.").max(100, "100'den büyük olamaz.")),
|
||||
invoicePrefix: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(10)
|
||||
.optional()
|
||||
.transform((v) => (v ? v.toUpperCase() : "INV")),
|
||||
});
|
||||
|
||||
export type WorkspaceSettingsInput = z.infer<typeof workspaceSettingsSchema>;
|
||||
Reference in New Issue
Block a user