Add confirmation modal for Super Admin role assignment
Prevents accidental Super Admin privilege grants by requiring users to type "make this user super admin" before the role change is applied. Modal blocks paste/drag input and reverts the dropdown on cancel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
51c4909ee7
commit
a5d5d51d2a
1 changed files with 102 additions and 0 deletions
|
|
@ -25,6 +25,11 @@ export const UserManagement: React.FC = () => {
|
|||
const [savedUserId, setSavedUserId] = useState<string | null>(null);
|
||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||
|
||||
// Super Admin confirmation modal
|
||||
const [pendingSuperAdminUser, setPendingSuperAdminUser] = useState<{ userId: string; userName: string } | null>(null);
|
||||
const [confirmationText, setConfirmationText] = useState('');
|
||||
const [confirmationError, setConfirmationError] = useState(false);
|
||||
|
||||
// New agency form
|
||||
const [newAgencyName, setNewAgencyName] = useState('');
|
||||
const [isCreatingAgency, setIsCreatingAgency] = useState(false);
|
||||
|
|
@ -57,6 +62,13 @@ export const UserManagement: React.FC = () => {
|
|||
};
|
||||
|
||||
const handleRoleChange = async (userId: string, newRole: string) => {
|
||||
const user = users.find(u => u.id === userId);
|
||||
if (newRole === 'super_admin' && user?.role !== 'super_admin') {
|
||||
setPendingSuperAdminUser({ userId, userName: user?.name || 'this user' });
|
||||
setConfirmationText('');
|
||||
setConfirmationError(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const updated = await apiService.updateUser(userId, { role: newRole });
|
||||
setUsers(prev => prev.map(u => u.id === userId ? updated : u));
|
||||
|
|
@ -67,6 +79,32 @@ export const UserManagement: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleConfirmSuperAdmin = async () => {
|
||||
if (confirmationText !== 'make this user super admin') {
|
||||
setConfirmationError(true);
|
||||
return;
|
||||
}
|
||||
if (!pendingSuperAdminUser) return;
|
||||
const { userId } = pendingSuperAdminUser;
|
||||
setPendingSuperAdminUser(null);
|
||||
setConfirmationText('');
|
||||
setConfirmationError(false);
|
||||
try {
|
||||
const updated = await apiService.updateUser(userId, { role: 'super_admin' });
|
||||
setUsers(prev => prev.map(u => u.id === userId ? updated : u));
|
||||
showSavedIndicator(userId);
|
||||
} catch (err) {
|
||||
console.error('Failed to update user role:', err);
|
||||
setError('Failed to update user role.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelSuperAdmin = () => {
|
||||
setPendingSuperAdminUser(null);
|
||||
setConfirmationText('');
|
||||
setConfirmationError(false);
|
||||
};
|
||||
|
||||
const handleAgencyChange = async (userId: string, agencyId: string | null) => {
|
||||
try {
|
||||
const updated = await apiService.updateUser(userId, { agency_id: agencyId });
|
||||
|
|
@ -224,6 +262,70 @@ export const UserManagement: React.FC = () => {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{/* Super Admin Confirmation Modal */}
|
||||
{pendingSuperAdminUser && (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
|
||||
onClick={handleCancelSuperAdmin}
|
||||
>
|
||||
<div
|
||||
className="bg-white rounded-[10px] shadow-xl max-w-md w-full mx-4 p-6"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="flex-shrink-0 h-10 w-10 rounded-full bg-amber-100 flex items-center justify-center">
|
||||
<svg className="h-5 w-5 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-black-title">Confirm Super Admin Assignment</h3>
|
||||
</div>
|
||||
<p className="text-sm text-grey-700 mb-4">
|
||||
You are about to grant Super Admin privileges to <strong className="text-black-title">{pendingSuperAdminUser.userName}</strong>. This gives full system access including user management. This action should only be performed intentionally.
|
||||
</p>
|
||||
<p className="text-sm text-grey-700 mb-2">
|
||||
To confirm, type <strong className="text-black-title">make this user super admin</strong> below:
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
value={confirmationText}
|
||||
onChange={(e) => {
|
||||
setConfirmationText(e.target.value);
|
||||
setConfirmationError(false);
|
||||
}}
|
||||
onPaste={(e) => e.preventDefault()}
|
||||
onDrop={(e) => e.preventDefault()}
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
placeholder="Type the phrase above..."
|
||||
className={`w-full p-2 border-2 rounded-[10px] text-sm text-black-title focus:outline-none focus:ring-2 transition ${
|
||||
confirmationError
|
||||
? 'border-error focus:ring-error'
|
||||
: 'border-grey-700 focus:ring-active-blue focus:border-active-blue'
|
||||
}`}
|
||||
autoFocus
|
||||
/>
|
||||
{confirmationError && (
|
||||
<p className="text-error text-xs mt-1">Please type the exact phrase to confirm.</p>
|
||||
)}
|
||||
<div className="flex justify-end gap-3 mt-5">
|
||||
<button
|
||||
onClick={handleCancelSuperAdmin}
|
||||
className="px-4 py-2 text-sm font-medium text-grey-700 hover:text-black-title border border-grey-300 rounded-full hover:bg-grey-100 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirmSuperAdmin}
|
||||
disabled={confirmationText !== 'make this user super admin'}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-active-blue rounded-full hover:bg-active-blue/90 transition-colors disabled:bg-grey-700 disabled:cursor-not-allowed"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Agency Management */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold text-primary-blue mb-4">Agencies ({agencies.length})</h2>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue