about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGriffin Smith <grfn@gws.fyi>2021-12-25T22·36-0500
committerclbot <clbot@tvl.fyi>2021-12-25T22·48+0000
commitb12dbaa3b6973bdc752e7ce6fa6f1d359a938a96 (patch)
tree19e2fb7018fbf877b3b6cb052380db430fc5a100
parentf093aa5bce455510bf7e05185282ef4d78e9b251 (diff)
refactor(grfn/bbbg): Live-filter for signup form r/3408
Rather than loading as the user types for the signup form, start the
page with the full list of attendees already loaded and filter that list
as the user types. There are never going to be more than 50 attendees,
so there's no perf cost here, and it's nice to have the list to scroll
through in the frontend.

Change-Id: Iba69b101856756801183979b9384503520b6701f
Reviewed-on: https://cl.tvl.fyi/c/depot/+/4624
Tested-by: BuildkiteCI
Reviewed-by: grfn <grfn@gws.fyi>
Autosubmit: grfn <grfn@gws.fyi>
-rw-r--r--users/grfn/bbbg/resources/public/main.js86
-rw-r--r--users/grfn/bbbg/src/bbbg/db/attendee.clj15
-rw-r--r--users/grfn/bbbg/src/bbbg/handlers/core.clj1
-rw-r--r--users/grfn/bbbg/src/bbbg/handlers/events.clj28
-rw-r--r--users/grfn/bbbg/src/bbbg/handlers/signup_form.clj42
-rw-r--r--users/grfn/bbbg/src/bbbg/styles.clj42
6 files changed, 154 insertions, 60 deletions
diff --git a/users/grfn/bbbg/resources/public/main.js b/users/grfn/bbbg/resources/public/main.js
index d4752f1141..2308ca5957 100644
--- a/users/grfn/bbbg/resources/public/main.js
+++ b/users/grfn/bbbg/resources/public/main.js
@@ -1,41 +1,59 @@
 window.onload = () => {
-  console.log("loaded");
   const input = document.getElementById("name-autocomplete");
   if (input != null) {
-    const eventID = document.getElementById("event-id").value;
-
-    const autocomplete = new autoComplete({
-      selector: "#name-autocomplete",
-      placeHolder: "Enter your name",
-      data: {
-        src: async (query) => {
-          const resp = await fetch(
-            `/attendees.json?q=${query}&event_id=${eventID}&attended=false`
-          );
-          console.log("got resp");
-          const { results } = await resp.json();
-          return results;
-        },
-        keys: ["bbbg.attendee/meetup-name"],
-      },
-      resultItem: {
-        highlight: {
-          render: true,
-        },
-      },
-    });
+    const attendeeList = document.getElementById("attendees-list");
+    const filterAttendees = (filter) => {
+      if (filter == "") {
+        for (let elt of attendeeList.querySelectorAll("li")) {
+          elt.classList.remove("hidden");
+        }
+
+        return;
+      }
+
+      let re = "";
+      for (let c of filter) {
+        re += `${c}.*`;
+      }
+      let filterRe = new RegExp(re, "i");
+
+      for (let elt of attendeeList.querySelectorAll("li")) {
+        const attendee = JSON.parse(elt.dataset.attendee);
+        if (attendee["bbbg.attendee/meetup-name"].match(filterRe) == null) {
+          elt.classList.add("hidden");
+        } else {
+          elt.classList.remove("hidden");
+        }
+      }
+    };
+
+    const attendeeIDInput = document.getElementById("attendee-id");
+    const submit = document.querySelector("#submit-button");
+    const signupForm = document.getElementById("signup-form");
+
+    input.oninput = (e) => {
+      filterAttendees(e.target.value);
+      attendeeIDInput.value = null;
+      submit.classList.add("hidden");
+      submit.setAttribute("disabled", "disabled");
+      signupForm.setAttribute("disabled", "disabled");
+    };
+
+    attendeeList.addEventListener("click", (e) => {
+      if (!(e.target instanceof HTMLLIElement)) {
+        return;
+      }
+      if (e.target.dataset.attendee == null) {
+        return;
+      }
+
+      const attendee = JSON.parse(e.target.dataset.attendee);
+      input.value = attendee["bbbg.attendee/meetup-name"];
+      attendeeIDInput.value = attendee["bbbg.attendee/id"];
 
-    input.addEventListener("selection", function (event) {
-      const attendee = event.detail.selection.value;
-      event.target.value = attendee["bbbg.attendee/meetup-name"];
-
-      const attendeeID = attendee["bbbg.attendee/id"];
-      document.getElementById("attendee-id").value = attendeeID;
-      document.getElementById("signup-form").removeAttribute("disabled");
-      document
-        .getElementById("signup-form")
-        .querySelector('input[type="submit"]')
-        .removeAttribute("disabled");
+      submit.classList.remove("hidden");
+      submit.removeAttribute("disabled");
+      signupForm.removeAttribute("disabled");
     });
   }
 
diff --git a/users/grfn/bbbg/src/bbbg/db/attendee.clj b/users/grfn/bbbg/src/bbbg/db/attendee.clj
index 5bc5430a94..68c3636e95 100644
--- a/users/grfn/bbbg/src/bbbg/db/attendee.clj
+++ b/users/grfn/bbbg/src/bbbg/db/attendee.clj
@@ -23,10 +23,17 @@
    (db/list db (search query q))))
 
 (defn for-event
-  ([query event-id]
-   (-> query
-       (merge-join :event_attendee [:= :attendee.id :event_attendee.attendee_id])
-       (merge-where [:= :event_attendee.event_id event-id]))))
+  ([db-or-query event-id]
+   (if (db/database? db-or-query)
+     (for-event db-or-query
+                {:select [:attendee.*]
+                 :from [:attendee]}
+                event-id)
+     (-> db-or-query
+         (merge-join :event_attendee [:= :attendee.id :event_attendee.attendee_id])
+         (merge-where [:= :event_attendee.event_id event-id]))))
+  ([db query event-id]
+   (db/list db (for-event query event-id))))
 
 (defn with-stats
   ([] (with-stats {:select [:attendee.*]
diff --git a/users/grfn/bbbg/src/bbbg/handlers/core.clj b/users/grfn/bbbg/src/bbbg/handlers/core.clj
index e30381de5b..e08b7a1e70 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/core.clj
+++ b/users/grfn/bbbg/src/bbbg/handlers/core.clj
@@ -76,7 +76,6 @@
         #_(flash/render-flash flash/test-flash)
         (flash/render-flash)
         body]
-       [:script {:src "https://cdnjs.cloudflare.com/ajax/libs/tarekraafat-autocomplete.js/10.2.6/autoComplete.js"}]
        [:script {:src "/main.js"}]]])))
 
 (defn page-response [& render-page-args]
diff --git a/users/grfn/bbbg/src/bbbg/handlers/events.clj b/users/grfn/bbbg/src/bbbg/handlers/events.clj
index 4bf9fa2d3e..6fe5b8727b 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/events.clj
+++ b/users/grfn/bbbg/src/bbbg/handlers/events.clj
@@ -31,6 +31,15 @@
     [:input {:type :file
              :name :attendees}]]])
 
