diff --git a/assets/js/dashboard.js b/assets/js/dashboard.js index ec706a1..43ea1e7 100644 --- a/assets/js/dashboard.js +++ b/assets/js/dashboard.js @@ -259,6 +259,12 @@ function removeFilter(filterType, filterValue) { } } else if (filterType === 'search') { params.delete('search'); + } else if (filterType === 'created_from') { + params.delete('created_from'); params.delete('created_to'); + } else if (filterType === 'updated_from') { + params.delete('updated_from'); params.delete('updated_to'); + } else if (filterType === 'closed_from') { + params.delete('closed_from'); params.delete('closed_to'); } else { params.delete(filterType); } @@ -310,44 +316,33 @@ function initSidebarFilters() { applyFiltersBtn.addEventListener('click', () => { const params = new URLSearchParams(window.location.search); - // Collect selected statuses + // Checkboxes const selectedStatuses = Array.from( document.querySelectorAll('.lt-filter-group input[name="status"]:checked') ).map(cb => cb.value); - - // Collect selected categories const selectedCategories = Array.from( document.querySelectorAll('.lt-filter-group input[name="category"]:checked') ).map(cb => cb.value); - - // Collect selected types const selectedTypes = Array.from( document.querySelectorAll('.lt-filter-group input[name="type"]:checked') ).map(cb => cb.value); - // Update URL parameters - if (selectedStatuses.length > 0) { - params.set('status', selectedStatuses.join(',')); - } else { - params.delete('status'); - } + if (selectedStatuses.length > 0) params.set('status', selectedStatuses.join(',')); + else params.delete('status'); + if (selectedCategories.length > 0) params.set('category', selectedCategories.join(',')); + else params.delete('category'); + if (selectedTypes.length > 0) params.set('type', selectedTypes.join(',')); + else params.delete('type'); - if (selectedCategories.length > 0) { - params.set('category', selectedCategories.join(',')); - } else { - params.delete('category'); - } + // Date inputs + const dateFields = ['created_from','created_to','updated_from','updated_to','closed_from','closed_to']; + dateFields.forEach(name => { + const el = document.getElementById('filter-' + name.replace('_', '-')); + if (el && el.value) params.set(name, el.value); + else params.delete(name); + }); - if (selectedTypes.length > 0) { - params.set('type', selectedTypes.join(',')); - } else { - params.delete('type'); - } - - // Reset to page 1 when filters change params.set('page', '1'); - - // Reload with new parameters window.location.search = params.toString(); }); } @@ -355,14 +350,10 @@ function initSidebarFilters() { if (clearFiltersBtn) { clearFiltersBtn.addEventListener('click', () => { const params = new URLSearchParams(window.location.search); - - // Remove filter parameters - params.delete('status'); - params.delete('category'); - params.delete('type'); + ['status','category','type', + 'created_from','created_to','updated_from','updated_to','closed_from','closed_to' + ].forEach(k => params.delete(k)); params.set('page', '1'); - - // Reload with cleared filters window.location.search = params.toString(); }); } diff --git a/controllers/DashboardController.php b/controllers/DashboardController.php index 45f738e..caa02d5 100644 --- a/controllers/DashboardController.php +++ b/controllers/DashboardController.php @@ -118,14 +118,18 @@ class DashboardController { // Validate date filters $createdFrom = $this->validateDate($_GET['created_from'] ?? null); - $createdTo = $this->validateDate($_GET['created_to'] ?? null); + $createdTo = $this->validateDate($_GET['created_to'] ?? null); $updatedFrom = $this->validateDate($_GET['updated_from'] ?? null); - $updatedTo = $this->validateDate($_GET['updated_to'] ?? null); + $updatedTo = $this->validateDate($_GET['updated_to'] ?? null); + $closedFrom = $this->validateDate($_GET['closed_from'] ?? null); + $closedTo = $this->validateDate($_GET['closed_to'] ?? null); if ($createdFrom) $filters['created_from'] = $createdFrom; - if ($createdTo) $filters['created_to'] = $createdTo; + if ($createdTo) $filters['created_to'] = $createdTo; if ($updatedFrom) $filters['updated_from'] = $updatedFrom; - if ($updatedTo) $filters['updated_to'] = $updatedTo; + if ($updatedTo) $filters['updated_to'] = $updatedTo; + if ($closedFrom) $filters['closed_from'] = $closedFrom; + if ($closedTo) $filters['closed_to'] = $closedTo; // Validate priority filters $priorityMin = $this->validatePriority($_GET['priority_min'] ?? null); diff --git a/models/TicketModel.php b/models/TicketModel.php index c73c8ce..31821d0 100644 --- a/models/TicketModel.php +++ b/models/TicketModel.php @@ -121,6 +121,18 @@ class TicketModel { $paramTypes .= 's'; } + // Date range - closed_at + if (!empty($filters['closed_from'])) { + $whereConditions[] = "DATE(t.closed_at) >= ?"; + $params[] = $filters['closed_from']; + $paramTypes .= 's'; + } + if (!empty($filters['closed_to'])) { + $whereConditions[] = "DATE(t.closed_at) <= ?"; + $params[] = $filters['closed_to']; + $paramTypes .= 's'; + } + // Priority range if (!empty($filters['priority_min'])) { $whereConditions[] = "t.priority >= ?"; diff --git a/views/DashboardView.php b/views/DashboardView.php index 699ba4f..75f3df7 100644 --- a/views/DashboardView.php +++ b/views/DashboardView.php @@ -56,6 +56,21 @@ if (!empty($_GET['assigned_to'])) { $label = $_GET['assigned_to'] === 'unassigned' ? 'Unassigned' : 'User #' . htmlspecialchars($_GET['assigned_to']); $activeFilters[] = ['type' => 'assigned_to', 'value' => $_GET['assigned_to'], 'label' => 'Assigned: ' . $label]; } +if (!empty($_GET['created_from']) || !empty($_GET['created_to'])) { + $from = $_GET['created_from'] ?? ''; $to = $_GET['created_to'] ?? ''; + $label = $from === $to && $from ? 'Created: ' . $from : 'Created: ' . ($from ?: '…') . ' – ' . ($to ?: '…'); + $activeFilters[] = ['type' => 'created_from', 'value' => $from, 'label' => $label]; +} +if (!empty($_GET['updated_from']) || !empty($_GET['updated_to'])) { + $from = $_GET['updated_from'] ?? ''; $to = $_GET['updated_to'] ?? ''; + $label = $from === $to && $from ? 'Updated: ' . $from : 'Updated: ' . ($from ?: '…') . ' – ' . ($to ?: '…'); + $activeFilters[] = ['type' => 'updated_from', 'value' => $from, 'label' => $label]; +} +if (!empty($_GET['closed_from']) || !empty($_GET['closed_to'])) { + $from = $_GET['closed_from'] ?? ''; $to = $_GET['closed_to'] ?? ''; + $label = $from === $to && $from ? 'Closed: ' . $from : 'Closed: ' . ($from ?: '…') . ' – ' . ($to ?: '…'); + $activeFilters[] = ['type' => 'closed_from', 'value' => $from, 'label' => $label]; +} $_lt_statuses = $GLOBALS['config']['TICKET_STATUSES']; $currentStatus = isset($_GET['status']) ? explode(',', $_GET['status']) : ['Open', 'Pending', 'In Progress']; @@ -446,6 +461,37 @@ include __DIR__ . '/layout_header.php'; + +
+ + + + +