Fix ticket age, bulk assign, add column visibility toggle
- TicketView: ticket age was measuring from last update not creation; fixed to always use created_at - dashboard.js: bulk assign used non-existent onSelect callback (no selection was ever stored); fixed to onChange with selected[0], added max:1 to enforce single-select - base.js: lt.combobox Enter key only fired when focusedIdx >= 0; now falls back to first filtered result when no arrow key used - DashboardView + dashboard.js + dashboard.css: add COLS ▾ button on table header that opens a checkbox panel to show/hide optional columns (Ticket ID, Category, Type, Created By, Assigned To, Created, Updated); state persisted in localStorage, Reset button restores all; core columns (Priority, Title, Status, Actions) always visible; data-col attributes added to all th/td for CSS targeting Notifications bell: was functional all along — was broken by the notifications.php 500 error (now fixed). Avg resolution: correct, tickets genuinely take ~158 days average on this dataset. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,43 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* ── Column toggle panel ─────────────────────────────────────── */
|
||||
.col-toggle-panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
right: 0;
|
||||
z-index: 200;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
min-width: 160px;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
||||
display: none;
|
||||
}
|
||||
.col-toggle-panel[aria-hidden="false"] { display: block; }
|
||||
.col-toggle-title {
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--text-muted);
|
||||
padding: 0.4rem 0.65rem 0.25rem;
|
||||
border-bottom: 1px solid var(--border-dim);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.col-toggle-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.3rem 0.65rem;
|
||||
font-size: 0.72rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.1s;
|
||||
}
|
||||
.col-toggle-row:hover { background: var(--bg-hover); }
|
||||
.col-toggle-footer {
|
||||
padding: 0.3rem 0.45rem;
|
||||
border-top: 1px solid var(--border-dim);
|
||||
}
|
||||
|
||||
/* Unit suffix on resolution time stat (smaller, muted) */
|
||||
.lt-stat-unit {
|
||||
font-size: 0.65em;
|
||||
|
||||
+1
-1
@@ -1963,7 +1963,7 @@
|
||||
inputEl.addEventListener('keydown', e => {
|
||||
if (e.key === 'ArrowDown') { e.preventDefault(); _moveFocus(1); }
|
||||
if (e.key === 'ArrowUp') { e.preventDefault(); _moveFocus(-1); }
|
||||
if (e.key === 'Enter') { e.preventDefault(); if (focusedIdx >= 0 && filtered[focusedIdx]) _toggle(filtered[focusedIdx].value); }
|
||||
if (e.key === 'Enter') { e.preventDefault(); const idx = focusedIdx >= 0 ? focusedIdx : 0; if (filtered[idx]) _toggle(filtered[idx].value); }
|
||||
if (e.key === 'Escape') { _setOpen(false); }
|
||||
if (e.key === 'Backspace' && !inputEl.value && selected.length) { _toggle(selected[selected.length - 1]); }
|
||||
});
|
||||
|
||||
+77
-1
@@ -603,7 +603,8 @@ function showBulkAssignModal() {
|
||||
label: u.display_name || u.username
|
||||
}));
|
||||
lt.combobox.init(input, items, {
|
||||
onSelect: function(item) { _bulkAssignUserId = item.value; }
|
||||
max: 1,
|
||||
onChange: function(selected) { _bulkAssignUserId = selected[0] || null; }
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -1543,3 +1544,78 @@ setInterval(initRelativeTimes, 60000);
|
||||
// Export for use in other scripts
|
||||
window.showLoadingOverlay = showLoadingOverlay;
|
||||
window.hideLoadingOverlay = hideLoadingOverlay;
|
||||
|
||||
// ── Column visibility toggle ──────────────────────────────────────
|
||||
(function initColToggle() {
|
||||
const LS_KEY = 'lt_col_visibility';
|
||||
|
||||
function getHidden() {
|
||||
try { return JSON.parse(localStorage.getItem(LS_KEY) || '[]'); } catch(_) { return []; }
|
||||
}
|
||||
function saveHidden(cols) {
|
||||
try { localStorage.setItem(LS_KEY, JSON.stringify(cols)); } catch(_) {}
|
||||
}
|
||||
|
||||
function applyVisibility(hidden) {
|
||||
const table = document.getElementById('tickets-table');
|
||||
if (!table) return;
|
||||
// All toggleable columns
|
||||
const all = ['ticket_id','category','type','created_by','assigned_to','created_at','updated_at'];
|
||||
all.forEach(col => {
|
||||
const vis = !hidden.includes(col);
|
||||
table.querySelectorAll('[data-col="' + col + '"]').forEach(el => {
|
||||
el.style.display = vis ? '' : 'none';
|
||||
});
|
||||
});
|
||||
// Update checkboxes
|
||||
document.querySelectorAll('.col-toggle-cb').forEach(cb => {
|
||||
cb.checked = !hidden.includes(cb.dataset.col);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const btn = document.getElementById('colToggleBtn');
|
||||
const panel = document.getElementById('colTogglePanel');
|
||||
const reset = document.getElementById('colToggleReset');
|
||||
if (!btn || !panel) return;
|
||||
|
||||
// Apply saved state on load
|
||||
applyVisibility(getHidden());
|
||||
|
||||
// Toggle panel open/close
|
||||
btn.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
const open = panel.getAttribute('aria-hidden') === 'false';
|
||||
panel.setAttribute('aria-hidden', open ? 'true' : 'false');
|
||||
btn.setAttribute('aria-expanded', open ? 'false' : 'true');
|
||||
btn.textContent = (open ? 'COLS \u25BE' : 'COLS \u25B4');
|
||||
});
|
||||
|
||||
// Close on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!btn.contains(e.target) && !panel.contains(e.target)) {
|
||||
panel.setAttribute('aria-hidden', 'true');
|
||||
btn.setAttribute('aria-expanded', 'false');
|
||||
btn.textContent = 'COLS \u25BE';
|
||||
}
|
||||
});
|
||||
|
||||
// Checkbox change
|
||||
panel.addEventListener('change', function(e) {
|
||||
if (!e.target.classList.contains('col-toggle-cb')) return;
|
||||
const hidden = Array.from(document.querySelectorAll('.col-toggle-cb'))
|
||||
.filter(cb => !cb.checked)
|
||||
.map(cb => cb.dataset.col);
|
||||
saveHidden(hidden);
|
||||
applyVisibility(hidden);
|
||||
});
|
||||
|
||||
// Reset
|
||||
if (reset) {
|
||||
reset.addEventListener('click', function() {
|
||||
saveHidden([]);
|
||||
applyVisibility([]);
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user