Domain Admin
This commit is contained in:
97
frontend/src/components/ChangeMyPasswordModal.jsx
Normal file
97
frontend/src/components/ChangeMyPasswordModal.jsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import Modal from './Modal';
|
||||
import { authAPI } from '../services/api';
|
||||
|
||||
const ChangeMyPasswordModal = ({ open, onClose, onToast }) => {
|
||||
const [currentPw, setCurrentPw] = useState('');
|
||||
const [newPw, setNewPw] = useState('');
|
||||
const [confirmPw, setConfirmPw] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setCurrentPw(''); setNewPw(''); setConfirmPw(''); setError('');
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const tooShort = newPw.length > 0 && newPw.length < 8;
|
||||
const mismatch = confirmPw.length > 0 && newPw !== confirmPw;
|
||||
const canSubmit =
|
||||
currentPw.length > 0 && newPw.length >= 8 && newPw === confirmPw;
|
||||
|
||||
const submit = async () => {
|
||||
if (!canSubmit) {
|
||||
if (!currentPw) setError('Please enter your current password.');
|
||||
else if (newPw.length < 8) setError('New password must have at least 8 characters.');
|
||||
else if (newPw !== confirmPw) setError('Passwords do not match.');
|
||||
return;
|
||||
}
|
||||
setBusy(true); setError('');
|
||||
try {
|
||||
await authAPI.changePassword(currentPw, newPw);
|
||||
onToast?.('Your password has been updated.', 'success');
|
||||
onClose();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal open={open} onClose={onClose} title="Change my password" size="sm">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 mb-2">Current password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={currentPw}
|
||||
onChange={(e) => setCurrentPw(e.target.value)}
|
||||
className="input-field"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 mb-2">New password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={newPw}
|
||||
onChange={(e) => setNewPw(e.target.value)}
|
||||
className={`input-field ${tooShort ? 'border-red-500 focus:ring-red-500' : ''}`}
|
||||
minLength={8}
|
||||
/>
|
||||
{tooShort && <p className="mt-1 text-xs text-red-600">Minimum 8 characters.</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-semibold text-gray-700 mb-2">Confirm new password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={confirmPw}
|
||||
onChange={(e) => setConfirmPw(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); submit(); } }}
|
||||
className={`input-field ${mismatch ? 'border-red-500 focus:ring-red-500' : ''}`}
|
||||
minLength={8}
|
||||
/>
|
||||
{mismatch && <p className="mt-1 text-xs text-red-600">Passwords do not match.</p>}
|
||||
{!mismatch && confirmPw.length > 0 && newPw === confirmPw && newPw.length >= 8 && (
|
||||
<p className="mt-1 text-xs text-green-600">Passwords match.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2 border-t border-gray-200">
|
||||
<button onClick={onClose} className="btn-secondary px-4 py-2" disabled={busy}>Cancel</button>
|
||||
<button onClick={submit} disabled={busy || !canSubmit} className="btn-primary">
|
||||
{busy ? 'Updating...' : 'Update password'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangeMyPasswordModal;
|
||||
Reference in New Issue
Block a user