const eventList = document.getElementById('event-list'); const eventModal = document.getElementById('event-modal'); const eventForm = document.getElementById('event-form'); const addEventBtn = document.getElementById('add-event-btn'); const cancelBtn = document.getElementById('cancel-btn'); const modalTitle = document.getElementById('modal-title'); const confirmModal = document.getElementById('confirm-modal'); const confirmCancelBtn = document.getElementById('confirm-cancel-btn'); const confirmDeleteBtn = document.getElementById('confirm-delete-btn'); let eventToDelete = null; const API_URL = '/events'; const AUTH_URL = '/auth'; let currentUser = null; // Helper for authenticated requests async function authenticatedFetch(url, options = {}) { const token = localStorage.getItem('jwt_token'); const headers = { ...options.headers, 'Authorization': `Bearer ${token}` }; let response = await fetch(url, { ...options, headers }); if (response.status === 401) { const refreshToken = localStorage.getItem('refresh_token'); if (refreshToken) { const refreshRes = await fetch('/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: refreshToken }) }); if (refreshRes.ok) { const tokens = await refreshRes.json(); localStorage.setItem('jwt_token', tokens.id_token); if (tokens.refresh_token) { localStorage.setItem('refresh_token', tokens.refresh_token); } // Retry original request return authenticatedFetch(url, options); } } // If refresh fails or no refresh token, logout logout(); } return response; } // Helper to format date in European format (DD.MM.YYYY) function formatDateEuropean(dateStr) { if (!dateStr) return ''; const date = new Date(dateStr); const day = String(date.getDate()).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, '0'); const year = date.getFullYear(); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); return `${day}.${month}.${year} ${hours}:${minutes}`; } // Helpers for custom date inputs function getDateValues(idPrefix) { const d = document.getElementById(`${idPrefix}-day`).value.padStart(2, '0'); const m = document.getElementById(`${idPrefix}-month`).value.padStart(2, '0'); const y = document.getElementById(`${idPrefix}-year`).value; return `${y}-${m}-${d}`; } function setDateValues(idPrefix, isoDate) { if (!isoDate) return; const [y, m, d] = isoDate.split('-'); document.getElementById(`${idPrefix}-day`).value = d; document.getElementById(`${idPrefix}-month`).value = m; document.getElementById(`${idPrefix}-year`).value = y; document.getElementById(`${idPrefix}-picker`).value = isoDate; } function initDateInputs() { ['event-from', 'event-to'].forEach(prefix => { const day = document.getElementById(`${prefix}-day`); const month = document.getElementById(`${prefix}-month`); const year = document.getElementById(`${prefix}-year`); const picker = document.getElementById(`${prefix}-picker`); if (!day || !month || !year || !picker) return; // Sync triple inputs to picker on change const syncToPicker = () => { const d = day.value.padStart(2, '0'); const m = month.value.padStart(2, '0'); const y = year.value; if (d && m && y.length === 4) { picker.value = `${y}-${m}-${d}`; } }; day.addEventListener('input', () => { if (day.value.length >= 2) month.focus(); syncToPicker(); }); month.addEventListener('input', () => { if (month.value.length >= 2) year.focus(); syncToPicker(); }); year.addEventListener('input', syncToPicker); // Sync picker to triple inputs on change picker.addEventListener('change', () => { if (picker.value) { const [y, m, d] = picker.value.split('-'); day.value = d; month.value = m; year.value = y; } }); }); } // Fetch all events async function fetchEvents() { try { const response = await authenticatedFetch(`${API_URL}?upcoming=true`); if (response.ok) { const events = await response.json(); renderEvents(events); } else { eventList.innerHTML = '
Please login to see your events.
'; } } catch (error) { console.error('Error fetching events:', error); } } // Initialize Scrolling Time Spinner function initTimeSelectors() { const fromHours = document.getElementById('event-from-hours'); const fromMinutes = document.getElementById('event-from-minutes'); const toHours = document.getElementById('event-to-hours'); const toMinutes = document.getElementById('event-to-minutes'); if (!fromHours || !fromMinutes || !toHours || !toMinutes) return; populateSpinner(fromHours, 24); populateSpinner(toHours, 24); populateSpinner(fromMinutes, 60, 5); populateSpinner(toMinutes, 60, 5); // Initial styles and scroll syncing [fromHours, fromMinutes, toHours, toMinutes].forEach(col => { col.addEventListener('scroll', () => updateSpinnerStyles(col)); // Small delay to ensure initial scroll-snap works setTimeout(() => updateSpinnerStyles(col), 100); }); } function populateSpinner(container, total, step = 1) { container.innerHTML = ''; // Add spacer items at top and bottom for 5-item view (2 spacers each) for (let i = 0; i < 2; i++) { const spacer = document.createElement('div'); spacer.className = 'spinner-item spacer'; container.appendChild(spacer); } for (let i = 0; i < total; i += step) { const item = document.createElement('div'); item.className = 'spinner-item'; const val = String(i).padStart(2, '0'); item.textContent = val; item.dataset.value = val; item.onclick = () => { container.scrollTo({ top: item.offsetTop - container.offsetTop - (container.clientHeight / 2 - item.clientHeight / 2), behavior: 'smooth' }); }; container.appendChild(item); } for (let i = 0; i < 2; i++) { const spacer = document.createElement('div'); spacer.className = 'spinner-item spacer'; container.appendChild(spacer); } } function updateSpinnerStyles(container) { const items = container.querySelectorAll('.spinner-item:not(.spacer)'); const containerCenter = container.scrollTop + container.clientHeight / 2; let closestItem = null; let minDistance = Infinity; items.forEach(item => { const itemCenter = item.offsetTop - container.offsetTop + item.clientHeight / 2; const distance = Math.abs(containerCenter - itemCenter); if (distance < minDistance) { minDistance = distance; closestItem = item; } item.classList.remove('active'); }); if (closestItem) { closestItem.classList.add('active'); } } function getSpinnerTime(idPrefix) { const hourCol = document.getElementById(`${idPrefix}-hours`); const minCol = document.getElementById(`${idPrefix}-minutes`); const hour = hourCol.querySelector('.spinner-item.active')?.dataset.value || '00'; const minute = minCol.querySelector('.spinner-item.active')?.dataset.value || '00'; return `${hour}:${minute}`; } function setSpinnerTime(idPrefix, timeStr) { if (!timeStr) return; const [h, m] = timeStr.split(':'); const hourCol = document.getElementById(`${idPrefix}-hours`); const minCol = document.getElementById(`${idPrefix}-minutes`); scrollSpinnerToValue(hourCol, h); scrollSpinnerToValue(minCol, m.slice(0, 2)); } function scrollSpinnerToValue(container, value) { const items = container.querySelectorAll('.spinner-item:not(.spacer)'); const targetItem = Array.from(items).find(item => item.dataset.value === value); if (targetItem) { // We scroll so the target item is centered container.scrollTop = targetItem.offsetTop - container.offsetTop - (container.clientHeight / 2 - targetItem.clientHeight / 2); } } // Render events in the grid function renderEvents(events) { eventList.innerHTML = ''; events.forEach(event => { const card = document.createElement('div'); card.className = 'event-card'; card.innerHTML = `From: ${formatDateEuropean(event.from)}
To: ${formatDateEuropean(event.to)}