// Snauw Counter - Frontend JavaScript // Global variables let selectedSeverity = null; // Fast datetime initialization - runs as soon as script loads function fastDateTimeInit() { const dateTimeInput = document.getElementById('incident_time'); if (dateTimeInput) { const now = new Date(); const formattedDateTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}T${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`; dateTimeInput.value = formattedDateTime; } } // Try to initialize datetime immediately if elements exist if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', fastDateTimeInit); } else { fastDateTimeInit(); } // Initialize app when DOM is loaded document.addEventListener('DOMContentLoaded', function() { // Initialize date/time FIRST - before any user interaction initializeDateTime(); trackDateTimeModification(); // Then initialize other components initializeSeveritySelector(); initializeAnonymousUserSupport(); loadChart(); updateHeaderCounter(); // Hide privacy notice als gebruiker het al eerder heeft weggedaan if (localStorage.getItem('privacy-notice-hidden')) { const notice = document.getElementById('privacy-notice'); if (notice) notice.style.display = 'none'; } }); // Initialize date/time fields with current date and time function initializeDateTime() { const dateTimeInput = document.getElementById('incident_time'); if (dateTimeInput) { // Get current date and time const now = new Date(); // Format to datetime-local format (YYYY-MM-DDTHH:mm) const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); const hours = String(now.getHours()).padStart(2, '0'); const minutes = String(now.getMinutes()).padStart(2, '0'); const formattedDateTime = `${year}-${month}-${day}T${hours}:${minutes}`; dateTimeInput.value = formattedDateTime; // Add helper text const helpText = dateTimeInput.nextElementSibling; if (helpText && helpText.tagName === 'SMALL') { helpText.innerHTML = `✓ Automatisch ingevuld: ${formatDisplayTime(now)}`; } } } // Format time for display function formatDisplayTime(date) { return date.toLocaleString('nl-NL', { weekday: 'short', day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' }); } // Update date/time when page becomes visible (for accuracy) document.addEventListener('visibilitychange', function() { if (!document.hidden) { // Only update if the field hasn't been manually changed const dateTimeInput = document.getElementById('incident_time'); if (dateTimeInput && !dateTimeInput.dataset.userModified) { initializeDateTime(); } } }); // Track if user manually modifies the datetime field function trackDateTimeModification() { const dateTimeInput = document.getElementById('incident_time'); if (dateTimeInput) { dateTimeInput.addEventListener('input', function() { this.dataset.userModified = 'true'; const helpText = this.nextElementSibling; if (helpText && helpText.tagName === 'SMALL') { helpText.innerHTML = 'Handmatig aangepast'; } }); // Add reset button const resetBtn = document.createElement('button'); resetBtn.type = 'button'; resetBtn.className = 'btn btn-secondary'; resetBtn.style.marginLeft = '0.5rem'; resetBtn.style.padding = '0.25rem 0.5rem'; resetBtn.style.fontSize = '0.8rem'; resetBtn.innerHTML = '🔄 Nu'; resetBtn.title = 'Reset naar huidige tijd'; resetBtn.onclick = function() { delete dateTimeInput.dataset.userModified; initializeDateTime(); }; // Only add reset button if it doesn't exist yet if (!dateTimeInput.parentNode.querySelector('.btn.btn-secondary')) { dateTimeInput.parentNode.insertBefore(resetBtn, dateTimeInput.nextSibling); } } } // Update date/time when page becomes visible (for accuracy) document.addEventListener('visibilitychange', function() { if (!document.hidden) { // Only update if the field hasn't been manually changed const dateTimeInput = document.getElementById('incident_time'); if (dateTimeInput && !dateTimeInput.dataset.userModified) { initializeDateTime(); } } }); // Quick add incident functionality function quickAddIncident() { // Check if we're already on add incident page if (window.location.pathname.includes('/add')) { return; } // For anonymous users, show quick add modal if (!document.body.dataset.isAuthenticated) { showQuickAddModal(); } else { // Redirect to add incident page window.location.href = '/add-incident'; } } // Show quick add modal for anonymous users function showQuickAddModal() { const modal = document.createElement('div'); modal.className = 'modal'; modal.innerHTML = `
`; document.body.appendChild(modal); // Initialize severity selector for modal initializeSeveritySelector(); // Add modal styles const style = document.createElement('style'); style.textContent = ` .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 2000; padding: 1rem; } .modal-content { background: white; border-radius: 12px; max-width: 500px; width: 100%; max-height: 80vh; overflow-y: auto; } .modal-header { display: flex; justify-content: space-between; align-items: center; padding: 1rem 1.5rem; border-bottom: 1px solid #e9ecef; } .modal-body { padding: 1.5rem; } .close-btn { background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #6c757d; } `; document.head.appendChild(style); } // Initialize severity selector buttons function initializeSeveritySelector() { const severityButtons = document.querySelectorAll('.severity-btn'); const severityInput = document.getElementById('severity-input'); severityButtons.forEach(button => { button.addEventListener('click', function() { // Remove active class from all buttons severityButtons.forEach(btn => btn.classList.remove('active')); // Add active class to clicked button this.classList.add('active'); // Update selected severity selectedSeverity = parseInt(this.dataset.severity); // Update hidden input value if it exists if (severityInput) { severityInput.value = selectedSeverity; } console.log(`Selected severity: ${selectedSeverity}`); }); }); } // Update header counter with total incidents function updateHeaderCounter() { const counterDigit1 = document.getElementById('counter-digit-1'); const counterDigit2 = document.getElementById('counter-digit-2'); const counterDigit3 = document.getElementById('counter-digit-3'); // Also update the old header-counter for backwards compatibility const counterElement = document.getElementById('header-counter'); if (!counterDigit1 && !counterElement) return; let totalIncidents = 0; if (document.body.dataset.isAuthenticated === 'true') { // For authenticated users - would normally fetch from API // For now, we'll use a placeholder totalIncidents = 0; } else { // For anonymous users - use localStorage const incidents = JSON.parse(localStorage.getItem('snauw_incidents') || '[]'); totalIncidents = incidents.length; } // Format number with leading zeros const formattedCount = totalIncidents.toString().padStart(3, '0'); // Update individual digits if they exist (new banner) if (counterDigit1 && counterDigit2 && counterDigit3) { counterDigit1.textContent = formattedCount[0]; counterDigit2.textContent = formattedCount[1]; counterDigit3.textContent = formattedCount[2]; // Add pulsing animation for new incidents if (totalIncidents > 0) { [counterDigit1, counterDigit2, counterDigit3].forEach(digit => { digit.style.animation = 'pulse 2s ease-in-out infinite'; }); } } // Update old counter element if it exists (backwards compatibility) if (counterElement) { counterElement.textContent = formattedCount; // Add pulsing animation for new incidents if (totalIncidents > 0) { counterElement.style.animation = 'pulse 2s ease-in-out infinite'; } } } // Generate Snauw-index buttons with descriptions function getSnauwIndexButtons() { const descriptions = [ 'Lichte correctie', 'Geprikkelde waarschuwing', 'Openlijke snauw', 'Bijtende aanval', 'Explosieve uitval' ]; return [...Array(5)].map((_, i) => ` `).join(''); } // Close modal function closeModal() { const modal = document.querySelector('.modal'); if (modal) { modal.remove(); } } // Submit quick incident for anonymous users async function submitQuickIncident(event) { event.preventDefault(); if (!selectedSeverity) { alert('Selecteer een Snauw-index'); return; } const notes = document.getElementById('quick-notes').value; const now = new Date(); const incident = { severity: selectedSeverity, notes: notes, timestamp: now.toISOString() }; // Store in localStorage for anonymous users let incidents = JSON.parse(localStorage.getItem('snauw_incidents') || '[]'); incident.id = Date.now(); // Simple ID for local storage incidents.push(incident); localStorage.setItem('snauw_incidents', JSON.stringify(incidents)); // Update header counter updateHeaderCounter(); // Add animation to counter const counterElement = document.getElementById('header-counter'); if (counterElement) { counterElement.classList.add('updated'); setTimeout(() => counterElement.classList.remove('updated'), 3000); } // Show success message showNotification('✅ Incident opgeslagen!', 'success'); // Close modal closeModal(); // Refresh statistics if on stats page if (window.location.pathname.includes('statistics')) { location.reload(); } } // Show notification function showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.className = `alert alert-${type}`; notification.textContent = message; notification.style.position = 'fixed'; notification.style.top = '2rem'; notification.style.right = '2rem'; notification.style.zIndex = '3000'; notification.style.minWidth = '250px'; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); } // Anonymous user support function initializeAnonymousUserSupport() { // Check if user has incidents in localStorage const localIncidents = localStorage.getItem('snauw_incidents'); if (localIncidents && !document.body.dataset.isAuthenticated) { const incidents = JSON.parse(localIncidents); if (incidents.length > 0) { showMigrationOffer(); } } } // Show migration offer to anonymous users with data function showMigrationOffer() { const incidents = JSON.parse(localStorage.getItem('snauw_incidents') || '[]'); if (incidents.length < 3) return; // Only show after a few incidents const lastOffer = localStorage.getItem('migration-offer-shown'); const oneWeekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000); if (lastOffer && parseInt(lastOffer) > oneWeekAgo) return; // Don't show too often const banner = document.createElement('div'); banner.className = 'migration-banner'; banner.innerHTML = `