Files
isletmem-kovakcrm/src/app/(dashboard)/settings/account/components/name-form.tsx
T
kovakmedya 89d456fc76 feat(profile): /settings/account — name, email, password
Replaces template's mock account form with real Appwrite-backed actions.

Server actions (lib/appwrite/profile-actions.ts):
- updateNameAction: account.updateName via session SDK; revalidates layout
  so the new name shows in sidebar/header right away.
- updateEmailAction: account.updateEmail (requires current password as
  Appwrite confirmation). Maps user_email_already_exists to a friendly
  Turkish message.
- updatePasswordAction: account.updatePassword(new, old). Validates
  old != empty, new >= 8 chars, new === confirm. Maps
  user_password_recently_used / user_password_mismatch.
- All audit-logged with entityType user_name / user_email / user_password
  and tenantId of the user's currently-active workspace (or 'global' if
  none). Audit failures swallowed.

UI:
- /settings/account is now an async server page that pulls the user via
  getCurrentUser, renders an account info card ($id, registration date,
  email verification, MFA), then 3 separate small forms — one card each
  — for name, email, password. Each form clears its own state and gives
  toast feedback independently.
- Removed the template's react-hook-form-based mock page.

Side note: skipped email-verification flow + MFA setup for later.
2026-04-30 06:47:53 +03:00

57 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useActionState, useEffect } from "react";
import { Loader2, 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 { updateNameAction } from "@/lib/appwrite/profile-actions";
import { initialProfileState } from "@/lib/appwrite/profile-types";
export function NameForm({ currentName }: { currentName: string }) {
const [state, formAction, isPending] = useActionState(updateNameAction, initialProfileState);
useEffect(() => {
if (state.ok) toast.success("İsim güncellendi.");
else if (state.error) toast.error(state.error);
}, [state]);
return (
<Card>
<CardHeader>
<CardTitle>Görünür isim</CardTitle>
<CardDescription>
Header'da, davetlerde ve takım listesinde görünecek isim.
</CardDescription>
</CardHeader>
<CardContent>
<form action={formAction} className="grid gap-4 md:grid-cols-[1fr_auto] md:items-end">
<div className="grid gap-2">
<Label htmlFor="name">İsim</Label>
<Input id="name" name="name" defaultValue={currentName} required maxLength={128} />
{state.fieldErrors?.name && (
<p className="text-destructive text-xs">{state.fieldErrors.name}</p>
)}
</div>
<Button type="submit" disabled={isPending}>
{isPending ? (
<>
<Loader2 className="size-4 animate-spin" />
Kaydediliyor...
</>
) : (
<>
<Save className="size-4" />
Kaydet
</>
)}
</Button>
</form>
</CardContent>
</Card>
);
}