Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4d3c8cc
Events IA Phase 1: Live vs On-demand terminology
Aug 9, 2026
8c1a81c
Events IA Phase 2: canonical scopes/constants for duplicated stat logic
Aug 9, 2026
f46d969
Events IA Phase 3: batched EventScholarshipFigures
Aug 9, 2026
b85629e
Events IA Phase 7: cross-event Reports hub + Attendees, owner-scoped
Aug 9, 2026
9562020
Events IA Phase 5/6: subnav restructure, remove Background page
Aug 9, 2026
4933547
Fix 500 on attendees charts when a city has scholarship recipients
Aug 9, 2026
7704bbb
Roster as its own per-event page (active registrants), sharing partials
Aug 9, 2026
1cd2d5d
Participation 'Attendees' link + wider reports Event dropdown
Aug 9, 2026
df47729
Split the report Event-type filter into Live vs On-demand trainings
Aug 9, 2026
ed311d4
Add an Attendees link under Full report on the participation hub card
Aug 9, 2026
964b6d2
Recipients page eyebrow returns to the event dashboard
Aug 9, 2026
0d524df
Ahoy page-view logging for event pages/reports; Roster charts toggle …
Aug 9, 2026
45c1e97
Kill the program-status N+1 on the event dashboard/roster pages
Aug 9, 2026
b67e557
Preload affiliations in the cross-event attendee services too
Aug 9, 2026
410b48e
Rename TrainingAttendees{Roster,Breakdowns} -> Attendees{Roster,Break…
Aug 9, 2026
9bd4407
Open cross-event report suite to event owners, scoped to their own ev…
Aug 9, 2026
7fc0655
Track preview/sample-ticket/reminder Ahoy views; rename Reports → "Ev…
Aug 9, 2026
d447b55
Event pages: shared report header, roster/recipients return paths, at…
Aug 9, 2026
3b58a15
Surface applied attendees drill-in filters as removable chips
Aug 9, 2026
7cf6dee
Unify roster/attendees/recipients charts pattern; wire real recipient…
Aug 9, 2026
f84d284
Charts frames: skeleton placeholder instead of "Loading…" text
Aug 9, 2026
b963bd1
panel-toggle: self-heal control state on connect
Aug 9, 2026
3bfdc9c
Breakdowns filter their own page; open the attendees index past atten…
Aug 9, 2026
3843d36
Section toggles: short Show/Hide beside the heading, not a block belo…
Aug 9, 2026
2b8b813
Drop the Staff tab from the event subnav
Aug 9, 2026
2916e1c
Batch the scholarship report's recipient splits instead of a dashboar…
Aug 9, 2026
af6681d
Point revenue drill-ins at registrations, and split the two recipient…
Aug 9, 2026
7332314
Preload orgs' affiliations on Onboarding; group the recipients list c…
Aug 9, 2026
d70523f
Mark the event admin pages admin-or-owner, matching their policies
Aug 9, 2026
9311732
Follow the grants donor→funder rename through this branch's scholarsh…
Aug 9, 2026
fa0cb48
Guard the onboarding preload against regressing to a per-org query
Aug 9, 2026
86b714c
Move the panel toggle bar into the first section's heading row
Aug 10, 2026
8935ad3
Follow the donor→funder rename into the recipients funder grouping
Aug 10, 2026
7baf339
Keep the breakdowns panel open when a chart row drills in
Aug 10, 2026
dfe2cb9
Bound the roster's sector and state drill-ins to the event's registrants
Aug 10, 2026
f314633
Let the recipients drill-in and the funder grouping compose
Aug 10, 2026
2f5c95f
Chip the attendees index's payment and funding drill-ins
Aug 10, 2026
51caf98
Roster counts who was there, not who was partly there
Aug 10, 2026
be630a8
Report incomplete attendance instead of excluding it
Aug 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ This codebase (Rails 8.1)
| Directory | Purpose | Count |
|---|---|---|
| `app/models/` | ActiveRecord models | ~80 files |
| `app/services/` | Service objects and POROs (e.g. `MoneyFormatter` for currency display) | ~40 files |
| `app/services/` | Service objects and POROs (e.g. `MoneyFormatter` for currency display) | ~49 files |
| `app/jobs/` | SolidQueue background jobs | 4 files |
| `app/models/concerns/` | Shared model modules | 16 concerns |

Expand All @@ -58,7 +58,7 @@ This codebase (Rails 8.1)
| Directory | Purpose | Count |
|---|---|---|
| `app/controllers/` | Rails controllers (admin/, events/) | ~78 files |
| `app/views/` | ERB templates | ~632 files |
| `app/views/` | ERB templates | ~745 files |
| `app/decorators/` | Draper decorators for view logic | ~40 files |
| `app/policies/` | ActionPolicy authorization rules | ~55 files |
| `app/presenters/` | Presentation objects | 6 files |
Expand Down Expand Up @@ -194,14 +194,16 @@ action, or `authorize! :workshop, to: :summary?`).

