/**
* 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;