/** * @file FirstPasswordPage.tsx * @description The screen an account signed in with a password an admin GAVE * (created or reset on the Users page — `mustChangePassword`) sees instead of * the app: the current password, a new one that must pass every rule (the * `PasswordRules` checklist ticks them as they are typed) and the same again, * sent to `POST /api/me/password`. On success the app opens; "Sign out" * leaves. The server refuses every other request meanwhile (EPWCHANGE). * @author Nguyễn Ngọc Trí Vĩ */ import { useState, type FormEvent } from "react"; import { useTranslation } from "react-i18next"; import { api, type AuthUser } from "../lib/api"; import { passwordProblems } from "../lib/passwordRules"; import { AuthCard } from "./AuthCard"; import { PasswordRules } from "./users/PasswordRules"; export function FirstPasswordPage({ user, onDone, onLogout, }: { user: AuthUser; onDone: () => void; onLogout: () => void; }) { const { t } = useTranslation(); const [current, setCurrent] = useState(""); const [next, setNext] = useState(""); const [again, setAgain] = useState(""); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const mismatch = again.length > 0 && again !== next; const strong = passwordProblems(next, user.username).length === 0; const ready = current.length > 0 && strong && again === next && !busy; const submit = async (e: FormEvent) => { e.preventDefault(); if (!ready) return; setBusy(true); setError(null); try { await api.me.changePassword(current, next); onDone(); } catch (err) { const code = (err as { code?: string }).code; setError( code && t(`users:errors.${code}`, { defaultValue: "" }) ? t(`users:errors.${code}`) : (err as Error).message ); } finally { setBusy(false); } }; const field = ( label: string, value: string, set: (v: string) => void, autoComplete: string, autoFocus = false ) => ( ); return ( {field(t("auth.currentPassword"), current, setCurrent, "current-password", true)} {field(t("auth.newPassword"), next, setNext, "new-password")} {field(t("auth.confirmPassword"), again, setAgain, "new-password")} {(mismatch || error) && (

{error ?? t("auth.mismatch")}

)}
); }