+(defn import-attendees-form [event]
+  [:form {:method :post
+          :action (str "/events/" (::event/id event) "/attendees")
+          :enctype "multipart/form-data"}
+   (import-attendee-list-form-group)
+   [:div.form-group
+    [:input {:type :submit
+             :value "Import"}]]])
+
 (defn event-page [{:keys [event]}]
   [:div.event-page
    [:h1 (format-date (::event/date event))]
@@ -46,13 +55,12 @@
     [:a {:href (str "/signup-forms/" (::event/id event) )}
      "Go to Signup Form"]]
    [:div
-    [:form {:method :post
-            :action (str "/events/" (::event/id event) "/attendees")
-            :enctype "multipart/form-data"}
-     (import-attendee-list-form-group)
-     [:div.form-group
-      [:input {:type :submit
-               :value "Import"}]]]]])
+    (import-attendees-form event)]])
+
+(defn import-attendees-page [{:keys [event]}]
+  [:div.page
+   [:h1 "Import Attendees for " (format-date (::event/date event))]
+   (import-attendees-form event)])
 
 (defn event-form
   ([] (event-form {}))
@@ -111,6 +119,12 @@
            (event-page {:event event}))
           (not-found "Event Not Found")))
 
+      (GET "/attendees/import" []
+        (if-let [event (db/get db :event id)]
+          (page-response
+           (import-attendees-page {:event event}))
+          (not-found "Event Not Found")))
+
       (POST "/attendees" [attendees]
         (let [num-imported (import-attendees! db id (:tempfile attendees))]
           (-> (redirect (str "/events/" id))
diff --git a/users/grfn/bbbg/src/bbbg/handlers/signup_form.clj b/users/grfn/bbbg/src/bbbg/handlers/signup_form.clj
index 58538716ba..4485327a7b 100644
--- a/users/grfn/bbbg/src/bbbg/handlers/signup_form.clj
+++ b/users/grfn/bbbg/src/bbbg/handlers/signup_form.clj
@@ -3,10 +3,13 @@
    [bbbg.db :as db]
    [bbbg.db.event :as db.event]
    [bbbg.event :as event]
-   [bbbg.handlers.core :refer [page-response authenticated?]]
+   [bbbg.handlers.core :refer [page-response authenticated? *authenticated?*]]
    [compojure.core :refer [GET context]]
    [java-time :refer [local-date]]
-   [ring.util.response :refer [redirect]]))
+   [ring.util.response :refer [redirect]]
+   [bbbg.db.attendee :as db.attendee]
+   [cheshire.core :as json]
+   [bbbg.attendee :as attendee]))
 
 (defn no-events-page [{:keys [authenticated?]}]
   [:div.no-events
@@ -17,14 +20,12 @@
       [:a {:href (str "/events/new?date=" (str (local-date)))} "Create Event"]
       [:a {:href "/events"} "All Events"]])])
 
