/** * Admin Users Page * Manage all users in the system */ let currentUser = null; let users = []; let offices = []; let managedOfficesMap = {}; // user_id -> [office_ids] document.addEventListener('DOMContentLoaded', async () => { if (!api.requireAuth()) return; currentUser = await api.getCurrentUser(); if (!currentUser) return; // Only admins can access if (currentUser.role !== 'admin') { window.location.href = '/presence'; return; } await Promise.all([loadUsers(), loadOffices()]); await loadManagedOffices(); renderUsers(); setupEventListeners(); }); async function loadUsers() { const response = await api.get('/api/users'); if (response && response.ok) { users = await response.json(); } } async function loadOffices() { const response = await api.get('/api/offices'); if (response && response.ok) { offices = await response.json(); const select = document.getElementById('editOffice'); offices.forEach(office => { const option = document.createElement('option'); option.value = office.id; option.textContent = office.name; select.appendChild(option); }); } } async function loadManagedOffices() { // Load managed offices for all managers const response = await api.get('/api/offices/managers/list'); if (response && response.ok) { const managers = await response.json(); managedOfficesMap = {}; managers.forEach(m => { managedOfficesMap[m.id] = m.offices.map(o => o.id); }); } } function getManagedOfficeNames(userId) { const officeIds = managedOfficesMap[userId] || []; if (officeIds.length === 0) return '-'; return officeIds.map(id => { const office = offices.find(o => o.id === id); return office ? office.name : id; }).join(', '); } function renderUsers(filter = '') { const tbody = document.getElementById('usersBody'); const filtered = filter ? users.filter(u => u.name.toLowerCase().includes(filter.toLowerCase()) || u.email.toLowerCase().includes(filter.toLowerCase()) ) : users; if (filtered.length === 0) { tbody.innerHTML = 'No users found'; return; } tbody.innerHTML = filtered.map(user => { const office = offices.find(o => o.id === user.office_id); const isManager = user.role === 'manager'; const managedOffices = isManager ? getManagedOfficeNames(user.id) : '-'; return ` ${user.name} ${user.email} ${user.role} ${office ? office.name : '-'} ${managedOffices} ${isManager ? (user.manager_parking_quota || 0) : '-'} ${isManager ? (user.manager_spot_prefix || '-') : '-'} ${user.id !== currentUser.id ? ` ` : ''} `; }).join(''); } function editUser(userId) { const user = users.find(u => u.id === userId); if (!user) return; document.getElementById('userId').value = user.id; document.getElementById('editName').value = user.name; document.getElementById('editEmail').value = user.email; document.getElementById('editRole').value = user.role; document.getElementById('editOffice').value = user.office_id || ''; document.getElementById('editQuota').value = user.manager_parking_quota || 0; document.getElementById('editPrefix').value = user.manager_spot_prefix || ''; const isManager = user.role === 'manager'; // Show/hide manager fields based on role document.getElementById('quotaGroup').style.display = isManager ? 'block' : 'none'; document.getElementById('prefixGroup').style.display = isManager ? 'block' : 'none'; document.getElementById('managedOfficesGroup').style.display = isManager ? 'block' : 'none'; // Build managed offices checkboxes const checkboxContainer = document.getElementById('managedOfficesCheckboxes'); const userManagedOffices = managedOfficesMap[userId] || []; checkboxContainer.innerHTML = offices.map(office => ` `).join(''); document.getElementById('userModal').style.display = 'flex'; } async function deleteUser(userId) { const user = users.find(u => u.id === userId); if (!user) return; if (!confirm(`Delete user "${user.name}"? This cannot be undone.`)) return; const response = await api.delete(`/api/users/${userId}`); if (response && response.ok) { await loadUsers(); await loadManagedOffices(); renderUsers(); utils.showMessage('User deleted', 'success'); } else { const error = await response.json(); utils.showMessage(error.detail || 'Failed to delete user', 'error'); } } function setupEventListeners() { // Search document.getElementById('searchInput').addEventListener('input', (e) => { renderUsers(e.target.value); }); // Modal document.getElementById('closeUserModal').addEventListener('click', () => { document.getElementById('userModal').style.display = 'none'; }); document.getElementById('cancelUser').addEventListener('click', () => { document.getElementById('userModal').style.display = 'none'; }); // Role change shows/hides manager fields document.getElementById('editRole').addEventListener('change', (e) => { const isManager = e.target.value === 'manager'; document.getElementById('quotaGroup').style.display = isManager ? 'block' : 'none'; document.getElementById('prefixGroup').style.display = isManager ? 'block' : 'none'; document.getElementById('managedOfficesGroup').style.display = isManager ? 'block' : 'none'; }); // Form submit document.getElementById('userForm').addEventListener('submit', async (e) => { e.preventDefault(); const userId = document.getElementById('userId').value; const role = document.getElementById('editRole').value; const data = { name: document.getElementById('editName').value, role: role, office_id: document.getElementById('editOffice').value || null }; if (role === 'manager') { data.manager_parking_quota = parseInt(document.getElementById('editQuota').value) || 0; data.manager_spot_prefix = document.getElementById('editPrefix').value.toUpperCase() || null; } const response = await api.put(`/api/users/${userId}`, data); if (response && response.ok) { // Update managed offices if manager if (role === 'manager') { await updateManagedOffices(userId); } document.getElementById('userModal').style.display = 'none'; await loadUsers(); await loadManagedOffices(); renderUsers(); utils.showMessage('User updated', 'success'); } else { const error = await response.json(); utils.showMessage(error.detail || 'Failed to update user', 'error'); } }); utils.setupModalClose('userModal'); } async function updateManagedOffices(userId) { // Get currently selected offices const checkboxes = document.querySelectorAll('input[name="managedOffice"]:checked'); const selectedOfficeIds = Array.from(checkboxes).map(cb => cb.value); // Get current managed offices const currentOfficeIds = managedOfficesMap[userId] || []; // Find offices to add and remove const toAdd = selectedOfficeIds.filter(id => !currentOfficeIds.includes(id)); const toRemove = currentOfficeIds.filter(id => !selectedOfficeIds.includes(id)); // Add new memberships for (const officeId of toAdd) { await api.post(`/api/offices/${officeId}/managers`, { user_id: userId }); } // Remove old memberships for (const officeId of toRemove) { await api.delete(`/api/offices/${officeId}/managers/${userId}`); } } // Make functions globally accessible window.editUser = editUser; window.deleteUser = deleteUser;