diff --git a/app/models/user/filtering.rb b/app/models/user/filtering.rb index 1b4f3070e5..5558abe1fb 100644 --- a/app/models/user/filtering.rb +++ b/app/models/user/filtering.rb @@ -28,6 +28,21 @@ def users @users ||= account.users.active.alphabetically end + # Returns users sorted with selected assignees first + def users_for_assignee_filter + @users_for_assignee_filter ||= account.users.active.sorted_by_selection(filter.assignees.pluck(:id)) + end + + # Returns users sorted with selected creators first + def users_for_creator_filter + @users_for_creator_filter ||= account.users.active.sorted_by_selection(filter.creators.pluck(:id)) + end + + # Returns users sorted with selected closers first + def users_for_closer_filter + @users_for_closer_filter ||= account.users.active.sorted_by_selection(filter.closers.pluck(:id)) + end + def filters @filters ||= user.filters.all end diff --git a/app/models/user/named.rb b/app/models/user/named.rb index 804e516ee6..0d07f55a36 100644 --- a/app/models/user/named.rb +++ b/app/models/user/named.rb @@ -3,6 +3,23 @@ module User::Named included do scope :alphabetically, -> { order("lower(name)") } + + # Sort users with selected IDs first, then unselected, both alphabetically + # More efficient than in-memory sorting for large user lists + scope :sorted_by_selection, ->(selected_ids) { + return alphabetically if selected_ids.blank? + + # Convert string IDs to binary blobs for SQLite comparison + uuid_type = ActiveRecord::Type::Uuid.new + binary_ids = selected_ids.map { |id| uuid_type.serialize(id) } + quoted_ids = binary_ids.map { |blob| connection.quote(blob) }.join(',') + + # Order by selection status FIRST, then alphabetically + order( + Arel.sql("CASE WHEN users.id IN (#{quoted_ids}) THEN 0 ELSE 1 END"), + Arel.sql("LOWER(name)") + ) + } end def first_name diff --git a/app/views/filters/settings/_assignees.html.erb b/app/views/filters/settings/_assignees.html.erb index ddff3f4fa8..71fac7d9b7 100644 --- a/app/views/filters/settings/_assignees.html.erb +++ b/app/views/filters/settings/_assignees.html.erb @@ -33,7 +33,7 @@ <% end %> - <% user_filtering.users.each do |user| %> + <% user_filtering.users_for_assignee_filter.each do |user| %> <%= tag.li class: "popup__item", data: { filter_target: "item", navigable_list_target: "item", multi_selection_combobox_target: "item", multi_selection_combobox_value: user.id, multi_selection_combobox_label: user.familiar_name }, role: "checkbox", aria: { checked: filter.assignees.include?(user) } do %> diff --git a/app/views/filters/settings/_closers.html.erb b/app/views/filters/settings/_closers.html.erb index 057c99d405..ff7622f22c 100644 --- a/app/views/filters/settings/_closers.html.erb +++ b/app/views/filters/settings/_closers.html.erb @@ -24,7 +24,7 @@ <% end %>