diff --git a/assets/css/dashboard.css b/assets/css/dashboard.css index 4a5dcaf..7d8d265 100644 --- a/assets/css/dashboard.css +++ b/assets/css/dashboard.css @@ -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; diff --git a/assets/js/base.js b/assets/js/base.js index cd20236..4d0e7e1 100644 --- a/assets/js/base.js +++ b/assets/js/base.js @@ -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]); } }); diff --git a/assets/js/dashboard.js b/assets/js/dashboard.js index 590b7ae..1574365 100644 --- a/assets/js/dashboard.js +++ b/assets/js/dashboard.js @@ -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([]); + }); + } + }); +})(); diff --git a/views/DashboardView.php b/views/DashboardView.php index 0737710..a5b909d 100644 --- a/views/DashboardView.php +++ b/views/DashboardView.php @@ -568,7 +568,40 @@ include __DIR__ . '/layout_header.php';
| Actions | +Actions | $col, 'dir' => $newDir, 'page' => 1]); $sortUrl = htmlspecialchars('?' . http_build_query($sortParams), ENT_QUOTES, 'UTF-8'); ?> -style="cursor:pointer">= $label ?> | @@ -646,20 +679,20 @@ include __DIR__ . '/layout_header.php'; aria-label="Select ticket = htmlspecialchars($row['ticket_id']) ?>"> -+ | = htmlspecialchars($row['ticket_id']) ?> | -+ | 'lt-badge-p1', 2 => 'lt-badge-p2', 3 => 'lt-badge-p3', default => 'lt-badge-p4' }; ?> P= $pNum ?> | - -= htmlspecialchars($row['category']) ?> | -= htmlspecialchars($row['type']) ?> | -+ | = htmlspecialchars($row['category']) ?> | += htmlspecialchars($row['type']) ?> | +'lt-dot-up', 'In Progress' => 'lt-dot-warn', @@ -670,8 +703,8 @@ include __DIR__ . '/layout_header.php'; = htmlspecialchars($row['status']) ?> | -= $creator ?> | -+ | = $creator ?> | += htmlspecialchars($assigneeDisplay) ?> @@ -679,10 +712,10 @@ include __DIR__ . '/layout_header.php'; Unassigned | -= $createdFmt ?> | -= $updatedFmt ?> | diff --git a/views/TicketView.php b/views/TicketView.php index e342fdd..21cba8e 100644 --- a/views/TicketView.php +++ b/views/TicketView.php @@ -64,9 +64,8 @@ function formatAction(array $event): string { } } -// Calculate ticket age -$lastUpdate = !empty($ticket['updated_at']) ? strtotime($ticket['updated_at']) : strtotime($ticket['created_at']); -$ageSeconds = time() - $lastUpdate; +// Calculate ticket age from creation (not last update) +$ageSeconds = time() - strtotime($ticket['created_at']); $ageDays = floor($ageSeconds / 86400); $ageHours = floor(($ageSeconds % 86400) / 3600); $ageClass = 'lt-text-muted'; |
|---|