feat: saved filter pills, mention autocomplete CSS, tooltips on dashboard table
- Dashboard: saved filter pills row above active filters bar — loads from API, click applies criteria as URL params, hidden when no saved filters exist - ticket.css: add TDS-styled CSS for @mention autocomplete dropdown (was unstyled) - Dashboard table: data-tooltip on Title and Assigned To columns for truncated text (lt.tooltip.init() auto-called by lt.init(), zero extra JS needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -270,6 +270,42 @@ kbd {
|
||||
.thread-depth-3 { margin-left: 2.25rem; }
|
||||
}
|
||||
|
||||
/* ── @mention autocomplete dropdown ─────────────────────────── */
|
||||
.mention-autocomplete {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 200;
|
||||
background: var(--bg-overlay, #060c14);
|
||||
border: 1px solid var(--accent-cyan-border, rgba(0,212,255,0.3));
|
||||
box-shadow: 0 8px 24px rgba(0,0,0,0.6);
|
||||
min-width: 200px;
|
||||
max-width: 320px;
|
||||
}
|
||||
.mention-autocomplete.active { display: block; }
|
||||
.mention-option {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 0.75rem;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid var(--border-dim, rgba(0,255,65,0.1));
|
||||
transition: background 0.1s;
|
||||
}
|
||||
.mention-option:last-child { border-bottom: none; }
|
||||
.mention-option.selected,
|
||||
.mention-option:hover {
|
||||
background: rgba(0,212,255,0.08);
|
||||
}
|
||||
.mention-username {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--accent-cyan, #00d4ff);
|
||||
}
|
||||
.mention-displayname {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ── Watcher avatar group in toolbar ────────────────────────── */
|
||||
.lt-avatar-group {
|
||||
display: flex;
|
||||
|
||||
+52
-2
@@ -365,6 +365,9 @@ include __DIR__ . '/layout_header.php';
|
||||
</div>
|
||||
</div><!-- /.lt-toolbar -->
|
||||
|
||||
<!-- Saved filter quick-switch pills -->
|
||||
<div id="savedFilterPills" class="saved-filter-pills lt-flex lt-flex-wrap lt-flex-gap-sm" style="display:none;padding:0.35rem 0 0.1rem" aria-label="Saved filters"></div>
|
||||
|
||||
<!-- Active filters bar -->
|
||||
<?php if (!empty($activeFilters)): ?>
|
||||
<div class="active-filters-bar lt-flex lt-flex-wrap lt-flex-gap-sm" role="group" aria-label="Active filters">
|
||||
@@ -502,7 +505,9 @@ include __DIR__ . '/layout_header.php';
|
||||
<?php $badgeClass = match($pNum) { 1 => 'lt-badge-p1', 2 => 'lt-badge-p2', 3 => 'lt-badge-p3', default => 'lt-badge-p4' }; ?>
|
||||
<span class="lt-badge <?= $badgeClass ?>">P<?= $pNum ?></span>
|
||||
</td>
|
||||
<td data-label="Title"><?= htmlspecialchars($row['title']) ?></td>
|
||||
<td data-label="Title" style="max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
|
||||
<span data-tooltip="<?= htmlspecialchars($row['title'], ENT_QUOTES, 'UTF-8') ?>" data-tooltip-pos="top"><?= htmlspecialchars($row['title']) ?></span>
|
||||
</td>
|
||||
<td data-label="Category" class="lt-text-muted lt-text-xs"><?= htmlspecialchars($row['category']) ?></td>
|
||||
<td data-label="Type" class="lt-text-muted lt-text-xs"><?= htmlspecialchars($row['type']) ?></td>
|
||||
<td data-label="Status">
|
||||
@@ -518,7 +523,12 @@ include __DIR__ . '/layout_header.php';
|
||||
</td>
|
||||
<td data-label="Created By" class="lt-text-xs"><?= $creator ?></td>
|
||||
<td data-label="Assigned To" class="lt-text-xs">
|
||||
<?= ($row['assigned_display_name'] ?? $row['assigned_username'] ?? null) ? $assignedTo : '<span class="lt-text-muted">Unassigned</span>' ?>
|
||||
<?php $assigneeDisplay = $row['assigned_display_name'] ?? $row['assigned_username'] ?? null; ?>
|
||||
<?php if ($assigneeDisplay): ?>
|
||||
<span data-tooltip="<?= htmlspecialchars($assigneeDisplay, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($assigneeDisplay) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="lt-text-muted">Unassigned</span>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td data-label="Created" class="lt-text-xs lt-text-muted ts-cell"
|
||||
data-ts="<?= htmlspecialchars($row['created_at'], ENT_QUOTES, 'UTF-8') ?>"
|
||||
@@ -916,6 +926,46 @@ if (window.lt) {
|
||||
lt.statsFilter.init();
|
||||
}
|
||||
|
||||
// Saved filter pills — load on page init
|
||||
(function() {
|
||||
lt.api.get('/api/saved_filters.php').then(function(data) {
|
||||
if (!data.success || !data.filters || !data.filters.length) return;
|
||||
var pillsWrap = document.getElementById('savedFilterPills');
|
||||
if (!pillsWrap) return;
|
||||
var html = '<span class="lt-text-xs lt-text-muted" style="align-self:center">Saved:</span>';
|
||||
data.filters.slice(0, 8).forEach(function(f) {
|
||||
var criteria = JSON.stringify(f.filter_criteria);
|
||||
html += '<button type="button" class="lt-btn lt-btn-ghost lt-btn-sm saved-filter-pill" ' +
|
||||
'data-criteria="' + lt.escHtml(criteria) + '" ' +
|
||||
'title="Apply saved filter: ' + lt.escHtml(f.filter_name) + '">' +
|
||||
lt.escHtml(f.filter_name) +
|
||||
(f.is_default ? ' <span style="color:var(--accent-amber)">★</span>' : '') +
|
||||
'</button>';
|
||||
});
|
||||
pillsWrap.innerHTML = html;
|
||||
pillsWrap.style.display = 'flex';
|
||||
|
||||
// Wire clicks: apply filter criteria as URL params
|
||||
pillsWrap.querySelectorAll('.saved-filter-pill').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
try {
|
||||
var c = JSON.parse(btn.dataset.criteria);
|
||||
var params = new URLSearchParams();
|
||||
if (c.search) params.set('search', c.search);
|
||||
if (c.status && c.status.length) params.set('status', c.status.join(','));
|
||||
if (c.priority_min) params.set('priority_min', c.priority_min);
|
||||
if (c.priority_max) params.set('priority_max', c.priority_max);
|
||||
if (c.assigned_to) params.set('assigned_to', c.assigned_to);
|
||||
if (c.created_by) params.set('created_by', c.created_by);
|
||||
if (c.created_from) params.set('created_from', c.created_from);
|
||||
if (c.created_to) params.set('created_to', c.created_to);
|
||||
window.location.href = '/?' + params.toString();
|
||||
} catch(e) {}
|
||||
});
|
||||
});
|
||||
}).catch(function() {});
|
||||
})();
|
||||
|
||||
// Helper: get date in server timezone
|
||||
function getServerDate() {
|
||||
var now = new Date();
|
||||
|
||||
Reference in New Issue
Block a user