From 332551e2e3ad7d5c711c1a512a40a0bc3941c1b6 Mon Sep 17 00:00:00 2001 From: central-worker-1 Date: Fri, 11 Sep 2026 13:31:09 -0700 Subject: [PATCH] The participants picker is sorted by the name it shows (#1706) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QA on [central-ticketing] #1598, reading the dropdown back after the picker became chips: typing `an` listed "Toews, Fast, Ivanov, Mwangi, Bergman, Anderson" — to an operator, no order at all. It was in fact perfectly ordered. `searchUsers()` came out of #1598's chip conversion with `->orderBy('name')`, and `users.name` is the cached "First Last" ("Abigail Toews", "Andrew Fast", "Avery Ivanov") — the one string a chip row no longer prints. A list labelled "Last, First" and collated by "First Last" reads as unsorted, which is #524's own reported defect; the sibling conversion on [central-staff-employment] #1597 names it as the thing it had to fix at the same moment. So the collation moves with the label: `PersonNames::orderUsers()`, the platform's single answer, which the reference search (`admin.settings.users.search`) and the other chipped pickers already use, and which honours the operator's `name_format` rather than assuming surname-first. Not cosmetic: the sort runs before `limit(10)`, so on a common surname the two collations offer a different set of people, not the same set shuffled. Three tests in the shell suite (TicketParticipantsAreChipsTest) — the collation, the `name_format` setting, and which ten survive the limit. The first and third are red against `orderBy('name')`. --- src/Http/Controllers/TicketController.php | 25 +++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Http/Controllers/TicketController.php b/src/Http/Controllers/TicketController.php index aca81de..c597662 100644 --- a/src/Http/Controllers/TicketController.php +++ b/src/Http/Controllers/TicketController.php @@ -5,6 +5,7 @@ namespace Dashboard\Ticketing\Http\Controllers; use App\Models\Setting; use App\Models\User; use App\Support\PersonChip; +use App\Support\PersonNames; use Dashboard\Ticketing\Models\PriorityLevel; use Dashboard\Ticketing\Models\Ticket; use Dashboard\Ticketing\Models\TicketAttachment; @@ -1132,11 +1133,23 @@ class TicketController extends Controller * operator-precedence hole the same shape opened on [central-inventory] * #1594. * - * `orderBy('name')` is new and is the only visible difference: the old query - * had no order at all, so ten rows arrived in whatever order the storage - * engine happened to return them and the same two letters could list the - * same people differently twice running. The reference search orders by - * name, and "a consistent shape" is what this card is for. + * AN ORDER AT ALL is new, and it is `PersonNames::orderUsers()` rather than + * `orderBy('name')`. The old query had no order, so ten rows arrived in + * whatever sequence the storage engine happened to return them and the same + * two letters could list the same people differently twice running. But the + * column that makes an order CONSISTENT is not automatically the one that + * makes it READABLE: `users.name` is the cached "First Last" ("Andrew Fast") + * and these rows are chips, which read "Last, First" ("Fast, Andrew"). A + * list labelled one way and collated the other is what an operator calls + * unsorted — "Toews, Fast, Ivanov, Mwangi, Bergman" is perfectly ordered by + * a string the row no longer shows. `PersonNames::orderUsers()` is the + * platform's one answer to that (#524), it is what the reference search + * (`admin.settings.users.search`) and the sibling conversions use, and it + * honours the operator's `name_format` rather than assuming surname-first. + * + * It also decides WHICH TEN come back, not merely their sequence, so this is + * not cosmetic: on a common surname the limit falls on a different set of + * people under the two collations. */ public function searchUsers(Request $request, Ticket $ticket) { @@ -1161,7 +1174,7 @@ class TicketController extends Controller ->orWhere('email', 'like', "%{$q}%"); }) ->whereNotIn('id', $excludeIds) - ->orderBy('name') + ->tap(fn ($query) => PersonNames::orderUsers($query)) ->limit(10) ->get(PersonChip::columns()) ));