fix: watcher avatars, dependency TDS styling, asset versions, nav dropdown light theme
- watch_ticket.php GET now returns watcher list (up to 6 users) for avatar group - TicketView: watcher avatar group rendered next to WATCH button, refreshes on toggle - Rewrite renderDependencies/renderDependents to use TDS lt-kv-grid/lt-badge/lt-btn classes - renderDependencies: show lt-alert--warning blocker banner when blocked_by has open tickets - Fix ALL hardcoded ?v=20260327 asset version strings in CreateTicketView + all admin views - base.css: fix .lt-nav-dropdown-menu hardcoded background → var(--bg-overlay) - base.css: add light-theme overrides for nav dropdown menu (background, links, hover) - ticket.css: add .lt-avatar-group and .lt-avatar--overflow styles for watcher display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+18
-6
@@ -84,16 +84,28 @@ $watchingStmt->execute();
|
|||||||
$watching = (bool)$watchingStmt->get_result()->fetch_assoc()['cnt'];
|
$watching = (bool)$watchingStmt->get_result()->fetch_assoc()['cnt'];
|
||||||
$watchingStmt->close();
|
$watchingStmt->close();
|
||||||
|
|
||||||
$countStmt = $conn->prepare(
|
// Fetch watcher list (up to 6) with display names for avatar group
|
||||||
"SELECT COUNT(*) as cnt FROM ticket_watchers WHERE ticket_id = ?"
|
$watchersStmt = $conn->prepare(
|
||||||
|
"SELECT u.user_id, COALESCE(u.display_name, u.username) AS display_name
|
||||||
|
FROM ticket_watchers tw
|
||||||
|
JOIN users u ON tw.user_id = u.user_id
|
||||||
|
WHERE tw.ticket_id = ?
|
||||||
|
ORDER BY tw.created_at ASC
|
||||||
|
LIMIT 6"
|
||||||
);
|
);
|
||||||
$countStmt->bind_param("i", $ticketId);
|
$watchersStmt->bind_param("i", $ticketId);
|
||||||
$countStmt->execute();
|
$watchersStmt->execute();
|
||||||
$count = (int)$countStmt->get_result()->fetch_assoc()['cnt'];
|
$watchersResult = $watchersStmt->get_result();
|
||||||
$countStmt->close();
|
$watchers = [];
|
||||||
|
while ($row = $watchersResult->fetch_assoc()) {
|
||||||
|
$watchers[] = ['user_id' => (int)$row['user_id'], 'display_name' => $row['display_name']];
|
||||||
|
}
|
||||||
|
$watchersStmt->close();
|
||||||
|
$count = count($watchers);
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'watching' => $watching,
|
'watching' => $watching,
|
||||||
'watcher_count' => $count,
|
'watcher_count' => $count,
|
||||||
|
'watchers' => $watchers,
|
||||||
]);
|
]);
|
||||||
|
|||||||
+18
-1
@@ -525,7 +525,7 @@ hr {
|
|||||||
top: calc(100% + 4px);
|
top: calc(100% + 4px);
|
||||||
left: 0;
|
left: 0;
|
||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
background: rgba(6,12,20,0.98);
|
background: var(--bg-overlay, rgba(6,12,20,0.98));
|
||||||
border: 1px solid var(--accent-cyan-border);
|
border: 1px solid var(--accent-cyan-border);
|
||||||
box-shadow: var(--box-glow-cyan), 0 16px 40px rgba(0,0,0,0.8);
|
box-shadow: var(--box-glow-cyan), 0 16px 40px rgba(0,0,0,0.8);
|
||||||
z-index: var(--z-dropdown);
|
z-index: var(--z-dropdown);
|
||||||
@@ -3781,6 +3781,23 @@ html[data-theme="light"] .lt-drawer-right-header { background: var(--bg-seconda
|
|||||||
html[data-theme="light"] .lt-drawer-right-footer { background: var(--bg-secondary); border-top-color: var(--border-color); }
|
html[data-theme="light"] .lt-drawer-right-footer { background: var(--bg-secondary); border-top-color: var(--border-color); }
|
||||||
html[data-theme="light"] .lt-drawer-right-overlay { background: rgba(30,40,70,0.35); }
|
html[data-theme="light"] .lt-drawer-right-overlay { background: rgba(30,40,70,0.35); }
|
||||||
|
|
||||||
|
/* — Nav dropdown menu — */
|
||||||
|
html[data-theme="light"] .lt-nav-dropdown-menu {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
html[data-theme="light"] .lt-nav-dropdown-menu::before { display: none; }
|
||||||
|
html[data-theme="light"] .lt-nav-dropdown-menu li a {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
border-bottom-color: var(--border-dim);
|
||||||
|
}
|
||||||
|
html[data-theme="light"] .lt-nav-dropdown-menu li a:hover {
|
||||||
|
color: var(--accent-orange);
|
||||||
|
background: var(--accent-orange-dim);
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* — Dropdowns & notification panel — */
|
/* — Dropdowns & notification panel — */
|
||||||
html[data-theme="light"] .lt-dropdown-panel {
|
html[data-theme="light"] .lt-dropdown-panel {
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
|
|||||||
@@ -270,6 +270,26 @@ kbd {
|
|||||||
.thread-depth-3 { margin-left: 2.25rem; }
|
.thread-depth-3 { margin-left: 2.25rem; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Watcher avatar group in toolbar ────────────────────────── */
|
||||||
|
.lt-avatar-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.lt-avatar-group .lt-avatar {
|
||||||
|
margin-left: -0.4rem;
|
||||||
|
border: 1px solid var(--bg-primary, #030508);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.lt-avatar-group .lt-avatar:first-child { margin-left: 0; }
|
||||||
|
.lt-avatar--overflow {
|
||||||
|
background: var(--bg-tertiary, #1a1f2e);
|
||||||
|
border: 1px solid var(--border-dim, rgba(0,255,65,0.2)) !important;
|
||||||
|
font-size: 0.55rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-muted);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Description read view ───────────────────────────────────── */
|
/* ── Description read view ───────────────────────────────────── */
|
||||||
/* Shown in read mode instead of a disabled (faded) textarea. */
|
/* Shown in read mode instead of a disabled (faded) textarea. */
|
||||||
/* Uses lt-markdown typography for full contrast on dark/OLED. */
|
/* Uses lt-markdown typography for full contrast on dark/OLED. */
|
||||||
|
|||||||
+49
-32
@@ -552,6 +552,12 @@ function showDependencyError(message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _depStatusBadge(status) {
|
||||||
|
const slug = (status || '').toLowerCase().replace(/ /g, '-');
|
||||||
|
const cls = status === 'Closed' ? 'lt-badge-closed' : status === 'Open' ? 'lt-badge-open' : 'lt-badge-sm';
|
||||||
|
return `<span class="lt-badge ${cls} lt-text-xs">${lt.escHtml(status)}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
function renderDependencies(dependencies) {
|
function renderDependencies(dependencies) {
|
||||||
const container = document.getElementById('dependenciesList');
|
const container = document.getElementById('dependenciesList');
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
@@ -563,64 +569,75 @@ function renderDependencies(dependencies) {
|
|||||||
'duplicates': 'Duplicates'
|
'duplicates': 'Duplicates'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check for open "blocked_by" dependencies — show alert
|
||||||
|
const blockers = (dependencies['blocked_by'] || []).filter(d => d.status !== 'Closed');
|
||||||
|
const blockerAlert = document.getElementById('blockerAlert');
|
||||||
|
if (blockers.length > 0) {
|
||||||
|
const alertHtml = `<div class="lt-alert lt-alert--warning" id="blockerAlert" role="alert" style="margin-bottom:0.75rem">
|
||||||
|
<span class="lt-alert-icon" aria-hidden="true">[!]</span>
|
||||||
|
<div class="lt-alert-body">
|
||||||
|
<div class="lt-alert-title">Blocked</div>
|
||||||
|
<div class="lt-alert-msg">This ticket is blocked by ${blockers.length} open ticket${blockers.length > 1 ? 's' : ''}:
|
||||||
|
${blockers.map(b => `<a href="/ticket/${lt.escHtml(b.depends_on_id)}" class="lt-text-cyan">#${lt.escHtml(b.depends_on_id)}</a>`).join(', ')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
// Insert blocker alert above the frame if not already there
|
||||||
|
const panel = document.getElementById('dependencies-panel');
|
||||||
|
if (panel && !panel.querySelector('#blockerAlert')) {
|
||||||
|
panel.insertAdjacentHTML('afterbegin', alertHtml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
let hasAny = false;
|
let hasAny = false;
|
||||||
|
|
||||||
for (const [type, items] of Object.entries(dependencies)) {
|
for (const [type, items] of Object.entries(dependencies)) {
|
||||||
if (items.length > 0) {
|
if (!items.length) continue;
|
||||||
hasAny = true;
|
hasAny = true;
|
||||||
html += `<div class="dependency-group">
|
const label = typeLabels[type] || type;
|
||||||
<h4>${typeLabels[type]}</h4>`;
|
html += `<div class="lt-kv-row" style="flex-direction:column;align-items:flex-start;gap:0.3rem">
|
||||||
|
<span class="lt-kv-label lt-text-xs">${lt.escHtml(label)}</span>`;
|
||||||
items.forEach(dep => {
|
items.forEach(dep => {
|
||||||
const statusClass = 'status-' + dep.status.toLowerCase().replace(/ /g, '-');
|
html += `<div class="lt-flex lt-flex-gap-sm lt-flex-align-center" style="width:100%;padding:0.25rem 0;border-bottom:1px solid rgba(0,255,65,0.08)">
|
||||||
html += `<div class="dependency-item">
|
<a href="/ticket/${lt.escHtml(dep.depends_on_id)}" class="lt-text-cyan lt-text-xs">
|
||||||
<div>
|
|
||||||
<a href="/ticket/${lt.escHtml(dep.depends_on_id)}">
|
|
||||||
#${lt.escHtml(dep.depends_on_id)}
|
#${lt.escHtml(dep.depends_on_id)}
|
||||||
</a>
|
</a>
|
||||||
<span class="dependency-title">${lt.escHtml(dep.title)}</span>
|
<span class="lt-text-sm" style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
|
||||||
<span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span>
|
title="${lt.escHtml(dep.title)}">${lt.escHtml(dep.title)}</span>
|
||||||
</div>
|
${_depStatusBadge(dep.status)}
|
||||||
<button data-action="remove-dependency" data-dependency-id="${lt.escHtml(String(dep.dependency_id))}" class="lt-btn lt-btn-sm">REMOVE</button>
|
<button data-action="remove-dependency"
|
||||||
|
data-dependency-id="${lt.escHtml(String(dep.dependency_id))}"
|
||||||
|
class="lt-btn lt-btn-ghost lt-btn-sm" aria-label="Remove dependency">✕</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasAny) {
|
container.innerHTML = hasAny ? `<div class="lt-kv-grid">${html}</div>` : '<p class="lt-text-muted lt-text-sm">No dependencies configured.</p>';
|
||||||
html = '<p class="lt-text-muted">No dependencies configured.</p>';
|
|
||||||
}
|
|
||||||
|
|
||||||
container.innerHTML = html;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDependents(dependents) {
|
function renderDependents(dependents) {
|
||||||
const container = document.getElementById('dependentsList');
|
const container = document.getElementById('dependentsList');
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
if (dependents.length === 0) {
|
if (!dependents.length) {
|
||||||
container.innerHTML = '<p class="lt-text-muted">No tickets depend on this one.</p>';
|
container.innerHTML = '<p class="lt-text-muted lt-text-sm">No tickets depend on this one.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const relLabels = { 'blocks':'blocks', 'blocked_by':'blocked by', 'relates_to':'relates to', 'duplicates':'duplicates' };
|
||||||
let html = '';
|
let html = '';
|
||||||
dependents.forEach(dep => {
|
dependents.forEach(dep => {
|
||||||
const statusClass = 'status-' + dep.status.toLowerCase().replace(/ /g, '-');
|
const relLabel = relLabels[dep.dependency_type] || dep.dependency_type;
|
||||||
html += `<div class="dependency-item">
|
html += `<div class="lt-flex lt-flex-gap-sm lt-flex-align-center" style="padding:0.25rem 0;border-bottom:1px solid rgba(0,255,65,0.08)">
|
||||||
<div>
|
<a href="/ticket/${lt.escHtml(dep.ticket_id)}" class="lt-text-cyan lt-text-xs">#${lt.escHtml(dep.ticket_id)}</a>
|
||||||
<a href="/ticket/${lt.escHtml(dep.ticket_id)}">
|
<span class="lt-text-xs lt-text-muted">${lt.escHtml(relLabel)}</span>
|
||||||
#${lt.escHtml(dep.ticket_id)}
|
<span class="lt-text-sm" style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
|
||||||
</a>
|
title="${lt.escHtml(dep.title)}">${lt.escHtml(dep.title)}</span>
|
||||||
<span class="dependency-title">${lt.escHtml(dep.title)}</span>
|
${_depStatusBadge(dep.status)}
|
||||||
<span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span>
|
|
||||||
<span class="dependency-title lt-text-amber">(${lt.escHtml(dep.dependency_type)})</span>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ require_once __DIR__ . '/../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'New Ticket';
|
$pageTitle = 'New Ticket';
|
||||||
$activeNav = 'dashboard';
|
$activeNav = 'dashboard';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327', '/assets/css/ticket.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}", "/assets/css/ticket.css?v={$_v}"];
|
||||||
$pageScripts = [
|
$pageScripts = [
|
||||||
'/assets/js/keyboard-shortcuts.js?v=20260327',
|
"/assets/js/keyboard-shortcuts.js?v={$_v}",
|
||||||
];
|
];
|
||||||
|
|
||||||
include __DIR__ . '/layout_header.php';
|
include __DIR__ . '/layout_header.php';
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ include __DIR__ . '/layout_header.php';
|
|||||||
</option>
|
</option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</select>
|
</select>
|
||||||
|
<span id="watcherAvatarGroup" class="lt-avatar-group lt-avatar-group--sm" aria-label="Watchers" style="display:none"></span>
|
||||||
<button type="button" id="watchButton" class="lt-btn lt-btn-ghost lt-btn-sm"
|
<button type="button" id="watchButton" class="lt-btn lt-btn-ghost lt-btn-sm"
|
||||||
title="Watch this ticket to receive Matrix notifications on updates">WATCH</button>
|
title="Watch this ticket to receive Matrix notifications on updates">WATCH</button>
|
||||||
<button type="button" id="editButton" class="lt-btn lt-btn-primary lt-btn-sm">EDIT</button>
|
<button type="button" id="editButton" class="lt-btn lt-btn-primary lt-btn-sm">EDIT</button>
|
||||||
@@ -868,6 +869,32 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
|
|
||||||
// Watch / Unwatch button
|
// Watch / Unwatch button
|
||||||
var watchBtn = document.getElementById('watchButton');
|
var watchBtn = document.getElementById('watchButton');
|
||||||
|
var watcherGroup = document.getElementById('watcherAvatarGroup');
|
||||||
|
|
||||||
|
function _renderWatcherAvatars(watchers) {
|
||||||
|
if (!watcherGroup) return;
|
||||||
|
if (!watchers || !watchers.length) { watcherGroup.style.display = 'none'; return; }
|
||||||
|
var avatarColors = ['lt-avatar--orange', 'lt-avatar--green', 'lt-avatar--purple', ''];
|
||||||
|
var html = '';
|
||||||
|
var shown = watchers.slice(0, 4);
|
||||||
|
shown.forEach(function (w) {
|
||||||
|
var words = (w.display_name || '').trim().split(/\s+/).filter(Boolean);
|
||||||
|
var initials = words.slice(0, 2).map(function (x) { return x[0].toUpperCase(); }).join('');
|
||||||
|
var hash = 0;
|
||||||
|
for (var i = 0; i < (w.display_name || '').length; i++) hash = ((hash << 5) - hash + (w.display_name || '').charCodeAt(i)) | 0;
|
||||||
|
var color = avatarColors[Math.abs(hash) % 4];
|
||||||
|
html += '<div class="lt-avatar lt-avatar--xs ' + color + '" title="' + lt.escHtml(w.display_name) + '" aria-label="' + lt.escHtml(w.display_name) + '">' +
|
||||||
|
'<img src="/api/user_avatar.php?user_id=' + w.user_id + '" alt="" class="lt-avatar-img" onerror="this.style.display=\'none\'">' +
|
||||||
|
'<span class="lt-avatar-initials">' + lt.escHtml(initials) + '</span>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
if (watchers.length > 4) {
|
||||||
|
html += '<div class="lt-avatar lt-avatar--xs lt-avatar--overflow" title="' + (watchers.length - 4) + ' more watchers">+' + (watchers.length - 4) + '</div>';
|
||||||
|
}
|
||||||
|
watcherGroup.innerHTML = html;
|
||||||
|
watcherGroup.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
if (watchBtn) {
|
if (watchBtn) {
|
||||||
var _watching = false;
|
var _watching = false;
|
||||||
// Fetch initial state
|
// Fetch initial state
|
||||||
@@ -880,6 +907,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
? 'You are watching this ticket. Click to stop.'
|
? 'You are watching this ticket. Click to stop.'
|
||||||
: 'Watch this ticket for Matrix notifications on updates.';
|
: 'Watch this ticket for Matrix notifications on updates.';
|
||||||
if (_watching) watchBtn.classList.add('lt-btn-active');
|
if (_watching) watchBtn.classList.add('lt-btn-active');
|
||||||
|
_renderWatcherAvatars(d.watchers || []);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function () {});
|
.catch(function () {});
|
||||||
@@ -897,6 +925,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
: 'Watch this ticket for Matrix notifications on updates.';
|
: 'Watch this ticket for Matrix notifications on updates.';
|
||||||
watchBtn.classList.toggle('lt-btn-active', _watching);
|
watchBtn.classList.toggle('lt-btn-active', _watching);
|
||||||
lt.toast.success(_watching ? 'Watching ticket' : 'Stopped watching ticket');
|
lt.toast.success(_watching ? 'Watching ticket' : 'Stopped watching ticket');
|
||||||
|
// Refresh watcher avatars from server
|
||||||
|
lt.api.get('/api/watch_ticket.php?ticket_id=' + window.ticketData.ticket_id)
|
||||||
|
.then(function (d2) { if (d2.success) _renderWatcherAvatars(d2.watchers || []); })
|
||||||
|
.catch(function () {});
|
||||||
} else {
|
} else {
|
||||||
lt.toast.error('Failed: ' + (d.error || 'Unknown error'));
|
lt.toast.error('Failed: ' + (d.error || 'Unknown error'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'API Keys';
|
$pageTitle = 'API Keys';
|
||||||
$activeNav = 'admin-api-keys';
|
$activeNav = 'admin-api-keys';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}"];
|
||||||
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
||||||
include __DIR__ . '/../../views/layout_header.php';
|
include __DIR__ . '/../../views/layout_header.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'Audit Log';
|
$pageTitle = 'Audit Log';
|
||||||
$activeNav = 'admin-audit-log';
|
$activeNav = 'admin-audit-log';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}"];
|
||||||
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
||||||
include __DIR__ . '/../../views/layout_header.php';
|
include __DIR__ . '/../../views/layout_header.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'Custom Fields';
|
$pageTitle = 'Custom Fields';
|
||||||
$activeNav = 'admin-custom-fields';
|
$activeNav = 'admin-custom-fields';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}"];
|
||||||
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
||||||
include __DIR__ . '/../../views/layout_header.php';
|
include __DIR__ . '/../../views/layout_header.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'Recurring Tickets';
|
$pageTitle = 'Recurring Tickets';
|
||||||
$activeNav = 'admin-recurring';
|
$activeNav = 'admin-recurring';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}"];
|
||||||
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
||||||
include __DIR__ . '/../../views/layout_header.php';
|
include __DIR__ . '/../../views/layout_header.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'Templates';
|
$pageTitle = 'Templates';
|
||||||
$activeNav = 'admin-templates';
|
$activeNav = 'admin-templates';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}"];
|
||||||
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
||||||
include __DIR__ . '/../../views/layout_header.php';
|
include __DIR__ . '/../../views/layout_header.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'User Activity';
|
$pageTitle = 'User Activity';
|
||||||
$activeNav = 'admin-user-activity';
|
$activeNav = 'admin-user-activity';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}"];
|
||||||
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
||||||
include __DIR__ . '/../../views/layout_header.php';
|
include __DIR__ . '/../../views/layout_header.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||||||
$nonce = SecurityHeadersMiddleware::getNonce();
|
$nonce = SecurityHeadersMiddleware::getNonce();
|
||||||
$pageTitle = 'Workflow Designer';
|
$pageTitle = 'Workflow Designer';
|
||||||
$activeNav = 'admin-workflow';
|
$activeNav = 'admin-workflow';
|
||||||
$pageStyles = ['/assets/css/dashboard.css?v=20260327'];
|
$_v = $GLOBALS['config']['ASSET_VERSION'] ?? '1';
|
||||||
|
$pageStyles = ["/assets/css/dashboard.css?v={$_v}"];
|
||||||
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
$pageScripts = ['/assets/js/keyboard-shortcuts.js'];
|
||||||
include __DIR__ . '/../../views/layout_header.php';
|
include __DIR__ . '/../../views/layout_header.php';
|
||||||
?>
|
?>
|
||||||
|
|||||||
Reference in New Issue
Block a user