You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.4 KiB
JavaScript
153 lines
4.4 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { useAuth } from '../../contexts/AuthContext';
|
|
import './ChangePasswordOverlay.css';
|
|
|
|
function ChangePasswordOverlay({ onClose }) {
|
|
const [oldPassword, setOldPassword] = useState('');
|
|
const [newPassword, setNewPassword] = useState('');
|
|
const [confirmPassword, setConfirmPassword] = useState('');
|
|
const [error, setError] = useState('');
|
|
const [success, setSuccess] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const { changePassword } = useAuth();
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
|
|
// Validation
|
|
if (newPassword !== confirmPassword) {
|
|
setError('New passwords do not match');
|
|
return;
|
|
}
|
|
|
|
if (newPassword.length < 8) {
|
|
setError('Password must be at least 8 characters');
|
|
return;
|
|
}
|
|
|
|
if (oldPassword === newPassword) {
|
|
setError('New password must be different from old password');
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
|
|
const result = await changePassword(oldPassword, newPassword);
|
|
|
|
if (result.success) {
|
|
setSuccess(true);
|
|
setTimeout(() => {
|
|
onClose();
|
|
}, 2000);
|
|
} else {
|
|
// Extract error message
|
|
let errorMsg = 'Failed to change password';
|
|
if (result.error.old_password) {
|
|
errorMsg = result.error.old_password[0];
|
|
} else if (result.error.new_password) {
|
|
errorMsg = result.error.new_password[0];
|
|
} else if (result.error.detail) {
|
|
errorMsg = result.error.detail;
|
|
}
|
|
setError(errorMsg);
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleOverlayClick = (e) => {
|
|
if (e.target.className === 'overlay') {
|
|
onClose();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="overlay" onClick={handleOverlayClick}>
|
|
<div className="overlay-content">
|
|
<div className="overlay-header">
|
|
<h2>Change Password</h2>
|
|
<button className="close-button" onClick={onClose} aria-label="Close">
|
|
×
|
|
</button>
|
|
</div>
|
|
|
|
{success ? (
|
|
<div className="success-message">
|
|
<div className="success-icon">✓</div>
|
|
<p>Password changed successfully!</p>
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="password-form">
|
|
{error && (
|
|
<div className="error-message">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="old-password">Current Password</label>
|
|
<input
|
|
id="old-password"
|
|
type="password"
|
|
value={oldPassword}
|
|
onChange={(e) => setOldPassword(e.target.value)}
|
|
placeholder="Enter current password"
|
|
required
|
|
autoFocus
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="new-password">New Password</label>
|
|
<input
|
|
id="new-password"
|
|
type="password"
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
placeholder="Enter new password"
|
|
required
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label htmlFor="confirm-password">Confirm New Password</label>
|
|
<input
|
|
id="confirm-password"
|
|
type="password"
|
|
value={confirmPassword}
|
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
placeholder="Re-enter new password"
|
|
required
|
|
disabled={isLoading}
|
|
/>
|
|
</div>
|
|
|
|
<div className="form-actions">
|
|
<button
|
|
type="button"
|
|
className="cancel-button"
|
|
onClick={onClose}
|
|
disabled={isLoading}
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
className="submit-button"
|
|
disabled={isLoading}
|
|
>
|
|
{isLoading ? 'Changing...' : 'Change Password'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default ChangePasswordOverlay;
|