Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ end
- `EventRegistrationServices::PublicRegistration` β€” Public registration handling
- `EventRegistrationReadiness` β€” Computes a registration's lifecycle `status` (`:not_ready` β†’ `:ready` β†’ `:certificate_due` β†’ `:completed`) from a pre-event "event ready" checklist, a post-event "completion work" checklist (attendance, scholarship tasks), and certificate delivery, returning the specific outstanding reasons. Reads payment/certificate state via `Registerable` (`paid_in_full?`, `certificate_sent?`) on both the registration and its `continuing_education_registrations`. Drives the registrants roster's single far-right Status badge column (with a short reason under "Not ready" and a cert-type note under "Certificate pending") and its matching filter
- `ReminderRecipientFilter` β€” Decides which event registrations stay checked on the bulk reminder page given the admin's filters (matches in memory, returns matching ids)
- `BuiltinCalloutCards` β€” Renders the live, per-registration ticket callout cards (payment, certificate, scholarship, CE hours, event details, videoconference), overlaying dynamic status (badge, colour, visibility guard, destination) on each materialized built-in row via `#card_for`. Rendered through the same `_callout_card` partial as `RegistrationTicketCallout`s. Skips any card an event has materialized (see `BuiltinCallouts`) so the two paths never double-render, and `#cards` serves as the fallback for events not yet seeded; `.editor_cards` builds the editor's preview cards. Handouts/FAQ are pure content cards with no builder here β€” they render from their row. Public show pages live under `app/views/events/callouts/` (`Events::CalloutsController`, slug-authorized)
- `BuiltinCallouts` β€” Owns the built-in callout definitions and materializes them into `RegistrationTicketCallout` rows in canonical ticket order: `seed` persists (on create, and lazily on edit so older events heal with no backfill), `build` makes the same rows in memory for the new-event form (with `builtin_key` round-tripped through nested attributes), `reset`/`customized?` back the "Restore default" control. All eight seed **hidden** by default β€” admins publish the ones they want; there's no config-based auto-publish. Built-ins are edited in the **same** callout-fields row as custom callouts (pre-filled title/subtitle/colour/icon/callout-page-text/resources; hidden instead of deleted; "Restore default" shown only when `.customized?`). "Content" cards (Handouts, FAQ) render their own copy/resources; "behavioral" cards render live status through `BuiltinCalloutCards#card_for`, which overlays the app's badge/visibility/destination on the row's editable presentation. Behavioral pages show the row's callout-page-text as an intro (`@builtin_intro`) and any linked resources below it. Videoconference drips a week before start via `display_from`. CE hours and Event details are edited like every other built-in β€” their title/text seed from the event's columns once (migrating existing content) then live on the row; the CE hours-offered/cost config still edits the event inline via `event_f` (`ce_config?`). The registrant CE/details pages read the row (falling back to the event columns pre-seed). Built-ins always seed and also materialize lazily on `edit`, so the editor shows the full set; the editor shows "Restore default" (or a static "Matches default") per row via `.customized?`. The visibility control is a `published` toggle (inverse of `hidden`)
- `CalloutContent` β€” Parses admin-authored callout HTML into ordered segments so **every** callout content page renders the same way: plain rich text, with each standard `<details><summary>…</summary>…</details>` disclosure (the markup any HTML generator/LLM produces; `<toggle>` and a `title` attribute are accepted aliases; `<details open>` starts expanded) rebuilt into a styled collapsible card. `<details>`/`<summary>` are also on the `form_label_html` allowlist (`FORM_LABEL_TAGS`, plus the `open` attribute), so a disclosure is never stripped on save β€” the parser only upgrades its styling. Rendered through the shared `app/views/events/callouts/_rich_content.html.erb` partial (which wraps each disclosure in `_toggle.html.erb`), used by the event-details ("Art supplies & what to bring"), CE hours, custom-callout, behavioural-card-intro, and FAQ pages. The FAQ page renders the editable `faq` callout `description` (each question a `<details>`), falling back to `BuiltinCallouts.faq_html` when the card isn't materialized. Content with no disclosure renders unchanged
- `BuiltinCalloutCards` β€” Renders the live, per-registration ticket callout cards (payment, certificate, scholarship, CE hours, videoconference), overlaying dynamic status (badge, colour, visibility guard, destination) on each materialized built-in row via `#card_for`. Rendered through the same `_callout_card` partial as `RegistrationTicketCallout`s. Skips any card an event has materialized (see `BuiltinCallouts`) so the two paths never double-render, and `#cards` serves as the fallback for events not yet seeded; `.editor_cards` builds the editor's preview cards. Art supplies, Handouts, and FAQ are pure content cards with no builder here β€” they render from their row. Public show pages live under `app/views/events/callouts/` (`Events::CalloutsController`, slug-authorized)
- `BuiltinCallouts` β€” Owns the built-in callout definitions and materializes them into `RegistrationTicketCallout` rows in canonical ticket order: `seed` persists (on create, and lazily on edit so older events heal with no backfill), `build` makes the same rows in memory for the new-event form (with `builtin_key` round-tripped through nested attributes), `reset`/`customized?` back the "Restore default" control. All eight seed **hidden** by default β€” admins publish the ones they want; there's no config-based auto-publish. Built-ins are edited in the **same** callout-fields row as custom callouts (pre-filled title/subtitle/colour/icon/callout-page-text/resources; hidden instead of deleted; "Restore default" shown only when `.customized?`). "Content" cards (Art supplies, Handouts, FAQ) render their own copy/resources on the generic callout page; "behavioral" cards render live status through `BuiltinCalloutCards#card_for`, which overlays the app's badge/visibility/destination on the row's editable presentation. Behavioral pages show the row's callout-page-text as an intro (`@builtin_intro`) and any linked resources below it. Videoconference drips a week before start via `display_from`. CE hours and Art supplies are edited like every other built-in β€” their title/text live entirely on the row (the legacy `event_details*`/`ce_hours_details*` event columns were dropped); the CE hours-offered/cost config still edits the event inline via `event_f` (`ce_config?`). The registrant CE page reads the row's title/description. Built-ins always seed and also materialize lazily on `edit`, so the editor shows the full set; the editor shows "Restore default" (or a static "Matches default") per row via `.customized?`. The visibility control is a `published` toggle (inverse of `hidden`)
- `CalloutContent` β€” Parses admin-authored callout HTML into ordered segments so **every** callout content page renders the same way: plain rich text, with each standard `<details><summary>…</summary>…</details>` disclosure (the markup any HTML generator/LLM produces; `<toggle>` and a `title` attribute are accepted aliases; `<details open>` starts expanded) rebuilt into a styled collapsible card. `<details>`/`<summary>` are also on the `form_label_html` allowlist (`FORM_LABEL_TAGS`, plus the `open` attribute), so a disclosure is never stripped on save β€” the parser only upgrades its styling. Rendered through the shared `app/views/events/callouts/_rich_content.html.erb` partial (which wraps each disclosure in `_toggle.html.erb`), used by the art-supplies ("Art supplies & what to bring", a content callout on the generic page), CE hours, custom-callout, behavioural-card-intro, and FAQ pages. The FAQ page renders the editable `faq` callout `description` (each question a `<details>`), falling back to `BuiltinCallouts.faq_html` when the card isn't materialized. Content with no disclosure renders unchanged

