Fix views/controllers/router: command palette, create form, admin views
- Consolidate the duplicated command palette to a single overlay + init in the footer; fix New Ticket to route to /ticket/create (was a 404 /create); keep the CSP nonce and all commands - TicketController create(): trim title, require a non-empty description, and honor the posted status (validated against the canonical list) instead of silently discarding it - UserActivityView: 'Active Users' counts only users active in the selected range, not every registered user - layout_footer/DashboardView: local esc() now escapes quotes so values used in HTML attributes can't break out - TicketView: comments tab badge shows the true total, not just page one - layout_header: gate the 'View activity log' link behind the admin flag - index.php: validate /admin/user-activity date params; anchor the legacy /ticket.php route; align the audit action-type whitelist with the dropdown - ApiKeysView: correct the external API sample to /create_ticket_api.php Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+3
-62
@@ -196,7 +196,9 @@ $_lt_assetVer = $GLOBALS['config']['ASSET_VERSION'] ?? '20260329';
|
||||
<div style="padding:0.75rem;font-size:0.75rem;color:var(--text-muted);text-align:center">Loading…</div>
|
||||
</div>
|
||||
<div class="lt-notif-panel-footer">
|
||||
<?php if ($_lt_isAdmin) : ?>
|
||||
<a href="/admin/audit-log" class="lt-btn lt-btn-ghost lt-btn-sm lt-w-full lt-text-center">View activity log</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -212,67 +214,6 @@ $_lt_assetVer = $GLOBALS['config']['ASSET_VERSION'] ?? '20260329';
|
||||
|
||||
</header><!-- /.lt-header -->
|
||||
|
||||
<!-- ── COMMAND PALETTE OVERLAY (Ctrl+K / ⌘K) ──────────────────── -->
|
||||
<div id="lt-cmd-overlay" class="lt-cmd-overlay" role="dialog" aria-modal="true" aria-label="Command palette" aria-hidden="true">
|
||||
<div id="lt-cmd-palette" class="lt-cmd-palette" role="combobox" aria-expanded="true" aria-haspopup="listbox">
|
||||
<div class="lt-cmd-input-wrap">
|
||||
<span aria-hidden="true" style="opacity:0.45;margin-right:0.4rem;font-size:0.9em">⌕</span>
|
||||
<input class="lt-cmd-input" type="text" placeholder="Type a command or search…"
|
||||
autocomplete="off" spellcheck="false" aria-label="Command search" aria-autocomplete="list"
|
||||
aria-controls="lt-cmd-results-list">
|
||||
<kbd style="font-size:0.6rem;opacity:0.4;white-space:nowrap">ESC</kbd>
|
||||
</div>
|
||||
<div class="lt-cmd-results" id="lt-cmd-results-list" role="listbox"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="<?= htmlspecialchars($nonce, ENT_QUOTES, 'UTF-8') ?>">
|
||||
(function() {
|
||||
var isAdmin = <?= json_encode($_lt_isAdmin) ?>;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var commands = [
|
||||
{ id: 'nav-dashboard', label: 'Dashboard', icon: '⌂', group: 'Navigate', action: function(){ location.href = '/'; } },
|
||||
{ id: 'nav-new-ticket', label: 'New Ticket', icon: '+', group: 'Navigate', kbd: 'N', action: function(){ location.href = '/create'; } },
|
||||
{ id: 'filter-mine', label: 'My Open Tickets', icon: '◈', group: 'Filter', action: function(){ location.href = '/?assigned_to=me&status=Open,In+Progress,Pending'; } },
|
||||
{ id: 'filter-unassigned', label: 'Unassigned Tickets', icon: '◌', group: 'Filter', action: function(){ location.href = '/?assigned_to=unassigned'; } },
|
||||
{ id: 'filter-critical', label: 'P1 Critical Tickets', icon: '!', group: 'Filter', action: function(){ location.href = '/?priority=1'; } },
|
||||
];
|
||||
if (isAdmin) {
|
||||
[
|
||||
{ id: 'admin-templates', label: 'Admin: Templates', icon: '▤', href: '/admin/templates' },
|
||||
{ id: 'admin-workflow', label: 'Admin: Workflow', icon: '⇌', href: '/admin/workflow' },
|
||||
{ id: 'admin-audit', label: 'Admin: Audit Log', icon: '📋', href: '/admin/audit-log' },
|
||||
{ id: 'admin-api-keys', label: 'Admin: API Keys', icon: '🔑', href: '/admin/api-keys' },
|
||||
{ id: 'admin-users', label: 'Admin: User Activity', icon: '👤', href: '/admin/user-activity' },
|
||||
{ id: 'admin-recurring', label: 'Admin: Recurring', icon: '↻', href: '/admin/recurring-tickets' },
|
||||
{ id: 'admin-fields', label: 'Admin: Custom Fields', icon: '⊞', href: '/admin/custom-fields' },
|
||||
].forEach(function(c) {
|
||||
commands.push({ id: c.id, label: c.label, icon: c.icon, group: 'Admin', action: function(href){ return function(){ location.href = href; }; }(c.href) });
|
||||
});
|
||||
}
|
||||
// Inject recent ticket IDs from localStorage
|
||||
try {
|
||||
var recent = JSON.parse(localStorage.getItem('lt_recent_tickets') || '[]');
|
||||
recent.slice(0, 5).forEach(function(id) {
|
||||
commands.push({ id: 'recent-' + id, label: 'Ticket #' + id, icon: '◷', group: 'Recent', tags: ['ticket'], action: function(tid){ return function(){ location.href = '/ticket/' + tid; }; }(id) });
|
||||
});
|
||||
} catch(_) {}
|
||||
if (window.lt && lt.cmdPalette) lt.cmdPalette.init(commands);
|
||||
// Bind the header ⌘K trigger here (no inline onclick — CSP blocks inline handlers)
|
||||
var cmdTrigger = document.getElementById('lt-cmd-trigger');
|
||||
if (cmdTrigger) {
|
||||
cmdTrigger.addEventListener('click', function() {
|
||||
if (window.lt && lt.cmdPalette) lt.cmdPalette.open();
|
||||
});
|
||||
}
|
||||
});
|
||||
// Keyboard shortcut: Ctrl+K / Cmd+K
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||
e.preventDefault();
|
||||
if (window.lt && lt.cmdPalette) lt.cmdPalette.open();
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<!-- Command palette overlay + init live in layout_footer.php (single instance) -->
|
||||
|
||||
<main class="lt-main lt-container" id="main-content" style="padding-top: calc(var(--header-height, 56px) + var(--space-lg, 1.5rem))">
|
||||
|
||||
Reference in New Issue
Block a user