384 lines
14 KiB
JavaScript
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}')">
|
|
×
|
|
</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}')">
|
|
×
|
|
</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}')">
|
|
×
|
|
</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;
|