### Business Logic

- `EventDashboard` — Aggregates per-event dashboard metrics (registrant/org/sector/state/county counts, scholarship totals, payment received/outstanding/total)
- `EventDashboard` — Aggregates per-event dashboard metrics (registrant/org/sector/state/county counts, scholarship totals, payment received/outstanding/total). One population per event — `EventRegistration.active` — so the money and the people figures always reconcile; "who completed the training" is an attendance figure over that population (`#attended_count`), never a narrower population
- `EventRevenueReport` — Cross-event revenue report grouped by calendar year (money in vs org subsidy vs net, CE fees, chart series) for the CEO revenue page
- `EventRevenueFigures` — Batch-loads the per-event money components `EventRevenueReport` rows are built from (registration payments/outstanding, funded/unfunded scholarships, discounts, CE paid/outstanding) in a fixed number of grouped queries; mirrors the `EventDashboard` definitions
- `EventScholarshipFigures` — Batch-loads the per-event scholarship figures `EventScholarshipReport` columns are built from (funded/unfunded dollars + counts, attended count) in a fixed number of grouped queries; optional `funder:` narrows to a donor's grants. Mirrors the `EventDashboard` funded/unfunded split, replacing the one-dashboard-per-event it used to build
- `EventParticipationReport` — Cross-event participation report grouped by calendar year (unique people trained vs attended seats vs per-status outcome counts, chart series) for the events participation page; sibling of `EventRevenueReport`
- `TrainingAttendeesRoster` — Cross-event counterpart to `EventDashboard`: builds the per-registrant lookup maps the shared `events/_registrant_roster` partial reads (sector/age/org/status/location/scholarship/CE plus the attended-trainings event column) for a paginated page of people who have attended a facilitator training; backs the `events#training_attendees` index
- `TrainingAttendeesBreakdowns` — Aggregate counterpart to `EventDashboard`'s breakdown methods: computes the chart datasets (sectors, age groups, locations, program status, life experiences, settings, organizations, scholarship/CE) over the whole filtered training-attendee population, profile-sourced, for the shared `events/_registrant_breakdowns` partial on the `events#training_attendees` index
- `AttendeesRoster` — Cross-event counterpart to `EventDashboard`: builds the per-registrant lookup maps the shared `events/_registrant_roster` partial reads (sector/age/org/status/location/scholarship/CE plus the events-attended column) for a paginated page of people; backs the `events#attendees` index. Takes `events:` + `registrations:` — the index's current filter scopes, already narrowed by `EventPolicy`'s `:reportable` scope — so a person's columns show what's in scope rather than their whole history, and never an event the viewer can't see
- `AttendeesBreakdowns` — Aggregate counterpart to `EventDashboard`'s breakdown methods: computes the chart datasets (sectors, age groups, locations, program status, life experiences, settings, organizations, scholarship/CE) over an arbitrary people set, profile-sourced, for the shared `events/_registrant_breakdowns` partial. Backs the `events#attendees` index charts (cross-event; `events:` / `registrations:` = the index's current filter scopes, already narrowed by `EventPolicy`'s `:reportable` scope) and the `events#recipients` charts frame (one event's scholarship recipients, `registrations:` = `EventRegistration.active` so it counts them regardless of attendance). Also exposes `*_registrant_ids_by_*` maps mirroring `EventDashboard`'s, so a breakdown row can drill in by person id — they regroup rows already loaded for the counts, adding no queries
- `AttendeesActiveFilters` — Human-readable, removable-chip descriptors for a page's "drill-in" filters (registrant_ids, organization, org city, age group / life experience / setting categories, country, school district, scholarship, CE, payment status, scholarship funding source, sector, state) — params that narrow a list without a field in a visible filter form. `CHIP_PARAMS` is the attendees index's set (omitting anything with its own control); pass `chip_params:` for a page with no filter form at all — `ROSTER_CHIP_PARAMS` for the per-event roster, `%w[ registrant_ids ]` for scholarship recipients
- `ReportPeriods` — Shared module (included by `EventRevenueReport` and `EventParticipationReport`) resolving the reporting-hub period toggle (this year / last year / all time) to a metric scope + label for the summary cards
- `EventScholarshipReport` — Cross-event scholarship report grouped by calendar year: scholarship dollars and award counts (funded vs unfunded, via `EventDashboard`) per facilitator training, plus an attended-trainee count split into "Training" (scheduled) vs "On-demand" (`event.on_demand?`). Sibling of `EventRevenueReport`/`EventParticipationReport` (includes `ReportPeriods`); powers the `events#scholarships` report page and the statistics-hub scholarship summary card
- `EventScholarshipReport` — Cross-event scholarship report grouped by calendar year: scholarship dollars and award counts (funded vs unfunded, via `EventScholarshipFigures`) per facilitator training, plus an attended-trainee count split into "Live" (scheduled instructor-led) vs "On-demand" (`event.on_demand?`). Sibling of `EventRevenueReport`/`EventParticipationReport` (includes `ReportPeriods`); powers the `events#scholarships` report page and the reports-hub scholarship summary card
- `ScholarshipApplication` — Gathers one person's scholarship-application answers for an event by field across all their submissions, so answers surface whether captured on a dedicated scholarship form, an embedded registration section, or the registration submission itself (used by the scholarship edit page and the public submission view)
- `WorkshopSearchService` — Complex filtering, sorting, pagination with ActionPolicy
- `WorkshopFromIdeaService` — Converts WorkshopIdea to Workshop with asset migration
Expand All @@ -215,7 +217,7 @@ action, or `authorize! :workshop, to: :summary?`).
- `RichTextMigrator` — Rich text migration utility
- `DisplayImagePresenter` — Image display logic
- `ScholarshipsGrouping` (presenter) — Groups scholarships into the index's funder → grant → recipient hierarchy; grant-free awards collect under a trailing "Unfunded" group
- `RegistrantCityBreakdown` (presenter) — Groups an event's registrants by the city of the org linked on their registration, counting registrants + scholarship recipients per city; drives the shared "Registrants by city" card on the background dashboard and scholarship-recipients page (fed plucked data by `EventDashboard`)
- `RegistrantCityBreakdown` (presenter) — Groups an event's registrants by the city of the org linked on their registration, counting registrants + scholarship recipients per city; drives the shared "Registrants by city" card inside `events/_registrant_breakdowns` on all three people-pages — per-event roster, cross-event attendees index, and scholarship recipients (fed plucked data by `EventDashboard` or `AttendeesBreakdowns`)
- `AllocationLedgerLabel` (presenter) — Shared payment-method/label + check-number labelling for an allocation, used by the invoice and receipt ledgers so they can't drift

