(() => { let charts = []; let mainMap = null; let modalMap = null; let refreshHandlers = []; let modalShownHandler = null; function parseDashboardData() { const script = document.getElementById('dashboard-page-data'); if (!script) return null; try { return JSON.parse(script.textContent || '{}'); } catch (err) { console.error('Failed to parse dashboard data', err); return null; } } function createLeafletMap(mapElement, mapData) { const map = L.map(mapElement).setView([50, 10], 4); const bounds = L.latLngBounds(); const defaultCenter = [50, 10]; const defaultZoom = 4; L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', maxZoom: 19 }).addTo(map); mapData.forEach(point => { if (point.lat === null || point.lon === null) return; const radius = Math.max(6, 4 + Math.log(point.count + 1) * 4); const marker = L.circleMarker([point.lat, point.lon], { radius, color: '#ff0000', fillColor: '#ff4444', fillOpacity: 0.75, weight: 2 }).addTo(map); marker.bindPopup(` ${point.city}, ${point.country}
Downloads: ${point.count} `); bounds.extend([point.lat, point.lon]); }); if (mapData.length === 0) { const msg = document.createElement('div'); msg.textContent = 'Keine Geo-Daten für den ausgewählten Zeitraum.'; msg.className = 'text-muted small mt-3'; mapElement.appendChild(msg); } if (bounds.isValid()) { map.fitBounds(bounds, { padding: [24, 24] }); } else { map.setView(defaultCenter, defaultZoom); } const refreshSize = () => setTimeout(() => map.invalidateSize(), 150); refreshSize(); window.addEventListener('resize', refreshSize); window.addEventListener('orientationchange', refreshSize); refreshHandlers.push(refreshSize); return { map, refreshSize }; } function initDashboardPage() { const mapElement = document.getElementById('dashboard-map'); if (!mapElement || mapElement.dataset.bound) return; mapElement.dataset.bound = '1'; const data = parseDashboardData(); if (!data) return; const mapData = Array.isArray(data.map_data) ? data.map_data : []; const distinctDeviceData = Array.isArray(data.distinct_device_data) ? data.distinct_device_data : []; const timeframeData = Array.isArray(data.timeframe_data) ? data.timeframe_data : []; const userAgentData = Array.isArray(data.user_agent_data) ? data.user_agent_data : []; const folderData = Array.isArray(data.folder_data) ? data.folder_data : []; const timeframe = data.timeframe || ''; if (typeof L !== 'undefined') { try { mainMap = createLeafletMap(mapElement, mapData); const modalEl = document.getElementById('mapModal'); if (modalEl) { modalShownHandler = () => { const modalMapElement = document.getElementById('dashboard-map-modal'); if (!modalMapElement) return; const modalBody = modalEl.querySelector('.modal-body'); const modalHeader = modalEl.querySelector('.modal-header'); if (modalBody && modalHeader) { const availableHeight = window.innerHeight - modalHeader.offsetHeight; modalBody.style.height = `${availableHeight}px`; modalMapElement.style.height = '100%'; } if (!modalMap) { modalMap = createLeafletMap(modalMapElement, mapData); } setTimeout(() => { if (modalMap && modalMap.map) { modalMap.map.invalidateSize(); if (modalMap.refreshSize) modalMap.refreshSize(); } }, 200); }; modalEl.addEventListener('shown.bs.modal', modalShownHandler); } } catch (err) { console.error('Dashboard map init failed:', err); } } if (typeof Chart === 'undefined') return; const shiftedLabels = timeframeData.map((item) => { if (timeframe === 'last24hours') { const bucketDate = new Date(item.bucket); const now = new Date(); const isCurrentHour = bucketDate.getFullYear() === now.getFullYear() && bucketDate.getMonth() === now.getMonth() && bucketDate.getDate() === now.getDate() && bucketDate.getHours() === now.getHours(); const bucketEnd = isCurrentHour ? now : new Date(bucketDate.getTime() + 3600 * 1000); return `${bucketDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - ${bucketEnd.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; } else if (timeframe === '7days' || timeframe === '30days') { const localDate = new Date(item.bucket); return localDate.toLocaleDateString(); } else if (timeframe === '365days') { const [year, month] = item.bucket.split('-'); const dateObj = new Date(year, month - 1, 1); return dateObj.toLocaleString([], { month: 'short', year: 'numeric' }); } else { return item.bucket; } }); const ctxDistinctDevice = document.getElementById('distinctDeviceChart'); if (ctxDistinctDevice) { charts.push(new Chart(ctxDistinctDevice.getContext('2d'), { type: 'bar', data: { labels: shiftedLabels, datasets: [{ label: 'Device Count', data: distinctDeviceData.map(item => item.count), borderWidth: 2 }] }, options: { responsive: true, plugins: { legend: { display: false } }, scales: { x: { title: { display: true, text: 'Time Range' } }, y: { title: { display: true, text: 'Device Count' }, beginAtZero: true, ticks: { maxTicksLimit: 10 } } } } })); } const ctxTimeframe = document.getElementById('downloadTimeframeChart'); if (ctxTimeframe) { charts.push(new Chart(ctxTimeframe.getContext('2d'), { type: 'bar', data: { labels: shiftedLabels, datasets: [{ label: 'Download Count', data: timeframeData.map(item => item.count), borderWidth: 2 }] }, options: { responsive: true, plugins: { legend: { display: false } }, scales: { x: { title: { display: true, text: 'Time Range' } }, y: { title: { display: true, text: 'Download Count' }, beginAtZero: true, ticks: { maxTicksLimit: 10 } } } } })); } const ctxUserAgent = document.getElementById('userAgentChart'); if (ctxUserAgent) { charts.push(new Chart(ctxUserAgent.getContext('2d'), { type: 'pie', data: { labels: userAgentData.map(item => item.device), datasets: [{ data: userAgentData.map(item => item.count) }] }, options: { responsive: true } })); } const ctxFolder = document.getElementById('folderChart'); if (ctxFolder) { charts.push(new Chart(ctxFolder.getContext('2d'), { type: 'pie', data: { labels: folderData.map(item => item.folder), datasets: [{ data: folderData.map(item => item.count) }] }, options: { responsive: true } })); } } function destroyDashboardPage() { charts.forEach(chart => { try { chart.destroy(); } catch (err) { // ignore } }); charts = []; if (mainMap && mainMap.map) { mainMap.map.remove(); } if (modalMap && modalMap.map) { modalMap.map.remove(); } mainMap = null; modalMap = null; refreshHandlers.forEach(handler => { window.removeEventListener('resize', handler); window.removeEventListener('orientationchange', handler); }); refreshHandlers = []; const modalEl = document.getElementById('mapModal'); if (modalEl && modalShownHandler) { modalEl.removeEventListener('shown.bs.modal', modalShownHandler); } modalShownHandler = null; } if (window.PageRegistry && typeof window.PageRegistry.register === 'function') { window.PageRegistry.register('dashboard', { init: initDashboardPage, destroy: destroyDashboardPage }); } })();