diff --git a/app/decorators/event_decorator.rb b/app/decorators/event_decorator.rb index 442ca544fe..814d46ec51 100644 --- a/app/decorators/event_decorator.rb +++ b/app/decorators/event_decorator.rb @@ -58,6 +58,22 @@ def date_range end end + # Same collapsed range as `date_range` but without the weekday prefix + # (e.g. "Sep 20-21, 2026") — for tighter contexts where the weekday is noise. + def short_date_range + s = start_date.in_time_zone(Time.zone) + e = (end_date || start_date).in_time_zone(Time.zone) + return s.strftime("%b %-d, %Y") if s.to_date == e.to_date + + if s.year == e.year && s.month == e.month + "#{s.strftime('%b')} #{s.strftime('%-d')}-#{e.strftime('%-d')}, #{s.strftime('%Y')}" + elsif s.year == e.year + "#{s.strftime('%b %-d')} - #{e.strftime('%b %-d')}, #{s.strftime('%Y')}" + else + "#{s.strftime('%b %-d, %Y')} - #{e.strftime('%b %-d, %Y')}" + end + end + def detail(length: nil) length ? description&.truncate(length) : description end diff --git a/app/views/events/_payment_badge.html.erb b/app/views/events/_payment_badge.html.erb index 96d83d4cf2..a69aeb9bda 100644 --- a/app/views/events/_payment_badge.html.erb +++ b/app/views/events/_payment_badge.html.erb @@ -1,8 +1,8 @@ <% if payment %> <% if payment.amount_cents_remaining > 0 %> <%= link_to payment_path(payment), - class: "inline-flex items-center gap-1 rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800 hover:bg-green-200 shrink-0" do %> - Paid — <%= dollars_from_cents(payment.amount_cents_remaining) %> remaining + class: "inline-flex items-center gap-1 rounded-full bg-orange-100 px-2.5 py-0.5 text-xs font-medium text-orange-800 hover:bg-orange-200 shrink-0" do %> + Paid — <%= dollars_from_cents(payment.amount_cents_remaining) %> unallocated <% end %> <% else %> diff --git a/app/views/events/bulk_payments/ticket.html.erb b/app/views/events/bulk_payments/ticket.html.erb index 111d0b8194..d3df32527f 100644 --- a/app/views/events/bulk_payments/ticket.html.erb +++ b/app/views/events/bulk_payments/ticket.html.erb @@ -2,6 +2,10 @@ <% attendees = @submission.bulk_payment_attendees total_cents = @payment&.amount_cents || @submission.bulk_payment_amount_cents(@event) + answers = @submission.answers_by_identifier + payer_name = [ answers["payer_first_name"], answers["payer_last_name"] ].compact_blank.join(" ").presence || @submission.person.name + payer_organization = answers["payer_organization"] + attendee_count = @submission.bulk_payment_attendee_count %>
@@ -59,79 +63,67 @@
<% end %> - -
-
+ +
+
<%= render "assets/display_image", resource: @event, - width: 48, height: 32, + width: "full", height: "full", variant: :index, link: event_path(@event), file: @event.display_image %>
- -
- <% if @event.respond_to?(:pre_title) && @event.pre_title.present? %> -

<%= @event.pre_title %>

+
+

<%= link_to @event.title, event_path(@event), class: "hover:underline" %>

+

<%= @event.short_date_range %>

+ <% if @event.labelled_cost.present? %> +

<%= @event.labelled_cost %>

<% end %> - -

<%= link_to @event.title, event_path(@event), class: "hover:underline" %>

- +
- -
-
<%= @event.times(display_day: true, display_date: true, styled: true) %>
- - <% if @event.labelled_cost.present? %> -
<%= @event.labelled_cost %>
- <% end %> - - <% if @event.location.present? %> -
<%= @event.location.name %>
- <% end %> -
- - -
- <% if @payment %> - - Payment received<% if total_cents.to_i > 0 %> — <%= dollars_from_cents(total_cents) %><% end %> - - <% else %> - - Payment pending<% if total_cents.to_i > 0 %> — <%= dollars_from_cents(total_cents) %><% end %> - - <% end %> -
- - - <%= link_to event_invoice_path(@event, submission_id: @submission.id, return_to: "bulk_payment_ticket"), - class: "flex items-center gap-3 rounded-xl border-2 border-blue-200 bg-blue-50 px-4 py-3 hover:bg-blue-100 transition-colors" do %> - - -
-

View invoice

-

Itemized invoice for this payment

+ +
+
+

Payer

+

<%= payer_name.presence || "—" %>

+ <% if payer_organization.present? %> +

<%= payer_organization %>

+ <% end %> +

+ + Covering <%= attendee_count %> <%= "registrant".pluralize(attendee_count) %> +

- - <% end %> - - -
+ +
+ <% if @payment %> + + Payment received<% if total_cents.to_i > 0 %> — <%= dollars_from_cents(total_cents) %><% end %> + + <% else %> + + Payment pending<% if total_cents.to_i > 0 %> — <%= dollars_from_cents(total_cents) %><% end %> + + <% end %> +
+
- +

