/** * dashboard.js - JavaScript for the dashboard functionality * * This file handles the interactive features of the dashboard, * including chart refreshing, dashboard filtering, and dashboard * customization. */ document.addEventListener("DOMContentLoaded", function () { // Set up Plotly default config based on theme function updatePlotlyTheme() { // Force a fresh check of the current theme const isDarkMode = document.documentElement.getAttribute("data-bs-theme") === "dark"; console.log( "updatePlotlyTheme called - Current theme mode:", isDarkMode ? "dark" : "light", ); window.plotlyDefaultLayout = { font: { color: isDarkMode ? "#f8f9fa" : "#212529", family: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', }, paper_bgcolor: isDarkMode ? "#343a40" : "#ffffff", plot_bgcolor: isDarkMode ? "#343a40" : "#ffffff", colorway: [ "#4285F4", "#EA4335", "#FBBC05", "#34A853", "#FF6D00", "#46BDC6", "#DB4437", "#0F9D58", "#AB47BC", "#00ACC1", ], margin: { l: 50, r: 30, t: 30, b: 50, pad: 10, }, hovermode: "closest", xaxis: { automargin: true, gridcolor: isDarkMode ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)", zerolinecolor: isDarkMode ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)", title: { font: { color: isDarkMode ? "#f8f9fa" : "#212529", }, }, tickfont: { color: isDarkMode ? "#f8f9fa" : "#212529", }, }, yaxis: { automargin: true, gridcolor: isDarkMode ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)", zerolinecolor: isDarkMode ? "rgba(255,255,255,0.2)" : "rgba(0,0,0,0.2)", title: { font: { color: isDarkMode ? "#f8f9fa" : "#212529", }, }, tickfont: { color: isDarkMode ? "#f8f9fa" : "#212529", }, }, legend: { font: { color: isDarkMode ? "#f8f9fa" : "#212529", }, bgcolor: isDarkMode ? "rgba(52, 58, 64, 0.8)" : "rgba(255, 255, 255, 0.8)", }, modebar: { bgcolor: isDarkMode ? "rgba(52, 58, 64, 0.8)" : "rgba(255, 255, 255, 0.8)", color: isDarkMode ? "#f8f9fa" : "#212529", activecolor: isDarkMode ? "#6ea8fe" : "#007bff", }, }; // Config for specific chart types window.plotlyBarConfig = { ...window.plotlyDefaultLayout, bargap: 0.1, bargroupgap: 0.2, }; window.plotlyPieConfig = { ...window.plotlyDefaultLayout, showlegend: true, legend: { ...window.plotlyDefaultLayout.legend, xanchor: "center", yanchor: "top", y: -0.2, x: 0.5, orientation: "h", }, }; } // Initialize theme setting updatePlotlyTheme(); // Listen for theme changes const observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.attributeName === "data-bs-theme") { console.log( "Theme changed detected by observer:", document.documentElement.getAttribute("data-bs-theme"), ); updatePlotlyTheme(); // Use a small delay to ensure styles have been applied setTimeout(refreshAllCharts, 100); } }); }); observer.observe(document.documentElement, { attributes: true }); // Chart responsiveness function resizeCharts() { const charts = document.querySelectorAll(".chart-container"); charts.forEach((chart) => { if (chart.id && window.Plotly) { Plotly.relayout(chart.id, { "xaxis.automargin": true, "yaxis.automargin": true, }); } }); } // Refresh all charts with current theme function refreshAllCharts() { if (!window.Plotly) return; const currentTheme = document.documentElement.getAttribute("data-bs-theme"); console.log("Refreshing charts with theme:", currentTheme); // Update the theme settings updatePlotlyTheme(); const charts = document.querySelectorAll(".chart-container"); charts.forEach(function (chart) { if (chart.id) { try { // Safe way to check if element has a plot const plotElement = document.getElementById(chart.id); if (plotElement && plotElement._fullLayout) { console.log("Updating chart theme for:", chart.id); // Determine chart type to apply appropriate settings let layoutUpdate = { ...window.plotlyDefaultLayout }; // Check if it's a bar chart if ( plotElement.data && plotElement.data.some((trace) => trace.type === "bar") ) { layoutUpdate = { ...window.plotlyBarConfig }; } // Check if it's a pie chart if ( plotElement.data && plotElement.data.some((trace) => trace.type === "pie") ) { layoutUpdate = { ...window.plotlyPieConfig }; } // Force paper and plot background colors based on current theme // This ensures the chart background always matches the current theme layoutUpdate.paper_bgcolor = currentTheme === "dark" ? "#343a40" : "#ffffff"; layoutUpdate.plot_bgcolor = currentTheme === "dark" ? "#343a40" : "#ffffff"; // Update font colors too layoutUpdate.font.color = currentTheme === "dark" ? "#f8f9fa" : "#212529"; // Apply layout updates Plotly.relayout(chart.id, layoutUpdate); } } catch (e) { console.error("Error updating chart theme:", e); } } }); } // Make refreshAllCharts available globally window.refreshAllCharts = refreshAllCharts; // Handle window resize window.addEventListener("resize", function () { if (window.Plotly) { resizeCharts(); } }); // Call resizeCharts on initial load if (window.Plotly) { // Use a longer delay to ensure charts are fully loaded setTimeout(function () { updatePlotlyTheme(); refreshAllCharts(); }, 300); } // Apply theme to newly created charts const originalPlotlyNewPlot = Plotly.newPlot; Plotly.newPlot = function () { const args = Array.from(arguments); // Get the layout argument (3rd argument) if (args.length >= 3 && typeof args[2] === "object") { // Ensure plotlyDefaultLayout is up to date updatePlotlyTheme(); // Apply current theme to new plot args[2] = { ...window.plotlyDefaultLayout, ...args[2] }; } return originalPlotlyNewPlot.apply(this, args); }; // Time range filtering const timeRangeDropdown = document.getElementById("timeRangeDropdown"); if (timeRangeDropdown) { const timeRangeLinks = timeRangeDropdown.querySelectorAll(".dropdown-item"); timeRangeLinks.forEach((link) => { link.addEventListener("click", function (e) { const url = new URL(this.href); const dashboardId = url.searchParams.get("dashboard_id"); const timeRange = url.searchParams.get("time_range"); // Fetch updated data via AJAX if (dashboardId) { fetchDashboardData(dashboardId, timeRange); e.preventDefault(); } }); }); } // Function to fetch dashboard data function fetchDashboardData(dashboardId, timeRange) { const loadingOverlay = document.createElement("div"); loadingOverlay.className = "loading-overlay"; loadingOverlay.innerHTML = '
Chart library not available. Please refresh the page.
Error rendering chart.
No time series data available