From 65195e989b187e727803d9770b33d1c569d53d88 Mon Sep 17 00:00:00 2001 From: maebeale Date: Tue, 14 Jul 2026 16:31:58 -0400 Subject: [PATCH] Consolidate CE + art supplies built-ins onto callout rows; drop legacy columns Every built-in callout page now renders its copy from the materialized callout row's "Callout page text" (CE, handouts, FAQ, scholarship previously dropped it). Move CE and art_supplies (formerly event_details) fully onto the row: drop the event-level ce_hours_details/_label and event_details/_label columns, delete the orphaned events#ce_hours and events#details pages, make art_supplies a content callout rendered on the generic callout page, and rename its magic_key. Refactor the scholarship/faq pages onto the shared callout-page layout. No data backfill. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/controllers/events/callouts_controller.rb | 19 +- app/controllers/events_controller.rb | 31 +-- ...registration_ticket_callouts_controller.rb | 6 +- app/models/event.rb | 13 - app/models/registration_ticket_callout.rb | 13 +- app/policies/event_policy.rb | 6 - app/services/default_ticket_callouts.rb | 17 +- app/services/magic_ticket_callouts.rb | 27 +-- .../events/_builtin_callout_card.html.erb | 28 --- app/views/events/_form.html.erb | 15 -- app/views/events/callouts/ce.html.erb | 12 +- app/views/events/callouts/faq.html.erb | 226 ++++++++---------- .../events/callouts/scholarship.html.erb | 151 +++++------- app/views/events/ce_hours.html.erb | 33 --- app/views/events/details.html.erb | 33 --- config/routes.rb | 2 - ...126_remove_ce_hours_details_from_events.rb | 15 ++ ...145155_remove_event_details_from_events.rb | 15 ++ db/schema.rb | 6 +- .../registration_ticket_callout_spec.rb | 9 + spec/requests/events/callouts_spec.rb | 39 +++ .../registration_ticket_callouts_spec.rb | 2 +- spec/requests/events_spec.rb | 38 +-- spec/services/default_ticket_callouts_spec.rb | 26 +- spec/services/magic_ticket_callouts_spec.rb | 21 +- spec/system/event_registration_show_spec.rb | 15 +- spec/views/page_bg_class_alignment_spec.rb | 2 - 27 files changed, 314 insertions(+), 506 deletions(-) delete mode 100644 app/views/events/_builtin_callout_card.html.erb delete mode 100644 app/views/events/ce_hours.html.erb delete mode 100644 app/views/events/details.html.erb create mode 100644 db/migrate/20260714120126_remove_ce_hours_details_from_events.rb create mode 100644 db/migrate/20260714145155_remove_event_details_from_events.rb diff --git a/app/controllers/events/callouts_controller.rb b/app/controllers/events/callouts_controller.rb index c6e37af524..cfc8fd2475 100644 --- a/app/controllers/events/callouts_controller.rb +++ b/app/controllers/events/callouts_controller.rb @@ -8,9 +8,10 @@ class CalloutsController < ApplicationController before_action :set_event_registration before_action :authorize_callout before_action :set_event - # These pages carry an editable intro (the built-in row's "Callout page text") - # above the app-controlled content, plus any resources linked to the row. - before_action :set_builtin_content, only: %i[ payment scholarship certificate videoconference ] + # Every built-in page carries an editable intro + title (the built-in row's + # "Callout page text" / title) above the app-controlled content, plus any + # resources linked to the row. + before_action :set_builtin_content, only: %i[ payment scholarship certificate ce videoconference handouts faq ] # Hidden Resource (by title) backing the handout links, in display order. # Missing ones (e.g. not seeded in an environment) are silently skipped. @@ -175,14 +176,16 @@ def set_event @event = @event_registration.event end - # The editable intro and linked resources for a built-in page, from the + # The editable intro, title, and linked resources for a built-in page, from the # materialized callout row for this action's magic_key. Nil/empty when the - # event hasn't materialized the card. Payment renders its own document list - # (W-9 + invoice/receipt) in its Documents section, so it skips the generic - # inline resource list here. + # event hasn't materialized the card. The CE action is `ce` but its row's key is + # `ce_hours`. Payment renders its own document list (W-9 + invoice/receipt) in + # its Documents section, so it skips the generic inline resource list here. def set_builtin_content - callout = @event.registration_ticket_callouts.find_by(magic_key: action_name) + magic_key = action_name == "ce" ? "ce_hours" : action_name + callout = @event.registration_ticket_callouts.find_by(magic_key:) @builtin_intro = callout&.description.presence + @builtin_title = callout&.title.presence @builtin_resources = callout && action_name != "payment" ? callout.resources.to_a : [] end diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 88a23e0fbb..f70a457d92 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -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! @@ -188,34 +188,7 @@ def onboarding # 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(magic_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? diff --git a/app/controllers/registration_ticket_callouts_controller.rb b/app/controllers/registration_ticket_callouts_controller.rb index 61cbd9d5f6..c2675cd3b7 100644 --- a/app/controllers/registration_ticket_callouts_controller.rb +++ b/app/controllers/registration_ticket_callouts_controller.rb @@ -3,9 +3,9 @@ class RegistrationTicketCalloutsController < ApplicationController before_action :set_event # Public detail page for a single registration ticket callout, linked from the - # call-out on the registration ticket (mirrors the events#details / #ce_hours - # pages). With no description and no linked resource there is nothing to show, - # so fall back to the event page. + # call-out on the registration ticket. Also the page for content built-ins like + # event_details. With no description and no linked resource there is nothing to + # show, so fall back to the event page. def show @callout = @event.registration_ticket_callouts.find(params[:id]) authorize! @callout, to: :show? diff --git a/app/models/event.rb b/app/models/event.rb index c95d1443ec..afeb58272f 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -210,19 +210,6 @@ 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" - end - # Virtual attributes for date/time inputs (Firefox datetime-local compat) attr_writer :start_date_date, :start_date_time, :end_date_date, :end_date_time, diff --git a/app/models/registration_ticket_callout.rb b/app/models/registration_ticket_callout.rb index fae52c84ef..27246f2202 100644 --- a/app/models/registration_ticket_callout.rb +++ b/app/models/registration_ticket_callout.rb @@ -11,15 +11,16 @@ class RegistrationTicketCallout < ApplicationRecord # that key. Admin-authored callouts have a nil magic_key. Magic callouts are # hidden rather than destroyed so they can be restored to their default. MAGIC_KEYS = %w[ - payment certificate scholarship ce_hours event_details + payment certificate scholarship ce_hours art_supplies videoconference handouts faq ].freeze - # "Content" magic callouts render their own editable copy/resources (like custom - # callouts). "Behavioral" magic callouts (the rest) render live per-registration - # status through MagicTicketCallouts#card_for — the row still owns the editable - # title/subtitle/text, order, visibility, and resources. - CONTENT_MAGIC_KEYS = %w[ handouts faq ].freeze + # "Content" magic callouts render their own editable copy/resources on the + # generic callout page (like custom callouts). "Behavioral" magic callouts (the + # rest) render live per-registration status through MagicTicketCallouts#card_for + # — the row still owns the editable title/subtitle/text, order, visibility, and + # resources. art_supplies is content: its page is just its title + copy. + CONTENT_MAGIC_KEYS = %w[ handouts faq art_supplies ].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. diff --git a/app/policies/event_policy.rb b/app/policies/event_policy.rb index d25f0edb02..b0d52da049 100644 --- a/app/policies/event_policy.rb +++ b/app/policies/event_policy.rb @@ -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, :autoshow_cost, @@ -169,8 +165,6 @@ def google_analytics? end alias_rule :preview?, to: :edit? - alias_rule :details?, to: :show? - alias_rule :ce_hours?, to: :show? private diff --git a/app/services/default_ticket_callouts.rb b/app/services/default_ticket_callouts.rb index e5c101094f..24e3579ec2 100644 --- a/app/services/default_ticket_callouts.rb +++ b/app/services/default_ticket_callouts.rb @@ -174,11 +174,9 @@ def definitions }, { magic_key: "ce_hours", - # Title/text seed from the event's CE columns (migrating existing content); - # thereafter they live on the row like every other built-in. The row also - # carries the CE hours-offered/cost config. - title: ->(event) { event.ce_hours_details_label }, - description: ->(event) { event.ce_hours_details }, + # Text lives on the row like every other built-in (edit it as the callout's + # "Callout page text"). The row also carries the CE hours-offered/cost config. + title: "CE hours", subtitle: "Continuing education — requirements & how to request", callout_type: "action", icon_class: "fa-solid fa-graduation-cap", @@ -186,11 +184,10 @@ def definitions hidden: ->(event) { !event.facilitator_training? } }, { - magic_key: "event_details", - # Title/text seed from the event's details columns (migrating existing - # content); thereafter they live on the row. - title: ->(event) { event.event_details_label }, - description: ->(event) { event.event_details }, + magic_key: "art_supplies", + # Text lives on the row like every other built-in (edit it as the callout's + # "Callout page text"); its page is just that title + copy. + title: "Art supplies & what to bring", subtitle: "Important info for this event — please read", callout_type: "reference", icon_class: "fa-solid fa-palette", diff --git a/app/services/magic_ticket_callouts.rb b/app/services/magic_ticket_callouts.rb index 2d54913c79..a619135909 100644 --- a/app/services/magic_ticket_callouts.rb +++ b/app/services/magic_ticket_callouts.rb @@ -24,9 +24,8 @@ def theme = DomainTheme.swatch(color) end # A registration-free description of one built-in card, for the event editor's - # callouts section. `key` is :ce_hours / :event_details for the two whose text - # admins edit via event columns; nil for the cards the app fully controls (shown - # greyed out). `magic_key` ties the card to its ticket behavior — once an event + # callouts section. `key` is nil for the app-controlled preview cards shown greyed + # out (all of them today). `magic_key` ties the card to its ticket behavior — once an event # has materialized that key into an editable row, the preview is dropped here and # the row is edited in the callout list instead. `subtitle` mirrors the card's # ticket subtitle; `visibility` describes when the app shows it (rendered next to @@ -58,7 +57,6 @@ def self.editor_cards(event) "certificate" => :certificate_card, "scholarship" => :scholarship_status_card, "ce_hours" => :ce_hours_card, - "event_details" => :event_details_card, "videoconference" => :videoconference_card, "handouts" => :handouts_card, "faq" => :faq_card @@ -74,7 +72,7 @@ def initialize(event_registration) # those from the row (calling #card_for for behavioral ones), so this is both the # non-materialized set and the fallback for events not yet seeded. def cards - CARD_BUILDERS.reject { |magic_key, _| materialized?(magic_key) || skip_in_fallback?(magic_key) } + CARD_BUILDERS.reject { |magic_key, _| materialized?(magic_key) } .filter_map { |_, builder| send(builder) } end @@ -87,8 +85,6 @@ def card_for(callout) builder = CARD_BUILDERS[callout.magic_key] base = builder && send(builder) return unless base - # Event details links to its page only when it has content to show. - return if callout.magic_key == "event_details" && callout.description.blank? base.with( title: callout.title, @@ -111,12 +107,6 @@ def materialized?(magic_key) @materialized_keys.include?(magic_key) end - # In the unseeded fallback, event-details content lives on the event column, so - # hide the card when it's blank (the row path checks the row in #card_for). - def skip_in_fallback?(magic_key) - magic_key == "event_details" && event.event_details.blank? - end - # Top card: an action card while a balance is due, a reference card once paid # in full. Its page lists every allocation with the running balance, plus the # linked documents (the W-9, and the invoice/receipt) for paid events. @@ -179,7 +169,7 @@ def ce_hours_card # the payment card, rather than the resting teal. due = registration.continuing_education_registrations.first&.remaining_cost.to_i.positive? Card.new(icon_class: "fa-solid fa-graduation-cap", color: due ? "orange" : "teal", - title: event.ce_hours_details_label, + title: "CE hours", subtitle: ce_hours_subtitle, href: registration_ce_path(registration.slug), target: nil, trailing_icon: "fa-solid fa-arrow-right", @@ -221,15 +211,6 @@ def ce_missing_text "License number needed" end - # "Art supplies & what to bring" — the event's own details page. - def event_details_card - Card.new(icon_class: "fa-solid fa-palette", color: "blue", - title: event.event_details_label, - subtitle: "Important info for this event — please read", - href: details_event_path(event, reg: registration.slug), - target: nil, trailing_icon: "fa-solid fa-arrow-right") - end - # Shown only when the event has a videoconference URL set. def videoconference_card return if event.videoconference_url.blank? diff --git a/app/views/events/_builtin_callout_card.html.erb b/app/views/events/_builtin_callout_card.html.erb deleted file mode 100644 index 77b4e6f7d6..0000000000 --- a/app/views/events/_builtin_callout_card.html.erb +++ /dev/null @@ -1,28 +0,0 @@ -<%# An editable built-in registration-ticket callout (CE hours, art supplies), - rendered inside the event form's callouts section and tinted in the colour the - card uses on the ticket. locals: f (event form builder), card - (MagicTicketCallouts::EditorCard), label_field/content_field (event attribute - symbols), title_placeholder, content_help, content_placeholder. %> -<% theme = card.theme %> -
- -
-
- Built in - <%= card.visibility %> -
-
-
- <%= f.label label_field, "Title", class: "block text-xs font-medium text-gray-600 mb-0.5" %> - <%= f.text_area label_field, rows: 2, placeholder: title_placeholder, - class: "w-full rounded border-gray-300 shadow-sm px-2 py-1 text-sm" %> -
-
- <%= f.label content_field, "Callout page text", class: "block text-xs font-medium text-gray-600 mb-0.5" %> -

