mirror of
https://github.com/kjanat/livegraphs-django.git
synced 2026-02-13 12:55:42 +01:00
refactor: move functions to outer scope for better performance
- Move setupAjaxPagination to outer scope in ajax-pagination.js - Move setupAjaxNavigation, reloadScripts, initializePageScripts to outer scope in ajax-navigation.js - Move updatePlotlyTheme, resizeCharts, updateDashboardStats, updateDashboardCharts to outer scope in dashboard.js - Move handleSidebarOnResize, setTheme, getSystemPreference to outer scope in main.js - Avoid recreating functions on every DOMContentLoaded call - All oxlint strict checks now pass (11 warnings -> 0)
This commit is contained in:
@@ -6,10 +6,50 @@
|
|||||||
* It intercepts link clicks, loads content via AJAX, and updates the browser history.
|
* It intercepts link clicks, loads content via AJAX, and updates the browser history.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
// Function to reload and execute scripts in new content
|
||||||
// Only initialize if AJAX navigation is enabled
|
function reloadScripts(container) {
|
||||||
if (typeof ENABLE_AJAX_NAVIGATION !== "undefined" && ENABLE_AJAX_NAVIGATION) {
|
const scripts = container.getElementsByTagName("script");
|
||||||
setupAjaxNavigation();
|
for (let script of scripts) {
|
||||||
|
const newScript = document.createElement("script");
|
||||||
|
|
||||||
|
// Copy all attributes
|
||||||
|
Array.from(script.attributes).forEach((attr) => {
|
||||||
|
newScript.setAttribute(attr.name, attr.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy inline script content
|
||||||
|
newScript.textContent = script.textContent;
|
||||||
|
|
||||||
|
// Replace old script with new one
|
||||||
|
script.parentNode.replaceChild(newScript, script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to initialize scripts needed for the new page content
|
||||||
|
function initializePageScripts() {
|
||||||
|
// Re-initialize any custom scripts that might be needed
|
||||||
|
if (typeof setupAjaxPagination === "function") {
|
||||||
|
setupAjaxPagination();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Bootstrap tooltips, popovers, etc.
|
||||||
|
if (typeof bootstrap !== "undefined") {
|
||||||
|
// Initialize tooltips
|
||||||
|
const tooltipTriggerList = [].slice.call(
|
||||||
|
document.querySelectorAll('[data-bs-toggle="tooltip"]'),
|
||||||
|
);
|
||||||
|
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||||
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize popovers
|
||||||
|
const popoverTriggerList = [].slice.call(
|
||||||
|
document.querySelectorAll('[data-bs-toggle="popover"]'),
|
||||||
|
);
|
||||||
|
popoverTriggerList.map(function (popoverTriggerEl) {
|
||||||
|
return new bootstrap.Popover(popoverTriggerEl);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to set up AJAX navigation for the application
|
// Function to set up AJAX navigation for the application
|
||||||
@@ -122,25 +162,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to reload and execute scripts in new content
|
|
||||||
function reloadScripts(container) {
|
|
||||||
const scripts = container.getElementsByTagName("script");
|
|
||||||
for (let script of scripts) {
|
|
||||||
const newScript = document.createElement("script");
|
|
||||||
|
|
||||||
// Copy all attributes
|
|
||||||
Array.from(script.attributes).forEach((attr) => {
|
|
||||||
newScript.setAttribute(attr.name, attr.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Copy inline script content
|
|
||||||
newScript.textContent = script.textContent;
|
|
||||||
|
|
||||||
// Replace old script with new one
|
|
||||||
script.parentNode.replaceChild(newScript, script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to handle form submissions
|
// Function to handle form submissions
|
||||||
function handleFormSubmission(form, e) {
|
function handleFormSubmission(form, e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -204,33 +225,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to initialize scripts needed for the new page content
|
|
||||||
function initializePageScripts() {
|
|
||||||
// Re-initialize any custom scripts that might be needed
|
|
||||||
if (typeof setupAjaxPagination === "function") {
|
|
||||||
setupAjaxPagination();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize Bootstrap tooltips, popovers, etc.
|
|
||||||
if (typeof bootstrap !== "undefined") {
|
|
||||||
// Initialize tooltips
|
|
||||||
const tooltipTriggerList = [].slice.call(
|
|
||||||
document.querySelectorAll('[data-bs-toggle="tooltip"]'),
|
|
||||||
);
|
|
||||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|
||||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize popovers
|
|
||||||
const popoverTriggerList = [].slice.call(
|
|
||||||
document.querySelectorAll('[data-bs-toggle="popover"]'),
|
|
||||||
);
|
|
||||||
popoverTriggerList.map(function (popoverTriggerEl) {
|
|
||||||
return new bootstrap.Popover(popoverTriggerEl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to attach event listeners to forms and links
|
// Function to attach event listeners to forms and links
|
||||||
function attachEventListeners() {
|
function attachEventListeners() {
|
||||||
// Handle AJAX navigation links
|
// Handle AJAX navigation links
|
||||||
@@ -271,4 +265,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
// Only initialize if AJAX navigation is enabled
|
||||||
|
if (typeof ENABLE_AJAX_NAVIGATION !== "undefined" && ENABLE_AJAX_NAVIGATION) {
|
||||||
|
setupAjaxNavigation();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
|
|
||||||
* ajax-pagination.js - Common JavaScript for AJAX pagination across the application
|
* ajax-pagination.js - Common JavaScript for AJAX pagination across the application
|
||||||
*
|
*
|
||||||
* This script handles AJAX-based pagination for all pages in the Chat Analytics Dashboard.
|
* This script handles AJAX-based pagination for all pages in the Chat Analytics Dashboard.
|
||||||
* It intercepts pagination link clicks, loads content via AJAX, and updates the browser history.
|
* It intercepts pagination link clicks, loads content via AJAX, and updates the browser history.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
// Initialize AJAX pagination
|
|
||||||
setupAjaxPagination();
|
|
||||||
|
|
||||||
// Function to set up AJAX pagination for the entire application
|
// Function to set up AJAX pagination for the entire application
|
||||||
function setupAjaxPagination() {
|
function setupAjaxPagination() {
|
||||||
// Configuration - can be customized per page if needed
|
// Configuration - can be customized per page if needed
|
||||||
@@ -104,4 +99,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
// Initialize AJAX pagination
|
||||||
|
setupAjaxPagination();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
* customization.
|
* customization.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
// Set up Plotly default config based on theme
|
// Set up Plotly default config based on theme
|
||||||
function updatePlotlyTheme() {
|
function updatePlotlyTheme() {
|
||||||
// Force a fresh check of the current theme
|
// Force a fresh check of the current theme
|
||||||
@@ -102,26 +101,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// Chart responsiveness
|
||||||
function resizeCharts() {
|
function resizeCharts() {
|
||||||
const charts = document.querySelectorAll(".chart-container");
|
const charts = document.querySelectorAll(".chart-container");
|
||||||
@@ -135,154 +114,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 =
|
|
||||||
'<div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div>';
|
|
||||||
document.querySelector("main").appendChild(loadingOverlay);
|
|
||||||
|
|
||||||
fetch(`/dashboard/api/dashboard/${dashboardId}/data/?time_range=${timeRange || "all"}`)
|
|
||||||
.then((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Network response was not ok: ${response.status}`);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
console.log("Dashboard API response:", data);
|
|
||||||
updateDashboardStats(data);
|
|
||||||
updateDashboardCharts(data);
|
|
||||||
|
|
||||||
// Update URL without page reload
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
url.searchParams.set("dashboard_id", dashboardId);
|
|
||||||
if (timeRange) {
|
|
||||||
url.searchParams.set("time_range", timeRange);
|
|
||||||
}
|
|
||||||
window.history.pushState({}, "", url);
|
|
||||||
|
|
||||||
document.querySelector(".loading-overlay").remove();
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error fetching dashboard data:", error);
|
|
||||||
document.querySelector(".loading-overlay").remove();
|
|
||||||
|
|
||||||
// Show error message
|
|
||||||
const alertElement = document.createElement("div");
|
|
||||||
alertElement.className = "alert alert-danger alert-dismissible fade show";
|
|
||||||
alertElement.setAttribute("role", "alert");
|
|
||||||
alertElement.innerHTML = `
|
|
||||||
Error loading dashboard data. Please try again.
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
||||||
`;
|
|
||||||
document.querySelector("main").prepend(alertElement);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to update dashboard statistics
|
// Function to update dashboard statistics
|
||||||
function updateDashboardStats(data) {
|
function updateDashboardStats(data) {
|
||||||
// Update total sessions
|
// Update total sessions
|
||||||
@@ -459,6 +290,175 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
// 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 });
|
||||||
|
|
||||||
|
// 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 =
|
||||||
|
'<div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div>';
|
||||||
|
document.querySelector("main").appendChild(loadingOverlay);
|
||||||
|
|
||||||
|
fetch(`/dashboard/api/dashboard/${dashboardId}/data/?time_range=${timeRange || "all"}`)
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Network response was not ok: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
console.log("Dashboard API response:", data);
|
||||||
|
updateDashboardStats(data);
|
||||||
|
updateDashboardCharts(data);
|
||||||
|
|
||||||
|
// Update URL without page reload
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.set("dashboard_id", dashboardId);
|
||||||
|
if (timeRange) {
|
||||||
|
url.searchParams.set("time_range", timeRange);
|
||||||
|
}
|
||||||
|
window.history.pushState({}, "", url);
|
||||||
|
|
||||||
|
document.querySelector(".loading-overlay").remove();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error fetching dashboard data:", error);
|
||||||
|
document.querySelector(".loading-overlay").remove();
|
||||||
|
|
||||||
|
// Show error message
|
||||||
|
const alertElement = document.createElement("div");
|
||||||
|
alertElement.className = "alert alert-danger alert-dismissible fade show";
|
||||||
|
alertElement.setAttribute("role", "alert");
|
||||||
|
alertElement.innerHTML = `
|
||||||
|
Error loading dashboard data. Please try again.
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
`;
|
||||||
|
document.querySelector("main").prepend(alertElement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Dashboard selector
|
// Dashboard selector
|
||||||
const dashboardSelector = document.querySelectorAll('a[href^="?dashboard_id="]');
|
const dashboardSelector = document.querySelectorAll('a[href^="?dashboard_id="]');
|
||||||
dashboardSelector.forEach((link) => {
|
dashboardSelector.forEach((link) => {
|
||||||
|
|||||||
@@ -6,6 +6,58 @@
|
|||||||
* the entire application, including navigation, forms, and UI interactions.
|
* the entire application, including navigation, forms, and UI interactions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Handle sidebar collapse on small screens
|
||||||
|
function handleSidebarOnResize() {
|
||||||
|
if (window.innerWidth < 768) {
|
||||||
|
document.querySelector(".sidebar")?.classList.remove("show");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme toggling functionality
|
||||||
|
function setTheme(theme, isUserPreference = false) {
|
||||||
|
console.log("Setting theme to:", theme, "User preference:", isUserPreference);
|
||||||
|
|
||||||
|
// Update the HTML attribute that controls theme
|
||||||
|
document.documentElement.setAttribute("data-bs-theme", theme);
|
||||||
|
|
||||||
|
// Save the theme preference to localStorage
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
|
||||||
|
// If this was a user choice (from the toggle button), record that fact
|
||||||
|
if (isUserPreference) {
|
||||||
|
localStorage.setItem("userPreferredTheme", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update toggle button icon
|
||||||
|
const themeToggle = document.getElementById("theme-toggle");
|
||||||
|
if (themeToggle) {
|
||||||
|
const icon = themeToggle.querySelector("i");
|
||||||
|
if (theme === "dark") {
|
||||||
|
icon.classList.remove("fa-moon");
|
||||||
|
icon.classList.add("fa-sun");
|
||||||
|
themeToggle.setAttribute("title", "Switch to light mode");
|
||||||
|
themeToggle.setAttribute("aria-label", "Switch to light mode");
|
||||||
|
} else {
|
||||||
|
icon.classList.remove("fa-sun");
|
||||||
|
icon.classList.add("fa-moon");
|
||||||
|
themeToggle.setAttribute("title", "Switch to dark mode");
|
||||||
|
themeToggle.setAttribute("aria-label", "Switch to dark mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're on a page with charts, refresh them to match the theme
|
||||||
|
if (typeof window.refreshAllCharts === "function") {
|
||||||
|
console.log("Calling refresh charts from theme toggle");
|
||||||
|
// Add a small delay to ensure DOM updates have completed
|
||||||
|
setTimeout(() => window.refreshAllCharts(), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user has a system preference for dark mode
|
||||||
|
function getSystemPreference() {
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Initialize tooltips
|
// Initialize tooltips
|
||||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||||
@@ -142,57 +194,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle sidebar collapse on small screens
|
window.addEventListener("resize", handleSidebarOnResize);
|
||||||
function handleSidebarOnResize() {
|
|
||||||
if (window.innerWidth < 768) {
|
|
||||||
document.querySelector(".sidebar")?.classList.remove("show");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("resize", handleSidebarOnResize); // Theme toggling functionality
|
|
||||||
function setTheme(theme, isUserPreference = false) {
|
|
||||||
console.log("Setting theme to:", theme, "User preference:", isUserPreference);
|
|
||||||
|
|
||||||
// Update the HTML attribute that controls theme
|
|
||||||
document.documentElement.setAttribute("data-bs-theme", theme);
|
|
||||||
|
|
||||||
// Save the theme preference to localStorage
|
|
||||||
localStorage.setItem("theme", theme);
|
|
||||||
|
|
||||||
// If this was a user choice (from the toggle button), record that fact
|
|
||||||
if (isUserPreference) {
|
|
||||||
localStorage.setItem("userPreferredTheme", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update toggle button icon
|
|
||||||
const themeToggle = document.getElementById("theme-toggle");
|
|
||||||
if (themeToggle) {
|
|
||||||
const icon = themeToggle.querySelector("i");
|
|
||||||
if (theme === "dark") {
|
|
||||||
icon.classList.remove("fa-moon");
|
|
||||||
icon.classList.add("fa-sun");
|
|
||||||
themeToggle.setAttribute("title", "Switch to light mode");
|
|
||||||
themeToggle.setAttribute("aria-label", "Switch to light mode");
|
|
||||||
} else {
|
|
||||||
icon.classList.remove("fa-sun");
|
|
||||||
icon.classList.add("fa-moon");
|
|
||||||
themeToggle.setAttribute("title", "Switch to dark mode");
|
|
||||||
themeToggle.setAttribute("aria-label", "Switch to dark mode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're on a page with charts, refresh them to match the theme
|
|
||||||
if (typeof window.refreshAllCharts === "function") {
|
|
||||||
console.log("Calling refresh charts from theme toggle");
|
|
||||||
// Add a small delay to ensure DOM updates have completed
|
|
||||||
setTimeout(() => window.refreshAllCharts(), 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the user has a system preference for dark mode
|
|
||||||
function getSystemPreference() {
|
|
||||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize theme based on saved preference or system setting
|
// Initialize theme based on saved preference or system setting
|
||||||
function initializeTheme() {
|
function initializeTheme() {
|
||||||
|
|||||||
Reference in New Issue
Block a user