From fc091b9e0d9f9616e6b2e57ffbf84b1d88354107 Mon Sep 17 00:00:00 2001 From: kovakmedya Date: Thu, 30 Apr 2026 06:44:11 +0300 Subject: [PATCH] =?UTF-8?q?feat(settings):=20/settings/workspace=20?= =?UTF-8?q?=E2=80=94=20edit=20company=20info=20+=20invoice=20defaults?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../workspace/components/workspace-form.tsx | 192 ++++++++++++++++++ .../(dashboard)/settings/workspace/page.tsx | 50 +++++ src/lib/appwrite/workspace-actions.ts | 106 ++++++++++ src/lib/appwrite/workspace-types.ts | 7 + src/lib/validation/workspace.ts | 44 ++++ 5 files changed, 399 insertions(+) create mode 100644 src/app/(dashboard)/settings/workspace/components/workspace-form.tsx create mode 100644 src/app/(dashboard)/settings/workspace/page.tsx create mode 100644 src/lib/appwrite/workspace-actions.ts create mode 100644 src/lib/appwrite/workspace-types.ts create mode 100644 src/lib/validation/workspace.ts diff --git a/src/app/(dashboard)/settings/workspace/components/workspace-form.tsx b/src/app/(dashboard)/settings/workspace/components/workspace-form.tsx new file mode 100644 index 0000000..7e6a758 --- /dev/null +++ b/src/app/(dashboard)/settings/workspace/components/workspace-form.tsx @@ -0,0 +1,192 @@ +"use client"; + +import { useActionState, useEffect } from "react"; +import { Building2, Loader2, Receipt, Save } from "lucide-react"; +import { toast } from "sonner"; + +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { updateWorkspaceSettingsAction } from "@/lib/appwrite/workspace-actions"; +import { initialWorkspaceSettingsState } from "@/lib/appwrite/workspace-types"; + +type Defaults = { + companyName: string; + companyTaxId: string; + companyAddress: string; + companyEmail: string; + companyPhone: string; + defaultVatRate: number; + invoicePrefix: string; + invoiceCounter: number; +}; + +export function WorkspaceSettingsForm({ + canEdit, + defaults, +}: { + canEdit: boolean; + defaults: Defaults; +}) { + const [state, formAction, isPending] = useActionState( + updateWorkspaceSettingsAction, + initialWorkspaceSettingsState, + ); + + useEffect(() => { + if (state.ok) toast.success("Bilgiler güncellendi."); + else if (state.error) toast.error(state.error); + }, [state]); + + return ( +
+
+ + + + + Şirket + + Resmi şirket bilgileriniz. + + +
+ + + {state.fieldErrors?.companyName && ( +

{state.fieldErrors.companyName}

+ )} +
+ +
+ + +
+ +
+ + +
+ +
+ + + {state.fieldErrors?.companyEmail && ( +

{state.fieldErrors.companyEmail}

+ )} +
+ +
+ +