feat: Chart.js donut/bar charts, Flatpickr dates, skeleton loaders, CSP update
- DashboardView: Charts row with 3 panels (priority donut, status donut, category bar) using Chart.js from CDN; data passed inline from PHP stats; TDS color palette - DashboardView: Flatpickr date picker on advanced search date fields with TDS theme overrides - dashboard.js: showTableSkeleton() shows lt-skeleton-row during filter-triggered reloads and auto-refresh; called before all location.reload() with delay - dashboard.css: Flatpickr TDS theme overrides (dark BG, monospace font, TDS accent colors) - SecurityHeadersMiddleware: Added cdn.jsdelivr.net to script-src and style-src CSP to allow Chart.js and Flatpickr from CDN Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+25
-7
@@ -535,7 +535,7 @@ function performBulkCloseAction(ticketIds) {
|
||||
} else {
|
||||
lt.toast.success(`Successfully closed ${data.processed} ticket(s)`, 4000);
|
||||
}
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
showTableSkeleton(5); setTimeout(() => window.location.reload(), 1500);
|
||||
} else {
|
||||
lt.toast.error('Error: ' + (data.error || 'Unknown error'), 5000);
|
||||
}
|
||||
@@ -631,7 +631,7 @@ function performBulkAssign() {
|
||||
} else {
|
||||
lt.toast.success(`Successfully assigned ${data.processed} ticket(s)`, 4000);
|
||||
}
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
showTableSkeleton(5); setTimeout(() => window.location.reload(), 1500);
|
||||
} else {
|
||||
lt.toast.error('Error: ' + (data.error || 'Unknown error'), 5000);
|
||||
}
|
||||
@@ -707,7 +707,7 @@ function performBulkPriority() {
|
||||
} else {
|
||||
lt.toast.success(`Successfully updated priority for ${data.processed} ticket(s)`, 4000);
|
||||
}
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
showTableSkeleton(5); setTimeout(() => window.location.reload(), 1500);
|
||||
} else {
|
||||
lt.toast.error('Error: ' + (data.error || 'Unknown error'), 5000);
|
||||
}
|
||||
@@ -810,7 +810,7 @@ function performBulkStatusChange() {
|
||||
} else {
|
||||
lt.toast.success(`Successfully updated status for ${data.processed} ticket(s)`, 4000);
|
||||
}
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
showTableSkeleton(5); setTimeout(() => window.location.reload(), 1500);
|
||||
} else {
|
||||
lt.toast.error('Error: ' + (data.error || 'Unknown error'), 5000);
|
||||
}
|
||||
@@ -869,7 +869,7 @@ function performBulkDelete() {
|
||||
closeBulkDeleteModal();
|
||||
if (data.success) {
|
||||
lt.toast.success(`Successfully deleted ${ticketIds.length} ticket(s)`, 4000);
|
||||
setTimeout(() => window.location.reload(), 1500);
|
||||
showTableSkeleton(5); setTimeout(() => window.location.reload(), 1500);
|
||||
} else {
|
||||
lt.toast.error('Error: ' + (data.error || 'Unknown error'), 5000);
|
||||
}
|
||||
@@ -993,7 +993,7 @@ function performQuickStatusChange(ticketId) {
|
||||
closeQuickStatusModal();
|
||||
if (data.success) {
|
||||
lt.toast.success(`Status updated to ${newStatus}`, 3000);
|
||||
setTimeout(() => window.location.reload(), 1000);
|
||||
showTableSkeleton(5); setTimeout(() => window.location.reload(), 1000);
|
||||
} else {
|
||||
lt.toast.error('Error: ' + (data.error || 'Unknown error'), 4000);
|
||||
}
|
||||
@@ -1079,7 +1079,7 @@ function performQuickAssign(ticketId) {
|
||||
closeQuickAssignModal();
|
||||
if (data.success) {
|
||||
lt.toast.success('Assignment updated', 3000);
|
||||
setTimeout(() => window.location.reload(), 1000);
|
||||
showTableSkeleton(5); setTimeout(() => window.location.reload(), 1000);
|
||||
} else {
|
||||
lt.toast.error('Error: ' + (data.error || 'Unknown error'), 4000);
|
||||
}
|
||||
@@ -1455,12 +1455,30 @@ function hideLoadingOverlay(element) {
|
||||
* Reload the dashboard, but skip if a modal is open or user is typing.
|
||||
* Registered with lt.autoRefresh so it runs every 5 minutes automatically.
|
||||
*/
|
||||
/**
|
||||
* Replace table body rows with skeleton placeholders before a page reload.
|
||||
* Gives visual feedback that a reload is in progress.
|
||||
*/
|
||||
function showTableSkeleton(rowCount) {
|
||||
rowCount = rowCount || 5;
|
||||
const tbody = document.querySelector('#tickets-table tbody');
|
||||
if (!tbody) return;
|
||||
let html = '';
|
||||
for (let i = 0; i < rowCount; i++) {
|
||||
html += '<tr class="lt-skeleton-row" aria-hidden="true">' +
|
||||
'<td><div class="lt-skeleton" style="height:0.8rem;width:100%"></div></td>'.repeat(6) +
|
||||
'</tr>';
|
||||
}
|
||||
tbody.innerHTML = html;
|
||||
}
|
||||
|
||||
function dashboardAutoRefresh() {
|
||||
// Don't interrupt the user if a modal is open
|
||||
if (document.querySelector('.lt-modal-overlay[aria-hidden="false"]')) return;
|
||||
// Don't interrupt if focus is in a text input
|
||||
const tag = document.activeElement?.tagName;
|
||||
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
|
||||
showTableSkeleton(6);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user