(() => { const dataElement = document.getElementById("dashboard-data"); if (!dataElement) { return; } let state = {}; try { state = JSON.parse(dataElement.textContent || "{}"); } catch (error) { console.error("Failed to parse dashboard data", error); return; } const statusElement = document.getElementById("dashboard-status"); const summaryContainer = document.getElementById("summary-metrics"); const summaryEmpty = document.getElementById("summary-empty"); const scenarioTableBody = document.querySelector("#scenario-table tbody"); const scenarioEmpty = document.getElementById("scenario-table-empty"); const overallMetricsList = document.getElementById("overall-metrics"); const overallMetricsEmpty = document.getElementById("overall-metrics-empty"); const recentList = document.getElementById("recent-simulations"); const recentEmpty = document.getElementById("recent-simulations-empty"); const maintenanceList = document.getElementById("upcoming-maintenance"); const maintenanceEmpty = document.getElementById( "upcoming-maintenance-empty" ); const refreshButton = document.getElementById("refresh-dashboard"); const costChartCanvas = document.getElementById("cost-chart"); const costChartEmpty = document.getElementById("cost-chart-empty"); const activityChartCanvas = document.getElementById("activity-chart"); const activityChartEmpty = document.getElementById("activity-chart-empty"); let costChartInstance = null; let activityChartInstance = null; const setStatus = (message, variant = "success") => { if (!statusElement) { return; } if (!message) { statusElement.hidden = true; statusElement.textContent = ""; statusElement.classList.remove("success", "error"); return; } statusElement.textContent = message; statusElement.hidden = false; statusElement.classList.toggle("success", variant === "success"); statusElement.classList.toggle("error", variant !== "success"); }; const renderSummaryMetrics = () => { if (!summaryContainer || !summaryEmpty) { return; } summaryContainer.innerHTML = ""; const metrics = Array.isArray(state.summary_metrics) ? state.summary_metrics : []; metrics.forEach((metric) => { const card = document.createElement("article"); card.className = "metric-card"; card.innerHTML = ` ${metric.label} ${metric.value} `; summaryContainer.appendChild(card); }); summaryEmpty.hidden = metrics.length > 0; }; const renderScenarioTable = () => { if (!scenarioTableBody || !scenarioEmpty) { return; } scenarioTableBody.innerHTML = ""; const rows = Array.isArray(state.scenario_rows) ? state.scenario_rows : []; rows.forEach((row) => { const tr = document.createElement("tr"); tr.innerHTML = ` ${row.scenario_name} ${row.parameter_display} ${row.equipment_display} ${row.capex_display} ${row.opex_display} ${row.production_display} ${row.consumption_display} ${row.maintenance_display} ${row.iterations_display} ${row.simulation_mean_display} `; scenarioTableBody.appendChild(tr); }); scenarioEmpty.hidden = rows.length > 0; }; const renderOverallMetrics = () => { if (!overallMetricsList || !overallMetricsEmpty) { return; } overallMetricsList.innerHTML = ""; const items = Array.isArray(state.overall_report_metrics) ? state.overall_report_metrics : []; items.forEach((item) => { const li = document.createElement("li"); li.className = "metric-list-item"; li.textContent = `${item.label}: ${item.value}`; overallMetricsList.appendChild(li); }); overallMetricsEmpty.hidden = items.length > 0; }; const renderRecentSimulations = () => { if (!recentList || !recentEmpty) { return; } recentList.innerHTML = ""; const runs = Array.isArray(state.recent_simulations) ? state.recent_simulations : []; runs.forEach((run) => { const item = document.createElement("li"); item.className = "metric-list-item"; item.textContent = `${run.scenario_name} · ${run.iterations_display} iterations · ${run.mean_display}`; recentList.appendChild(item); }); recentEmpty.hidden = runs.length > 0; }; const renderMaintenanceReminders = () => { if (!maintenanceList || !maintenanceEmpty) { return; } maintenanceList.innerHTML = ""; const items = Array.isArray(state.upcoming_maintenance) ? state.upcoming_maintenance : []; items.forEach((item) => { const li = document.createElement("li"); li.innerHTML = ` ${item.equipment_name} · ${item.scenario_name} ${item.date_display} · ${item.cost_display} · ${item.description} `; maintenanceList.appendChild(li); }); maintenanceEmpty.hidden = items.length > 0; }; const buildChartConfig = (dataset, overrides = {}) => ({ type: dataset.type || "bar", data: { labels: dataset.labels || [], datasets: dataset.datasets || [], }, options: Object.assign( { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: "top" }, tooltip: { enabled: true }, }, scales: { x: { stacked: dataset.stacked ?? false }, y: { stacked: dataset.stacked ?? false, beginAtZero: true }, }, }, overrides.options || {} ), }); const renderCharts = () => { if (costChartInstance) { costChartInstance.destroy(); } if (activityChartInstance) { activityChartInstance.destroy(); } const costData = state.scenario_cost_chart || {}; const activityData = state.scenario_activity_chart || {}; if (costChartCanvas && state.cost_chart_has_data) { costChartInstance = new Chart( costChartCanvas, buildChartConfig(costData, { options: { scales: { y: { beginAtZero: true, ticks: { callback: (value) => typeof value === "number" ? value.toLocaleString(undefined, { maximumFractionDigits: 0, }) : value, }, }, }, }, }) ); if (costChartEmpty) { costChartEmpty.hidden = true; } costChartCanvas.classList.remove("hidden"); } else if (costChartEmpty && costChartCanvas) { costChartEmpty.hidden = false; costChartCanvas.classList.add("hidden"); } if (activityChartCanvas && state.activity_chart_has_data) { activityChartInstance = new Chart( activityChartCanvas, buildChartConfig(activityData, { options: { scales: { y: { beginAtZero: true, ticks: { callback: (value) => typeof value === "number" ? value.toLocaleString(undefined, { maximumFractionDigits: 0, }) : value, }, }, }, }, }) ); if (activityChartEmpty) { activityChartEmpty.hidden = true; } activityChartCanvas.classList.remove("hidden"); } else if (activityChartEmpty && activityChartCanvas) { activityChartEmpty.hidden = false; activityChartCanvas.classList.add("hidden"); } }; const renderView = () => { renderSummaryMetrics(); renderScenarioTable(); renderOverallMetrics(); renderRecentSimulations(); renderMaintenanceReminders(); renderCharts(); }; const refreshDashboard = async () => { setStatus("Refreshing dashboard…", "success"); if (refreshButton) { refreshButton.classList.add("is-loading"); } try { const response = await fetch("/ui/dashboard/data", { headers: { "X-Requested-With": "XMLHttpRequest" }, }); if (!response.ok) { throw new Error("Unable to refresh dashboard data."); } const payload = await response.json(); state = payload || {}; renderView(); setStatus("Dashboard updated.", "success"); } catch (error) { console.error(error); setStatus(error.message || "Failed to refresh dashboard.", "error"); } finally { if (refreshButton) { refreshButton.classList.remove("is-loading"); } } }; renderView(); if (refreshButton) { refreshButton.addEventListener("click", refreshDashboard); } })();