Skip to content
Closed
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions app/views/events/callouts/_ondemand_certificate.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<%# The on-demand facilitator-training certificate β€” the painted brushstroke frame
from the on-demand certificate art (app/assets/images/certificates), distinct
from the live-training frame (_training_certificate). The AWBW logo is baked
into the frame, so it isn't overlaid here; every other line β€” title, recipient,
course, dates, CE clause, signature strip β€” is rendered live over the frame, so
the same dynamic content flows onto whichever training the registrant completed.
The signature strip is the admin-managed upload passed in as signature_file, so
real signatures never live in this public repo.
Sizes use container-query units (cqw) so the whole certificate scales as one. %>
<article class="certificate @container relative mx-auto aspect-[2200/1556] w-full max-w-5xl overflow-hidden bg-white print:max-w-none">
<%= image_tag "certificates/certificate-ondemand-border.png", alt: "",
class: "pointer-events-none absolute inset-0 h-full w-full object-fill" %>

<%# The frame's baked-in logo fills the top ~28%, so content starts below it. %>
<div class="absolute inset-x-[8%] top-[30%] bottom-[7.5%] flex flex-col items-center justify-between px-[3%] text-center">
<div class="flex flex-col items-center gap-[1cqw]">
<p class="font-display text-[4.6cqw] font-bold tracking-tight text-gray-900">Certificate of Completion</p>
<p class="text-[2.2cqw] text-gray-500">presented to</p>
<p class="text-[4.6cqw] leading-tight font-bold tracking-tight text-gray-900"><%= recipient %></p>
</div>

<div class="flex flex-col items-center gap-[0.6cqw]">
<p class="text-[2.2cqw] text-gray-500">for successfully completing</p>
<p class="text-[3cqw] leading-tight font-bold text-gray-900"><%= event.title %></p>
<p class="text-[2.4cqw] font-semibold text-gray-700"><%= event.date_range %></p>
<% if show_ce_clause %>
<p class="mt-[1cqw] max-w-[80%] text-[1.7cqw] leading-relaxed text-gray-600">
A Window Between Worlds has issued <span class="font-semibold text-gray-800"><%= ce_hours_label %> hours</span>
of continuing education (CE) credit for this training, in accordance with our approval by
<%= link_to "CAMFT", ContinuingEducationRegistration::ACCREDITATION_URL,
target: "_blank", rel: "noopener",
class: "font-medium text-purple-800 underline decoration-amber-400 underline-offset-2 hover:text-purple-950" %>
(provider #<%= ContinuingEducationRegistration::ACCREDITATION_PROVIDER_NUMBER %>).
</p>
<% end %>
</div>

<%# Served same-origin via the proxy (never the raw storage URL), and only
embedded here β€” so the signature image is reachable only from an issued
certificate, not by browsing the repo or the resources index. %>
<% if signature_file %>
<%= image_tag rails_storage_proxy_path(signature_file),
alt: "Signed by Christy Turek Rials, Training & Outreach Director; Rudy Hernandez, Program Director; and Tiombe Wallace, Consultant",
class: "w-[60cqw]" %>
<% else %>
<span></span>
<% end %>
</div>
</article>
15 changes: 11 additions & 4 deletions app/views/events/callouts/certificate.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,18 @@
</div>
<% end %>

<%# Facilitator trainings get the branded painted-border certificate (full-bleed
in print so the painted border reaches the paper edge); every other event
keeps the default CSS-drawn completion certificate, inset from the edge. %>
<%# Facilitator trainings get a branded painted-border certificate (full-bleed
in print so the painted border reaches the paper edge) β€” on-demand trainings
use their own frame, live trainings the scheduled-training frame; every other
event keeps the default CSS-drawn completion certificate, inset from the edge. %>
<% training = @event.facilitator_training? %>
<% certificate_partial = training ? "training_certificate" : "completion_certificate" %>
<% certificate_partial = if @event.on_demand_facilitator_training?
"ondemand_certificate"
elsif training
"training_certificate"
else
"completion_certificate"
end %>
<div class="px-4 py-6 sm:px-8 <%= training ? "print:p-0" : "print:p-[0.4in]" %>">
<%= render certificate_partial,
event: event,
Expand Down
12 changes: 12 additions & 0 deletions config/features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3347,6 +3347,18 @@
Consultant. Every other event keeps the standard certificate of completion.
action_path: /events
pr_number: 2504

- name: "Own certificate design for on-demand trainings"
area: events
display_status: user_facing
released_on: 2026-09-03
summary: >-
On-demand facilitator training completions now download their own painted
certificate frame, distinct from the scheduled-training design. The
recipient's name, course title, dates, CE credit, and signatures still
print live over it.
action_path: /events
pr_number: 2526
- name: "Slider questions on forms"
area: registration
display_status: admin_facing
Expand Down
18 changes: 18 additions & 0 deletions spec/requests/events/callouts_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,24 @@
end
end

context "for an on-demand facilitator training event" do
let(:event) { create(:event, :ended, facilitator_training: true, on_demand: true, title: "On-Demand Windows Facilitator Training 2027") }

it "renders the on-demand certificate frame with the live course title" do
get registration_certificate_path(registration.slug)

expect(response).to have_http_status(:success)
expect(response.body).to include("certificate-ondemand-border")
expect(response.body).to include("Certificate of Completion")
expect(response.body).to include("presented to")
expect(response.body).to include(registration.registrant.full_name)
expect(response.body).to include("On-Demand Windows Facilitator Training 2027")
# Not the scheduled-training frame, nor the default completion copy.
expect(response.body).not_to include("certificate-training-border")
expect(response.body).not_to include("This certifies that")
end
end

it "uses the default completion certificate for non-training events" do
get registration_certificate_path(registration.slug)

Expand Down