- Registrants<% if attendees.any? %> (<%= attendees.size %>)<% end %> + Registrant details<% if attendees.any? %> (<%= attendees.size %> of <%= attendee_count %>)<% end %>

<% if attendees.any? %>
@@ -157,28 +149,37 @@ <% end %>
- - <%= link_to event_bulk_payment_path(@event, reg: @submission.slug), - class: "flex items-center gap-3 rounded-xl border-2 border-gray-200 bg-white px-4 py-3 hover:bg-gray-50 transition-colors" do %> - + +
+ + + <%= link_to event_invoice_path(@event, submission_id: @submission.id, return_to: "bulk_payment_ticket"), + class: "flex items-center gap-3 rounded-xl border-2 border-blue-200 bg-blue-50 px-4 py-3 hover:bg-blue-100 transition-colors" do %> +
-

View submission

-

The <%= Form::BULK_PAYMENT_PUBLIC_NAME %> form you submitted

+

View invoice

+

Itemized invoice for this payment

- + <% end %> - -
+ +
+ <%= link_to "View your form responses", + event_bulk_payment_path(@event, reg: @submission.slug), + class: "text-sm text-gray-500 hover:text-blue-600 underline" %> <%= button_to "Resend confirmation email", bulk_payment_resend_confirmation_path(@submission.slug), class: "text-sm text-gray-500 hover:text-blue-600 underline bg-transparent border-0 cursor-pointer p-0" %>
<% if allowed_to?(:dashboard?, @event) %> -
+
+ + Admin only +

Payment allocations

<%= render "events/payment_badge", payment: @payment %> diff --git a/spec/decorators/event_decorator_spec.rb b/spec/decorators/event_decorator_spec.rb index a71071d298..dc53a71588 100644 --- a/spec/decorators/event_decorator_spec.rb +++ b/spec/decorators/event_decorator_spec.rb @@ -111,6 +111,33 @@ end end + describe "#short_date_range" do + it "shows a single date without a weekday when there is no end date" do + event = build(:event, start_date: Time.zone.local(2026, 6, 11, 12), end_date: nil).decorate + expect(event.short_date_range).to eq("Jun 11, 2026") + end + + it "shows a single date when start and end fall on the same day" do + event = build(:event, start_date: Time.zone.local(2026, 6, 11, 9), end_date: Time.zone.local(2026, 6, 11, 17)).decorate + expect(event.short_date_range).to eq("Jun 11, 2026") + end + + it "collapses month and year for a same-month range" do + event = build(:event, start_date: Time.zone.local(2026, 9, 20, 9), end_date: Time.zone.local(2026, 9, 21, 17)).decorate + expect(event.short_date_range).to eq("Sep 20-21, 2026") + end + + it "collapses only the year for a same-year, cross-month range" do + event = build(:event, start_date: Time.zone.local(2026, 6, 30, 9), end_date: Time.zone.local(2026, 7, 2, 17)).decorate + expect(event.short_date_range).to eq("Jun 30 - Jul 2, 2026") + end + + it "shows both years for a cross-year range" do + event = build(:event, start_date: Time.zone.local(2025, 12, 31, 9), end_date: Time.zone.local(2026, 1, 2, 17)).decorate + expect(event.short_date_range).to eq("Dec 31, 2025 - Jan 2, 2026") + end + end + describe "#labelled_cost" do it "returns nil when cost_cents is nil" do event = build(:event, cost_cents: nil).decorate diff --git a/spec/requests/events/bulk_payments_spec.rb b/spec/requests/events/bulk_payments_spec.rb index 4fa2d23cc7..711a6c32a2 100644 --- a/spec/requests/events/bulk_payments_spec.rb +++ b/spec/requests/events/bulk_payments_spec.rb @@ -272,6 +272,23 @@ def get_ticket expect(response.body).to include("jordan@example.com") end + it "shows the payer's name, organization, and registrants-covered count" do + submission.form_answers.create!(form_field: payer_first_name_field, submitted_answer: "Alex", + question_name_when_answered: "Payer first name") + submission.form_answers.create!(form_field: payer_last_name_field, submitted_answer: "Chen", + question_name_when_answered: "Payer last name") + submission.form_answers.create!(form_field: org_field, submitted_answer: "Bright Futures Academy", + question_name_when_answered: "Organization") + + get_ticket + + expect(response.body).to include("Payer") + expect(response.body).to include("Alex Chen") + expect(response.body).to include("Bright Futures Academy") + # One attendee is listed and no explicit count is set, so the covered count is 1. + expect(response.body).to include("Covering 1 registrant") + end + it "does not show per-person actions like cancelling a registration" do get_ticket @@ -284,9 +301,10 @@ def get_ticket expect(response.body).to include("return_to=bulk_payment_ticket") end - it "links 'View submission' to the public submission page" do + it "links 'View your form responses' to the public submission page" do get_ticket + expect(response.body).to include("View your form responses") expect(response.body).to include(event_bulk_payment_path(event, reg: submission.slug)) end