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