### Affiliations

Expand Down
4 changes: 3 additions & 1 deletion app/controllers/events/callouts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ def sign_agreement
end
end

# CE hours status: hours, amount owed, and license number.
# CE hours status: hours, amount owed, and license number. The heading and the
# requirements copy live on the materialized ce_hours callout row now.
def ce
@ce_callout = @event.registration_ticket_callouts.find_by(builtin_key: "ce_hours")
case params[:checkout]
when "success"
flash.now[:notice] = "Your CE payment was successful."
Expand Down
35 changes: 2 additions & 33 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class EventsController < ApplicationController
include AhoyTracking, TagAssignable
skip_before_action :authenticate_user!, only: [ :index, :show, :staff, :details, :ce_hours ]
skip_before_action :authenticate_user!, only: [ :index, :show, :staff ]
skip_before_action :verify_authenticity_token, only: [ :preview ]
before_action :set_event, only: %i[ show edit update destroy preview dashboard sample_ticket background registrants onboarding details ce_hours staff edit_staff update_staff recipients bulk_payments preview_reminder confirm_reminder send_reminder copy_registration_form allocate_bulk_payment create_bulk_payment ]
before_action :set_event, only: %i[ show edit update destroy preview dashboard sample_ticket background registrants onboarding staff edit_staff update_staff recipients bulk_payments preview_reminder confirm_reminder send_reminder copy_registration_form allocate_bulk_payment create_bulk_payment ]

