import { useState } from "react"; import { useTranslation } from "react-i18next"; import { changeMyPassword, updateMyProfile, type SessionUser } from "./api.js"; // Self-service profile: the signed-in user edits their OWN display name + email and // changes their OWN password (proving the current one). This is NOT the admin // user-manager (UsersManager.tsx) — it never touches another account, username, or // role, and needs no `user:*` permission. See routes/auth.ts (/api/auth/profile, // /api/auth/password) and wiki/entities/local-jwt-auth.md. const MIN_PASSWORD = 8; export function Profile({ user, setUser, }: { user: SessionUser; setUser: (u: SessionUser | null) => void; }) { const { t } = useTranslation(); // --- Account (name / email) --- const [fullName, setFullName] = useState(user.fullName ?? ""); const [email, setEmail] = useState(user.email ?? ""); const [accountMsg, setAccountMsg] = useState(null); const [savingAccount, setSavingAccount] = useState(false); async function saveAccount() { setAccountMsg(null); setSavingAccount(true); try { const next = await updateMyProfile({ fullName, email }); // Keep the router-context user in sync so the header reflects the change. setUser(next); setFullName(next.fullName ?? ""); setEmail(next.email ?? ""); setAccountMsg(t("profile.profileSaved")); } catch (e) { setAccountMsg((e as Error).message); } finally { setSavingAccount(false); } } // --- Password --- const [current, setCurrent] = useState(""); const [next, setNext] = useState(""); const [confirm, setConfirm] = useState(""); const [pwMsg, setPwMsg] = useState(null); const [savingPw, setSavingPw] = useState(false); async function changePassword() { setPwMsg(null); if (next.length < MIN_PASSWORD) { setPwMsg(t("profile.passwordTooShort", { min: MIN_PASSWORD })); return; } if (next !== confirm) { setPwMsg(t("profile.passwordsDontMatch")); return; } setSavingPw(true); try { await changeMyPassword(current, next); setCurrent(""); setNext(""); setConfirm(""); setPwMsg(t("profile.passwordChanged")); } catch (e) { setPwMsg((e as Error).message); } finally { setSavingPw(false); } } return (

{t("profile.title")}

{/* Account: display name + email (username + role are read-only — admin-managed). */}

{t("profile.accountSection")}

{t("profile.username")} {user.username}
{t("profile.role")} {user.roleName}
{accountMsg && {accountMsg}}
{/* Password: requires the current one (server enforces). */}

{t("profile.passwordSection")}

{pwMsg && {pwMsg}}
); }