From a7ad602e7ff80362c8f74bcc274400c735c5676a Mon Sep 17 00:00:00 2001
From: Felix Eckhofer <felix@eckhofer.com>
Date: Sat, 16 Nov 2024 11:34:26 +0100
Subject: [PATCH] Add simple filter control for unassigned users

---
 app/assets/stylesheets/application.css        |  3 +++
 .../controllers/filteredlist_controller.js    | 21 +++++++++++++++++++
 app/views/assignments/_filteredlist.html.erb  | 17 +++++++++++++++
 .../assignments/_filteredlist_option.html.erb |  6 ++++++
 app/views/assignments/_user_add.html.erb      |  4 +++-
 app/views/sessions/_session.html.erb          | 10 ++-------
 6 files changed, 52 insertions(+), 9 deletions(-)
 create mode 100644 app/javascript/controllers/filteredlist_controller.js
 create mode 100644 app/views/assignments/_filteredlist.html.erb
 create mode 100644 app/views/assignments/_filteredlist_option.html.erb

diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 9ee413d..f2150fe 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -58,3 +58,6 @@
   padding: 0 8px;
   border-right: 1px dashed gray;
 }
+.hidden {
+  display: none;
+}
diff --git a/app/javascript/controllers/filteredlist_controller.js b/app/javascript/controllers/filteredlist_controller.js
new file mode 100644
index 0000000..ef334ef
--- /dev/null
+++ b/app/javascript/controllers/filteredlist_controller.js
@@ -0,0 +1,21 @@
+import { Controller } from "@hotwired/stimulus";
+
+export default class extends Controller {
+  static targets = ["input", "list"];
+
+  connect() {
+    //this.options = Array.from(this.listTarget.children)
+  }
+
+  filter() {
+    const query = this.inputTarget.value.toLowerCase()
+    // Filter options by the `data-filteredlist-match` attribute
+    for (const option of this.listTarget.children) {
+      if (option.dataset.filteredlistMatch.includes(query)) {
+        option.classList.remove("hidden")
+      } else {
+        option.classList.add("hidden")
+      }
+    }
+  }
+}
diff --git a/app/views/assignments/_filteredlist.html.erb b/app/views/assignments/_filteredlist.html.erb
new file mode 100644
index 0000000..bfbba5b
--- /dev/null
+++ b/app/views/assignments/_filteredlist.html.erb
@@ -0,0 +1,17 @@
+<div class="relative inline-block w-64" data-controller="filteredlist">
+  <div class="relative">
+    <input
+      type="text"
+      data-filteredlist-target="input"
+      data-action="input->filteredlist#filter"
+      autocomplete="off"
+      class="w-full border border-gray-300 rounded-md shadow-sm pl-3 pr-10 py-2 text-sm focus:ring-indigo-500 focus:border-indigo-500"
+      placeholder="Filter..."
+    />
+    <ul data-filteredlist-target="list" class="flex flex-row flex-wrap gap-1 my-1 flex-shrink-0">
+      <% users.each do |user| %>
+        <%= render partial: 'assignments/filteredlist_option', locals: { session: session, user: user } %>
+      <% end %>
+    </ul>
+  </div>
+</div>
diff --git a/app/views/assignments/_filteredlist_option.html.erb b/app/views/assignments/_filteredlist_option.html.erb
new file mode 100644
index 0000000..6579860
--- /dev/null
+++ b/app/views/assignments/_filteredlist_option.html.erb
@@ -0,0 +1,6 @@
+<li
+  data-filteredlist-match="<%= user.name.downcase %>"
+  data-turbo-track="dynamic"
+>
+  <%= render partial: 'assignments/user_add', locals: { user: user, session: session } %>
+</li>
diff --git a/app/views/assignments/_user_add.html.erb b/app/views/assignments/_user_add.html.erb
index 65c56c2..819b115 100644
--- a/app/views/assignments/_user_add.html.erb
+++ b/app/views/assignments/_user_add.html.erb
@@ -1,4 +1,5 @@
 <% disabled = local_assigns.fetch(:disabled, false) %>
+<span class="unassigned-user">
 <% if not disabled %>
 <span class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10" style="background-color: <%= user.avatar_color %>" title="<%= user.name %>">
   <span style="color: <%= user.text_color %>"><%= user.name %></span>
@@ -23,4 +24,5 @@
       <span class="absolute -inset-1"></span>
   </button>
 </span>
-<% end %>
\ No newline at end of file
+<% end %>
+</span>
diff --git a/app/views/sessions/_session.html.erb b/app/views/sessions/_session.html.erb
index 46217df..dd370bf 100644
--- a/app/views/sessions/_session.html.erb
+++ b/app/views/sessions/_session.html.erb
@@ -1,5 +1,5 @@
 <% unassigned_users = User.all - session.assignments.collect(&:user) %>
-<%= turbo_frame_tag dom_id(session) do %>
+<%= turbo_frame_tag dom_id(session), method: "morph" do %>
   <div class="session shadow hover:shadow-lg overflow-scroll text-sm w-full !h-full min-h-full hover:!min-h-max <%= session.translators_needed? ? "translators-needed" : "no-translators-needed" %> <%= session.backup_needed? ? "backup-needed" : "no-backup-needed" %> <%= session.assignees? ? "has-assignees" : "no-assignees" %> <%= (session.ends_at < Time.now ? "past" : "") %>">
     <h4>
       <small class="text-2xs uppercase font-light bg-black/10 rounded-sm p-1 mr-1 lang-<%= session.language %>"><%= session.language %></small>
@@ -58,12 +58,6 @@
       </div>
     <% end %>
     <small>unassigned (<%= unassigned_users.length %>)</small>
-    <ul class="flex flex-row flex-wrap gap-1 my-1 flex-shrink-0">
-      <% unassigned_users.each do |user| %>
-        <li>
-          <span class="unassigned-user"><%= render partial: 'assignments/user_add', locals: { user: user, session: session } %></span>
-        </li>
-      <% end %>
-    </ul>
+    <%= render partial: 'assignments/filteredlist', locals: { session: session, users: unassigned_users } %>
   </div>
 <% end %>
-- 
GitLab