'use strict'; /** * @file js/app.js * @description Initialisiert die Resonanzband-Heatmap-Anwendung. Lädt Daten von /heatmap und reagiert auf Nutzerinteraktionen. * @version 1.0.0 */ /** * Lädt Heatmap-Daten von der API /heatmap. * @param {Object} [filters] - optionale Filterparameter { cohort_id, time_window } * @returns {Promise} - Promise mit Heatmap-Daten und Metadaten */ async function loadHeatmapData(filters = {}) { try { const url = new URL('/heatmap', window.location.origin); Object.entries(filters).forEach(([key, value]) => { if (value !== undefined && value !== '') { url.searchParams.append(key, value); } }); const response = await fetch(url.toString(), { method: 'GET' }); if (!response.ok) { throw new Error(`API-Fehler: ${response.status}`); } const data = await response.json(); renderHeatmap(data); } catch (err) { console.error('Fehler beim Laden der Heatmap-Daten:', err); renderError(`Fehler beim Laden der Daten: ${err.message}`); } } /** * Rendert die Heatmap auf Basis der übergebenen Daten. * @param {Object} data - Heatmap-Datenobjekt */ function renderHeatmap(data) { const canvas = document.getElementById('heatmap-canvas'); if (!canvas) return; const ctx = canvas.getContext('2d'); const matrix = data?.matrix || []; const rows = matrix.length; const cols = rows > 0 ? matrix[0].length : 0; const cellWidth = canvas.width / (cols || 1); const cellHeight = canvas.height / (rows || 1); ctx.clearRect(0, 0, canvas.width, canvas.height); matrix.forEach((row, y) => { row.forEach((value, x) => { const color = getColorForValue(value); ctx.fillStyle = color; ctx.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight); }); }); renderMetrics(data); } /** * Zeigt grundlegende Metriken aus den Heatmap-Daten an. * @param {Object} data - Heatmap-Datenobjekt mit Metadaten */ function renderMetrics(data) { const metricsContainer = document.getElementById('heatmap-metrics'); if (!metricsContainer) return; const { band_center, band_width, cluster_score } = data || {}; metricsContainer.innerHTML = `

Band Center: ${band_center ?? '–'}

Band Width: ${band_width ?? '–'}

Cluster Score: ${cluster_score ?? '–'}

`; } /** * Berechnet eine Farbskala für Heatmap-Werte. * @param {number} value - numerischer Intensitätswert (0–1) * @returns {string} CSS-Farbwert (rgba) */ function getColorForValue(value) { const v = Math.max(0, Math.min(1, value)); const r = Math.floor(255 * v); const g = Math.floor(100 * (1 - v)); const b = 150; return `rgba(${r}, ${g}, ${b}, 0.9)`; } /** * Zeigt eine Fehlermeldung im UI an. * @param {string} message - Text der Fehlermeldung */ function renderError(message) { const container = document.getElementById('heatmap-container'); if (container) container.innerHTML = `
${message}
`; } /** * Behandelt Änderungen der Filter und ruft neue Daten ab. * @param {Event} event - Auslösendes Event */ function handleFilterChange(event) { const form = event.currentTarget.form || document.getElementById('filter-form'); if (!form) return; const formData = new FormData(form); const filters = Object.fromEntries(formData.entries()); loadHeatmapData(filters); } /** * Initialisiert die App, bindet Event-Listener und lädt erste Heatmap-Daten. */ function initApp() { const form = document.getElementById('filter-form'); if (form) { form.addEventListener('change', handleFilterChange); form.addEventListener('submit', (e) => { e.preventDefault(); handleFilterChange(e); }); } loadHeatmapData(); } window.addEventListener('load', initApp);