<%= content_help %>

- <%= f.text_area content_field, rows: 3, placeholder: content_placeholder, - class: "w-full rounded border-gray-300 shadow-sm px-2 py-1 text-sm font-mono" %> -
-
-
-
diff --git a/app/views/events/_form.html.erb b/app/views/events/_form.html.erb index 0180b5f4c0..6565cbbfde 100644 --- a/app/views/events/_form.html.erb +++ b/app/views/events/_form.html.erb @@ -580,20 +580,6 @@
<% MagicTicketCallouts.editor_cards(@event).each do |card| %> - <% case card.key %> - <% when :ce_hours %> - <%= render "events/builtin_callout_card", f: f, card: card, - label_field: :ce_hours_details_label, content_field: :ce_hours_details, - title_placeholder: "CE hours", - content_help: "CE requirements, payment, sign-in rules, and the post-training evaluation — shown on its own page linked from the ticket. Accepts basic HTML — bold, italics, links, lists, headings, and line breaks.", - content_placeholder: "e.g.

AWBW is approved by CAMFT…

Before the training

  • Email your license number
" %> - <% when :event_details %> - <%= render "events/builtin_callout_card", f: f, card: card, - label_field: :event_details_label, content_field: :event_details, - title_placeholder: "Before you attend", - content_help: "Materials, supplies, what to bring, and policies — shown on its own page linked from the ticket. Leave blank to hide it entirely. Accepts basic HTML — bold, italics, links, lists, headings, and line breaks.", - content_placeholder: "e.g.

