318 lines
12 KiB
JavaScript
318 lines
12 KiB
JavaScript
|
|
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');
|
|
}
|
|
});
|
|
}
|
|
}
|