Domain Admin

This commit is contained in:
2026-04-27 16:57:08 -05:00
parent 31b3fd8c9f
commit 85b608e3d4
10 changed files with 903 additions and 61 deletions

View 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;