Workshop 1

  • Clear glass stones
" %> - <% else %>
@@ -639,7 +625,6 @@ <% end %>
- <% end %> <% end %> diff --git a/app/views/events/callouts/ce.html.erb b/app/views/events/callouts/ce.html.erb index e888aa2c4e..4671f43c95 100644 --- a/app/views/events/callouts/ce.html.erb +++ b/app/views/events/callouts/ce.html.erb @@ -1,5 +1,6 @@ <% content_for(:page_bg_class, "public") %> -<% content_for(:page_title, "#{@event.ce_hours_details_label} — #{@event.title}") %> +<% ce_title = @builtin_title.presence || "CE hours" %> +<% content_for(:page_title, "#{ce_title} — #{@event.title}") %> <% ce_registration = @event_registration.continuing_education_registrations.first %> <%# @@ -14,7 +15,7 @@ {} end %> -<%= render layout: "events/callouts/callout_page", locals: { title: @event.ce_hours_details_label, **callout_eyebrow } do %> +<%= render layout: "events/callouts/callout_page", locals: { title: ce_title, **callout_eyebrow } do %> <% if @event_registration.ce_registered? %> <% license = ce_registration&.professional_license %> <% license_kind = license&.kind %> @@ -228,11 +229,4 @@ <%= form.submit "Request CE credit", class: "rounded-lg bg-teal-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-teal-300 cursor-pointer" %> <% end %> <% end %> - - <% if @event.ce_hours_details.present? %> -
-

Details

-
<%= form_label_html(@event.ce_hours_details) %>
-
- <% end %> <% end %> diff --git a/app/views/events/callouts/faq.html.erb b/app/views/events/callouts/faq.html.erb index f7cb450470..31e203cfc6 100644 --- a/app/views/events/callouts/faq.html.erb +++ b/app/views/events/callouts/faq.html.erb @@ -1,131 +1,107 @@ <% content_for(:page_bg_class, "public") %> <% content_for(:page_title, "Frequently asked questions — #{@event.title}") %> -
- <%= link_to registration_ticket_path(@event_registration.slug), - class: "inline-flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-700 px-2 py-1" do %> - Back to ticket - <% end %> -
- -
-
-
-
-
- <%= image_tag("logo.png", alt: "Organization logo", class: "h-9 w-auto") %> -
-
-

Frequently asked questions

-

<%= @event.title %>

-
-
-
-
- -
-

2-day training — general questions