### Event Registrations
Expand Down Expand Up @@ -355,7 +357,7 @@ end
- `timeframe` — Date range filtering
- `toggle_lock` — Lock/unlock toggle UI
- `toggle_user_icon` — User icon visibility toggle
- `us_map_chart` — US states choropleth map (event Background states breakdown)
- `us_map_chart` — US states choropleth map (event roster / attendees index states breakdown)

### JS Dependencies

Expand Down
13 changes: 10 additions & 3 deletions app/controllers/event_registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,16 @@ def update
when "ticket" then redirect_to registration_ticket_path(@event_registration.slug), notice: notice, status: :see_other
when "preview_reminder" then redirect_to preview_reminder_event_path(@event_registration.event), notice: notice, status: :see_other
when "onboarding" then redirect_to helpers.onboarding_event_row_path(@event_registration.event, @event_registration.id), notice: notice, status: :see_other
when "training_attendees" then redirect_to training_attendees_events_path, notice: notice, status: :see_other
when "attendees" then redirect_to attendees_events_path, notice: notice, status: :see_other
when "roster" then redirect_to roster_event_path(@event_registration.event), notice: notice, status: :see_other
# Two ways back to the recipients page: the shout-outs section (the
# feature-a-shout-out flow) or the recipient's own card (their name).
when "recipients" then redirect_to recipients_event_path(@event_registration.event, anchor: "shout-outs"), notice: notice, status: :see_other
when "recipient_card" then redirect_to helpers.recipients_event_card_path(@event_registration.event, @event_registration.slug), notice: notice, status: :see_other
else
# No explicit origin: keep admins in the management context (the
# roster) rather than dropping them on the public registration show.
# registrants list) rather than dropping them on the public
# registration show.
if allowed_to?(:manage?, with: EventRegistrationPolicy)
redirect_to helpers.registrants_event_row_path(@event_registration.event, @event_registration.id), notice: notice, status: :see_other
else
Expand Down Expand Up @@ -302,7 +307,9 @@ def destroy
case params[:return_to]
when "registrants" then redirect_to registrants_event_path(event)
when "onboarding" then redirect_to onboarding_event_path(event)
when "training_attendees" then redirect_to training_attendees_events_path
when "attendees" then redirect_to attendees_events_path
when "roster" then redirect_to roster_event_path(event)
when "recipients", "recipient_card" then redirect_to recipients_event_path(event)
else redirect_to event_registrations_path
end
end
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/events/bulk_payments_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
module Events
class BulkPaymentsController < ApplicationController
include AhoyTracking
before_action :set_event

def index
authorize! @event
track_view("events.bulk_payments", { event_id: @event.id })

@event_registrations = @event.event_registrations.active.includes(:registrant)
@submissions = @event.form_submissions
Expand Down
Loading