Skip to content

Commit 78fc0f8

Browse files
maebealeclaudeCopilot
authored
Group manage registrants header actions into dropdowns (#1568)
* Group manage registrants header actions into dropdowns The action bar had grown to 7 flat, undifferentiated links with no hierarchy. Collapse them into two scoped dropdowns plus one link so the header stays usable as more form entry points get added: - Forms: Add registrant, Public registration, Scholarship version - Bulk actions: Send reminder, Download CSV - Edit event sits apart on the right since it acts on the event, not the registrants Also drops the redundant "View" link (duplicated the back link) and relabels for clarity ("Edit" -> "Edit event", "New registration" -> "Add registrant"). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Surface Add registrant as primary button outside Forms dropdown Adding a registrant is the primary action on this page, so burying it in a dropdown hurt discoverability. Pull it out as a primary-outline button (matching the New event registration button on the registrations index). The Forms dropdown now holds only the two public-form links, which makes its label accurate; it renders only when a registration form exists (otherwise both items would be gated away, leaving an empty menu). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Match index convention: primary action far right, design-system buttons Reorder the header so emphasis increases left to right and ends on the primary action, matching the new/add button placement on the index pages (event_registrations, organizations, events all put the primary button far right): Edit event Forms ▾ Bulk actions ▾ Add registrant Also swap the two dropdown triggers from ad-hoc classes to the btn design-system variant (btn-utility-outline) so their height, radius, padding, and shadow match the primary button instead of sitting shorter, and drop the now-unneeded Edit event divider. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Add rel=noopener noreferrer to target=_blank form links Prevent reverse-tabnabbing on the public-form links opened in a new tab, matching the existing convention in _videoconference_link. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Carry as_visitor and scholarship_form gating into forms menu Main added as_visitor: true to the public-registration links and gated the scholarship link on event.scholarship_form. Those lived on the inline links this branch replaced with the Forms dropdown, so the rebase dropped them — reapply them inside _forms_menu. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Hide Forms dropdown when no registration or scholarship form exists Gate each form link on its own form's presence and the whole dropdown on either being present, so admins do not see an empty Forms menu (or a broken public-registration link) when neither form is configured. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Always show Forms dropdown; offer Select a form when none configured When neither a registration nor scholarship form is configured, the Forms menu now links to the event edit forms section instead of hiding, so admins always have a path to set one up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Add base-form management link to Forms dropdown Give admins a way to reach the form configuration from the Forms menu: Change base form when one is set, Enable forms when none is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Rename Forms menu to Form actions; expand form section on anchor Rename the _forms_menu partial to _form_actions_menu and relabel the trigger 'Form actions' to better describe what it does (open public forms, change/enable the base form). Add a reveal-section Stimulus controller so the Change base form / Enable forms links land on the event edit page with the Registration Form section expanded and scrolled into view, rather than collapsed or off-screen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 1e05e35 commit 78fc0f8

7 files changed

Lines changed: 88 additions & 15 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ This codebase (Rails 8.1)
7171
| Directory | Purpose |
7272
|---|---|
7373
| `app/frontend/entrypoints/` | Vite entry points (application.js, application.css) |
74-
| `app/frontend/javascript/controllers/` | Stimulus controllers (34) |
74+
| `app/frontend/javascript/controllers/` | Stimulus controllers (35) |
7575
| `app/frontend/javascript/rhino/` | Rich text editor customizations (mentions, grid) |
7676
| `app/frontend/stylesheets/` | Tailwind CSS and component styles |
7777

@@ -272,6 +272,7 @@ end
272272
- `prefetch_lazy` — Prefetch lazy-loaded content
273273
- `print_options` — Print options toggle for analytics
274274
- `remote_select` — AJAX-powered select dropdown
275+
- `reveal_section` — Expand a collapsible section and scroll to it when loaded via matching URL hash
275276
- `rhino_source` — Rich text editor integration
276277
- `searchable_checkbox` — TomSelect checkbox-style multi-select
277278
- `searchable_select` — Tom Select autocomplete

app/frontend/javascript/controllers/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ application.register("prefetch-lazy", PrefetchLazyController)
7575
import PrintOptionsController from "./print_options_controller"
7676
application.register("print-options", PrintOptionsController)
7777

78+
import RevealSectionController from "./reveal_section_controller"
79+
application.register("reveal-section", RevealSectionController)
80+
7881
import RhinoSourceController from "./rhino_source_controller"
7982
application.register("rhino-source", RhinoSourceController)
8083

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Controller } from "@hotwired/stimulus"
2+
3+
// Expands a collapsible section and scrolls it into view when the page loads
4+
// with a matching URL hash — e.g. when linked from another page so the user
5+
// lands on the section already open rather than collapsed or scrolled past.
6+
//
7+
// <div data-controller="dropdown reveal-section"
8+
// data-reveal-section-anchor-value="registration_form_section"
9+
// data-reveal-section-target="toggle"> ... the toggle button
10+
// data-reveal-section-target="content"> ... the collapsible body
11+
//
12+
// The toggle target is clicked (reusing the dropdown controller) only when the
13+
// content is currently hidden, keeping the dropdown's open/closed state honest.
14+
export default class extends Controller {
15+
static targets = ["toggle", "content"]
16+
static values = { anchor: String }
17+
18+
connect() {
19+
if (window.location.hash.slice(1) !== this.anchorValue) return
20+
if (this.hasContentTarget && this.contentTarget.classList.contains("hidden")) {
21+
this.toggleTarget.click()
22+
}
23+
this.element.scrollIntoView({ behavior: "smooth", block: "start" })
24+
}
25+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<% dropdown_id = "bulk-actions-menu-#{SecureRandom.hex(4)}" %>
2+
<% item_class = "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50" %>
3+
<div data-controller="dropdown" class="relative">
4+
<button type="button"
5+
data-action="dropdown#toggle"
6+
data-dropdown-payload-param='[{"<%= dropdown_id %>":"hidden"}]'
7+
class="btn btn-utility-outline">
8+
<span>Bulk actions</span>
9+
<i class="fa-solid fa-chevron-down w-3 h-3 text-gray-400"></i>
10+
</button>
11+
12+
<div id="<%= dropdown_id %>"
13+
data-dropdown-target="content"
14+
class="hidden absolute right-0 z-10 mt-1 bg-white border border-gray-200 rounded-md shadow-lg py-1 min-w-[180px]">
15+
<%= link_to "Send reminder", preview_reminder_event_path(@event), class: item_class %>
16+
<%= link_to "Download CSV", manage_event_path(@event, format: :csv), class: item_class, data: { turbo_frame: "_top" } %>
17+
</div>
18+
</div>

app/views/events/_form.html.erb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,18 +318,21 @@
318318
</div>
319319

320320
<% if allowed_to?(:manage?, @event) %>
321-
<div class="admin-only registration-form-section" data-controller="dropdown">
321+
<div class="admin-only registration-form-section"
322+
data-controller="dropdown reveal-section"
323+
data-reveal-section-anchor-value="registration_form_section">
322324
<button
323325
type="button"
324326
id="registration_form_button"
325327
class="flex items-center justify-between cursor-pointer w-full bg-gray-500 text-white hover:bg-gray-300 px-3 py-2 rounded-t-md"
326328
data-action="dropdown#toggle"
329+
data-reveal-section-target="toggle"
327330
data-dropdown-payload-param='[{"registration_form_section":"hidden"}, {"registration_form_arrow":"rotate-180"}, {"registration_form_button":"rounded-t-md rounded-md"}]'>
328331
Registration Form
329332
<i id="registration_form_arrow" class="fa-solid fa-chevron-down w-4 h-4 transition-transform duration-300 rotate-180"></i>
330333
</button>
331334

332-
<div id="registration_form_section" class="border border-gray-300 p-4 rounded-b-md">
335+
<div id="registration_form_section" class="border border-gray-300 p-4 rounded-b-md" data-reveal-section-target="content">
333336
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
334337
<div>
335338
<p class="text-sm mb-2">
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<% dropdown_id = "form-actions-menu-#{SecureRandom.hex(4)}" %>
2+
<% item_class = "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50" %>
3+
<div data-controller="dropdown" class="relative">
4+
<button type="button"
5+
data-action="dropdown#toggle"
6+
data-dropdown-payload-param='[{"<%= dropdown_id %>":"hidden"}]'
7+
class="btn btn-utility-outline">
8+
<span>Form actions</span>
9+
<i class="fa-solid fa-chevron-down w-3 h-3 text-gray-400"></i>
10+
</button>
11+
12+
<div id="<%= dropdown_id %>"
13+
data-dropdown-target="content"
14+
class="hidden absolute right-0 z-10 mt-1 bg-white border border-gray-200 rounded-md shadow-lg py-1 min-w-[180px]">
15+
<% if @event.event_forms.registration.exists? || @event.scholarship_form %>
16+
<% if @event.event_forms.registration.exists? %>
17+
<%= link_to "Public registration", new_event_public_registration_path(@event, as_visitor: true), class: item_class, target: "_blank", rel: "noopener noreferrer" %>
18+
<% end %>
19+
<% if @event.scholarship_form %>
20+
<%= link_to "Scholarship version", new_event_public_registration_path(@event, scholarship_requested: true, as_visitor: true), class: item_class, target: "_blank", rel: "noopener noreferrer" %>
21+
<% end %>
22+
<div class="my-1 border-t border-gray-100"></div>
23+
<%= link_to "Change base form", edit_event_path(@event, anchor: "registration_form_section"), class: item_class %>
24+
<% else %>
25+
<%= link_to "Enable forms", edit_event_path(@event, anchor: "registration_form_section"), class: item_class %>
26+
<% end %>
27+
</div>
28+
</div>

app/views/events/manage.html.erb

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,15 @@
1010
<%= @event.title %>
1111
</p>
1212
</div>
13-
<div class="flex gap-2">
14-
<%= link_to "Send reminder", preview_reminder_event_path(@event), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
15-
<%= link_to "Download CSV", manage_event_path(@event, format: :csv), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1", data: { turbo_frame: "_top" } %>
16-
<%= link_to "View", event_path(@event), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
13+
<div class="flex items-center gap-2">
1714
<% if allowed_to?(:edit?, @event) %>
18-
<%= link_to "Edit", edit_event_path(@event), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
19-
<%= link_to "New registration", new_event_registration_path(event_id: @event.id, return_to: "manage"), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
20-
<% if @event.event_forms.registration.exists? %>
21-
<%= link_to "Public registration", new_event_public_registration_path(@event, as_visitor: true), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1", target: "_blank" %>
22-
<% if @event.scholarship_form %>
23-
<%= link_to "Scholarship version", new_event_public_registration_path(@event, scholarship_requested: true, as_visitor: true), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1", target: "_blank" %>
24-
<% end %>
25-
<% end %>
15+
<%= link_to "Edit event", edit_event_path(@event), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
16+
<%= render "form_actions_menu" %>
2617
<% end %>
18+
<%= render "bulk_actions_menu" %>
19+
<% if allowed_to?(:new?, EventRegistration) %>
20+
<%= link_to "Add registrant", new_event_registration_path(event_id: @event.id, return_to: "manage"), class: "admin-only bg-blue-100 btn btn-primary-outline" %>
21+
<% end %>
2722
</div>
2823
</div>
2924

0 commit comments

Comments
 (0)