feat: aggiunti: loggica random, tema scuro, correzioni mail, miglioramenti generali, cache;

This commit is contained in:
StefanoSalemi
2026-04-17 18:27:37 +02:00
parent a7ef46640d
commit 104ad53a9a
26 changed files with 861 additions and 216 deletions

View File

@@ -59,7 +59,7 @@ function renderOffices() {
const tbody = document.getElementById('officesBody');
if (offices.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" class="text-center">Nessun ufficio trovato</td></tr>';
tbody.innerHTML = '<tr><td colspan="5" class="text-center">Nessun gruppo trovato</td></tr>';
return;
}
@@ -72,7 +72,7 @@ function renderOffices() {
<td>${office.user_count || 0} utenti</td>
<td>
<button class="btn btn-sm btn-secondary" onclick="editOffice('${office.id}')">Modifica</button>
<button class="btn btn-sm btn-danger" onclick="deleteOffice('${office.id}')" ${office.user_count > 0 ? 'title="Impossibile eliminare uffici con utenti" disabled' : ''}>Elimina</button>
<button class="btn btn-sm btn-danger" onclick="deleteOffice('${office.id}')" ${office.user_count > 0 ? 'title="Impossibile eliminare gruppi con utenti" disabled' : ''}>Elimina</button>
</td>
</tr>
`;
@@ -103,30 +103,30 @@ async function editOffice(officeId) {
document.getElementById('officeCutoffHour').value = office.booking_window_end_hour != null ? office.booking_window_end_hour : 18;
document.getElementById('officeCutoffMinute').value = office.booking_window_end_minute != null ? office.booking_window_end_minute : 0;
openModal('Modifica Ufficio');
openModal('Modifica Gruppo');
}
async function deleteOffice(officeId) {
const office = offices.find(o => o.id === officeId);
if (!office) return;
if (!confirm(`Eliminare l'ufficio "${office.name}"?`)) return;
if (!confirm(`Eliminare il gruppo "${office.name}"?`)) return;
const response = await api.delete(`/api/offices/${officeId}`);
if (response && response.ok) {
utils.showMessage('Ufficio eliminato', 'success');
utils.showMessage('Gruppo eliminato', 'success');
api.invalidateCache('/api/offices'); // Clear cache
await loadOffices();
} else {
const error = await response.json();
utils.showMessage(error.detail || 'Impossibile eliminare l\'ufficio', 'error');
utils.showMessage(error.detail || 'Impossibile eliminare il gruppo', 'error');
}
}
function setupEventListeners() {
// Add button
document.getElementById('addOfficeBtn').addEventListener('click', () => {
openModal('Nuovo Ufficio');
openModal('Nuovo Gruppo');
});
// Modal close
@@ -177,7 +177,7 @@ async function handleOfficeSubmit(e) {
if (response && response.ok) {
closeModal();
utils.showMessage(officeId ? 'Ufficio aggiornato' : 'Ufficio creato', 'success');
utils.showMessage(officeId ? 'Gruppo aggiornato' : 'Gruppo creato', 'success');
api.invalidateCache('/api/offices'); // Clear cache
await loadOffices();
} else {

View File

@@ -129,7 +129,7 @@ async function editUser(userId) {
// Populate office dropdown
const officeSelect = document.getElementById('editOffice');
officeSelect.innerHTML = '<option value="">Nessun ufficio</option>';
officeSelect.innerHTML = '<option value="">Nessun gruppo</option>';
offices.forEach(o => {
const option = document.createElement('option');
option.value = o.id;

View File

@@ -42,6 +42,20 @@ const ICONS = {
settings: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>`,
sun: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>`,
moon: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>`
};
@@ -50,8 +64,8 @@ const NAV_ITEMS = [
{ href: '/team-calendar', icon: 'users', label: 'Calendario del team' },
{ href: '/team-rules', icon: 'rules', label: 'Regole parcheggio', roles: ['admin', 'manager'] },
{ href: '/admin/users', icon: 'user', label: 'Gestione Utenti', roles: ['admin'] },
{ href: '/admin/offices', icon: 'building', label: 'Gestione Uffici', roles: ['admin'] },
{ href: '/parking-settings', icon: 'settings', label: 'Impostazioni Ufficio', roles: ['admin', 'manager'] }
{ href: '/admin/offices', icon: 'building', label: 'Gestione Gruppi', roles: ['admin'] },
{ href: '/parking-settings', icon: 'settings', label: 'Impostazioni Gruppi', roles: ['admin', 'manager'] }
];
function getIcon(name) {
@@ -98,9 +112,10 @@ async function initNav() {
// Setup user menu (logout) & mobile menu
setupUserMenu();
setupMobileMenu();
setupThemeToggle();
// CHECK: Block access if user has no office (and is not admin)
// Admins are allowed to access "Gestione Uffici" even without an office
// Admins are allowed to access "Gestione Gruppi" even without an office
if (currentUser && !currentUser.office_id && currentUser.role !== 'admin') {
navContainer.innerHTML = ''; // Clear nav
@@ -124,14 +139,14 @@ async function initNav() {
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
</div>
<h2 style="margin-bottom: 1rem;">Ufficio non assegnato</h2>
<h2 style="margin-bottom: 1rem;">Gruppo non assegnato</h2>
<p class="text-secondary" style="margin-bottom: 1.5rem; line-height: 1.6;">
Il tuo account <strong>${currentUser.email}</strong> è attivo, ma non sei ancora stato assegnato a nessuno ufficio.
Il tuo account <strong>${currentUser.email}</strong> è attivo, ma non sei ancora stato assegnato a nessuno gruppo.
</p>
<div style="padding: 1.5rem; background: #f8fafc; border-radius: 8px; text-align: left;">
<div style="font-weight: 600; margin-bottom: 0.5rem; color: var(--text);">Cosa fare?</div>
<div style="font-size: 0.95rem; color: var(--text-secondary);">
Contatta l'amministratore di sistema per richiedere l'assegnazione al tuo ufficio di competenza.<br>
Contatta l'amministratore di sistema per richiedere l'assegnazione al tuo gruppo di competenza.<br>
<a href="mailto:s.salemi@sielte.it" style="color: var(--primary); text-decoration: none; font-weight: 500; margin-top: 0.5rem; display: inline-block;">s.salemi@sielte.it</a>
</div>
</div>
@@ -212,6 +227,35 @@ function setupUserMenu() {
}
}
function setupThemeToggle() {
// Apply immediate theme to avoid flash
const savedTheme = localStorage.getItem('theme') || 'system';
applyTheme(savedTheme);
// Watch for system theme changes if set to system
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (localStorage.getItem('theme') === 'system') {
applyTheme('system');
}
});
}
function applyTheme(theme) {
let isDark = false;
if (theme === 'system') {
isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
} else {
isDark = theme === 'dark';
}
if (isDark) {
document.documentElement.setAttribute('data-theme', 'dark');
} else {
document.documentElement.removeAttribute('data-theme');
}
localStorage.setItem('theme', theme);
}
// Export for use in other scripts
window.getIcon = getIcon;

View File

@@ -49,7 +49,7 @@ async function loadOffices() {
}
} catch (e) {
console.error(e);
utils.showMessage('Errore caricamento uffici', 'error');
utils.showMessage('Errore caricamento gruppi', 'error');
}
} else {
// Manager uses their own office
@@ -58,7 +58,7 @@ async function loadOffices() {
if (currentUser.office_id) {
await loadOfficeSettings(currentUser.office_id);
} else {
utils.showMessage('Nessun ufficio assegnato al manager', 'error');
utils.showMessage('Nessun gruppo assegnato al manager', 'error');
}
}
}
@@ -86,7 +86,7 @@ function populateHourSelect() {
async function loadOfficeSettings(id) {
const officeId = id;
if (!officeId) {
utils.showMessage('Nessun ufficio selezionato', 'error');
utils.showMessage('Nessun gruppo selezionato', 'error');
return;
}
@@ -98,6 +98,7 @@ async function loadOfficeSettings(id) {
currentOffice = office;
// Populate form
document.getElementById('assignmentModeSelect').value = office.assignment_mode || 'fairness';
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;
@@ -136,6 +137,7 @@ function setupEventListeners() {
if (!currentOffice) return;
const data = {
assignment_mode: document.getElementById('assignmentModeSelect').value,
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)
@@ -242,7 +244,7 @@ function setupEventListeners() {
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;
if (!confirm('ATTENZIONE: Stai per eliminare TUTTI GLI STATI (Presente/Assente/ecc) e relative assegnazioni per tutti gli utenti del gruppo nel periodo selezionato. \n\nQuesta azione è irreversibile. Procedere?')) return;
const dateStart = document.getElementById('testDateStart').value;
const dateEnd = document.getElementById('testDateEnd').value;
@@ -251,7 +253,7 @@ function setupEventListeners() {
// Validate office
if (!currentOffice || !currentOffice.id) {
return utils.showMessage('Errore: Nessun ufficio selezionato', 'error');
return utils.showMessage('Errore: Nessun gruppo selezionato', 'error');
}
const endDateVal = dateEnd || dateStart;
@@ -286,7 +288,7 @@ function setupEventListeners() {
// Validate office
if (!currentOffice || !currentOffice.id) {
return utils.showMessage('Errore: Nessun ufficio selezionato', 'error');
return utils.showMessage('Errore: Nessun gruppo selezionato', 'error');
}
utils.showMessage('Invio mail di test in corso...', 'warning');
@@ -329,7 +331,7 @@ function setupEventListeners() {
// Validate office
if (!currentOffice || !currentOffice.id) {
return utils.showMessage('Errore: Nessun ufficio selezionato', 'error');
return utils.showMessage('Errore: Nessun gruppo selezionato', 'error');
}
if (!dateVal) {

View File

@@ -175,11 +175,12 @@ function renderCalendar() {
return date >= start && date <= end;
});
const isClosed = isWeeklyClosed || isSpecificClosed;
const isClosed = isHoliday || isWeeklyClosed || isSpecificClosed;
if (isClosed) {
cell.classList.add('closed');
cell.title = "Ufficio Chiuso";
if (isHoliday) cell.title = "Festività";
else cell.title = "Gruppo Chiuso";
} else if (presence) {
cell.classList.add(`status-${presence.status}`);
}
@@ -418,10 +419,10 @@ function initParkingStatus() {
if (headerDisplay) headerDisplay.textContent = currentUser.office_name;
} else {
const nameDisplay = document.getElementById('statusOfficeName');
if (nameDisplay) nameDisplay.textContent = 'Tuo Ufficio';
if (nameDisplay) nameDisplay.textContent = 'Tuo Gruppo';
const headerDisplay = document.getElementById('currentOfficeDisplay');
if (headerDisplay) headerDisplay.textContent = 'Tuo Ufficio';
if (headerDisplay) headerDisplay.textContent = 'Tuo Gruppo';
}
}
@@ -455,6 +456,34 @@ async function loadDailyStatus() {
const officeId = currentUser.office_id;
const grid = document.getElementById('spotsGrid');
const badge = document.getElementById('spotsCountBadge');
// Check if it's a closing day
const dayOfWeek = statusDate.getDay();
const isHoliday = utils.isItalianHoliday(statusDate);
const isWeeklyClosed = weeklyClosingDays.includes(dayOfWeek);
const isSpecificClosed = specificClosingDays.some(d => {
const start = new Date(d.date);
const end = d.end_date ? new Date(d.end_date) : start;
start.setHours(0, 0, 0, 0); end.setHours(0, 0, 0, 0);
const check = new Date(statusDate); check.setHours(0, 0, 0, 0);
return check >= start && check <= end;
});
if (isHoliday || isWeeklyClosed || isSpecificClosed) {
if (grid) {
grid.innerHTML = `
<div style="width:100%; text-align:center; padding:1rem 1rem; color: var(--text-secondary); background: var(--bg-hover); border-radius: 8px;">
<div style="font-size: 1.2rem; font-weight: 500;">Ufficio Chiuso</div>
<div style="font-size: 0.9rem; margin-top: 0.5rem; opacity: 0.8;">Nessun parcheggio disponibile in questa data.</div>
</div>`;
}
if (badge) badge.style.display = 'none';
return;
} else {
if (badge) badge.style.display = 'inline-block';
}
// Keep grid height to avoid jump if possible, or just loading styling
if (grid) grid.innerHTML = '<div style="width:100%; text-align:center; padding:2rem; color:var(--text-secondary);">Caricamento...</div>';
@@ -505,10 +534,9 @@ function renderParkingStatus(assignments) {
// Colors: Free = Green (default), Occupied = Yellow (requested)
// Yellow palette: Border #eab308, bg #fefce8, text #a16207, icon #eab308
const borderColor = isFree ? '#22c55e' : '#eab308';
const bgColor = isFree ? '#f0fdf4' : '#fefce8';
const textColor = isFree ? '#15803d' : '#a16207';
const iconColor = isFree ? '#22c55e' : '#eab308';
const borderColor = isFree ? 'var(--spot-free-border)' : 'var(--spot-occ-border)';
const bgColor = isFree ? 'var(--spot-free-bg)' : 'var(--spot-occ-bg)';
const textColor = isFree ? 'var(--spot-free-text)' : 'var(--spot-occ-text)';
const el = document.createElement('div');
el.className = 'spot-card';
@@ -527,15 +555,49 @@ function renderParkingStatus(assignments) {
transition: all 0.2s;
`;
// New Car Icon (Front Facing Sedan style or similar simple shape)
// Using a cleaner SVG path
el.innerHTML = `
<div style="font-weight: 600; font-size: 1.1rem; color: #1f2937;">${spotName}</div>
<div style="font-weight: 600; font-size: 1.1rem; color: var(--text);">${spotName}</div>
<div style="color: ${textColor}; font-size: 0.85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;" title="${statusText}">
${statusText}
</div>
`;
if (isFree && (currentUser.role === 'admin' || currentUser.role === 'manager')) {
el.style.cursor = 'pointer';
el.addEventListener('mouseenter', () => el.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)');
el.addEventListener('mouseleave', () => el.style.boxShadow = 'none');
el.title = "Clicca per assegnare manualmente";
el.addEventListener('click', () => {
openAdminAssignModal(a.spot_id, spotName);
});
}
if (!isFree && (currentUser.role === 'admin' || currentUser.role === 'manager')) {
el.style.cursor = 'pointer';
el.addEventListener('mouseenter', () => el.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)');
el.addEventListener('mouseleave', () => el.style.boxShadow = 'none');
el.title = "Clicca per liberare questo posto";
el.addEventListener('click', async () => {
if (!confirm(`Vuoi liberare il posto ${spotName} occupato da ${statusText}?`)) return;
utils.showMessage('Rilascio in corso...', 'warning');
const response = await api.post('/api/parking/reassign-spot', {
assignment_id: a.id,
new_user_id: null
});
if (response && response.ok) {
utils.showMessage('Posto liberato con successo', 'success');
loadDailyStatus();
loadParkingAssignments();
renderCalendar();
} else {
const err = await response.json();
utils.showMessage(err.detail || 'Impossibile liberare il posto', 'error');
}
});
}
grid.appendChild(el);
});
@@ -569,6 +631,97 @@ function setupStatusListeners() {
loadDailyStatus();
}
});
setupAdminAssignModal();
}
// ----------------------------------------------------------------------------
// Admin Manual Assign Logic
// ----------------------------------------------------------------------------
let currentManualSpotId = null;
function setupAdminAssignModal() {
const closeBtn = document.getElementById('closeAdminAssignModal');
const cancelBtn = document.getElementById('cancelAdminAssign');
const form = document.getElementById('adminAssignForm');
const modal = document.getElementById('adminAssignSpotModal');
if (closeBtn) closeBtn.addEventListener('click', () => modal.style.display = 'none');
if (cancelBtn) cancelBtn.addEventListener('click', () => modal.style.display = 'none');
if (form) {
form.addEventListener('submit', async (e) => {
e.preventDefault();
const targetUserId = document.getElementById('adminAssignUser').value;
if (!targetUserId || !currentManualSpotId) return;
const dateStr = utils.formatDate(statusDate);
utils.showMessage('Assegnazione in corso...', 'warning');
const response = await api.post('/api/parking/manual-assign', {
date: dateStr,
user_id: targetUserId,
spot_id: currentManualSpotId,
office_id: currentUser.office_id
});
if (response && response.ok) {
utils.showMessage('Posto assegnato con successo', 'success');
modal.style.display = 'none';
loadDailyStatus(); // refresh parking status grid
// optionally refresh my presences if it affected the logged in admin
loadParkingAssignments();
} else {
const err = await response.json();
utils.showMessage(err.detail || 'Impossibile assegnare il parcheggio', 'error');
}
});
}
}
async function openAdminAssignModal(spotId, spotName) {
currentManualSpotId = spotId;
const modal = document.getElementById('adminAssignSpotModal');
const infoDisplay = document.getElementById('adminAssignSpotInfo');
const selectUser = document.getElementById('adminAssignUser');
const dateStr = utils.formatDate(statusDate);
infoDisplay.innerHTML = `Seleziona l'utente a cui assegnare il posto <strong>${spotName}</strong> per la giornata del <strong>${dateStr}</strong>.`;
selectUser.innerHTML = '<option value="">Caricamento...</option>';
modal.style.display = 'flex';
// Fetch team presences for the office effectively identifying people physically present but without parking
try {
const response = await api.get(`/api/presence/team?start_date=${dateStr}&end_date=${dateStr}&office_id=${currentUser.office_id}`);
if (response && response.ok) {
const result = await response.json();
const members = Array.isArray(result) ? result : [];
// Filter out people who already have parking
const availableMembers = members.filter(m => {
const presence = m.presences && m.presences.find(p => p.date === dateStr);
const hasParking = m.parking_dates && m.parking_dates.includes(dateStr);
return presence && presence.status === 'present' && !hasParking;
});
if (availableMembers.length === 0) {
selectUser.innerHTML = '<option value="">Nessun utente presente e senza parcheggio oggi...</option>';
} else {
selectUser.innerHTML = '<option value="">Seleziona utente...</option>';
availableMembers.forEach(user => {
const option = document.createElement('option');
option.value = user.id;
option.textContent = `${user.name}`;
selectUser.appendChild(option);
});
}
}
} catch (e) {
console.error("Errore fetch team presences:", e);
console.error(e);
selectUser.innerHTML = '<option value="">Errore caricamento utenti</option>';
}
}

View File

@@ -48,7 +48,7 @@ function updateOfficeDisplay() {
// If user is employee, show their office name directly
if (currentUser.role === 'employee') {
display.textContent = currentUser.office_name || "Mio Ufficio";
display.textContent = currentUser.office_name || "Mio Gruppo";
return;
}
@@ -63,10 +63,10 @@ function updateOfficeDisplay() {
// let text = option.textContent.split('(')[0].trim();
display.textContent = option.textContent;
} else {
display.textContent = "Tutti gli Uffici";
display.textContent = "Tutti i Gruppi";
}
} else {
display.textContent = "Tutti gli Uffici";
display.textContent = "Tutti i Gruppi";
}
}
@@ -312,7 +312,7 @@ function renderCalendar() {
// Build header row
const dayNames = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'];
let headerHtml = '<th>Nome</th><th>Ufficio</th>';
let headerHtml = '<th>Nome</th><th>Gruppo</th>';
for (let i = 0; i < dayCount; i++) {
const date = new Date(startDate);
@@ -348,8 +348,20 @@ function renderCalendar() {
let bodyHtml = '';
teamData.forEach(member => {
let nameHtml = member.name || 'Unknown';
if (member.ratio !== undefined && member.ratio !== null) {
nameHtml += `
<span style="margin-left: 6px; font-size: 0.8rem; color: var(--text-secondary); cursor: help;" title="(Giorni in cui hai avuto parcheggio) / (Giorni in sede)">
<svg style="vertical-align: text-bottom; margin-right: 2px;" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line>
</svg>
${member.ratio.toFixed(2)}
</span>
`;
}
bodyHtml += `<tr>
<td class="member-name">${member.name || 'Unknown'}</td>
<td class="member-name" style="text-align: left; padding-left: 1rem;">${nameHtml}</td>
<td class="member-manager">${member.office_name || '-'}</td>`;
for (let i = 0; i < dayCount; i++) {
@@ -384,7 +396,7 @@ function renderCalendar() {
// (Already have dateStr)
const memberRules = officeClosingRules[member.office_id];
let isClosed = false;
let isClosed = isHoliday;
if (memberRules) {
// Check weekly

View File

@@ -134,6 +134,7 @@ async function saveWeeklyClosingDays() {
await Promise.all(promises);
utils.showMessage('Giorni di chiusura aggiornati', 'success');
api.invalidateCache(`/api/offices/${currentOfficeId}/weekly-closing-days`);
await loadWeeklyClosingDays(currentOfficeId);
} catch (error) {
console.error(error);
@@ -176,6 +177,7 @@ async function loadClosingDays(officeId) {
async function addClosingDay(data) {
const response = await api.post(`/api/offices/${currentOfficeId}/closing-days`, data);
if (response && response.ok) {
api.invalidateCache(`/api/offices/${currentOfficeId}/closing-days`);
await loadClosingDays(currentOfficeId);
document.getElementById('closingDayModal').style.display = 'none';
document.getElementById('closingDayForm').reset();
@@ -189,6 +191,7 @@ 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) {
api.invalidateCache(`/api/offices/${currentOfficeId}/closing-days`);
await loadClosingDays(currentOfficeId);
}
}