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 (
{PASSWORD_RULES.map((rule) => {
const ok = password.length > 0 && rule.test(password);
return (
-
{ok ? "✓" : "○"}
{rule.label}
);
})}
);
}
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 (
);
}