400 lines
10 KiB
JavaScript
400 lines
10 KiB
JavaScript
/**
|
|
* MYP Platform Chart-Renderer
|
|
* Erstellt und verwaltet Diagramme mit ApexCharts
|
|
* Version: 1.0.0
|
|
*/
|
|
|
|
// Speicher für aktive Chart-Instanzen
|
|
const activeCharts = {};
|
|
|
|
/**
|
|
* Initialisiert alle Diagramme auf der Seite
|
|
*/
|
|
function initCharts() {
|
|
// Prüfen, ob ApexCharts verfügbar ist
|
|
if (typeof ApexCharts === 'undefined') {
|
|
console.error('ApexCharts ist nicht geladen. Bitte ApexCharts vor chart-renderer.js einbinden.');
|
|
return;
|
|
}
|
|
|
|
// Alle Diagramm-Container mit data-chart-type Attribut finden
|
|
const chartContainers = document.querySelectorAll('[data-chart-type]');
|
|
|
|
// Für jeden Container ein Diagramm erstellen
|
|
chartContainers.forEach(container => {
|
|
const chartId = container.id;
|
|
const chartType = container.getAttribute('data-chart-type');
|
|
|
|
// Prüfen, ob Container eine ID hat
|
|
if (!chartId) {
|
|
console.error('Chart-Container benötigt eine ID:', container);
|
|
return;
|
|
}
|
|
|
|
// Bereits erstellte Charts nicht neu initialisieren
|
|
if (activeCharts[chartId]) {
|
|
return;
|
|
}
|
|
|
|
// Daten aus data-chart-data Attribut laden (als JSON)
|
|
let chartData = {};
|
|
try {
|
|
const dataAttr = container.getAttribute('data-chart-data');
|
|
if (dataAttr) {
|
|
chartData = JSON.parse(dataAttr);
|
|
}
|
|
} catch (error) {
|
|
console.error(`Fehler beim Parsen der Chart-Daten für ${chartId}:`, error);
|
|
return;
|
|
}
|
|
|
|
// Chart basierend auf Typ erstellen
|
|
createChart(container, chartType, chartData);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Erstellt ein einzelnes Diagramm
|
|
* @param {HTMLElement} container - Der Container für das Diagramm
|
|
* @param {string} chartType - Typ des Diagramms (line, area, bar, pie, donut, radial)
|
|
* @param {Object} chartData - Daten und Optionen für das Diagramm
|
|
*/
|
|
function createChart(container, chartType, chartData = {}) {
|
|
const chartId = container.id;
|
|
let chartOptions = {};
|
|
|
|
// Diagramm-Typ-spezifische Konfiguration laden
|
|
switch(chartType.toLowerCase()) {
|
|
case 'line':
|
|
chartOptions = getLineChartConfig(
|
|
chartData.series || [],
|
|
chartData.categories || [],
|
|
chartData.options || {}
|
|
);
|
|
break;
|
|
|
|
case 'area':
|
|
chartOptions = getAreaChartConfig(
|
|
chartData.series || [],
|
|
chartData.categories || [],
|
|
chartData.options || {}
|
|
);
|
|
break;
|
|
|
|
case 'bar':
|
|
chartOptions = getBarChartConfig(
|
|
chartData.series || [],
|
|
chartData.categories || [],
|
|
chartData.options || {}
|
|
);
|
|
break;
|
|
|
|
case 'pie':
|
|
chartOptions = getPieChartConfig(
|
|
chartData.series || [],
|
|
chartData.labels || [],
|
|
chartData.options || {}
|
|
);
|
|
break;
|
|
|
|
case 'donut':
|
|
chartOptions = getDonutChartConfig(
|
|
chartData.series || [],
|
|
chartData.labels || [],
|
|
chartData.options || {}
|
|
);
|
|
break;
|
|
|
|
case 'radial':
|
|
chartOptions = getRadialChartConfig(
|
|
chartData.series || [],
|
|
chartData.labels || [],
|
|
chartData.options || {}
|
|
);
|
|
break;
|
|
|
|
default:
|
|
console.error(`Unbekannter Chart-Typ: ${chartType}`);
|
|
return;
|
|
}
|
|
|
|
// Dark Mode Anpassungen
|
|
updateChartTheme(chartOptions);
|
|
|
|
// Chart erstellen und speichern
|
|
try {
|
|
const chart = new ApexCharts(container, chartOptions);
|
|
chart.render();
|
|
|
|
// Referenz speichern
|
|
activeCharts[chartId] = {
|
|
instance: chart,
|
|
type: chartType,
|
|
lastData: chartData
|
|
};
|
|
|
|
return chart;
|
|
} catch (error) {
|
|
console.error(`Fehler beim Erstellen des Charts ${chartId}:`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Aktualisiert ein bestehendes Diagramm mit neuen Daten
|
|
* @param {string} chartId - ID des Diagramm-Containers
|
|
* @param {Object} newData - Neue Daten für das Diagramm
|
|
*/
|
|
function updateChart(chartId, newData) {
|
|
const chartInfo = activeCharts[chartId];
|
|
|
|
if (!chartInfo) {
|
|
console.error(`Chart mit ID ${chartId} nicht gefunden.`);
|
|
return;
|
|
}
|
|
|
|
const chart = chartInfo.instance;
|
|
|
|
// Aktualisieren basierend auf Chart-Typ
|
|
if (chartInfo.type === 'pie' || chartInfo.type === 'donut' || chartInfo.type === 'radial') {
|
|
// Für Pie/Donut/Radial-Charts
|
|
chart.updateSeries(newData.series || []);
|
|
if (newData.labels) {
|
|
chart.updateOptions({
|
|
labels: newData.labels
|
|
});
|
|
}
|
|
} else {
|
|
// Für Line/Area/Bar-Charts
|
|
chart.updateSeries(newData.series || []);
|
|
if (newData.categories) {
|
|
chart.updateOptions({
|
|
xaxis: {
|
|
categories: newData.categories
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Zusätzliche Optionen aktualisieren
|
|
if (newData.options) {
|
|
chart.updateOptions(newData.options);
|
|
}
|
|
|
|
// Gespeicherte Daten aktualisieren
|
|
chartInfo.lastData = {
|
|
...chartInfo.lastData,
|
|
...newData
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Lädt Diagrammdaten über AJAX und aktualisiert das Diagramm
|
|
* @param {string} chartId - ID des Diagramm-Containers
|
|
* @param {string} url - URL zur Datenbeschaffung
|
|
* @param {Function} successCallback - Callback nach erfolgreicher Aktualisierung
|
|
*/
|
|
function loadChartData(chartId, url, successCallback) {
|
|
const container = document.getElementById(chartId);
|
|
|
|
if (!container) {
|
|
console.error(`Container mit ID ${chartId} nicht gefunden.`);
|
|
return;
|
|
}
|
|
|
|
// Lade-Animation anzeigen
|
|
container.classList.add('chart-loading');
|
|
|
|
// Daten vom Server laden
|
|
fetch(url)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Netzwerkantwort war nicht ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
// Lade-Animation entfernen
|
|
container.classList.remove('chart-loading');
|
|
|
|
const chartInfo = activeCharts[chartId];
|
|
|
|
// Chart erstellen, falls nicht vorhanden
|
|
if (!chartInfo) {
|
|
const chartType = container.getAttribute('data-chart-type');
|
|
if (chartType) {
|
|
createChart(container, chartType, data);
|
|
} else {
|
|
console.error(`Kein Chart-Typ für ${chartId} definiert.`);
|
|
}
|
|
} else {
|
|
// Bestehendes Chart aktualisieren
|
|
updateChart(chartId, data);
|
|
}
|
|
|
|
// Callback aufrufen, falls vorhanden
|
|
if (typeof successCallback === 'function') {
|
|
successCallback(data);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Fehler beim Laden der Chart-Daten:', error);
|
|
container.classList.remove('chart-loading');
|
|
|
|
// Fehlermeldung im Container anzeigen
|
|
container.innerHTML = `
|
|
<div class="chart-error">
|
|
<svg class="w-10 h-10 text-red-500 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
</svg>
|
|
<h3 class="text-lg font-medium">Fehler beim Laden</h3>
|
|
<p class="text-sm text-gray-500">Die Diagrammdaten konnten nicht geladen werden.</p>
|
|
</div>
|
|
`;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Aktualisiert die Farbthemen basierend auf dem Dark Mode
|
|
* @param {Object} chartOptions - Chart-Optionen Objekt
|
|
*/
|
|
function updateChartTheme(chartOptions) {
|
|
const isDarkMode = document.documentElement.classList.contains('dark');
|
|
|
|
// Theme anpassen
|
|
if (isDarkMode) {
|
|
// Dark Mode Einstellungen
|
|
chartOptions.theme = {
|
|
mode: 'dark',
|
|
palette: 'palette1'
|
|
};
|
|
|
|
chartOptions.grid = {
|
|
...chartOptions.grid,
|
|
borderColor: '#334155'
|
|
};
|
|
|
|
// Text Farben anpassen
|
|
chartOptions.xaxis = {
|
|
...chartOptions.xaxis,
|
|
labels: {
|
|
...chartOptions.xaxis?.labels,
|
|
style: {
|
|
...chartOptions.xaxis?.labels?.style,
|
|
colors: '#94a3b8'
|
|
}
|
|
}
|
|
};
|
|
|
|
chartOptions.yaxis = {
|
|
...chartOptions.yaxis,
|
|
labels: {
|
|
...chartOptions.yaxis?.labels,
|
|
style: {
|
|
...chartOptions.yaxis?.labels?.style,
|
|
colors: '#94a3b8'
|
|
}
|
|
}
|
|
};
|
|
} else {
|
|
// Light Mode Einstellungen
|
|
chartOptions.theme = {
|
|
mode: 'light',
|
|
palette: 'palette1'
|
|
};
|
|
|
|
chartOptions.grid = {
|
|
...chartOptions.grid,
|
|
borderColor: '#e2e8f0'
|
|
};
|
|
|
|
// Text Farben anpassen
|
|
chartOptions.xaxis = {
|
|
...chartOptions.xaxis,
|
|
labels: {
|
|
...chartOptions.xaxis?.labels,
|
|
style: {
|
|
...chartOptions.xaxis?.labels?.style,
|
|
colors: '#64748b'
|
|
}
|
|
}
|
|
};
|
|
|
|
chartOptions.yaxis = {
|
|
...chartOptions.yaxis,
|
|
labels: {
|
|
...chartOptions.yaxis?.labels,
|
|
style: {
|
|
...chartOptions.yaxis?.labels?.style,
|
|
colors: '#64748b'
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
return chartOptions;
|
|
}
|
|
|
|
/**
|
|
* Event-Listener für Dark Mode Änderungen
|
|
*/
|
|
function setupDarkModeListener() {
|
|
window.addEventListener('darkModeChanged', function(event) {
|
|
const isDark = event.detail?.isDark;
|
|
|
|
// Alle aktiven Charts aktualisieren
|
|
Object.keys(activeCharts).forEach(chartId => {
|
|
const chartInfo = activeCharts[chartId];
|
|
const chart = chartInfo.instance;
|
|
|
|
// Theme aktualisieren
|
|
const updatedOptions = updateChartTheme({
|
|
grid: chart.opts.grid,
|
|
xaxis: chart.opts.xaxis,
|
|
yaxis: chart.opts.yaxis
|
|
});
|
|
|
|
// Chart aktualisieren
|
|
chart.updateOptions({
|
|
theme: updatedOptions.theme,
|
|
grid: updatedOptions.grid,
|
|
xaxis: updatedOptions.xaxis,
|
|
yaxis: updatedOptions.yaxis
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Entfernt alle Chart-Instanzen
|
|
*/
|
|
function destroyAllCharts() {
|
|
Object.keys(activeCharts).forEach(chartId => {
|
|
const chartInfo = activeCharts[chartId];
|
|
if (chartInfo && chartInfo.instance) {
|
|
chartInfo.instance.destroy();
|
|
}
|
|
});
|
|
|
|
// Aktive Charts zurücksetzen
|
|
Object.keys(activeCharts).forEach(key => delete activeCharts[key]);
|
|
}
|
|
|
|
/**
|
|
* Entfernt eine spezifische Chart-Instanz
|
|
* @param {string} chartId - ID des Diagramm-Containers
|
|
*/
|
|
function destroyChart(chartId) {
|
|
const chartInfo = activeCharts[chartId];
|
|
|
|
if (chartInfo && chartInfo.instance) {
|
|
chartInfo.instance.destroy();
|
|
delete activeCharts[chartId];
|
|
}
|
|
}
|
|
|
|
// DOM bereit Event-Listener
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initCharts();
|
|
setupDarkModeListener();
|
|
});
|