From a3298e7dbe8d177a604ef18eb37c740c7d1fd137 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 9 Jan 2026 16:42:13 -0500 Subject: [PATCH] fix: Enable proper sorting for Created By and Assigned To columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed server-side sorting for user-related columns on dashboard: Problem: - Clicking "Created By" or "Assigned To" headers didn't sort - Columns were missing from $allowedColumns validation - Fell back to ticket_id sort, appearing random to users Solution: 1. Added 'created_by' and 'assigned_to' to $allowedColumns array 2. Smart sort expression mapping: - created_by → sorts by display_name/username (not user ID) - assigned_to → uses CASE to put unassigned at end, then sorts by name - Other columns → use table prefix (t.column_name) 3. Database-level NULL handling for assigned_to: - Uses CASE WHEN to sort unassigned tickets last - Regardless of ASC/DESC direction - Then alphabetically sorts assigned users Result: - A→Z: Alice, Bob, Charlie... Unassigned - Z→A: Zack, Yolanda, Xavier... Unassigned - Consistent grouping and predictable order Co-Authored-By: Claude Sonnet 4.5 --- models/TicketModel.php | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/models/TicketModel.php b/models/TicketModel.php index c76f98f..a9cf330 100644 --- a/models/TicketModel.php +++ b/models/TicketModel.php @@ -151,26 +151,38 @@ class TicketModel { } // Validate sort column to prevent SQL injection - $allowedColumns = ['ticket_id', 'title', 'status', 'priority', 'category', 'type', 'created_at', 'updated_at']; + $allowedColumns = ['ticket_id', 'title', 'status', 'priority', 'category', 'type', 'created_at', 'updated_at', 'created_by', 'assigned_to']; if (!in_array($sortColumn, $allowedColumns)) { $sortColumn = 'ticket_id'; } - + + // Map column names to actual sort expressions + // For user columns, sort by display name with NULL handling for unassigned + $sortExpression = $sortColumn; + if ($sortColumn === 'created_by') { + $sortExpression = "COALESCE(u_created.display_name, u_created.username, 'System')"; + } elseif ($sortColumn === 'assigned_to') { + // Put unassigned (NULL) at the end regardless of sort direction + $sortExpression = "CASE WHEN t.assigned_to IS NULL THEN 1 ELSE 0 END, COALESCE(u_assigned.display_name, u_assigned.username)"; + } else { + $sortExpression = "t.$sortColumn"; + } + // Validate sort direction $sortDirection = strtolower($sortDirection) === 'asc' ? 'ASC' : 'DESC'; - + // Get total count for pagination $countSql = "SELECT COUNT(*) as total FROM tickets $whereClause"; $countStmt = $this->conn->prepare($countSql); - + if (!empty($params)) { $countStmt->bind_param($paramTypes, ...$params); } - + $countStmt->execute(); $totalResult = $countStmt->get_result(); $totalTickets = $totalResult->fetch_assoc()['total']; - + // Get tickets with pagination and creator info $sql = "SELECT t.*, u_created.username as creator_username, @@ -181,7 +193,7 @@ class TicketModel { LEFT JOIN users u_created ON t.created_by = u_created.user_id LEFT JOIN users u_assigned ON t.assigned_to = u_assigned.user_id $whereClause - ORDER BY $sortColumn $sortDirection + ORDER BY $sortExpression $sortDirection LIMIT ? OFFSET ?"; $stmt = $this->conn->prepare($sql);