deerflow2/frontend/src/components/workspace/settings/account-settings-page.tsx
Willem Jiang af6e48ccaa
fix(i18n): add Chinese translations for account settings page (#2712)
The account settings page had all user-facing strings (profile labels,
  password form placeholders, validation messages, button text) hardcoded
  in English. Replace them with i18n translation keys so the page renders
  correctly when the locale is set to Chinese.

 Fixed #2710
2026-05-04 11:15:16 +08:00

142 lines
4.4 KiB
TypeScript

"use client";
import { LogOutIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { fetch, getCsrfHeaders } from "@/core/api/fetcher";
import { useAuth } from "@/core/auth/AuthProvider";
import { parseAuthError } from "@/core/auth/types";
import { useI18n } from "@/core/i18n/hooks";
import { SettingsSection } from "./settings-section";
export function AccountSettingsPage() {
const { user, logout } = useAuth();
const { t } = useI18n();
const [currentPassword, setCurrentPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [message, setMessage] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const handleChangePassword = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
setMessage("");
if (newPassword !== confirmPassword) {
setError(t.settings.account.passwordMismatch);
return;
}
if (newPassword.length < 8) {
setError(t.settings.account.passwordTooShort);
return;
}
setLoading(true);
try {
const res = await fetch("/api/v1/auth/change-password", {
method: "POST",
headers: {
"Content-Type": "application/json",
...getCsrfHeaders(),
},
body: JSON.stringify({
current_password: currentPassword,
new_password: newPassword,
}),
});
if (!res.ok) {
const data = await res.json();
const authError = parseAuthError(data);
setError(authError.message);
return;
}
setMessage(t.settings.account.passwordChangedSuccess);
setCurrentPassword("");
setNewPassword("");
setConfirmPassword("");
} catch {
setError(t.settings.account.networkError);
} finally {
setLoading(false);
}
};
return (
<div className="space-y-8">
<SettingsSection title={t.settings.account.profileTitle}>
<div className="space-y-2">
<div className="grid grid-cols-[max-content_max-content] items-center gap-4">
<span className="text-muted-foreground text-sm">
{t.settings.account.email}
</span>
<span className="text-sm font-medium">{user?.email ?? "—"}</span>
<span className="text-muted-foreground text-sm">
{t.settings.account.role}
</span>
<span className="text-sm font-medium capitalize">
{user?.system_role ?? "—"}
</span>
</div>
</div>
</SettingsSection>
<SettingsSection
title={t.settings.account.changePasswordTitle}
description={t.settings.account.changePasswordDescription}
>
<form onSubmit={handleChangePassword} className="max-w-sm space-y-3">
<Input
type="password"
placeholder={t.settings.account.currentPassword}
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
required
/>
<Input
type="password"
placeholder={t.settings.account.newPassword}
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
required
minLength={8}
/>
<Input
type="password"
placeholder={t.settings.account.confirmNewPassword}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
minLength={8}
/>
{error && <p className="text-sm text-red-500">{error}</p>}
{message && <p className="text-sm text-green-500">{message}</p>}
<Button type="submit" variant="outline" size="sm" disabled={loading}>
{loading
? t.settings.account.updating
: t.settings.account.updatePassword}
</Button>
</form>
</SettingsSection>
<SettingsSection title="" description="">
<Button
variant="destructive"
size="sm"
onClick={logout}
className="gap-2"
>
<LogOutIcon className="size-4" />
{t.settings.account.signOut}
</Button>
</SettingsSection>
</div>
);
}