-
- <% faqs = [ - { - q: "Who is this training designed for?", - a: [ "This training is designed for anyone interested in incorporating healing arts into the work they do with individuals, groups, or communities. Participants come from a wide range of backgrounds, including education, mental health, social services, healthcare, community organizing, advocacy, and nonprofit work." ] - }, - { - q: "Do I need to be an artist or have art experience to participate?", - a: [ "No prior art experience or artistic background is required to participate in the training. The AWBW approach is centered on the creative process rather than artistic skill or technique." ] - }, - { - q: "Is the training trauma-informed?", - a: [ "Yes. The AWBW model, philosophy, and facilitation approach are rooted in trauma-informed practices. The training includes dedicated content focused on trauma-informed facilitation and creating supportive, inclusive environments for participants." ] - }, - { - q: "Do I need to work for an organization to participate in the training or become a facilitator?", - a: [ "No. You do not need to be affiliated with an organization or agency to participate in the training or become an AWBW Facilitator. While many facilitators implement the workshops within organizations, schools, or community programs, others use the workshops in private practice, community spaces, support groups, creative gatherings, or personal healing work. The workshops are designed to be flexible and adaptable across a variety of settings and populations." ] - }, - { - q: "Can I use the workshops with youth? Adults? Families?", - a: [ - "Yes. AWBW workshops are designed to be flexible and adaptable for individuals of all ages, including youth, adults, families, and intergenerational groups.", - "Within the facilitator portal, workshops can be searched and filtered by focus area, population, theme, and other categories to help facilitators find workshops that best fit the communities they serve." - ] - }, - { - q: "How many workshops are included in the curriculum library?", - a: [ "The AWBW facilitator portal includes access to a library of more than 600 art workshops and facilitation resources." ] - }, - { - q: "Are workshops available in Spanish or other languages?", - a: [ - "Yes. A number of workshops have been translated into Spanish. Within the facilitator portal, facilitators can use the search filters to locate workshops that include Spanish translations.", - "AWBW continues to work toward increasing language accessibility and expanding translated resources over time." - ] - }, - { - q: "What materials do I need for the art workshops?", - a: [ - "There are no required art materials for participation. Workshops can be completed using any materials you already have available, such as paper, pencils, pens, crayons, markers, colored pencils, paint, collage materials, or other creative supplies. The focus of the AWBW approach is on the creative process rather than the materials themselves.", - "Prior to the training, we will also send participants a list of optional art supplies that may be helpful to have on hand, as well as printable worksheets for each of the art workshops that will be facilitated during the training. While neither the supplies nor the worksheets are required, many participants find them helpful for engaging more fully in the workshop experience.", - "We will walk through the art supplies suggested for each workshop so that, if trainees choose to use those materials in their own facilitation, they feel familiar and comfortable incorporating those materials into their own facilitation practice." - ] - }, - { - q: "What ongoing support does AWBW provide after completing the on-demand training?", - a: [ - "Once you complete the On-Demand Training and receive your certification, you become part of a thriving community of over 1,500 Windows Facilitators across the country and abroad, all learning from and supporting one another.", - "As a certified facilitator, you'll gain access to:" - ], - list: [ - "The AWBW Facilitator Portal, which houses a curriculum of over 600 art workshops, downloadable resources, toolkits, handouts, the Facilitator Manual, and tools for tracking attendance and gathering feedback", - "Ongoing professional development, including live art workshops, Q&As, and recorded presentations by subject matter experts", - "Virtual Community of Practice events, where you can connect with fellow facilitators and experience new workshops", - "AWBW staff support for workshop selection, facilitation questions, art supply tips, and more", - "A bi-monthly newsletter with new workshops, stories, and resources" - ] - }, - { - q: "Can multiple staff members from one organization participate?", - a: [ - "Yes. Multiple staff members from the same organization are welcome and encouraged to participate in the training.", - "Many organizations choose to train teams of staff members in order to integrate trauma-informed healing arts practices more broadly across their programs, services, and communities." - ] - }, - { - q: "Are there any fees, other than the training fee, associated with becoming a facilitator?", - a: [ - "AWBW has an annual membership fee of $100 per program to support the sustainability of the organization and continued facilitator resources and support.", - "The membership fee covers all facilitators connected to the same program or organization for the calendar year, regardless of the number of facilitators participating. Only one membership fee payment is required per program annually." - ] - } - ] %> - <% faqs.each do |faq| %> -
- -
- <%= faq[:q] %> - -
-
-
- <% faq[:a].each do |paragraph| %> -

<%= paragraph %>

+<%= render layout: "events/callouts/callout_page", locals: { title: @builtin_title.presence || "Frequently asked questions" } do %> +

2-day training — general questions

+
+ <% faqs = [ + { + q: "Who is this training designed for?", + a: [ "This training is designed for anyone interested in incorporating healing arts into the work they do with individuals, groups, or communities. Participants come from a wide range of backgrounds, including education, mental health, social services, healthcare, community organizing, advocacy, and nonprofit work." ] + }, + { + q: "Do I need to be an artist or have art experience to participate?", + a: [ "No prior art experience or artistic background is required to participate in the training. The AWBW approach is centered on the creative process rather than artistic skill or technique." ] + }, + { + q: "Is the training trauma-informed?", + a: [ "Yes. The AWBW model, philosophy, and facilitation approach are rooted in trauma-informed practices. The training includes dedicated content focused on trauma-informed facilitation and creating supportive, inclusive environments for participants." ] + }, + { + q: "Do I need to work for an organization to participate in the training or become a facilitator?", + a: [ "No. You do not need to be affiliated with an organization or agency to participate in the training or become an AWBW Facilitator. While many facilitators implement the workshops within organizations, schools, or community programs, others use the workshops in private practice, community spaces, support groups, creative gatherings, or personal healing work. The workshops are designed to be flexible and adaptable across a variety of settings and populations." ] + }, + { + q: "Can I use the workshops with youth? Adults? Families?", + a: [ + "Yes. AWBW workshops are designed to be flexible and adaptable for individuals of all ages, including youth, adults, families, and intergenerational groups.", + "Within the facilitator portal, workshops can be searched and filtered by focus area, population, theme, and other categories to help facilitators find workshops that best fit the communities they serve." + ] + }, + { + q: "How many workshops are included in the curriculum library?", + a: [ "The AWBW facilitator portal includes access to a library of more than 600 art workshops and facilitation resources." ] + }, + { + q: "Are workshops available in Spanish or other languages?", + a: [ + "Yes. A number of workshops have been translated into Spanish. Within the facilitator portal, facilitators can use the search filters to locate workshops that include Spanish translations.", + "AWBW continues to work toward increasing language accessibility and expanding translated resources over time." + ] + }, + { + q: "What materials do I need for the art workshops?", + a: [ + "There are no required art materials for participation. Workshops can be completed using any materials you already have available, such as paper, pencils, pens, crayons, markers, colored pencils, paint, collage materials, or other creative supplies. The focus of the AWBW approach is on the creative process rather than the materials themselves.", + "Prior to the training, we will also send participants a list of optional art supplies that may be helpful to have on hand, as well as printable worksheets for each of the art workshops that will be facilitated during the training. While neither the supplies nor the worksheets are required, many participants find them helpful for engaging more fully in the workshop experience.", + "We will walk through the art supplies suggested for each workshop so that, if trainees choose to use those materials in their own facilitation, they feel familiar and comfortable incorporating those materials into their own facilitation practice." + ] + }, + { + q: "What ongoing support does AWBW provide after completing the on-demand training?", + a: [ + "Once you complete the On-Demand Training and receive your certification, you become part of a thriving community of over 1,500 Windows Facilitators across the country and abroad, all learning from and supporting one another.", + "As a certified facilitator, you'll gain access to:" + ], + list: [ + "The AWBW Facilitator Portal, which houses a curriculum of over 600 art workshops, downloadable resources, toolkits, handouts, the Facilitator Manual, and tools for tracking attendance and gathering feedback", + "Ongoing professional development, including live art workshops, Q&As, and recorded presentations by subject matter experts", + "Virtual Community of Practice events, where you can connect with fellow facilitators and experience new workshops", + "AWBW staff support for workshop selection, facilitation questions, art supply tips, and more", + "A bi-monthly newsletter with new workshops, stories, and resources" + ] + }, + { + q: "Can multiple staff members from one organization participate?", + a: [ + "Yes. Multiple staff members from the same organization are welcome and encouraged to participate in the training.", + "Many organizations choose to train teams of staff members in order to integrate trauma-informed healing arts practices more broadly across their programs, services, and communities." + ] + }, + { + q: "Are there any fees, other than the training fee, associated with becoming a facilitator?", + a: [ + "AWBW has an annual membership fee of $100 per program to support the sustainability of the organization and continued facilitator resources and support.", + "The membership fee covers all facilitators connected to the same program or organization for the calendar year, regardless of the number of facilitators participating. Only one membership fee payment is required per program annually." + ] + } + ] %> + <% faqs.each do |faq| %> +
+ +
+ <%= faq[:q] %> + +
+
+
+ <% faq[:a].each do |paragraph| %> +

