Implement data integration tasks with Celery, including periodic fetching and manual refresh of chat data; add utility functions for data processing and transcript handling; create views and URLs for manual data refresh; establish Redis and Celery configuration; enhance error handling and logging; introduce scripts for data cleanup and fixing dashboard data; update documentation for Redis and Celery setup and troubleshooting.

This commit is contained in:
2025-05-18 13:33:11 +00:00
parent e8f2d2adc2
commit 8bbbb109bd
63 changed files with 4601 additions and 164 deletions

View File

@ -2,6 +2,186 @@
* dashboard.css - Styles specific to dashboard functionality
*/
/* Theme variables */
:root {
/* Light theme (default) */
--bg-color: #f8f9fa;
--text-color: #212529;
--card-bg: #ffffff;
--card-border: #dee2e6;
--card-header-bg: #f1f3f5;
--sidebar-bg: #f8f9fa;
--navbar-bg: #343a40;
--navbar-color: #ffffff;
--link-color: #007bff;
--secondary-text: #6c757d;
--border-color: #e9ecef;
--input-bg: #ffffff;
--input-border: #ced4da;
--table-stripe: rgba(0, 0, 0, 0.05);
--stats-card-bg: #f1f3f5;
--icon-bg: #e9f2ff;
--icon-color: #007bff;
--theme-transition:
color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
/* Dark theme */
[data-bs-theme="dark"] {
--bg-color: #212529;
--text-color: #f8f9fa;
--card-bg: #343a40;
--card-border: #495057;
--card-header-bg: #495057;
--sidebar-bg: #2c3034;
--navbar-bg: #1c1f23;
--navbar-color: #f8f9fa;
--link-color: #6ea8fe;
--secondary-text: #adb5bd;
--border-color: #495057;
--input-bg: #2b3035;
--input-border: #495057;
--table-stripe: rgba(255, 255, 255, 0.05);
--stats-card-bg: #2c3034;
--icon-bg: #1e3a8a;
--icon-color: #6ea8fe;
}
/* Apply theme variables */
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: var(--theme-transition);
}
.card {
background-color: var(--card-bg);
border-color: var(--card-border);
transition: var(--theme-transition);
}
.card-header {
background-color: var(--card-header-bg);
border-bottom-color: var(--card-border);
transition: var(--theme-transition);
}
.navbar-dark {
background-color: var(--navbar-bg) !important;
border-bottom: 1px solid var(--border-color);
}
.navbar-dark .navbar-brand,
.navbar-dark .nav-link,
.navbar-dark .navbar-text {
color: var(--navbar-color) !important;
}
.navbar-dark .btn-outline-light {
border-color: var(--border-color);
color: var(--navbar-color);
}
.navbar-dark .btn-outline-light:hover {
background-color: rgba(255, 255, 255, 0.1);
border-color: var(--border-color);
}
.sidebar {
background-color: var(--sidebar-bg) !important;
}
/* Sidebar navigation styling with dark mode support */
.sidebar .nav-link {
color: var(--text-color);
transition: all 0.2s ease;
border-radius: 0.375rem;
margin: 0.1rem 0.5rem;
padding: 0.5rem 1rem;
}
.sidebar .nav-link:hover {
color: var(--link-color);
background-color: rgba(0, 0, 0, 0.05);
}
[data-bs-theme="dark"] .sidebar .nav-link:hover {
background-color: rgba(255, 255, 255, 0.05);
}
.sidebar .nav-link.active {
color: var(--link-color);
background-color: rgba(13, 110, 253, 0.1);
font-weight: 600;
}
[data-bs-theme="dark"] .sidebar .nav-link.active {
background-color: rgba(110, 168, 254, 0.1);
}
.sidebar .nav-link i {
color: var(--secondary-text);
width: 20px;
text-align: center;
margin-right: 0.5rem;
}
.sidebar .nav-link:hover i,
.sidebar .nav-link.active i {
color: var(--link-color);
}
.sidebar .nav-header {
color: var(--secondary-text);
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 0.5rem 1.25rem;
margin-top: 1rem;
}
.table {
color: var(--text-color);
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: var(--table-stripe);
}
.nav-link {
color: var(--link-color);
}
.stats-card {
background-color: var(--stats-card-bg) !important;
}
.stat-card .stat-icon {
background-color: var(--icon-bg);
color: var(--icon-color);
}
.form-control,
.form-select {
background-color: var(--input-bg);
border-color: var(--input-border);
color: var(--text-color);
}
/* Footer */
footer {
background-color: var(--card-bg);
border-top: 1px solid var(--border-color);
color: var(--secondary-text);
margin-top: 2rem;
padding: 1.5rem 0;
transition: var(--theme-transition);
}
[data-bs-theme="dark"] footer {
background-color: var(--navbar-bg);
}
/* Dashboard grid layout */
.dashboard-grid {
display: grid;
@ -291,7 +471,43 @@
}
}
/* --- Stat Boxes Alignment Fix (Bottom Align, No Overlap) --- */
/* Preserve colored background for stat cards in both themes */
.col-md-3 .card.stats-card.bg-primary {
background-color: var(--bs-primary) !important;
color: white !important;
}
.col-md-3 .card.stats-card.bg-success {
background-color: var(--bs-success) !important;
color: white !important;
}
.col-md-3 .card.stats-card.bg-info {
background-color: var(--bs-info) !important;
color: white !important;
}
.col-md-3 .card.stats-card.bg-warning {
background-color: var(--bs-warning) !important;
color: white !important;
}
.col-md-3 .card.stats-card.bg-danger {
background-color: var(--bs-danger) !important;
color: white !important;
}
.col-md-3 .card.stats-card.bg-secondary {
background-color: var(--bs-secondary) !important;
color: white !important;
}
.col-md-3 .card.stats-card.bg-light {
background-color: var(--bs-light) !important;
color: var(--bs-dark) !important;
}
/* Stats Cards Alignment Fix (Bottom Align, No Overlap) */
.stats-row {
display: flex;
flex-wrap: wrap;

View File

@ -7,6 +7,122 @@
*/
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");
@ -20,6 +136,66 @@ 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) {
@ -27,6 +203,29 @@ document.addEventListener("DOMContentLoaded", function () {
}
});
// 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) {
@ -157,11 +356,14 @@ document.addEventListener("DOMContentLoaded", function () {
},
],
{
...window.plotlyDefaultLayout,
margin: { t: 10, r: 10, b: 40, l: 40 },
xaxis: {
...window.plotlyDefaultLayout.xaxis,
title: "Date",
},
yaxis: {
...window.plotlyDefaultLayout.yaxis,
title: "Number of Sessions",
},
},
@ -204,6 +406,7 @@ document.addEventListener("DOMContentLoaded", function () {
},
],
{
...window.plotlyDefaultLayout,
margin: { t: 10, r: 10, b: 10, l: 10 },
},
);
@ -229,8 +432,10 @@ document.addEventListener("DOMContentLoaded", function () {
},
],
{
...window.plotlyDefaultLayout,
margin: { t: 10, r: 10, b: 40, l: 100 },
xaxis: {
...window.plotlyDefaultLayout.xaxis,
title: "Number of Sessions",
},
},
@ -255,6 +460,7 @@ document.addEventListener("DOMContentLoaded", function () {
},
],
{
...window.plotlyDefaultLayout,
margin: { t: 10, r: 10, b: 10, l: 10 },
},
);

