From 8cc835983bdb3651b530d7f7d1933ffb96d2b948 Mon Sep 17 00:00:00 2001 From: hamaguchi <93605959+LeandroHamaguchi@users.noreply.github.com> Date: Sun, 5 Apr 2026 16:45:02 -0400 Subject: [PATCH 1/3] Add Volunteers Shortcuts: Implement VolunteersShortcutsModal for bulk actions and filtering. Enhance FilterBar with shortcut presets for missing contact information. Update TableToolbar to include shortcuts button for admin users. Refactor volunteers data handling to support new filters and shortcuts functionality. --- src/app/globals.css | 21 + .../volunteers/AddVolunteerModal.tsx | 6 +- src/components/volunteers/FilterBar.tsx | 138 +++- src/components/volunteers/TableToolbar.tsx | 90 +-- .../volunteers/VolunteersShortcutsModal.tsx | 730 ++++++++++++++++++ src/components/volunteers/VolunteersTable.tsx | 144 ++++ .../volunteers/volunteersShortcutsUtils.ts | 206 +++++ src/lib/api/getVolunteersByMultipleColumns.ts | 82 +- src/lib/volunteerFilterShortcuts.ts | 11 + .../volunteersShortcutsUtils.test.ts | 169 ++++ .../getVolunteersByMultipleColumns.test.ts | 18 + 11 files changed, 1527 insertions(+), 88 deletions(-) create mode 100644 src/components/volunteers/VolunteersShortcutsModal.tsx create mode 100644 src/components/volunteers/volunteersShortcutsUtils.ts create mode 100644 src/lib/volunteerFilterShortcuts.ts create mode 100644 tests/components/volunteersShortcutsUtils.test.ts diff --git a/src/app/globals.css b/src/app/globals.css index 9fe15a08..cb3f2488 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -37,4 +37,25 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } + + /* Pointer on interactive controls (Tailwind preflight defaults buttons to default cursor). */ + button:not(:disabled), + input[type="button"]:not(:disabled), + input[type="submit"]:not(:disabled), + input[type="reset"]:not(:disabled), + [role="button"]:not([aria-disabled="true"]) { + cursor: pointer; + } + + button:disabled, + input[type="button"]:disabled, + input[type="submit"]:disabled, + input[type="reset"]:disabled, + [role="button"][aria-disabled="true"] { + cursor: not-allowed; + } + + ::file-selector-button { + cursor: pointer; + } } diff --git a/src/components/volunteers/AddVolunteerModal.tsx b/src/components/volunteers/AddVolunteerModal.tsx index 3c1bab3b..af9eb0a4 100644 --- a/src/components/volunteers/AddVolunteerModal.tsx +++ b/src/components/volunteers/AddVolunteerModal.tsx @@ -252,11 +252,11 @@ function CohortField({ onRemove={() => removeAt(i)} /> ))} -
+
+ setBulkField(e.target.value as BulkShortcutField) + } + className="rounded-lg border border-gray-300 bg-white px-2 py-1.5 text-sm text-gray-900" + > + + + + + + + {bulkField === "opt_in_communication" && ( + + )} + {bulkField === "pronouns" && ( + + )} + {(bulkField === "pseudonym" || bulkField === "notes") && ( + + )} + +
+ + )} + + toggleSection("duplicates")} + contentClassName="gap-3" + > +

+ Same email (case-insensitive, trimmed) or same phone (digits + only, 7+ digits). A row link jumps to the correct pagination + page and scrolls that volunteer into view. +

+
+ + +
+ {duplicateClusters.length === 0 ? ( +

+ No duplicate groups found. +

+ ) : ( + + )} +
+ + toggleSection("missingContact")} + > +

+ Adds a server filter (combined with your existing opt-in filter + using AND). Closes this panel so you can work in the table. +

+
+ + + +
+
+ + toggleSection("batchCreate")} + > +

+ One volunteer per line, fields separated by{" "} + commas. Example:{" "} + + name, email, phone + + . If the second value has no{" "} + @, it is stored as phone. + Avoid commas inside names. New volunteers have no roles or + cohorts until you edit them. +

+