let currentUser = null; let currentOffice = null; document.addEventListener('DOMContentLoaded', async () => { if (!api.requireAuth()) return; currentUser = await api.getCurrentUser(); if (!currentUser) return; // Only Manager or Admin if (!['admin', 'manager'].includes(currentUser.role)) { window.location.href = '/'; return; } // Initialize UI populateHourSelect(); // Set default date to tomorrow const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); document.getElementById('testDateStart').valueAsDate = tomorrow; await loadOffices(); setupEventListeners(); }); async function loadOffices() { const select = document.getElementById('officeSelect'); const card = document.getElementById('officeSelectionCard'); const content = document.getElementById('settingsContent'); // Only Admins see the selector if (currentUser.role === 'admin') { card.style.display = 'block'; content.style.display = 'none'; // Hide until selected try { const response = await api.get('/api/offices'); if (response && response.ok) { const offices = await response.json(); offices.forEach(office => { const option = document.createElement('option'); option.value = office.id; option.textContent = office.name; select.appendChild(option); }); } } catch (e) { console.error(e); utils.showMessage('Errore caricamento uffici', 'error'); } } else { // Manager uses their own office card.style.display = 'none'; content.style.display = 'block'; if (currentUser.office_id) { await loadOfficeSettings(currentUser.office_id); } else { utils.showMessage('Nessun ufficio assegnato al manager', 'error'); } } } function populateHourSelect() { const select = document.getElementById('bookingWindowHour'); select.innerHTML = ''; for (let h = 0; h < 24; h++) { const option = document.createElement('option'); option.value = h; option.textContent = h.toString().padStart(2, '0'); select.appendChild(option); } } async function loadOfficeSettings(id) { const officeId = id; if (!officeId) { utils.showMessage('Nessun ufficio selezionato', 'error'); return; } try { const response = await api.get(`/api/offices/${officeId}`); if (!response.ok) throw new Error('Failed to load office'); const office = await response.json(); currentOffice = office; // Populate form document.getElementById('bookingWindowEnabled').checked = office.booking_window_enabled || false; document.getElementById('bookingWindowHour').value = office.booking_window_end_hour ?? 18; // Default 18 document.getElementById('bookingWindowMinute').value = office.booking_window_end_minute ?? 0; updateVisibility(); } catch (e) { console.error(e); utils.showMessage('Errore nel caricamento impostazioni', 'error'); } } function updateVisibility() { const enabled = document.getElementById('bookingWindowEnabled').checked; document.getElementById('cutoffTimeGroup').style.display = enabled ? 'block' : 'none'; } function setupEventListeners() { // Office Select document.getElementById('officeSelect').addEventListener('change', (e) => { const id = e.target.value; if (id) { document.getElementById('settingsContent').style.display = 'block'; loadOfficeSettings(id); } else { document.getElementById('settingsContent').style.display = 'none'; } }); // Toggle visibility document.getElementById('bookingWindowEnabled').addEventListener('change', updateVisibility); // Save Settings document.getElementById('scheduleForm').addEventListener('submit', async (e) => { e.preventDefault(); if (!currentOffice) return; const data = { booking_window_enabled: document.getElementById('bookingWindowEnabled').checked, booking_window_end_hour: parseInt(document.getElementById('bookingWindowHour').value), booking_window_end_minute: parseInt(document.getElementById('bookingWindowMinute').value) }; try { const res = await api.put(`/api/offices/${currentOffice.id}`, data); if (res) { utils.showMessage('Impostazioni salvate con successo', 'success'); currentOffice = res; } } catch (e) { utils.showMessage('Errore nel salvataggio', 'error'); } }); // Test Tools // Test Tools document.getElementById('runAllocationBtn').addEventListener('click', async () => { if (!confirm('Sei sicuro di voler avviare l\'assegnazione ORA? Questo potrebbe sovrascrivere le assegnazioni esistenti per la data selezionata.')) return; const dateStart = document.getElementById('testDateStart').value; const dateEnd = document.getElementById('testDateEnd').value; if (!dateStart) return utils.showMessage('Seleziona una data di inizio', 'error'); let start = new Date(dateStart); let end = dateEnd ? new Date(dateEnd) : new Date(dateStart); if (end < start) { return utils.showMessage('La data di fine deve essere successiva alla data di inizio', 'error'); } let current = new Date(start); let successCount = 0; let errorCount = 0; utils.showMessage('Avvio assegnazione...', 'success'); while (current <= end) { const dateStr = utils.formatDate(current); try { await api.post('/api/parking/run-allocation', { date: dateStr, office_id: currentOffice.id }); successCount++; } catch (e) { console.error(`Error for ${dateStr}`, e); errorCount++; } current.setDate(current.getDate() + 1); } if (errorCount === 0) { utils.showMessage(`Assegnazione completata per ${successCount} giorni.`, 'success'); } else { utils.showMessage(`Completato con errori: ${successCount} successi, ${errorCount} errori.`, 'warning'); } }); document.getElementById('clearAssignmentsBtn').addEventListener('click', async () => { if (!confirm('ATTENZIONE: Stai per eliminare TUTTE le assegnazioni per il periodo selezionato. Procedere?')) return; const dateStart = document.getElementById('testDateStart').value; const dateEnd = document.getElementById('testDateEnd').value; if (!dateStart) return utils.showMessage('Seleziona una data di inizio', 'error'); let start = new Date(dateStart); let end = dateEnd ? new Date(dateEnd) : new Date(dateStart); if (end < start) { return utils.showMessage('La data di fine deve essere successiva alla data di inizio', 'error'); } let current = new Date(start); let totalRemoved = 0; utils.showMessage('Rimozione in corso...', 'warning'); // Loop is fine, but maybe redundant if we could batch clean? // Backend clear-assignments is per day. while (current <= end) { const dateStr = utils.formatDate(current); try { const res = await api.post('/api/parking/clear-assignments', { date: dateStr, office_id: currentOffice.id }); if (res && res.ok) { const data = await res.json(); totalRemoved += (data.count || 0); } } catch (e) { console.error(`Error clearing ${dateStr}`, e); } current.setDate(current.getDate() + 1); } utils.showMessage(`Operazione eseguita.`, 'warning'); }); const clearPresenceBtn = document.getElementById('clearPresenceBtn'); if (clearPresenceBtn) { clearPresenceBtn.addEventListener('click', async () => { if (!confirm('ATTENZIONE: Stai per eliminare TUTTI GLI STATI (Presente/Assente/ecc) e relative assegnazioni per tutti gli utenti dell\'ufficio nel periodo selezionato. \n\nQuesta azione รจ irreversibile. Procedere?')) return; const dateStart = document.getElementById('testDateStart').value; const dateEnd = document.getElementById('testDateEnd').value; if (!dateStart) return utils.showMessage('Seleziona una data di inizio', 'error'); // Validate office if (!currentOffice || !currentOffice.id) { return utils.showMessage('Errore: Nessun ufficio selezionato', 'error'); } const endDateVal = dateEnd || dateStart; utils.showMessage('Rimozione stati in corso...', 'warning'); try { const res = await api.post('/api/presence/admin/clear-office-presence', { start_date: dateStart, end_date: endDateVal, office_id: currentOffice.id }); if (res && res.ok) { const data = await res.json(); utils.showMessage(`Operazione completata. Rimossi ${data.count_presence} stati e ${data.count_parking} assegnazioni.`, 'success'); } else { const err = await res.json(); utils.showMessage('Errore: ' + (err.detail || 'Operazione fallita'), 'error'); } } catch (e) { console.error(e); utils.showMessage('Errore di comunicazione col server', 'error'); } }); } const testEmailBtn = document.getElementById('testEmailBtn'); if (testEmailBtn) { testEmailBtn.addEventListener('click', async () => { const dateVal = document.getElementById('testEmailDate').value; // Validate office if (!currentOffice || !currentOffice.id) { return utils.showMessage('Errore: Nessun ufficio selezionato', 'error'); } utils.showMessage('Invio mail di test in corso...', 'warning'); try { const res = await api.post('/api/parking/test-email', { date: dateVal || null, office_id: currentOffice.id }); if (res && res.status !== 403 && res.status !== 500 && res.ok !== false) { // API wrapper usually returns response object or parses JSON? // api.post returns response object if 200-299, but wrapper handles some. // Let's assume standard fetch response or check wrapper. // api.js Wrapper returns fetch Response. const data = await res.json(); if (data.success) { let msg = `Email inviata con successo per la data: ${data.date}.`; if (data.mode === 'FILE') { msg += ' (SMTP disabilitato: Loggato su file)'; } utils.showMessage(msg, 'success'); } else { utils.showMessage('Invio fallito. Controlla i log del server.', 'error'); } } else { const err = res ? await res.json() : {}; utils.showMessage('Errore: ' + (err.detail || 'Invio fallito'), 'error'); } } catch (e) { console.error(e); utils.showMessage('Errore di comunicazione col server', 'error'); } }); } }