Add UI enhancements and new features
Keyboard Navigation: - Add J/K keys for Gmail-style ticket list navigation - Add N key for new ticket, C for comment focus - Add G then D for go to dashboard (vim-style) - Add 1-4 number keys for quick status changes on ticket page - Add Enter to open selected ticket - Update keyboard help modal with all new shortcuts Ticket Age Indicator: - Show "Last activity: X days ago" on ticket view - Visual warning (yellow pulse) for tickets idle >5 days - Critical warning (red pulse) for tickets idle >10 days Ticket Clone Feature: - Add "Clone" button on ticket view - Creates copy with [CLONE] prefix in title - Preserves description, priority, category, type, visibility - Automatically creates "relates_to" dependency to original Active Filter Badges: - Show visual badges above ticket table for active filters - Click X on badge to remove individual filter - "Clear All" button to reset all filters - Color-coded by filter type (status, priority, search) Visual Enhancements: - Add keyboard-selected row highlighting for J/K navigation - Smooth animations for filter badges Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -356,6 +356,49 @@ $nonce = SecurityHeadersMiddleware::getNonce();
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Active Filters Display -->
|
||||
<?php
|
||||
$activeFilters = [];
|
||||
if (!empty($_GET['status'])) {
|
||||
$statuses = explode(',', $_GET['status']);
|
||||
foreach ($statuses as $s) {
|
||||
$activeFilters[] = ['type' => 'status', 'value' => trim($s), 'label' => 'Status: ' . trim($s)];
|
||||
}
|
||||
}
|
||||
if (!empty($_GET['priority'])) {
|
||||
$priorities = is_array($_GET['priority']) ? $_GET['priority'] : explode(',', $_GET['priority']);
|
||||
foreach ($priorities as $p) {
|
||||
$activeFilters[] = ['type' => 'priority', 'value' => trim($p), 'label' => 'Priority: P' . trim($p)];
|
||||
}
|
||||
}
|
||||
if (!empty($_GET['category'])) {
|
||||
$activeFilters[] = ['type' => 'category', 'value' => $_GET['category'], 'label' => 'Category: ' . $_GET['category']];
|
||||
}
|
||||
if (!empty($_GET['type'])) {
|
||||
$activeFilters[] = ['type' => 'type', 'value' => $_GET['type'], 'label' => 'Type: ' . $_GET['type']];
|
||||
}
|
||||
if (!empty($_GET['assigned_to'])) {
|
||||
$activeFilters[] = ['type' => 'assigned_to', 'value' => $_GET['assigned_to'], 'label' => 'Assigned To: ' . ($_GET['assigned_to'] === 'unassigned' ? 'Unassigned' : 'User #' . $_GET['assigned_to'])];
|
||||
}
|
||||
if (!empty($_GET['search'])) {
|
||||
$activeFilters[] = ['type' => 'search', 'value' => $_GET['search'], 'label' => 'Search: "' . htmlspecialchars(substr($_GET['search'], 0, 20)) . (strlen($_GET['search']) > 20 ? '...' : '') . '"'];
|
||||
}
|
||||
?>
|
||||
<?php if (!empty($activeFilters)): ?>
|
||||
<div class="active-filters-bar">
|
||||
<span class="active-filters-label">Active Filters:</span>
|
||||
<div class="active-filters-list">
|
||||
<?php foreach ($activeFilters as $filter): ?>
|
||||
<span class="filter-badge" data-filter-type="<?php echo htmlspecialchars($filter['type']); ?>" data-filter-value="<?php echo htmlspecialchars($filter['value']); ?>">
|
||||
<?php echo htmlspecialchars($filter['label']); ?>
|
||||
<button type="button" class="filter-remove" data-action="remove-filter" data-filter-type="<?php echo htmlspecialchars($filter['type']); ?>" data-filter-value="<?php echo htmlspecialchars($filter['value']); ?>" title="Remove filter">×</button>
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-action="clear-all-filters">Clear All</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
|
||||
Reference in New Issue
Block a user