fix: workspace settings — emlak fields (officeName/phone/email/address), add createdBy to createRow

This commit is contained in:
egecankomur
2026-05-05 13:15:50 +03:00
parent 115e5cd159
commit 19c1ecef47
3 changed files with 57 additions and 118 deletions
@@ -1,7 +1,7 @@
"use client";
import { useActionState, useEffect } from "react";
import { Building2, Loader2, Receipt, Save } from "lucide-react";
import { Building2, Loader2, Save } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
@@ -13,14 +13,11 @@ 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;
officeName: string;
phone: string;
email: string;
address: string;
defaultCurrency: string;
};
export function WorkspaceSettingsForm({
@@ -47,128 +44,74 @@ export function WorkspaceSettingsForm({
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Building2 className="size-4" />
Şirket
Ofis Bilgileri
</CardTitle>
<CardDescription>Resmi şirket bilgileriniz.</CardDescription>
<CardDescription>Müşterilere ve sunumlarda gösterilecek ofis bilgileri.</CardDescription>
</CardHeader>
<CardContent className="grid gap-5 md:grid-cols-2">
<div className="md:col-span-2 grid gap-2">
<Label htmlFor="companyName">Şirket adı *</Label>
<Label htmlFor="officeName">Ofis adı *</Label>
<Input
id="companyName"
name="companyName"
defaultValue={defaults.companyName}
id="officeName"
name="officeName"
defaultValue={defaults.officeName}
required
placeholder="Örn. Acme Yazılım Ltd. Şti."
placeholder="Örn. Kovak Emlak"
/>
{state.fieldErrors?.companyName && (
<p className="text-destructive text-xs">{state.fieldErrors.companyName}</p>
{state.fieldErrors?.officeName && (
<p className="text-destructive text-xs">{state.fieldErrors.officeName}</p>
)}
</div>
<div className="grid gap-2">
<Label htmlFor="companyTaxId">Vergi numarası</Label>
<Label htmlFor="phone">Telefon</Label>
<Input
id="companyTaxId"
name="companyTaxId"
defaultValue={defaults.companyTaxId}
inputMode="numeric"
placeholder="1234567890"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="companyPhone">Telefon</Label>
<Input
id="companyPhone"
name="companyPhone"
id="phone"
name="phone"
type="tel"
defaultValue={defaults.companyPhone}
defaultValue={defaults.phone}
placeholder="+90 555 123 45 67"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="companyEmail">Email</Label>
<Label htmlFor="email">Email</Label>
<Input
id="companyEmail"
name="companyEmail"
id="email"
name="email"
type="email"
defaultValue={defaults.companyEmail}
placeholder="info@firma.com"
defaultValue={defaults.email}
placeholder="info@ofis.com"
/>
{state.fieldErrors?.companyEmail && (
<p className="text-destructive text-xs">{state.fieldErrors.companyEmail}</p>
)}
</div>
<div className="grid gap-2">
<Label htmlFor="companyAddress">Adres</Label>
<Label htmlFor="defaultCurrency">Varsayılan para birimi</Label>
<select
id="defaultCurrency"
name="defaultCurrency"
defaultValue={defaults.defaultCurrency}
className="border-input bg-background h-9 rounded-md border px-3 text-sm"
>
<option value="TRY">TRY Türk Lirası</option>
<option value="USD">USD Amerikan Doları</option>
<option value="EUR">EUR Euro</option>
</select>
</div>
<div className="grid gap-2">
<Label htmlFor="address">Adres</Label>
<Textarea
id="companyAddress"
name="companyAddress"
id="address"
name="address"
rows={2}
defaultValue={defaults.companyAddress}
defaultValue={defaults.address}
placeholder="İl, ilçe, açık adres"
/>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Receipt className="size-4" />
Faturalama
</CardTitle>
<CardDescription>
Yeni fatura oluştururken kullanılan varsayılanlar.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-5 md:grid-cols-3">
<div className="grid gap-2">
<Label htmlFor="invoicePrefix">Fatura ön eki</Label>
<Input
id="invoicePrefix"
name="invoicePrefix"
defaultValue={defaults.invoicePrefix}
maxLength={10}
placeholder="INV"
style={{ textTransform: "uppercase" }}
/>
<p className="text-muted-foreground text-xs">
Örn. <code>INV-2026-0001</code>
</p>
</div>
<div className="grid gap-2">
<Label htmlFor="defaultVatRate">Varsayılan KDV %</Label>
<Input
id="defaultVatRate"
name="defaultVatRate"
type="number"
step="0.1"
min="0"
max="100"
defaultValue={defaults.defaultVatRate}
/>
{state.fieldErrors?.defaultVatRate && (
<p className="text-destructive text-xs">{state.fieldErrors.defaultVatRate}</p>
)}
</div>
<div className="grid gap-2">
<Label>Sayaç</Label>
<div className="bg-muted/50 flex h-9 items-center rounded-md border px-3 text-sm tabular-nums">
{defaults.invoiceCounter}
</div>
<p className="text-muted-foreground text-xs">
Bir sonraki fatura numarası: bu sayı + 1
</p>
</div>
</CardContent>
</Card>
{canEdit && (
<div className="flex justify-end">
<Button type="submit" disabled={isPending}>
@@ -41,14 +41,11 @@ export default async function WorkspaceSettingsPage() {
<WorkspaceSettingsForm
canEdit={canEdit}
defaults={{
companyName: officeName,
companyTaxId: "",
companyAddress: ctx.settings?.address ?? "",
companyEmail: ctx.settings?.email ?? "",
companyPhone: ctx.settings?.phone ?? "",
defaultVatRate: 20,
invoicePrefix: "INV",
invoiceCounter: 0,
officeName: ctx.settings?.officeName ?? "",
phone: ctx.settings?.phone ?? "",
email: ctx.settings?.email ?? "",
address: ctx.settings?.address ?? "",
defaultCurrency: ctx.settings?.defaultCurrency ?? "TRY",
}}
/>
</div>
+10 -11
View File
@@ -27,13 +27,11 @@ function flattenErrors(err: z.ZodError): Record<string, string> {
function pickFormFields(formData: FormData) {
return {
companyName: String(formData.get("companyName") ?? "").trim(),
companyTaxId: String(formData.get("companyTaxId") ?? "").trim(),
companyAddress: String(formData.get("companyAddress") ?? "").trim(),
companyEmail: String(formData.get("companyEmail") ?? "").trim(),
companyPhone: String(formData.get("companyPhone") ?? "").trim(),
defaultVatRate: String(formData.get("defaultVatRate") ?? "20"),
invoicePrefix: String(formData.get("invoicePrefix") ?? "").trim(),
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(),
};
}
@@ -49,10 +47,11 @@ export async function updateWorkspaceSettingsAction(
return { ok: false, error: "Düzenleme yetkiniz yok." };
}
const parsed = workspaceSettingsSchema.safeParse(pickFormFields(formData));
if (!parsed.success) {
return { ok: false, error: "Form geçersiz.", fieldErrors: flattenErrors(parsed.error) };
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();
@@ -79,7 +78,7 @@ export async function updateWorkspaceSettingsAction(
DATABASE_ID,
TABLES.tenantSettings,
ID.unique(),
{ tenantId: ctx.tenantId, ...parsed.data },
{ tenantId: ctx.tenantId, ...parsed.data, createdBy: ctx.user.id },
[
Permission.read(Role.team(ctx.tenantId)),
Permission.update(Role.team(ctx.tenantId, "owner")),