<%= paragraph %>

+ <% end %> + <% if faq[:list] %> +
    + <% faq[:list].each do |item| %> +
  • <%= item %>
  • <% end %> - <% if faq[:list] %> -
      - <% faq[:list].each do |item| %> -
    • <%= item %>
    • - <% end %> -
    - <% end %> -
-
- <% end %> -
+ + <% end %> +
+
+ <% end %> +
-
-

Still have questions?

-

You'll receive more details as we get closer to the training dates. In the meantime, please <%= link_to "reach out", contact_us_path, class: "text-blue-700 underline hover:text-blue-900" %> — we're always happy to help. We look forward to creating and connecting with you!

-
-
+
+

Still have questions?

+

You'll receive more details as we get closer to the training dates. In the meantime, please <%= link_to "reach out", contact_us_path, class: "text-blue-700 underline hover:text-blue-900" %> — we're always happy to help. We look forward to creating and connecting with you!

-
+<% end %> diff --git a/app/views/events/callouts/scholarship.html.erb b/app/views/events/callouts/scholarship.html.erb index 0f6260a572..54a9a1f583 100644 --- a/app/views/events/callouts/scholarship.html.erb +++ b/app/views/events/callouts/scholarship.html.erb @@ -5,103 +5,76 @@ eyebrow back there instead of the registrant's ticket. Gated on edit access so a registrant who lands on this URL still gets the default ticket back. %> <% from_scholarship_edit = params[:return_to] == "scholarship" && @scholarship && allowed_to?(:edit?, @scholarship) %> -
- <% if from_scholarship_edit %> - <%= link_to edit_scholarship_path(@scholarship), - class: "inline-flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-700 px-2 py-1" do %> - Back to scholarship - <% end %> - <% else %> - <%= link_to registration_ticket_path(@event_registration.slug), - class: "inline-flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-700 px-2 py-1" do %> - Back to ticket - <% end %> - <% end %> -
+<% eyebrow = from_scholarship_edit ? { back_path: edit_scholarship_path(@scholarship), back_label: "Back to scholarship" } : {} %> -
-
-
-
-
- <%= image_tag("logo.png", alt: "Organization logo", class: "h-9 w-auto") %> -
-
-

Scholarship

-

<%= @event.title %>

-
+<%= render layout: "events/callouts/callout_page", locals: { title: @builtin_title.presence || "Scholarship", **eyebrow } do %> +
+ <% if @scholarship && allowed_to?(:edit?, @scholarship) %> + <%# Admin-only jump to the management surface for this scholarship. Hidden from + registrants; opens in a new tab so the registrant view is kept. %> +
+ <%= link_to edit_scholarship_path(@scholarship), target: "_blank", rel: "noopener", + class: "inline-flex items-center gap-1 text-xs rounded-full border px-2 py-0.5 bg-sky-100 text-sky-700 border-sky-200 hover:bg-sky-200 transition" do %> + + Edit scholarship + + <% end %>
-
-
- -
- <% if @scholarship && allowed_to?(:edit?, @scholarship) %> - <%# Admin-only jump to the management surface for this scholarship. Hidden from - registrants; opens in a new tab so the registrant view is kept. %> -
- <%= link_to edit_scholarship_path(@scholarship), target: "_blank", rel: "noopener", - class: "inline-flex items-center gap-1 text-xs rounded-full border px-2 py-0.5 bg-sky-100 text-sky-700 border-sky-200 hover:bg-sky-200 transition" do %> - - Edit scholarship - - <% end %> -
- <% end %> + <% end %> - <% if @scholarship %> - <% grant = @scholarship.grant %> -
-
-

Amount awarded

-

<%= dollars_from_cents(@scholarship.amount_cents) %>

-
- <% if @scholarship.tasks_completed? %> - - Tasks completed - - <% else %> - - Tasks outstanding - - <% end %> + <% if @scholarship %> + <% grant = @scholarship.grant %> +
+
+

Amount awarded

+

<%= dollars_from_cents(@scholarship.amount_cents) %>

- - <% if grant %> -
-

Funder

-

<%= grant.funder_name %>

-

<%= grant.name %>

-
- -
- <%= render "scholarships/grant_criteria_tasks", grant: %> -
- <% end %> - <% else %> -
- - + <% if @scholarship.tasks_completed? %> + + Tasks completed + + <% else %> + + Tasks outstanding -
-

Scholarship requested

-

Your scholarship request is under review. We'll be in touch about next steps — check back here for your award details.

-
+ <% end %> +
+ + <% if grant %> +
+

Funder

+

<%= grant.funder_name %>

+

<%= grant.name %>

- <% end %> - <%# Jump to the registrant's full submission, where their scholarship - application answers are surfaced (inline for embedded-section forms, or - in a dedicated "Scholarship application" card for separate forms). %> - <% if @form_responses_available %>
- <%= link_to event_public_registration_path(@event, reg: @event_registration.slug), - class: "inline-flex items-center gap-1.5 text-sm font-medium #{DomainTheme.text_class_for(:scholarships, intensity: 700)} hover:underline" do %> - - Review your form responses - - <% end %> + <%= render "scholarships/grant_criteria_tasks", grant: %>
<% end %> -
+ <% else %> +
+ + + +
+

Scholarship requested

+

Your scholarship request is under review. We'll be in touch about next steps — check back here for your award details.

