/** * Team Rules Page * Manage closing days, parking guarantees, and exclusions * * Rules are set at manager level for their parking pool. */ let currentUser = null; let selectedManagerId = null; let managerUsers = []; document.addEventListener('DOMContentLoaded', async () => { currentUser = await api.requireAuth(); if (!currentUser) return; // Only managers and admins can access if (currentUser.role === 'employee') { window.location.href = '/presence'; return; } await loadManagers(); setupEventListeners(); }); async function loadManagers() { const response = await api.get('/api/managers'); if (response && response.ok) { const managers = await response.json(); const select = document.getElementById('managerSelect'); // Filter to managers this user can see let filteredManagers = managers; if (currentUser.role === 'manager') { // Manager only sees themselves filteredManagers = managers.filter(m => m.id === currentUser.id); } // Show managers in dropdown let totalManagers = 0; let firstManagerId = null; filteredManagers.forEach(manager => { const option = document.createElement('option'); option.value = manager.id; // Show manager name with user count and parking quota const userCount = manager.managed_user_count || 0; const quota = manager.parking_quota || 0; option.textContent = `${manager.name} (${userCount} users, ${quota} spots)`; select.appendChild(option); totalManagers++; if (!firstManagerId) firstManagerId = manager.id; }); // Auto-select if only one manager if (totalManagers === 1 && firstManagerId) { select.value = firstManagerId; await selectManager(firstManagerId); } } } async function selectManager(managerId) { selectedManagerId = managerId; if (!managerId) { document.getElementById('rulesContent').style.display = 'none'; document.getElementById('noManagerMessage').style.display = 'block'; return; } document.getElementById('rulesContent').style.display = 'block'; document.getElementById('noManagerMessage').style.display = 'none'; await Promise.all([ loadWeeklyClosingDays(), loadClosingDays(), loadGuarantees(), loadExclusions(), loadManagerUsers() ]); } async function loadWeeklyClosingDays() { const response = await api.get(`/api/managers/${selectedManagerId}/weekly-closing-days`); if (response && response.ok) { const days = await response.json(); const weekdays = days.map(d => d.weekday); // Update checkboxes document.querySelectorAll('#weeklyClosingDays input[type="checkbox"]').forEach(cb => { const weekday = parseInt(cb.dataset.weekday); cb.checked = weekdays.includes(weekday); }); } } async function loadManagerUsers() { const response = await api.get(`/api/managers/${selectedManagerId}/users`); if (response && response.ok) { managerUsers = await response.json(); updateUserSelects(); } } function updateUserSelects() { ['guaranteeUser', 'exclusionUser'].forEach(selectId => { const select = document.getElementById(selectId); select.innerHTML = ''; managerUsers.forEach(user => { const option = document.createElement('option'); option.value = user.id; option.textContent = user.name; select.appendChild(option); }); }); } async function loadClosingDays() { const response = await api.get(`/api/managers/${selectedManagerId}/closing-days`); const container = document.getElementById('closingDaysList'); if (response && response.ok) { const days = await response.json(); if (days.length === 0) { container.innerHTML = ''; return; } container.innerHTML = days.map(day => `
${utils.formatDateDisplay(day.date)} ${day.reason ? `${day.reason}` : ''}
`).join(''); } } function formatDateRange(startDate, endDate) { if (!startDate && !endDate) return ''; if (startDate && !endDate) return `From ${utils.formatDateDisplay(startDate)}`; if (!startDate && endDate) return `Until ${utils.formatDateDisplay(endDate)}`; return `${utils.formatDateDisplay(startDate)} - ${utils.formatDateDisplay(endDate)}`; } async function loadGuarantees() { const response = await api.get(`/api/managers/${selectedManagerId}/guarantees`); const container = document.getElementById('guaranteesList'); if (response && response.ok) { const guarantees = await response.json(); if (guarantees.length === 0) { container.innerHTML = ''; return; } container.innerHTML = guarantees.map(g => { const dateRange = formatDateRange(g.start_date, g.end_date); return `
${g.user_name} ${dateRange ? `${dateRange}` : ''}
`}).join(''); } } async function loadExclusions() { const response = await api.get(`/api/managers/${selectedManagerId}/exclusions`); const container = document.getElementById('exclusionsList'); if (response && response.ok) { const exclusions = await response.json(); if (exclusions.length === 0) { container.innerHTML = ''; return; } container.innerHTML = exclusions.map(e => { const dateRange = formatDateRange(e.start_date, e.end_date); return `
${e.user_name} ${dateRange ? `${dateRange}` : ''}
`}).join(''); } } // Delete functions async function deleteClosingDay(id) { if (!confirm('Delete this closing day?')) return; const response = await api.delete(`/api/managers/${selectedManagerId}/closing-days/${id}`); if (response && response.ok) { await loadClosingDays(); } } async function deleteGuarantee(id) { if (!confirm('Remove this parking guarantee?')) return; const response = await api.delete(`/api/managers/${selectedManagerId}/guarantees/${id}`); if (response && response.ok) { await loadGuarantees(); } } async function deleteExclusion(id) { if (!confirm('Remove this parking exclusion?')) return; const response = await api.delete(`/api/managers/${selectedManagerId}/exclusions/${id}`); if (response && response.ok) { await loadExclusions(); } } function setupEventListeners() { // Manager selection document.getElementById('managerSelect').addEventListener('change', (e) => { selectManager(e.target.value); }); // Weekly closing day checkboxes document.querySelectorAll('#weeklyClosingDays input[type="checkbox"]').forEach(cb => { cb.addEventListener('change', async (e) => { const weekday = parseInt(e.target.dataset.weekday); if (e.target.checked) { // Add weekly closing day const response = await api.post(`/api/managers/${selectedManagerId}/weekly-closing-days`, { weekday }); if (!response || !response.ok) { e.target.checked = false; const error = await response.json(); alert(error.detail || 'Failed to add weekly closing day'); } } else { // Remove weekly closing day - need to find the ID first const getResponse = await api.get(`/api/managers/${selectedManagerId}/weekly-closing-days`); if (getResponse && getResponse.ok) { const days = await getResponse.json(); const day = days.find(d => d.weekday === weekday); if (day) { const deleteResponse = await api.delete(`/api/managers/${selectedManagerId}/weekly-closing-days/${day.id}`); if (!deleteResponse || !deleteResponse.ok) { e.target.checked = true; } } } } }); }); // Modal openers document.getElementById('addClosingDayBtn').addEventListener('click', () => { document.getElementById('closingDayForm').reset(); document.getElementById('closingDayModal').style.display = 'flex'; }); document.getElementById('addGuaranteeBtn').addEventListener('click', () => { document.getElementById('guaranteeForm').reset(); document.getElementById('guaranteeModal').style.display = 'flex'; }); document.getElementById('addExclusionBtn').addEventListener('click', () => { document.getElementById('exclusionForm').reset(); document.getElementById('exclusionModal').style.display = 'flex'; }); // Modal closers ['closeClosingDayModal', 'cancelClosingDay'].forEach(id => { document.getElementById(id).addEventListener('click', () => { document.getElementById('closingDayModal').style.display = 'none'; }); }); ['closeGuaranteeModal', 'cancelGuarantee'].forEach(id => { document.getElementById(id).addEventListener('click', () => { document.getElementById('guaranteeModal').style.display = 'none'; }); }); ['closeExclusionModal', 'cancelExclusion'].forEach(id => { document.getElementById(id).addEventListener('click', () => { document.getElementById('exclusionModal').style.display = 'none'; }); }); // Form submissions document.getElementById('closingDayForm').addEventListener('submit', async (e) => { e.preventDefault(); const data = { date: document.getElementById('closingDate').value, reason: document.getElementById('closingReason').value || null }; const response = await api.post(`/api/managers/${selectedManagerId}/closing-days`, data); if (response && response.ok) { document.getElementById('closingDayModal').style.display = 'none'; await loadClosingDays(); } else { const error = await response.json(); alert(error.detail || 'Failed to add closing day'); } }); document.getElementById('guaranteeForm').addEventListener('submit', async (e) => { e.preventDefault(); const data = { user_id: document.getElementById('guaranteeUser').value, start_date: document.getElementById('guaranteeStartDate').value || null, end_date: document.getElementById('guaranteeEndDate').value || null }; const response = await api.post(`/api/managers/${selectedManagerId}/guarantees`, data); if (response && response.ok) { document.getElementById('guaranteeModal').style.display = 'none'; await loadGuarantees(); } else { const error = await response.json(); alert(error.detail || 'Failed to add guarantee'); } }); document.getElementById('exclusionForm').addEventListener('submit', async (e) => { e.preventDefault(); const data = { user_id: document.getElementById('exclusionUser').value, start_date: document.getElementById('exclusionStartDate').value || null, end_date: document.getElementById('exclusionEndDate').value || null }; const response = await api.post(`/api/managers/${selectedManagerId}/exclusions`, data); if (response && response.ok) { document.getElementById('exclusionModal').style.display = 'none'; await loadExclusions(); } else { const error = await response.json(); alert(error.detail || 'Failed to add exclusion'); } }); // Modal background clicks utils.setupModalClose('closingDayModal'); utils.setupModalClose('guaranteeModal'); utils.setupModalClose('exclusionModal'); } // Make delete functions globally accessible window.deleteClosingDay = deleteClosingDay; window.deleteGuarantee = deleteGuarantee; window.deleteExclusion = deleteExclusion;