2025-10-25 11:39:24 +02:00

158 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// components/ChangePasswordModal.tsx
import React, { useState, useEffect } from "react";
import { Typography, TextField, Button, Box } from "@mui/material";
import toast from "react-hot-toast";
interface Props {
open: boolean;
onClose: () => void;
onSubmit: (passwordData: {
currentPassword?: string;
newPassword: string;
}) => void;
isUserChange?: boolean;
}
export const ChangePassword: React.FC<Props> = ({
open,
onSubmit,
isUserChange = false,
}) => {
const [currentPassword, setCurrentPassword] = useState("");
const [password, setPassword] = useState<string>("");
const [confirm, setConfirm] = useState<string>("");
const [errors, setErrors] = useState<string[]>([]);
const [isValid, setIsValid] = useState(false);
// Validate password rules
useEffect(() => {
if (!password) {
setErrors([]);
setIsValid(false);
return;
}
const newErrors: string[] = [];
if (password.length < 8) newErrors.push("At least 8 characters");
if (!/[A-Z]/.test(password)) newErrors.push("One uppercase letter");
if (!/[a-z]/.test(password)) newErrors.push("One lowercase letter");
if (!/[0-9]/.test(password)) newErrors.push("One number");
if (!/[!@#$%^&*(),.?\":{}|<>]/.test(password))
newErrors.push("One special character");
if (password !== confirm) newErrors.push("Passwords do not match");
// If user change mode, require current password
if (isUserChange && !currentPassword)
newErrors.push("Current password required");
setErrors(newErrors);
setIsValid(newErrors.length === 0);
}, [confirm, password, currentPassword, isUserChange]);
const handleSubmit = () => {
if (!isValid) {
toast.error("Please fix the validation errors before continuing");
return;
}
onSubmit({
...(isUserChange ? { currentPassword } : {}),
newPassword: password,
});
};
return (
<Box
sx={{
backgroundColor: "white",
p: 3,
borderRadius: 2,
width: 400,
display: open ? "flex" : "none",
flexDirection: "column",
gap: 2,
boxShadow: 3,
}}
>
{isUserChange ? (
<Typography variant="body2" color="text.secondary">
Please enter your current password and choose a new one below.
</Typography>
) : (
<Typography variant="body2" color="text.secondary">
Youre currently logged in with a temporary password. Please set a new
one to continue.
</Typography>
)}
{isUserChange && (
<TextField
label="Current Password"
type="password"
value={currentPassword}
onChange={e => setCurrentPassword(e.target.value)}
fullWidth
error={isUserChange && !currentPassword}
helperText={
isUserChange && !currentPassword
? "Current password is required"
: ""
}
/>
)}
<TextField
label="New Password"
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
fullWidth
error={!isValid && password.length > 0}
/>
<TextField
label="Confirm Password"
type="password"
value={confirm}
onChange={e => setConfirm(e.target.value)}
fullWidth
error={confirm.length > 0 && password !== confirm}
/>
<Box sx={{ height: "80px" }}>
<Typography
variant="caption"
color={isValid ? "success.main" : "error.main"}
sx={{
fontWeight: 500,
visibility: password.length > 0 ? "visible" : "hidden",
}}
>
<p>
{isValid
? "✓ Password meets all requirements"
: "Password must contain:"}
</p>
</Typography>
{!isValid && (
<Typography
variant="caption"
sx={{ color: "error.main", fontSize: "0.8rem" }}
>
{errors.join(", ")}
</Typography>
)}
</Box>
<Button
variant="contained"
sx={{ width: "100%", height: 40, fontSize: 16 }}
onClick={handleSubmit}
disabled={!isValid}
>
Update Password
</Button>
</Box>
);
};