View File

@ -148,5 +148,99 @@ document.addEventListener("DOMContentLoaded", function () {
}
}
window.addEventListener("resize", handleSidebarOnResize);
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
function initializeTheme() {
// Check if the user has explicitly set a preference
const hasUserPreference = localStorage.getItem("userPreferredTheme") === "true";
const savedTheme = localStorage.getItem("theme");
const systemTheme = getSystemPreference();
console.log("Theme initialization:", {
hasUserPreference,
savedTheme,
systemTheme,
});
// Use saved theme if it exists and was set by user
// Otherwise, use system preference
if (hasUserPreference && savedTheme) {
setTheme(savedTheme);
} else {
// No user preference, use system preference
setTheme(systemTheme);
// Clear any saved theme to ensure it uses system preference
localStorage.removeItem("userPreferredTheme");
}
}
// Initialize theme on page load
initializeTheme();
// Listen for system preference changes
const colorSchemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
colorSchemeMediaQuery.addEventListener("change", (e) => {
// Only update theme based on system if user hasn't set a preference
const hasUserPreference = localStorage.getItem("userPreferredTheme") === "true";
console.log("System preference changed. Following system?", !hasUserPreference);
if (!hasUserPreference) {
setTheme(e.matches ? "dark" : "light");
}
});
// Theme toggle button functionality
const themeToggle = document.getElementById("theme-toggle");
if (themeToggle) {
themeToggle.addEventListener("click", function () {
const currentTheme = document.documentElement.getAttribute("data-bs-theme") || "light";
const newTheme = currentTheme === "dark" ? "light" : "dark";
console.log("Manual theme toggle from", currentTheme, "to", newTheme);
setTheme(newTheme, true); // true indicates this is a user preference
});
}
});