import React, { useState, useEffect } from 'react'; import apiService from '../services/apiService'; import type { UserManagementResponse, AgencyResponse, UserChangeLogEntry } from '../services/apiService'; import { UserIcon } from './icons/UserIcon'; import { PlusIcon } from './icons/PlusIcon'; import { ChevronDownIcon } from './icons/ChevronDownIcon'; import { ClockIcon } from './icons/ClockIcon'; import { useUser } from '../contexts/UserContext'; import type { UserRole } from '../types'; const ROLE_OPTIONS: { value: UserRole; label: string }[] = [ { value: 'super_admin', label: 'Super Admin' }, { value: 'oversight_admin', label: 'Oversight Admin' }, { value: 'agency_admin', label: 'Agency Admin' }, { value: 'basic_user', label: 'Basic User' }, ]; const formatDate = (iso: string) => { return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }; export const UserManagement: React.FC = () => { const { user: currentUser, isSuperAdmin, refresh } = useUser(); const [users, setUsers] = useState([]); const [agencies, setAgencies] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [savedUserId, setSavedUserId] = useState(null); const [successMessage, setSuccessMessage] = useState(null); // Super Admin confirmation modal const [pendingSuperAdminUser, setPendingSuperAdminUser] = useState<{ userId: string; userName: string } | null>(null); const [confirmationText, setConfirmationText] = useState(''); const [confirmationError, setConfirmationError] = useState(false); // Change history modal const [historyUser, setHistoryUser] = useState<{ id: string; name: string } | null>(null); const [historyEntries, setHistoryEntries] = useState([]); const [historyLoading, setHistoryLoading] = useState(false); // New agency form const [newAgencyName, setNewAgencyName] = useState(''); const [isCreatingAgency, setIsCreatingAgency] = useState(false); useEffect(() => { loadData(); }, []); const loadData = async () => { setIsLoading(true); try { const [usersData, agenciesData] = await Promise.all([ apiService.getUsers(), apiService.getAgencies(), ]); setUsers(usersData); setAgencies(agenciesData); } catch (err) { console.error('Failed to load user management data:', err); setError('Failed to load data. You may not have permission.'); } finally { setIsLoading(false); } }; const showSavedIndicator = (userId: string) => { setSavedUserId(userId); setError(null); setTimeout(() => setSavedUserId(prev => prev === userId ? null : prev), 2000); }; 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)); showSavedIndicator(userId); if (userId === currentUser?.id) await refresh(); } catch (err) { console.error('Failed to update user role:', err); setError('Failed to update user role.'); } }; 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); if (userId === currentUser?.id) await refresh(); } 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 }); setUsers(prev => prev.map(u => u.id === userId ? updated : u)); showSavedIndicator(userId); if (userId === currentUser?.id) await refresh(); } catch (err) { console.error('Failed to update user agency:', err); setError('Failed to update user agency.'); } }; const formatRoleLabel = (role: string): string => { const found = ROLE_OPTIONS.find(r => r.value === role); return found ? found.label : role; }; const formatChangeDescription = (entry: UserChangeLogEntry): string => { if (entry.change_type === 'user_created') { return `User account created (${formatRoleLabel(entry.new_value || 'basic_user')})`; } if (entry.change_type === 'role_changed') { return `Role changed from ${formatRoleLabel(entry.old_value || '')} to ${formatRoleLabel(entry.new_value || '')}`; } if (entry.change_type === 'agency_changed') { return `Agency changed from ${entry.old_value || 'None'} to ${entry.new_value || 'None'}`; } return entry.change_type; }; const formatHistoryDate = (iso: string): string => { const d = new Date(iso); return d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) + ', ' + d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }); }; const handleOpenHistory = async (user: UserManagementResponse) => { setHistoryUser({ id: user.id, name: user.name }); setHistoryLoading(true); setHistoryEntries([]); try { const entries = await apiService.getUserChangeHistory(user.id); setHistoryEntries(entries); } catch (err) { console.error('Failed to load change history:', err); } finally { setHistoryLoading(false); } }; const handleCloseHistory = () => { setHistoryUser(null); setHistoryEntries([]); }; const handleCreateAgency = async (e: React.FormEvent) => { e.preventDefault(); if (!newAgencyName.trim()) return; setIsCreatingAgency(true); try { const newAgency = await apiService.createAgency(newAgencyName.trim()); setAgencies(prev => [...prev, newAgency].sort((a, b) => a.name.localeCompare(b.name))); setNewAgencyName(''); setError(null); setSuccessMessage('Agency created successfully'); setTimeout(() => setSuccessMessage(prev => prev === 'Agency created successfully' ? null : prev), 3000); } catch (err) { console.error('Failed to create agency:', err); setError('Failed to create agency.'); } finally { setIsCreatingAgency(false); } }; if (isLoading) { return (

Loading user management...

); } return (

User Management

{isSuperAdmin ? 'Manage user roles and agency assignments. Users are provisioned automatically via Azure AD.' : 'View user roles and agency assignments.'}

{error && (
{error}
)} {successMessage && (
{successMessage}
)} {/* Users Table */}

Users ({users.length})

{users.map((user, index) => ( ))} {users.length === 0 && ( )}
Name Email Role Agency Created
{user.name}
{user.email} {isSuperAdmin ? (
) : ( {formatRoleLabel(user.role)} )}
{isSuperAdmin ? (
) : ( {user.agency || 'Unassigned'} )}
{formatDate(user.created_at)}
{savedUserId === user.id && ( Saved )}
No users found.
{/* Super Admin Confirmation Modal */} {pendingSuperAdminUser && (
e.stopPropagation()} >

Confirm Super Admin Assignment

You are about to grant Super Admin privileges to {pendingSuperAdminUser.userName}. This gives full system access including user management. This action should only be performed intentionally.

To confirm, type make this user super admin below:

{ 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-oliver-black focus:outline-none focus:ring-2 transition ${ confirmationError ? 'border-error focus:ring-error' : 'border-oliver-azure focus:ring-oliver-azure focus:border-oliver-azure' }`} autoFocus /> {confirmationError && (

Please type the exact phrase to confirm.

)}
)} {/* Change History Modal */} {historyUser && (
e.stopPropagation()} >

Change History — {historyUser.name}

{historyLoading ? (
) : historyEntries.length === 0 ? (

No change history recorded.

) : ( {historyEntries.map((entry) => ( ))}
Date Change Changed by
{formatHistoryDate(entry.created_at)} {formatChangeDescription(entry)} {entry.changed_by_name || 'System'}
)}
)} {/* Agency Management */}

Agencies ({agencies.length})

{isSuperAdmin && (
setNewAgencyName(e.target.value)} placeholder="New agency name..." className="flex-grow p-2 border-2 border-oliver-azure rounded-[10px] focus:ring-2 focus:ring-oliver-azure focus:border-oliver-azure transition text-oliver-black" />
)}
{agencies.map(agency => (
{agency.name} {users.filter(u => u.agency_id === agency.id).length} user(s)
))} {agencies.length === 0 && (
No agencies created yet.
)}
); };