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