def index
authorize!
Expand Down Expand Up @@ -185,37 +185,6 @@ def onboarding
end
end

# Public "Before you attend" page (materials, supplies, policies). Linked from
# the registration ticket. When no details are set there is nothing to show, so
# fall back to the event page.
def details
authorize! @event, to: :details?

callout = @event.registration_ticket_callouts.find_by(builtin_key: "event_details")
@event_details_title = callout&.title.presence || @event.event_details_label
@event_details_body = callout&.description.presence || @event.event_details

if @event_details_body.blank?
redirect_to event_path(@event, reg: params[:reg].presence)
return
end

@event = @event.decorate
end

# Public CE hours page (continuing education requirements, payment, sign-in
# rules). Linked from the registration ticket. When no details are set there
# is nothing to show, so fall back to the event page.
def ce_hours
authorize! @event, to: :ce_hours?

unless @event.ce_eligible?
redirect_to event_path(@event, reg: params[:reg].presence)
return
end

@event = @event.decorate
end

def staff
authorize! @event, to: :staff?
Expand Down
16 changes: 5 additions & 11 deletions app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,11 @@ def name
title
end

# Heading shown on the ticket call-out and the details page. Falls back to the
# default even when an admin clears it, so the section never renders unlabelled.
def event_details_label
super.presence || "Before you attend"
end

# Heading shown on the CE hours ticket call-out and its details page. Falls
# back to the default even when an admin clears it, so the section never
# renders unlabelled.
def ce_hours_details_label
super.presence || "CE hours"
# The CE hours callout's admin-edited heading, read from its materialized row's
# title (that's where the label lives now the ce_hours_details_label column is
# gone). Falls back to the default so the CE card/mailer never render unlabelled.
def ce_hours_label
registration_ticket_callouts.find_by(builtin_key: "ce_hours")&.title.presence || "CE hours"
end

# Virtual attributes for date/time inputs (Firefox datetime-local compat)
Expand Down
10 changes: 5 additions & 5 deletions app/models/registration_ticket_callout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ class RegistrationTicketCallout < ApplicationRecord
# that key. Admin-authored callouts have a nil builtin_key. Built-in callouts are
# hidden rather than destroyed so they can be restored to their default.
BUILTIN_KEYS = %w[
payment certificate scholarship ce_hours event_details
payment certificate scholarship ce_hours art_supplies
videoconference handouts faq
].freeze

# "Content" built-in callouts render their own editable copy/resources (like custom
# callouts). "Behavioral" built-in callouts (the rest) render live per-registration
# status through BuiltinCalloutCards#card_for β€” the row still owns the editable
# title/subtitle/text, order, visibility, and resources.
CONTENT_BUILTIN_KEYS = %w[ handouts faq ].freeze
# callouts), on the generic callout page. "Behavioral" built-in callouts (the rest)
# render live per-registration status through BuiltinCalloutCards#card_for β€” the row
# still owns the editable title/subtitle/text, order, visibility, and resources.
CONTENT_BUILTIN_KEYS = %w[ art_supplies handouts faq ].freeze

# Behavioral built-ins that also carry event-level config edited inline in their
# row (CE hours offered / cost); their text lives on the row like everything else.
Expand Down
6 changes: 0 additions & 6 deletions app/policies/event_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,6 @@ def google_analytics?
:short_description,
:rhino_header,
:rhino_description,
:event_details,
:event_details_label,
:ce_hours_details,
:ce_hours_details_label,
:ce_hours_offered,
:ce_hours_cost,
:ce_hours_request_deadline,
Expand Down Expand Up @@ -171,8 +167,6 @@ def google_analytics?
end

alias_rule :preview?, to: :edit?
alias_rule :details?, to: :show?
alias_rule :ce_hours?, to: :show?

private

Expand Down
Loading