Files
org-parking/frontend/js/team-rules.js
2026-01-13 11:20:12 +01:00

384 lines
14 KiB
JavaScript

/**
* Team Rules Page
* Manage closing days, guarantees, and exclusions
* Office-centric model
*/
let currentUser = null;
let offices = [];
let currentOfficeId = null;
let officeUsers = [];
let currentWeeklyClosingDays = [];
document.addEventListener('DOMContentLoaded', async () => {
currentUser = await api.requireAuth();
if (!currentUser) return;
// Only admins and managers can access this page
if (currentUser.role !== 'admin' && currentUser.role !== 'manager') {
window.location.href = '/presence';
return;
}
await loadOffices();
setupEventListeners();
});
async function loadOffices() {
const select = document.getElementById('officeSelect');
const card = document.getElementById('officeSelectionCard');
// Only Admins can see the office selector
if (currentUser.role !== 'admin') {
if (card) card.style.display = 'none';
}
const response = await api.get('/api/offices');
if (response && response.ok) {
offices = await response.json();
let filteredOffices = offices;
if (currentUser.role === 'manager') {
// Manager only sees their own office
if (currentUser.office_id) {
filteredOffices = offices.filter(o => o.id === currentUser.office_id);
} else {
filteredOffices = [];
}
}
filteredOffices.forEach(office => {
const option = document.createElement('option');
option.value = office.id;
option.textContent = office.name;
select.appendChild(option);
});
// Auto-select for managers
if (currentUser.role === 'manager' && filteredOffices.length === 1) {
select.value = filteredOffices[0].id;
loadOfficeRules(filteredOffices[0].id);
}
}
}
async function loadOfficeRules(officeId) {
if (!officeId) {
document.getElementById('rulesContent').style.display = 'none';
document.getElementById('noOfficeMessage').style.display = 'block';
return;
}
currentOfficeId = officeId;
document.getElementById('rulesContent').style.display = 'block';
document.getElementById('noOfficeMessage').style.display = 'none';
// Load users for this office (for dropdowns)
await loadOfficeUsers(officeId);
await Promise.all([
loadWeeklyClosingDays(officeId),
loadClosingDays(officeId),
loadGuarantees(officeId),
loadExclusions(officeId)
]);
}
async function loadOfficeUsers(officeId) {
const response = await api.get(`/api/offices/${officeId}/users`);
if (response && response.ok) {
officeUsers = await response.json();
}
}
// Weekly Closing Days
async function loadWeeklyClosingDays(officeId) {
const response = await api.get(`/api/offices/${officeId}/weekly-closing-days`);
if (response && response.ok) {
const days = await response.json();
currentWeeklyClosingDays = days;
const activeWeekdays = days.map(d => d.weekday);
document.querySelectorAll('#weeklyClosingDays input[type="checkbox"]').forEach(cb => {
const weekday = parseInt(cb.dataset.weekday);
cb.checked = activeWeekdays.includes(weekday);
});
}
}
async function saveWeeklyClosingDays() {
const btn = document.getElementById('saveWeeklyClosingDaysBtn');
if (!btn) return;
const originalText = btn.textContent;
btn.disabled = true;
btn.textContent = 'Salvataggio...';
try {
const promises = [];
const checkboxes = document.querySelectorAll('#weeklyClosingDays input[type="checkbox"]');
for (const cb of checkboxes) {
const weekday = parseInt(cb.dataset.weekday);
const isChecked = cb.checked;
const existingEntry = currentWeeklyClosingDays.find(d => d.weekday === weekday);
if (isChecked && !existingEntry) {
// Add
promises.push(api.post(`/api/offices/${currentOfficeId}/weekly-closing-days`, { weekday }));
} else if (!isChecked && existingEntry) {
// Remove
promises.push(api.delete(`/api/offices/${currentOfficeId}/weekly-closing-days/${existingEntry.id}`));
}
}
await Promise.all(promises);
utils.showMessage('Giorni di chiusura aggiornati', 'success');
await loadWeeklyClosingDays(currentOfficeId);
} catch (error) {
console.error(error);
utils.showMessage('Errore durante il salvataggio', 'error');
} finally {
btn.disabled = false;
btn.textContent = originalText;
}
}
// Closing Days
async function loadClosingDays(officeId) {
const response = await api.get(`/api/offices/${officeId}/closing-days`);
const container = document.getElementById('closingDaysList');
if (response && response.ok) {
const days = await response.json();
if (days.length === 0) {
container.innerHTML = '<p class="text-muted">Nessun giorno di chiusura specifico.</p>';
return;
}
container.innerHTML = days.map(day => `
<div class="rule-item">
<div class="rule-info">
<strong>${utils.formatDateDisplay(day.date)}${day.end_date ? ' - ' + utils.formatDateDisplay(day.end_date) : ''}</strong>
${day.reason ? `<span class="rule-note">${day.reason}</span>` : ''}
</div>
<button class="btn-icon btn-danger" onclick="deleteClosingDay('${day.id}')">
&times;
</button>
</div>
`).join('');
}
}
async function addClosingDay(data) {
const response = await api.post(`/api/offices/${currentOfficeId}/closing-days`, data);
if (response && response.ok) {
await loadClosingDays(currentOfficeId);
document.getElementById('closingDayModal').style.display = 'none';
document.getElementById('closingDayForm').reset();
} else {
const error = await response.json();
alert(error.detail || 'Impossibile aggiungere il giorno di chiusura');
}
}
async function deleteClosingDay(id) {
if (!confirm('Eliminare questo giorno di chiusura?')) return;
const response = await api.delete(`/api/offices/${currentOfficeId}/closing-days/${id}`);
if (response && response.ok) {
await loadClosingDays(currentOfficeId);
}
}
// Guarantees
async function loadGuarantees(officeId) {
const response = await api.get(`/api/offices/${officeId}/guarantees`);
const container = document.getElementById('guaranteesList');
if (response && response.ok) {
const guarantees = await response.json();
if (guarantees.length === 0) {
container.innerHTML = '<p class="text-muted">Nessuna garanzia di parcheggio attiva.</p>';
return;
}
container.innerHTML = guarantees.map(g => `
<div class="rule-item">
<div class="rule-info">
<strong>${g.user_name || 'Utente sconosciuto'}</strong>
<span class="rule-dates">
${g.start_date ? 'Dal ' + utils.formatDateDisplay(g.start_date) : 'Da sempre'}
${g.end_date ? ' al ' + utils.formatDateDisplay(g.end_date) : ''}
</span>
${g.notes ? `<span class="rule-note">${g.notes}</span>` : ''}
</div>
<button class="btn-icon btn-danger" onclick="deleteGuarantee('${g.id}')">
&times;
</button>
</div>
`).join('');
}
}
async function addGuarantee(data) {
const response = await api.post(`/api/offices/${currentOfficeId}/guarantees`, data);
if (response && response.ok) {
await loadGuarantees(currentOfficeId);
document.getElementById('guaranteeModal').style.display = 'none';
document.getElementById('guaranteeForm').reset();
} else {
const error = await response.json();
alert(error.detail || 'Impossibile aggiungere la garanzia');
}
}
async function deleteGuarantee(id) {
if (!confirm('Eliminare questa garanzia?')) return;
const response = await api.delete(`/api/offices/${currentOfficeId}/guarantees/${id}`);
if (response && response.ok) {
await loadGuarantees(currentOfficeId);
}
}
// Exclusions
async function loadExclusions(officeId) {
const response = await api.get(`/api/offices/${officeId}/exclusions`);
const container = document.getElementById('exclusionsList');
if (response && response.ok) {
const exclusions = await response.json();
if (exclusions.length === 0) {
container.innerHTML = '<p class="text-muted">Nessuna esclusione attiva.</p>';
return;
}
container.innerHTML = exclusions.map(e => `
<div class="rule-item">
<div class="rule-info">
<strong>${e.user_name || 'Utente sconosciuto'}</strong>
<span class="rule-dates">
${e.start_date ? 'Dal ' + utils.formatDateDisplay(e.start_date) : 'Da sempre'}
${e.end_date ? ' al ' + utils.formatDateDisplay(e.end_date) : ''}
</span>
${e.notes ? `<span class="rule-note">${e.notes}</span>` : ''}
</div>
<button class="btn-icon btn-danger" onclick="deleteExclusion('${e.id}')">
&times;
</button>
</div>
`).join('');
}
}
async function addExclusion(data) {
const response = await api.post(`/api/offices/${currentOfficeId}/exclusions`, data);
if (response && response.ok) {
await loadExclusions(currentOfficeId);
document.getElementById('exclusionModal').style.display = 'none';
document.getElementById('exclusionForm').reset();
} else {
const error = await response.json();
alert(error.detail || 'Impossibile aggiungere l\'esclusione');
}
}
async function deleteExclusion(id) {
if (!confirm('Eliminare questa esclusione?')) return;
const response = await api.delete(`/api/offices/${currentOfficeId}/exclusions/${id}`);
if (response && response.ok) {
await loadExclusions(currentOfficeId);
}
}
function populateUserSelects() {
const selects = ['guaranteeUser', 'exclusionUser'];
selects.forEach(id => {
const select = document.getElementById(id);
const currentVal = select.value;
select.innerHTML = '<option value="">Seleziona utente...</option>';
officeUsers.forEach(user => {
const option = document.createElement('option');
option.value = user.id;
option.textContent = user.name;
select.appendChild(option);
});
if (currentVal) select.value = currentVal;
});
}
function setupEventListeners() {
// Office select
document.getElementById('officeSelect').addEventListener('change', (e) => {
loadOfficeRules(e.target.value);
});
// Save Weekly closing days
const saveBtn = document.getElementById('saveWeeklyClosingDaysBtn');
if (saveBtn) {
saveBtn.addEventListener('click', saveWeeklyClosingDays);
}
// Modals
const modals = [
{ id: 'closingDayModal', btn: 'addClosingDayBtn', close: 'closeClosingDayModal', cancel: 'cancelClosingDay' },
{ id: 'guaranteeModal', btn: 'addGuaranteeBtn', close: 'closeGuaranteeModal', cancel: 'cancelGuarantee' },
{ id: 'exclusionModal', btn: 'addExclusionBtn', close: 'closeExclusionModal', cancel: 'cancelExclusion' }
];
modals.forEach(m => {
document.getElementById(m.btn).addEventListener('click', () => {
if (m.id !== 'closingDayModal') populateUserSelects();
document.getElementById(m.id).style.display = 'flex';
});
document.getElementById(m.close).addEventListener('click', () => {
document.getElementById(m.id).style.display = 'none';
});
document.getElementById(m.cancel).addEventListener('click', () => {
document.getElementById(m.id).style.display = 'none';
});
utils.setupModalClose(m.id);
});
// Forms
document.getElementById('closingDayForm').addEventListener('submit', (e) => {
e.preventDefault();
addClosingDay({
date: document.getElementById('closingDate').value,
end_date: document.getElementById('closingEndDate').value || null,
reason: document.getElementById('closingReason').value || null
});
});
document.getElementById('guaranteeForm').addEventListener('submit', (e) => {
e.preventDefault();
addGuarantee({
user_id: document.getElementById('guaranteeUser').value,
start_date: document.getElementById('guaranteeStartDate').value || null,
end_date: document.getElementById('guaranteeEndDate').value || null,
notes: document.getElementById('guaranteeNotes').value || null
});
});
document.getElementById('exclusionForm').addEventListener('submit', (e) => {
e.preventDefault();
addExclusion({
user_id: document.getElementById('exclusionUser').value,
start_date: document.getElementById('exclusionStartDate').value || null,
end_date: document.getElementById('exclusionEndDate').value || null,
notes: document.getElementById('exclusionNotes').value || null
});
});
}
// Global functions
window.deleteClosingDay = deleteClosingDay;
window.deleteGuarantee = deleteGuarantee;
window.deleteExclusion = deleteExclusion;