import { useState, useMemo } from "react"; import { changePassword } from "../api/auth"; interface PasswordRule { label: string; test: (pw: string) => boolean; } const PASSWORD_RULES: PasswordRule[] = [ { label: "At least 12 characters", test: (pw) => pw.length >= 12 }, { label: "At least one uppercase letter", test: (pw) => /[A-Z]/.test(pw) }, { label: "At least one lowercase letter", test: (pw) => /[a-z]/.test(pw) }, { label: "At least one digit", test: (pw) => /[0-9]/.test(pw) }, { label: "At least one special character (!@#$%^&*…)", test: (pw) => /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?`~]/.test(pw), }, ]; export function PasswordPolicyChecklist({ password }: { password: string }) { return ( ); } interface Props { onSuccess: () => void; isForced?: boolean; } export default function ChangePasswordModal({ onSuccess, isForced }: Props) { const [currentPw, setCurrentPw] = useState(""); const [newPw, setNewPw] = useState(""); const [confirmPw, setConfirmPw] = useState(""); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const allRulesPass = useMemo( () => PASSWORD_RULES.every((r) => r.test(newPw)), [newPw], ); const canSubmit = currentPw.length > 0 && allRulesPass && newPw === confirmPw && !loading; async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!canSubmit) return; setLoading(true); setError(null); try { await changePassword(currentPw, newPw); onSuccess(); } catch (err: unknown) { const msg = (err as { response?: { data?: { detail?: string } } })?.response?.data ?.detail ?? "Failed to change password"; setError(msg); } finally { setLoading(false); } } return (

Change Password

{isForced && (

You must change your password before continuing.

)} {error && (
{error}
)} setCurrentPw(e.target.value)} autoFocus /> setNewPw(e.target.value)} /> setConfirmPw(e.target.value)} /> {confirmPw.length > 0 && newPw !== confirmPw && (

Passwords do not match

)}
); }