+
+
+ <% end %> + + <%# Jump to the registrant's full submission, where their scholarship + application answers are surfaced (inline for embedded-section forms, or + in a dedicated "Scholarship application" card for separate forms). %> + <% if @form_responses_available %> +
+ <%= link_to event_public_registration_path(@event, reg: @event_registration.slug), + class: "inline-flex items-center gap-1.5 text-sm font-medium #{DomainTheme.text_class_for(:scholarships, intensity: 700)} hover:underline" do %> + + Review your form responses + + <% end %> +
+ <% end %>
-
+<% end %> diff --git a/app/views/events/ce_hours.html.erb b/app/views/events/ce_hours.html.erb deleted file mode 100644 index b41405c443..0000000000 --- a/app/views/events/ce_hours.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -<% content_for(:page_bg_class, "public") %> - -<% reg_slug = params[:reg].presence %> -<% back_path = reg_slug ? registration_ticket_path(reg_slug) : event_path(@event) %> -<% back_label = reg_slug ? "Back to ticket" : "Back to event" %> -
- <%= link_to back_path, class: "inline-flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-700 px-2 py-1" do %> - <%= back_label %> - <% end %> -
- -
-
-
-
-
- <%= image_tag("logo.png", alt: "Organization logo", class: "h-9 w-auto") %> -
-
-

<%= @event.ce_hours_details_label %>

-

<%= @event.title %>

-
-
-
-
- -
-
- <%= form_label_html(@event.ce_hours_details) %> -
-
-
-
diff --git a/app/views/events/details.html.erb b/app/views/events/details.html.erb deleted file mode 100644 index 90cf3ec5c9..0000000000 --- a/app/views/events/details.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -<% content_for(:page_bg_class, "public") %> - -<% reg_slug = params[:reg].presence %> -<% back_path = reg_slug ? registration_ticket_path(reg_slug) : event_path(@event) %> -<% back_label = reg_slug ? "Back to ticket" : "Back to event" %> -
- <%= link_to back_path, class: "inline-flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-700 px-2 py-1" do %> - <%= back_label %> - <% end %> -
- -
-
-
-
-
- <%= image_tag("logo.png", alt: "Organization logo", class: "h-9 w-auto") %> -
-
-

<%= @event_details_title %>

-

<%= @event.title %>

-
-
-
-
- -
-
- <%= form_label_html(@event_details_body) %> -
-
-
-
diff --git a/config/routes.rb b/config/routes.rb index 03470351e6..054e615df6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -137,8 +137,6 @@ get :background get :registrants get :onboarding - get :details - get :ce_hours get :staff get "staff/edit", action: :edit_staff, as: :edit_staff patch "staff", action: :update_staff diff --git a/db/migrate/20260714120126_remove_ce_hours_details_from_events.rb b/db/migrate/20260714120126_remove_ce_hours_details_from_events.rb new file mode 100644 index 0000000000..5a8c9e32e1 --- /dev/null +++ b/db/migrate/20260714120126_remove_ce_hours_details_from_events.rb @@ -0,0 +1,15 @@ +class RemoveCeHoursDetailsFromEvents < ActiveRecord::Migration[8.0] + # CE page text moved onto the ce_hours registration_ticket_callout row (its + # "Callout page text"), like every other built-in. These event columns are no + # longer read or written. No backfill: the ticket system isn't in production use + # for CE, and rows seed their own defaults. + def up + remove_column :events, :ce_hours_details, if_exists: true + remove_column :events, :ce_hours_details_label, if_exists: true + end + + def down + add_column :events, :ce_hours_details, :text unless column_exists?(:events, :ce_hours_details) + add_column :events, :ce_hours_details_label, :string, default: "CE hours", null: false unless column_exists?(:events, :ce_hours_details_label) + end +end diff --git a/db/migrate/20260714145155_remove_event_details_from_events.rb b/db/migrate/20260714145155_remove_event_details_from_events.rb new file mode 100644 index 0000000000..4deb8be270 --- /dev/null +++ b/db/migrate/20260714145155_remove_event_details_from_events.rb @@ -0,0 +1,15 @@ +class RemoveEventDetailsFromEvents < ActiveRecord::Migration[8.0] + # The "Before you attend" content moved onto the event_details + # registration_ticket_callout row (its title + "Callout page text"), which now + # renders on the generic callout page like every other content built-in. These + # event columns are no longer read or written. No backfill. + def up + remove_column :events, :event_details, if_exists: true + remove_column :events, :event_details_label, if_exists: true + end + + def down + add_column :events, :event_details, :text unless column_exists?(:events, :event_details) + add_column :events, :event_details_label, :string, default: "Before you attend", null: false unless column_exists?(:events, :event_details_label) + end +end diff --git a/db/schema.rb b/db/schema.rb index a46c974399..a71e912b48 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_07_14_113355) do +ActiveRecord::Schema[8.1].define(version: 2026_07_14_145155) do create_table "action_text_mentions", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.bigint "action_text_rich_text_id", null: false t.datetime "created_at", null: false @@ -534,16 +534,12 @@ t.boolean "autoshow_videoconference_label", default: true, null: false t.boolean "autoshow_videoconference_link", default: true, null: false t.integer "ce_hours_cost_cents" - t.text "ce_hours_details" - t.string "ce_hours_details_label", default: "CE hours", null: false t.decimal "ce_hours_offered", precision: 5, scale: 2 t.integer "cost_cents" t.datetime "created_at", null: false t.integer "created_by_id" t.text "description" t.datetime "end_date", precision: nil - t.text "event_details" - t.string "event_details_label", default: "Before you attend", null: false t.boolean "facilitator_training", default: false, null: false t.boolean "featured", default: false, null: false t.text "ga4_snippet" diff --git a/spec/models/registration_ticket_callout_spec.rb b/spec/models/registration_ticket_callout_spec.rb index 6d50769252..9a07031d09 100644 --- a/spec/models/registration_ticket_callout_spec.rb +++ b/spec/models/registration_ticket_callout_spec.rb @@ -69,6 +69,15 @@ expect(event.registration_ticket_callouts.visible).to eq([ shown ]) end + + it "treats art_supplies as a content callout (renders its own page), not behavioral" do + event = create(:event) + art_supplies = create(:registration_ticket_callout, event:, magic_key: "art_supplies") + certificate = create(:registration_ticket_callout, event:, magic_key: "certificate") + + expect(art_supplies.behavioral_magic?).to be(false) + expect(certificate.behavioral_magic?).to be(true) + end end describe "#published (inverse of hidden)" do diff --git a/spec/requests/events/callouts_spec.rb b/spec/requests/events/callouts_spec.rb index cb9732c1fb..9ffe8b43d5 100644 --- a/spec/requests/events/callouts_spec.rb +++ b/spec/requests/events/callouts_spec.rb @@ -102,6 +102,45 @@ end end + # Every built-in page renders the intro text an admin types into its materialized + # callout row's "Callout page text" (description), above the app-controlled body — + # the same field custom callouts use. Previously CE/handouts/FAQ/scholarship + # silently dropped it. + describe "built-in page intro text (callout row description)" do + let(:event) { create(:event, facilitator_training: true) } + + before { DefaultTicketCallouts.seed(event) } + + def describe_callout(magic_key, html) + event.registration_ticket_callouts.find_by(magic_key:).update!(description: html) + end + + it "renders the CE callout's description" do + describe_callout("ce_hours", "

