Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b8606cc
Events IA Phase 1: Live vs On-demand terminology
Aug 9, 2026
b3634b8
Events IA Phase 2: canonical scopes/constants for duplicated stat logic
Aug 9, 2026
c6c7807
Events IA Phase 3: batched EventScholarshipFigures
Aug 9, 2026
c39db91
Events IA Phase 7: cross-event Reports hub + Attendees, owner-scoped
Aug 9, 2026
ba2ef68
Events IA Phase 5/6: subnav restructure, remove Background page
Aug 9, 2026
aaf58d9
Fix 500 on attendees charts when a city has scholarship recipients
Aug 9, 2026
a7d238e
Roster as its own per-event page (active registrants), sharing partials
Aug 9, 2026
daa479b
Participation 'Attendees' link + wider reports Event dropdown
Aug 9, 2026
503e126
Split the report Event-type filter into Live vs On-demand trainings
Aug 9, 2026
862d59e
Add an Attendees link under Full report on the participation hub card
Aug 9, 2026
cd4998b
Recipients page eyebrow returns to the event dashboard
Aug 9, 2026
d9faa30
Ahoy page-view logging for event pages/reports; Roster charts toggle …
Aug 9, 2026
2bc4e38
Kill the program-status N+1 on the event dashboard/roster pages
Aug 9, 2026
8865f2a
Preload affiliations in the cross-event attendee services too
Aug 9, 2026
8b42f60
Rename TrainingAttendees{Roster,Breakdowns} -> Attendees{Roster,Break…
Aug 9, 2026
ff1ebc3
Open cross-event report suite to event owners, scoped to their own ev…
Aug 9, 2026
885308a
Track preview/sample-ticket/reminder Ahoy views; rename Reports → "Ev…
Aug 9, 2026
57272e6
Event pages: shared report header, roster/recipients return paths, at…
Aug 9, 2026
1c0a4f6
Surface applied attendees drill-in filters as removable chips
Aug 9, 2026
986d4ae
Unify roster/attendees/recipients charts pattern; wire real recipient…
Aug 9, 2026
2460b06
Charts frames: skeleton placeholder instead of "Loading…" text
Aug 9, 2026
571fe0c
panel-toggle: self-heal control state on connect
Aug 9, 2026
f741091
Breakdowns filter their own page; open the attendees index past atten…
Aug 9, 2026
fe1b850
Section toggles: short Show/Hide beside the heading, not a block belo…
Aug 9, 2026
c8b68b6
Drop the Staff tab from the event subnav
Aug 9, 2026
40ccd54
Batch the scholarship report's recipient splits instead of a dashboar…
Aug 9, 2026
45037a6
Point revenue drill-ins at registrations, and split the two recipient…
Aug 9, 2026
e2099e0
Preload orgs' affiliations on Onboarding; group the recipients list c…
Aug 9, 2026
859b2d4
Mark the event admin pages admin-or-owner, matching their policies
Aug 9, 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
16 changes: 9 additions & 7 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 @@ -196,11 +196,13 @@ action, or `authorize! :workshop, to: :summary?`).
- `EventDashboard` — Aggregates per-event dashboard metrics (registrant/org/sector/state/county counts, scholarship totals, payment received/outstanding/total)
- `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, 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 @@ -214,7 +216,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 @@ -354,7 +356,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