(() => {
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
});
}
})();