From 2afd98a2e929ae876b9b18617ac23d28f4823481 Mon Sep 17 00:00:00 2001 From: maebeale Date: Sat, 13 Jun 2026 17:15:12 -0400 Subject: [PATCH 1/2] Route notification detail page noticeable via routable_path PR #1656 fixed the notifications index but left the detail page calling polymorphic_path unconditionally, which raises for FormSubmission noticeables (bulk payments) that have no polymorphic route. Reuse the routable_path helper so the detail page links the same viewable destinations and degrades to plain text for routeless records. Co-Authored-By: Claude Opus 4.8 --- app/views/notifications/show.html.erb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb index d407511b84..ae9ec0e81d 100644 --- a/app/views/notifications/show.html.erb +++ b/app/views/notifications/show.html.erb @@ -143,10 +143,13 @@
Noticeable
- <% if @notification.noticeable.present? %> + <% record_path = @notification.noticeable.present? ? routable_path(@notification.noticeable) : nil %> + <% if record_path %> <%= link_to @notification.noticeable.class.name, - polymorphic_path(@notification.noticeable), + record_path, class: "text-blue-600 hover:underline" %> + <% elsif @notification.noticeable.present? %> + <%= @notification.noticeable.class.name %> <% else %> — <% end %> From b59885ed51586ac54c3bb4c0ad53225404a4a5c2 Mon Sep 17 00:00:00 2001 From: maebeale Date: Sat, 13 Jun 2026 17:25:30 -0400 Subject: [PATCH 2/2] Show noticeable record name, not model class, on notification links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Record/Noticeable links rendered the raw model class ("EventRegistration", "FormSubmission"), which says nothing about which record it is. Add noticeable_label (registrant · event, submitter · form, or the record's own name) and noticeable_type_label ("Registration", "Bulk payment", "Form submission"). Render as a small type label over a text link rather than a button so it mirrors the People column and doesn't compete with the row's View action. Co-Authored-By: Claude Opus 4.8 --- app/helpers/application_helper.rb | 28 +++++++++++++++++ app/views/notifications/_index.html.erb | 19 +++++++----- app/views/notifications/show.html.erb | 17 ++++++----- spec/helpers/application_helper_spec.rb | 40 +++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7be741c90e..2a3b3090cf 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -282,6 +282,34 @@ def form_submission_link_path(submission) form_submission_path(submission) end + # A friendly type name for a noticeable record, e.g. "Registration" or "Bulk + # payment" rather than the raw model name ("EventRegistration"). + def noticeable_type_label(record) + return "Registration" if record.is_a?(EventRegistration) + + if record.is_a?(FormSubmission) + return record.role == "bulk_payment" ? "Bulk payment" : "Form submission" + end + + record.class.name.underscore.humanize + end + + # A human-friendly name for a noticeable record (the registrant and event for a + # registration, the submitter and form for a submission, otherwise the record's + # own title/name), so links read as the thing rather than its model class. + def noticeable_label(record) + label = case record + when EventRegistration + [ record.registrant&.name, record.event&.title ].compact_blank.join(" · ") + when FormSubmission + [ record.person&.name, record.form&.name ].compact_blank.join(" · ") + else + record.try(:title) || record.try(:name) || record.try(:full_name) + end + + label.presence || "##{record.id}" + end + def search_page(params) params[:search] ? params[:search][:page] : 1 end diff --git a/app/views/notifications/_index.html.erb b/app/views/notifications/_index.html.erb index fbf0df0d54..fd146fdcd0 100644 --- a/app/views/notifications/_index.html.erb +++ b/app/views/notifications/_index.html.erb @@ -43,14 +43,17 @@ - <% record_path = notification.noticeable.present? ? routable_path(notification.noticeable) : nil %> - <% if record_path %> - <%= link_to notification.noticeable.class.name, - record_path, - data: { turbo_frame: "_top" }, - class: "btn btn-secondary-outline" %> - <% elsif notification.noticeable.present? %> - <%= notification.noticeable.class.name %> + <% if notification.noticeable.present? %> + <% record_path = routable_path(notification.noticeable) %> +
<%= noticeable_type_label(notification.noticeable) %>
+ <% if record_path %> + <%= link_to noticeable_label(notification.noticeable), + record_path, + data: { turbo_frame: "_top" }, + class: "font-medium text-blue-600 hover:underline" %> + <% else %> + <%= noticeable_label(notification.noticeable) %> + <% end %> <% else %> <% end %> diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb index ae9ec0e81d..94c9354eae 100644 --- a/app/views/notifications/show.html.erb +++ b/app/views/notifications/show.html.erb @@ -143,13 +143,16 @@
Noticeable
- <% record_path = @notification.noticeable.present? ? routable_path(@notification.noticeable) : nil %> - <% if record_path %> - <%= link_to @notification.noticeable.class.name, - record_path, - class: "text-blue-600 hover:underline" %> - <% elsif @notification.noticeable.present? %> - <%= @notification.noticeable.class.name %> + <% if @notification.noticeable.present? %> + <% record_path = routable_path(@notification.noticeable) %> + <%= noticeable_type_label(@notification.noticeable) %> + <% if record_path %> + <%= link_to noticeable_label(@notification.noticeable), + record_path, + class: "font-medium text-blue-600 hover:underline" %> + <% else %> + <%= noticeable_label(@notification.noticeable) %> + <% end %> <% else %> — <% end %> diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 0d6f7b22a0..49f4a50c76 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -361,4 +361,44 @@ expect(helper.routable_path(submission)).to eq(form_submission_path(submission)) end end + + describe "#noticeable_type_label" do + it "names registrations and bulk payments in plain language" do + registration = build(:event_registration) + bulk = build(:form_submission, role: "bulk_payment") + submission = build(:form_submission, role: "registration") + + expect(helper.noticeable_type_label(registration)).to eq("Registration") + expect(helper.noticeable_type_label(bulk)).to eq("Bulk payment") + expect(helper.noticeable_type_label(submission)).to eq("Form submission") + end + + it "humanizes other model names" do + expect(helper.noticeable_type_label(build(:user))).to eq("User") + end + end + + describe "#noticeable_label" do + it "describes a registration by registrant and event" do + person = create(:person, first_name: "Jane", last_name: "Doe") + event = create(:event, title: "Summer Workshop") + registration = create(:event_registration, registrant: person, event: event) + + expect(helper.noticeable_label(registration)).to eq("#{person.name} · Summer Workshop") + end + + it "describes a form submission by submitter and form" do + person = create(:person, first_name: "Jane", last_name: "Doe") + form = create(:form, name: "Bulk Payment") + submission = create(:form_submission, person: person, form: form) + + expect(helper.noticeable_label(submission)).to eq("#{person.name} · Bulk Payment") + end + + it "uses the record's own name for other models" do + user = build(:user) + + expect(helper.noticeable_label(user)).to eq(user.name) + end + end end