Primo commit
This commit is contained in:
@@ -8,14 +8,31 @@ let currentDate = new Date();
|
||||
let presenceData = {};
|
||||
let parkingData = {};
|
||||
let currentAssignmentId = null;
|
||||
let weeklyClosingDays = [];
|
||||
let specificClosingDays = [];
|
||||
let statusDate = new Date();
|
||||
let statusViewMode = 'daily';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
currentUser = await api.requireAuth();
|
||||
if (!currentUser) return;
|
||||
|
||||
await Promise.all([loadPresences(), loadParkingAssignments()]);
|
||||
await Promise.all([loadPresences(), loadParkingAssignments(), loadClosingDays()]);
|
||||
|
||||
// Initialize Modal Logic
|
||||
ModalLogic.init({
|
||||
onMarkPresence: handleMarkPresence,
|
||||
onClearPresence: handleClearPresence,
|
||||
onReleaseParking: handleReleaseParking,
|
||||
onReassignParking: handleReassignParking
|
||||
});
|
||||
|
||||
renderCalendar();
|
||||
setupEventListeners();
|
||||
|
||||
// Initialize Parking Status
|
||||
initParkingStatus();
|
||||
setupStatusListeners();
|
||||
});
|
||||
|
||||
async function loadPresences() {
|
||||
@@ -56,10 +73,32 @@ async function loadParkingAssignments() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function loadClosingDays() {
|
||||
if (!currentUser.office_id) return;
|
||||
try {
|
||||
const [weeklyRes, specificRes] = await Promise.all([
|
||||
api.get(`/api/offices/${currentUser.office_id}/weekly-closing-days`),
|
||||
api.get(`/api/offices/${currentUser.office_id}/closing-days`)
|
||||
]);
|
||||
|
||||
if (weeklyRes && weeklyRes.ok) {
|
||||
const days = await weeklyRes.json();
|
||||
weeklyClosingDays = days.map(d => d.weekday);
|
||||
}
|
||||
|
||||
if (specificRes && specificRes.ok) {
|
||||
specificClosingDays = await specificRes.json();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error loading closing days:', e);
|
||||
}
|
||||
}
|
||||
|
||||
function renderCalendar() {
|
||||
const year = currentDate.getFullYear();
|
||||
const month = currentDate.getMonth();
|
||||
const weekStartDay = currentUser.week_start_day || 0; // 0=Sunday, 1=Monday
|
||||
const weekStartDay = currentUser.week_start_day || 1; // 0=Sunday, 1=Monday (default to Monday)
|
||||
|
||||
// Update month header
|
||||
document.getElementById('currentMonth').textContent = `${utils.getMonthName(month)} ${year}`;
|
||||
@@ -78,7 +117,7 @@ function renderCalendar() {
|
||||
grid.innerHTML = '';
|
||||
|
||||
// Day headers - reorder based on week start day
|
||||
const allDayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
const allDayNames = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'];
|
||||
const dayNames = [];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
dayNames.push(allDayNames[(weekStartDay + i) % 7]);
|
||||
@@ -120,7 +159,25 @@ function renderCalendar() {
|
||||
if (isHoliday) cell.classList.add('holiday');
|
||||
if (isToday) cell.classList.add('today');
|
||||
|
||||
if (presence) {
|
||||
// Check closing days
|
||||
// Note: JS getDay(): 0=Sunday, 1=Monday...
|
||||
// DB WeekDay: 0=Sunday, etc. (They match)
|
||||
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;
|
||||
// Reset times for strict date comparison
|
||||
start.setHours(0, 0, 0, 0);
|
||||
end.setHours(0, 0, 0, 0);
|
||||
return date >= start && date <= end;
|
||||
});
|
||||
|
||||
const isClosed = isWeeklyClosed || isSpecificClosed;
|
||||
|
||||
if (isClosed) {
|
||||
cell.classList.add('closed');
|
||||
cell.title = "Ufficio Chiuso";
|
||||
} else if (presence) {
|
||||
cell.classList.add(`status-${presence.status}`);
|
||||
}
|
||||
|
||||
@@ -134,140 +191,60 @@ function renderCalendar() {
|
||||
${parkingBadge}
|
||||
`;
|
||||
|
||||
cell.addEventListener('click', () => openDayModal(dateStr, presence, parking));
|
||||
if (!isClosed) {
|
||||
cell.addEventListener('click', () => openDayModal(dateStr, presence, parking));
|
||||
}
|
||||
grid.appendChild(cell);
|
||||
}
|
||||
}
|
||||
|
||||
function openDayModal(dateStr, presence, parking) {
|
||||
const modal = document.getElementById('dayModal');
|
||||
const title = document.getElementById('dayModalTitle');
|
||||
|
||||
title.textContent = utils.formatDateDisplay(dateStr);
|
||||
|
||||
// Highlight current status
|
||||
document.querySelectorAll('.status-btn').forEach(btn => {
|
||||
const status = btn.dataset.status;
|
||||
if (presence && presence.status === status) {
|
||||
btn.classList.add('active');
|
||||
} else {
|
||||
btn.classList.remove('active');
|
||||
}
|
||||
ModalLogic.openModal({
|
||||
dateStr,
|
||||
presence,
|
||||
parking
|
||||
});
|
||||
|
||||
// Update parking section
|
||||
const parkingSection = document.getElementById('parkingSection');
|
||||
const parkingInfo = document.getElementById('parkingInfo');
|
||||
const releaseBtn = document.getElementById('releaseParkingBtn');
|
||||
|
||||
if (parking) {
|
||||
parkingSection.style.display = 'block';
|
||||
const spotName = parking.spot_display_name || parking.spot_id;
|
||||
parkingInfo.innerHTML = `<strong>Parking:</strong> Spot ${spotName}`;
|
||||
releaseBtn.dataset.assignmentId = parking.id;
|
||||
document.getElementById('reassignParkingBtn').dataset.assignmentId = parking.id;
|
||||
currentAssignmentId = parking.id;
|
||||
} else {
|
||||
parkingSection.style.display = 'none';
|
||||
}
|
||||
|
||||
modal.dataset.date = dateStr;
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
async function markPresence(status) {
|
||||
const modal = document.getElementById('dayModal');
|
||||
const date = modal.dataset.date;
|
||||
|
||||
async function handleMarkPresence(status, date) {
|
||||
const response = await api.post('/api/presence/mark', { date, status });
|
||||
if (response && response.ok) {
|
||||
await Promise.all([loadPresences(), loadParkingAssignments()]);
|
||||
renderCalendar();
|
||||
modal.style.display = 'none';
|
||||
ModalLogic.closeModal();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(error.detail || 'Failed to mark presence');
|
||||
alert(error.detail || 'Impossibile segnare la presenza');
|
||||
}
|
||||
}
|
||||
|
||||
async function clearPresence() {
|
||||
const modal = document.getElementById('dayModal');
|
||||
const date = modal.dataset.date;
|
||||
|
||||
if (!confirm('Clear presence for this date?')) return;
|
||||
|
||||
async function handleClearPresence(date) {
|
||||
const response = await api.delete(`/api/presence/${date}`);
|
||||
if (response && response.ok) {
|
||||
await Promise.all([loadPresences(), loadParkingAssignments()]);
|
||||
renderCalendar();
|
||||
modal.style.display = 'none';
|
||||
ModalLogic.closeModal();
|
||||
}
|
||||
}
|
||||
|
||||
async function releaseParking() {
|
||||
const modal = document.getElementById('dayModal');
|
||||
const releaseBtn = document.getElementById('releaseParkingBtn');
|
||||
const assignmentId = releaseBtn.dataset.assignmentId;
|
||||
|
||||
if (!assignmentId) return;
|
||||
if (!confirm('Release your parking spot for this date?')) return;
|
||||
async function handleReleaseParking(assignmentId) {
|
||||
if (!confirm('Rilasciare il parcheggio per questa data?')) return;
|
||||
|
||||
const response = await api.post(`/api/parking/release-my-spot/${assignmentId}`);
|
||||
|
||||
if (response && response.ok) {
|
||||
await loadParkingAssignments();
|
||||
renderCalendar();
|
||||
modal.style.display = 'none';
|
||||
ModalLogic.closeModal();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(error.detail || 'Failed to release parking spot');
|
||||
alert(error.detail || 'Impossibile rilasciare il parcheggio');
|
||||
}
|
||||
}
|
||||
|
||||
async function openReassignModal() {
|
||||
const assignmentId = currentAssignmentId;
|
||||
if (!assignmentId) return;
|
||||
|
||||
// Load eligible users
|
||||
const response = await api.get(`/api/parking/eligible-users/${assignmentId}`);
|
||||
if (!response || !response.ok) {
|
||||
const error = await response.json();
|
||||
alert(error.detail || 'Failed to load eligible users');
|
||||
return;
|
||||
}
|
||||
|
||||
const users = await response.json();
|
||||
const select = document.getElementById('reassignUser');
|
||||
select.innerHTML = '<option value="">Select user...</option>';
|
||||
|
||||
if (users.length === 0) {
|
||||
select.innerHTML = '<option value="">No eligible users available</option>';
|
||||
} else {
|
||||
users.forEach(user => {
|
||||
const option = document.createElement('option');
|
||||
option.value = user.id;
|
||||
option.textContent = user.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
// Get spot info from parking data
|
||||
const parking = Object.values(parkingData).find(p => p.id === assignmentId);
|
||||
if (parking) {
|
||||
const spotName = parking.spot_display_name || parking.spot_id;
|
||||
document.getElementById('reassignSpotInfo').textContent = `Spot ${spotName}`;
|
||||
}
|
||||
|
||||
document.getElementById('dayModal').style.display = 'none';
|
||||
document.getElementById('reassignModal').style.display = 'flex';
|
||||
}
|
||||
|
||||
async function confirmReassign() {
|
||||
const assignmentId = currentAssignmentId;
|
||||
const newUserId = document.getElementById('reassignUser').value;
|
||||
|
||||
async function handleReassignParking(assignmentId, newUserId) {
|
||||
// Basic validation handled by select; confirm
|
||||
if (!assignmentId || !newUserId) {
|
||||
alert('Please select a user');
|
||||
alert('Seleziona un utente');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -279,13 +256,15 @@ async function confirmReassign() {
|
||||
if (response && response.ok) {
|
||||
await loadParkingAssignments();
|
||||
renderCalendar();
|
||||
document.getElementById('reassignModal').style.display = 'none';
|
||||
ModalLogic.closeModal();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(error.detail || 'Failed to reassign parking spot');
|
||||
alert(error.detail || 'Impossibile riassegnare il parcheggio');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setupEventListeners() {
|
||||
// Month navigation
|
||||
document.getElementById('prevMonth').addEventListener('click', async () => {
|
||||
@@ -300,69 +279,255 @@ function setupEventListeners() {
|
||||
renderCalendar();
|
||||
});
|
||||
|
||||
// Day modal
|
||||
document.getElementById('closeDayModal').addEventListener('click', () => {
|
||||
document.getElementById('dayModal').style.display = 'none';
|
||||
// Quick Entry Logic
|
||||
const quickEntryModal = document.getElementById('quickEntryModal');
|
||||
const quickEntryBtn = document.getElementById('quickEntryBtn');
|
||||
const closeQuickEntryBtn = document.getElementById('closeQuickEntryModal');
|
||||
const cancelQuickEntryBtn = document.getElementById('cancelQuickEntry');
|
||||
const quickEntryForm = document.getElementById('quickEntryForm');
|
||||
|
||||
if (quickEntryBtn) {
|
||||
quickEntryBtn.addEventListener('click', () => {
|
||||
// Default dates: tomorrow
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
document.getElementById('qeStartDate').valueAsDate = tomorrow;
|
||||
document.getElementById('qeEndDate').valueAsDate = tomorrow;
|
||||
document.getElementById('qeStatus').value = '';
|
||||
|
||||
// Clear selections
|
||||
document.querySelectorAll('.qe-status-btn').forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
quickEntryModal.style.display = 'flex';
|
||||
});
|
||||
}
|
||||
|
||||
if (closeQuickEntryBtn) closeQuickEntryBtn.addEventListener('click', () => quickEntryModal.style.display = 'none');
|
||||
if (cancelQuickEntryBtn) cancelQuickEntryBtn.addEventListener('click', () => quickEntryModal.style.display = 'none');
|
||||
|
||||
// Status selection in QE
|
||||
document.querySelectorAll('.qe-status-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
document.querySelectorAll('.qe-status-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.getElementById('qeStatus').value = btn.dataset.status;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.status-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => markPresence(btn.dataset.status));
|
||||
});
|
||||
if (quickEntryForm) {
|
||||
quickEntryForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
document.getElementById('clearDayBtn').addEventListener('click', clearPresence);
|
||||
document.getElementById('releaseParkingBtn').addEventListener('click', releaseParking);
|
||||
document.getElementById('reassignParkingBtn').addEventListener('click', openReassignModal);
|
||||
const startStr = document.getElementById('qeStartDate').value;
|
||||
const endStr = document.getElementById('qeEndDate').value;
|
||||
const status = document.getElementById('qeStatus').value;
|
||||
|
||||
utils.setupModalClose('dayModal');
|
||||
if (!status) return utils.showMessage('Seleziona uno stato', 'error');
|
||||
if (!startStr || !endStr) return utils.showMessage('Seleziona le date', 'error');
|
||||
|
||||
// Reassign modal
|
||||
document.getElementById('closeReassignModal').addEventListener('click', () => {
|
||||
document.getElementById('reassignModal').style.display = 'none';
|
||||
});
|
||||
document.getElementById('cancelReassign').addEventListener('click', () => {
|
||||
document.getElementById('reassignModal').style.display = 'none';
|
||||
});
|
||||
document.getElementById('confirmReassign').addEventListener('click', confirmReassign);
|
||||
utils.setupModalClose('reassignModal');
|
||||
const startDate = new Date(startStr);
|
||||
const endDate = new Date(endStr);
|
||||
|
||||
// Bulk mark
|
||||
document.getElementById('bulkMarkBtn').addEventListener('click', () => {
|
||||
document.getElementById('bulkMarkModal').style.display = 'flex';
|
||||
});
|
||||
if (endDate < startDate) return utils.showMessage('La data di fine non può essere precedente alla data di inizio', 'error');
|
||||
|
||||
document.getElementById('closeBulkModal').addEventListener('click', () => {
|
||||
document.getElementById('bulkMarkModal').style.display = 'none';
|
||||
});
|
||||
quickEntryModal.style.display = 'none';
|
||||
utils.showMessage('Inserimento in corso...', 'warning');
|
||||
|
||||
document.getElementById('cancelBulk').addEventListener('click', () => {
|
||||
document.getElementById('bulkMarkModal').style.display = 'none';
|
||||
});
|
||||
const promises = [];
|
||||
let current = new Date(startDate);
|
||||
|
||||
utils.setupModalClose('bulkMarkModal');
|
||||
while (current <= endDate) {
|
||||
const dStr = current.toISOString().split('T')[0];
|
||||
if (status === 'clear') {
|
||||
promises.push(api.delete(`/api/presence/${dStr}`));
|
||||
} else {
|
||||
promises.push(api.post('/api/presence/mark', { date: dStr, status: status }));
|
||||
}
|
||||
current.setDate(current.getDate() + 1);
|
||||
}
|
||||
|
||||
document.getElementById('bulkMarkForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
utils.showMessage('Inserimento completato!', 'success');
|
||||
await Promise.all([loadPresences(), loadParkingAssignments()]);
|
||||
renderCalendar();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
utils.showMessage('Errore durante l\'inserimento. Alcuni giorni potrebbero non essere stati aggiornati.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const startDate = document.getElementById('startDate').value;
|
||||
const endDate = document.getElementById('endDate').value;
|
||||
const status = document.getElementById('bulkStatus').value;
|
||||
const weekdaysOnly = document.getElementById('weekdaysOnly').checked;
|
||||
// ----------------------------------------------------------------------------
|
||||
// Parking Status Logic
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const data = { start_date: startDate, end_date: endDate, status };
|
||||
if (weekdaysOnly) {
|
||||
data.days = [1, 2, 3, 4, 5]; // Mon-Fri (JS weekday)
|
||||
}
|
||||
function initParkingStatus() {
|
||||
updateStatusHeader();
|
||||
loadDailyStatus();
|
||||
|
||||
const response = await api.post('/api/presence/mark-bulk', data);
|
||||
// Update office name if available
|
||||
if (currentUser && currentUser.office_name) {
|
||||
const nameDisplay = document.getElementById('statusOfficeName');
|
||||
if (nameDisplay) nameDisplay.textContent = currentUser.office_name;
|
||||
|
||||
const headerDisplay = document.getElementById('currentOfficeDisplay');
|
||||
if (headerDisplay) headerDisplay.textContent = currentUser.office_name;
|
||||
} else {
|
||||
const nameDisplay = document.getElementById('statusOfficeName');
|
||||
if (nameDisplay) nameDisplay.textContent = 'Tuo Ufficio';
|
||||
|
||||
const headerDisplay = document.getElementById('currentOfficeDisplay');
|
||||
if (headerDisplay) headerDisplay.textContent = 'Tuo Ufficio';
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatusHeader() {
|
||||
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
|
||||
const dateStr = statusDate.toLocaleDateString('it-IT', options);
|
||||
const capitalizedDate = dateStr.charAt(0).toUpperCase() + dateStr.slice(1);
|
||||
|
||||
const statusDateDisplay = document.getElementById('statusDateDisplay');
|
||||
if (statusDateDisplay) statusDateDisplay.textContent = capitalizedDate;
|
||||
|
||||
const pickerDateDisplay = document.getElementById('pickerDateDisplay');
|
||||
if (pickerDateDisplay) pickerDateDisplay.textContent = utils.formatDate(statusDate);
|
||||
|
||||
const summaryDateDisplay = document.getElementById('summaryDateDisplay');
|
||||
if (summaryDateDisplay) summaryDateDisplay.textContent = dateStr;
|
||||
|
||||
const picker = document.getElementById('statusDatePicker');
|
||||
if (picker) {
|
||||
const yyyy = statusDate.getFullYear();
|
||||
const mm = String(statusDate.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(statusDate.getDate()).padStart(2, '0');
|
||||
picker.value = `${yyyy}-${mm}-${dd}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDailyStatus() {
|
||||
if (!currentUser || !currentUser.office_id) return;
|
||||
|
||||
const dateStr = utils.formatDate(statusDate);
|
||||
const officeId = currentUser.office_id;
|
||||
|
||||
const grid = document.getElementById('spotsGrid');
|
||||
// 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>';
|
||||
|
||||
try {
|
||||
const response = await api.get(`/api/parking/assignments/${dateStr}?office_id=${officeId}`);
|
||||
if (response && response.ok) {
|
||||
const results = await response.json();
|
||||
alert(`Marked ${results.length} dates`);
|
||||
document.getElementById('bulkMarkModal').style.display = 'none';
|
||||
await Promise.all([loadPresences(), loadParkingAssignments()]);
|
||||
renderCalendar();
|
||||
const assignments = await response.json();
|
||||
renderParkingStatus(assignments);
|
||||
} else {
|
||||
const error = await response.json();
|
||||
alert(error.detail || 'Failed to bulk mark');
|
||||
if (grid) grid.innerHTML = '<div style="width:100%; text-align:center; padding:1rem;">Impossibile caricare i dati.</div>';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error loading parking status", e);
|
||||
if (grid) grid.innerHTML = '<div style="width:100%; text-align:center; padding:1rem;">Errore di caricamento.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderParkingStatus(assignments) {
|
||||
const grid = document.getElementById('spotsGrid');
|
||||
if (!grid) return;
|
||||
|
||||
grid.innerHTML = '';
|
||||
|
||||
if (!assignments || assignments.length === 0) {
|
||||
grid.innerHTML = '<div style="width:100%; text-align:center; padding:1rem;">Nessun posto configurato o disponibile.</div>';
|
||||
const badge = document.getElementById('spotsCountBadge');
|
||||
if (badge) badge.textContent = `Liberi: 0/0`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort
|
||||
assignments.sort((a, b) => {
|
||||
const nameA = a.spot_display_name || a.spot_id;
|
||||
const nameB = b.spot_display_name || b.spot_id;
|
||||
return nameA.localeCompare(nameB, undefined, { numeric: true, sensitivity: 'base' });
|
||||
});
|
||||
|
||||
let total = assignments.length;
|
||||
let free = 0;
|
||||
|
||||
assignments.forEach(a => {
|
||||
const isFree = !a.user_id;
|
||||
if (isFree) free++;
|
||||
|
||||
const spotName = a.spot_display_name || a.spot_id;
|
||||
const statusText = isFree ? 'Libero' : (a.user_name || 'Occupato');
|
||||
|
||||
// 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 el = document.createElement('div');
|
||||
el.className = 'spot-card';
|
||||
el.style.cssText = `
|
||||
border: 1px solid ${borderColor};
|
||||
background: ${bgColor};
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
width: 140px;
|
||||
min-width: 120px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
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="color: ${textColor}; font-size: 0.85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;" title="${statusText}">
|
||||
${statusText}
|
||||
</div>
|
||||
`;
|
||||
|
||||
grid.appendChild(el);
|
||||
});
|
||||
|
||||
const badge = document.getElementById('spotsCountBadge');
|
||||
if (badge) badge.textContent = `Liberi: ${free}/${total}`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function setupStatusListeners() {
|
||||
const prevDay = document.getElementById('statusPrevDay');
|
||||
if (prevDay) prevDay.addEventListener('click', () => {
|
||||
statusDate.setDate(statusDate.getDate() - 1);
|
||||
updateStatusHeader();
|
||||
loadDailyStatus();
|
||||
});
|
||||
|
||||
const nextDay = document.getElementById('statusNextDay');
|
||||
if (nextDay) nextDay.addEventListener('click', () => {
|
||||
statusDate.setDate(statusDate.getDate() + 1);
|
||||
updateStatusHeader();
|
||||
loadDailyStatus();
|
||||
});
|
||||
|
||||
const datePicker = document.getElementById('statusDatePicker');
|
||||
if (datePicker) datePicker.addEventListener('change', (e) => {
|
||||
if (e.target.value) {
|
||||
statusDate = new Date(e.target.value);
|
||||
updateStatusHeader();
|
||||
loadDailyStatus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user