Bring your license number.

") + get registration_ce_path(registration.slug) + expect(response.body).to include("Bring your license number.") + end + + it "renders the handouts callout's description" do + describe_callout("handouts", "

Download these before class.

") + get registration_handouts_path(registration.slug) + expect(response.body).to include("Download these before class.") + end + + it "renders the FAQ callout's description" do + describe_callout("faq", "

Read this intro first.

") + get registration_faq_path(registration.slug) + expect(response.body).to include("Read this intro first.") + end + + it "renders the scholarship callout's description" do + registration.update!(scholarship_requested: true) + describe_callout("scholarship", "

About your scholarship.

") + get registration_scholarship_path(registration.slug) + expect(response.body).to include("About your scholarship.") + end + end + describe "POST /registration/:slug/ce/pay" do let(:event) { create(:event, ce_hours_offered: 6, ce_hours_cost_cents: 15_000) } let(:fake_session) { double(url: "https://checkout.stripe.com/test", id: "cs_test_123") } diff --git a/spec/requests/events/registration_ticket_callouts_spec.rb b/spec/requests/events/registration_ticket_callouts_spec.rb index fda9eb9a77..29b46d6910 100644 --- a/spec/requests/events/registration_ticket_callouts_spec.rb +++ b/spec/requests/events/registration_ticket_callouts_spec.rb @@ -246,7 +246,7 @@ } expect(event.registration_ticket_callouts.magic.pluck(:magic_key)).to contain_exactly( - "payment", "certificate", "scholarship", "ce_hours", "event_details", + "payment", "certificate", "scholarship", "ce_hours", "art_supplies", "videoconference", "handouts", "faq" ) end diff --git a/spec/requests/events_spec.rb b/spec/requests/events_spec.rb index ee87397c8a..c3809342ac 100644 --- a/spec/requests/events_spec.rb +++ b/spec/requests/events_spec.rb @@ -182,40 +182,6 @@ def offer_ce!(target_event) end end - describe "GET /details" do - let(:event) { create(:event, :published, :publicly_visible) } - - it "renders the details page when details are present" do - event.update!(event_details_label: "Art supplies", event_details: "

Bring scissors

") - get details_event_path(event) - expect(response).to have_http_status(:ok) - expect(response.body).to include("Art supplies") - expect(response.body).to include("Bring scissors") - end - - it "redirects to the event when details are blank" do - get details_event_path(event) - expect(response).to redirect_to(event_path(event)) - end - end - - describe "GET /ce_hours" do - let(:event) { create(:event, :published, :publicly_visible) } - - it "renders the CE hours page when the event is CE-eligible" do - event.update!(ce_hours_offered: 6, ce_hours_details_label: "Continuing education", ce_hours_details: "

Email your license number

") - get ce_hours_event_path(event) - expect(response).to have_http_status(:ok) - expect(response.body).to include("Continuing education") - expect(response.body).to include("Email your license number") - end - - it "redirects to the event when the event is not CE-eligible" do - get ce_hours_event_path(event) - expect(response).to redirect_to(event_path(event)) - end - end - describe "GET /new" do context "as admin" do it "renders successfully" do @@ -263,10 +229,10 @@ def offer_ce!(target_event) expect(response.body).to include(VisibilityFlagsHelper::FLAG_DEFINITIONS[:public_registration_enabled][:description]) end - it "renders the built-in 'Before you attend' card as an editable row" do + it "renders the built-in 'Art supplies' card as an editable row" do get edit_event_path(event) expect(response.body).to include("Registration ticket callouts") - expect(response.body).to include("Before you attend") + expect(response.body).to include("Art supplies") # Its text now lives on the callout row, not the event columns. expect(response.body).not_to include("event[event_details_label]") end diff --git a/spec/services/default_ticket_callouts_spec.rb b/spec/services/default_ticket_callouts_spec.rb index 7006d97eb2..a9c1e15cd5 100644 --- a/spec/services/default_ticket_callouts_spec.rb +++ b/spec/services/default_ticket_callouts_spec.rb @@ -9,7 +9,7 @@ keys = event.registration_ticket_callouts.magic.pluck(:magic_key) expect(keys).to contain_exactly( - "payment", "certificate", "scholarship", "ce_hours", "event_details", + "payment", "certificate", "scholarship", "ce_hours", "art_supplies", "videoconference", "handouts", "faq" ) end @@ -17,13 +17,13 @@ it "seeds callouts in canonical ticket order" do form = create(:form) event = create(:event, facilitator_training: true, ce_hours_offered: 6, - event_details: "

x

", videoconference_url: "https://example.com/z") + videoconference_url: "https://example.com/z") event.event_forms.create!(form:, role: "scholarship") described_class.seed(event) expect(event.registration_ticket_callouts.ordered.map(&:magic_key)).to eq( - %w[payment certificate scholarship ce_hours event_details videoconference handouts faq] + %w[payment certificate scholarship ce_hours art_supplies videoconference handouts faq] ) end @@ -88,21 +88,17 @@ expect(payment.resources).to be_empty # no W-9 on a free event end - it "migrates CE hours and event-details content from the event onto the row" do - event = create(:event, ce_hours_details_label: "Continuing education", - ce_hours_details: "

CAMFT approved.

", event_details_label: "Art supplies", - event_details: "

Bring paper.

") + it "seeds the CE hours and art supplies cards with static titles and no page text" do + event = create(:event) described_class.seed(event) ce = event.registration_ticket_callouts.find_by(magic_key: "ce_hours") - details = event.registration_ticket_callouts.find_by(magic_key: "event_details") - expect(ce.title).to eq("Continuing education") - expect(ce.description).to eq("