-(defn signup-page [event]
+(defn signup-page [{:keys [event attendees]}]
   [:div.signup-page
    [:form#signup-form
     {:method "POST"
      :action "/event_attendees"
      :disabled "disabled"}
-    [:input#event-id {:type "hidden" :name "event_id" :value (::event/id event)}]
-    [:input#attendee-id {:type "hidden" :name "attendee_id"}]
     [:input#name-autocomplete
      {:type "search"
       :title "Name"
@@ -34,9 +35,29 @@
       :autocomplete "off"
       :autocapitalize "off"
       :maxlength "2048"}]
-    [:input {:type "submit"
-             :value "Sign In"
-             :disabled "disabled"}]]])
+    [:input#attendee-id {:type "hidden" :name "attendee_id"}]
+    [:input#event-id {:type "hidden" :name "event_id" :value (::event/id event)}]
+    [:input#submit-button.hidden
+     {:type "submit"
+      :value "Sign In"
+      :disabled "disabled"}]]
+   [:ul#attendees-list
+    (if (seq attendees)
+      (for [attendee attendees]
+        [:li {:data-attendee (json/generate-string attendee)
+              :role "button"}
+         (::attendee/meetup-name attendee)])
+      [:li.no-attendees
+       [:p
+        "Nobody has RSVPed to this event yet, or no attendee list has been
+         imported"]
+       (when *authenticated?*
+         [:p
+          [:a.button
+           {:href (str "/events/"
+                       (::event/id event)
+                       "/attendees/import")}
+           "Import Attendee List"]])])]])
 
 (defn event-not-found []
   [:div.event-not-found
@@ -55,5 +76,8 @@
 
    (GET "/:event-id" [event-id]
      (if-let [event (db/get db :event event-id)]
-       (page-response (signup-page event))
+       (let [attendees (db.attendee/for-event db event-id)]
+         (page-response
+          (signup-page {:event event
+                        :attendees attendees})))
        (event-not-found)))))
diff --git a/users/grfn/bbbg/src/bbbg/styles.clj b/users/grfn/bbbg/src/bbbg/styles.clj
index 61e1132b40..09f5eb3111 100644
--- a/users/grfn/bbbg/src/bbbg/styles.clj
+++ b/users/grfn/bbbg/src/bbbg/styles.clj
@@ -145,17 +145,25 @@
       {:outline "none"
        :border-color purple}]))
 
-  [(attr= "type" "submit") :button
+  [(attr= "type" "submit") :button :.button
    {:background-color (color/lighten blue 30)
     :padding "0.6rem 0.75rem"
     :border-radius "3px"
     :border [[(px 1) "solid" (color/lighten blue 30)]]
-    :cursor :pointer}
+    :cursor :pointer
+    :display :inline-block}
    [(& hover)
-    {:border-color blue}]
+    {:border-color blue
+     :text-decoration :none
+     :box-shadow [[0 "1px" "5px" "rgba(0,0,0,0.075)"]]}
+    [(& :a)
+     {:text-decoration :none}]]
    [(& active)
     {:background-color blue
-     :color :white}]])
+     :color :white
+     :box-shadow :none}
+    [(& :a)
+     {:text-decoration :none}]]])
 
 (defstyles tables
   [:table
@@ -230,7 +238,28 @@
     {:margin-left "1rem"}]
 
    [(attr= "type" "submit")
-    {:flex 0}]])
+    {:flex 0}]]
+
+  [:#attendees-list
+   {:list-style "none"
+    :overflow-y "auto"
+    :height "calc(100vh - 8.32425rem)"}
+
+   [:li
+    {:padding "0.75rem 1rem"
+     :margin "0.35rem 0"
+     :border-radius "3px"
+     :background-color silver}]]
+
+  [:.no-attendees
+   {:text-align "center"
+    :margin-top "6rem"}
+
+   [:.button
+    {:margin-top "0.5rem"}]]
+
+  [:.hidden
+   {:display :none}])
 
 (defstyles styles
   forms
@@ -259,6 +288,9 @@
      :margin-left "auto"
      :margin-right "auto"})]
 
+  [(attr= "role" "button")
+   {:cursor :pointer}]
+
   [:a {:color blue
        :text-decoration :none}
    link-conditional-styles])