CAMFT approved.

") - expect(details.title).to eq("Art supplies") - expect(details.description).to eq("

Bring paper.

") - # A freshly-migrated card matches its default. - expect(described_class.customized?(ce)).to be(false) + art_supplies = event.registration_ticket_callouts.find_by(magic_key: "art_supplies") + expect(ce.title).to eq("CE hours") + expect(ce.description).to be_blank + expect(art_supplies.title).to eq("Art supplies & what to bring") + expect(art_supplies.description).to be_blank end it "reports whether a materialized callout has been customized" do @@ -189,7 +185,7 @@ expect(event.registration_ticket_callouts.ordered.first).to eq(custom) expect(event.registration_ticket_callouts.ordered.map(&:magic_key).compact).to eq( - %w[payment certificate scholarship ce_hours event_details videoconference handouts faq] + %w[payment certificate scholarship ce_hours art_supplies videoconference handouts faq] ) end end diff --git a/spec/services/magic_ticket_callouts_spec.rb b/spec/services/magic_ticket_callouts_spec.rb index bbf2b60ad0..a19b8b97d4 100644 --- a/spec/services/magic_ticket_callouts_spec.rb +++ b/spec/services/magic_ticket_callouts_spec.rb @@ -55,7 +55,7 @@ def card(reg, title) end it "uses the arrow trailing icon for every card" do - event.update!(ce_hours_details: "6 hours", videoconference_url: "https://example.zoom.us/j/1") + event.update!(videoconference_url: "https://example.zoom.us/j/1") trailing = described_class.new(registration).cards.map(&:trailing_icon).uniq expect(trailing).to eq([ "fa-solid fa-arrow-right" ]) end @@ -75,10 +75,10 @@ def card(reg, title) end it "shows the CE card only when the registrant requested CE credit" do - expect(card_titles(registration)).not_to include(event.ce_hours_details_label) + expect(card_titles(registration)).not_to include("CE hours") license = create(:professional_license, :placeholder, person: registration.registrant) create(:continuing_education_registration, event_registration: registration, professional_license: license) - expect(card_titles(registration.reload)).to include(event.ce_hours_details_label) + expect(card_titles(registration.reload)).to include("CE hours") end it "turns the CE card orange while a balance is due, teal once paid" do @@ -87,7 +87,7 @@ def card(reg, title) ce = create(:continuing_education_registration, event_registration: registration, professional_license: license) # Balance due, license still needed: orange card, amber chip naming what's needed. - needs = card(registration.reload, event.ce_hours_details_label) + needs = card(registration.reload, "CE hours") expect(needs.theme).to eq(DomainTheme.swatch("orange")) expect(needs.subtitle).to eq("6 hours") expect(needs.badge).to eq("$150 · License number needed") @@ -95,14 +95,14 @@ def card(reg, title) # License provided but still owing: orange card, amber "$X due" chip like the payment card. license.update!(number: "LIC123") - due = card(registration.reload, event.ce_hours_details_label) + due = card(registration.reload, "CE hours") expect(due.theme).to eq(DomainTheme.swatch("orange")) expect(due.badge).to eq("$150 due") expect(due.badge_classes).to be_nil # Paid in full: resting teal card, no "due" chip. create(:allocation, allocatable: ce, amount: 15_000) - paid = card(registration.reload, event.ce_hours_details_label) + paid = card(registration.reload, "CE hours") expect(paid.theme).to eq(DomainTheme.swatch("teal")) expect(paid.badge).to be_nil end @@ -143,19 +143,20 @@ def card(reg, title) end it "places payment first and FAQ last in the full ordering" do - event.update!(facilitator_training: true, event_details: "Bring supplies", - ce_hours_details: "6 hours", ce_hours_offered: 6, + event.update!(facilitator_training: true, + ce_hours_offered: 6, videoconference_url: "https://example.zoom.us/j/123", start_date: 3.days.ago, end_date: 2.days.ago) registration.update!(status: "attended", scholarship_requested: true) license = create(:professional_license, :placeholder, person: registration.registrant) create(:continuing_education_registration, event_registration: registration, professional_license: license) + # art_supplies is a content callout (no fallback card); it renders from its + # materialized row on the generic callout page, not here. expect(card_titles(registration)).to eq([ "Make your payment", "Certificate of completion", "Scholarship", - event.ce_hours_details_label, - event.event_details_label, + "CE hours", "Videoconference", "Handouts", "Frequently asked questions" diff --git a/spec/system/event_registration_show_spec.rb b/spec/system/event_registration_show_spec.rb index db88b5bb54..c6c430295e 100644 --- a/spec/system/event_registration_show_spec.rb +++ b/spec/system/event_registration_show_spec.rb @@ -60,20 +60,25 @@ end describe "before-you-attend call-out" do - it "links to the details page using the event's label when details are present" do - event.update!(event_details_label: "Art supplies", event_details: "

Bring scissors

") + let!(:details) do + create(:registration_ticket_callout, event:, magic_key: "art_supplies", + title: "Art supplies", callout_type: "reference", hidden: true) + end + + it "links to its own callout page when published with content" do + details.update!(description: "

Bring scissors

", hidden: false) sign_in(user) visit registration_ticket_path(registration.slug) - expect(page).to have_link("Art supplies", href: details_event_path(event, reg: registration.slug)) + expect(page).to have_link("Art supplies", href: event_registration_ticket_callout_path(event, details, reg: registration.slug)) end - it "is hidden when no details are set" do + it "is hidden when the callout is not published" do sign_in(user) visit registration_ticket_path(registration.slug) - expect(page).to have_no_link(href: details_event_path(event, reg: registration.slug)) + expect(page).to have_no_link(href: event_registration_ticket_callout_path(event, details, reg: registration.slug)) end end diff --git a/spec/views/page_bg_class_alignment_spec.rb b/spec/views/page_bg_class_alignment_spec.rb index e79302a91d..ac739ab097 100644 --- a/spec/views/page_bg_class_alignment_spec.rb +++ b/spec/views/page_bg_class_alignment_spec.rb @@ -209,8 +209,6 @@ "app/views/events/callouts/handouts.html.erb" => "public", "app/views/events/callouts/resource.html.erb" => "public", "app/views/events/callouts/videoconference.html.erb" => "public", - "app/views/events/details.html.erb" => "public", - "app/views/events/ce_hours.html.erb" => "public", "app/views/registration_ticket_callouts/show.html.erb" => "public", # ─── bulk payment views ───