Skip to content
Open
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
39 changes: 38 additions & 1 deletion app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class EventsController < ApplicationController
include AhoyTracking, TagAssignable
skip_before_action :authenticate_user!, only: [ :index, :show ]
skip_before_action :verify_authenticity_token, only: [ :preview ]
before_action :set_event, only: %i[ show edit update destroy preview manage copy_registration_form ]
before_action :set_event, only: %i[ show edit update destroy preview manage remind send_reminder copy_registration_form ]

def index
authorize!
Expand Down Expand Up @@ -59,6 +59,43 @@ def manage
end
end

def remind
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this preview_reminder? The word remind sounds like taking the actual action of reminding. This action deals with the preview.

authorize! @event, to: :remind?
@event = @event.decorate
@event_registrations = @event.event_registrations
.includes(:payments, registrant: [ :user, :contact_methods ])
.joins(:registrant)
.select { |r| r.registrant.preferred_email.present? }
@sample_registration = @event_registrations.first
@days_until_event = @event.start_date.present? ? (@event.start_date.to_date - Date.current).to_i : nil

if @sample_registration
mail = EventMailer.event_registration_reminder(@sample_registration, days_until_event: @days_until_event)
@reminder_preview_html = mail.html_part&.body&.decoded
end
end

def send_reminder
authorize! @event, to: :send_reminder?
allowed_ids = Array(params[:registration_ids]).map(&:to_i).reject(&:zero?)
registrations = @event.event_registrations
.where(id: allowed_ids)
.includes(registrant: [ :user, :contact_methods ])
.select { |r| r.registrant.preferred_email.present? }
days_until = @event.start_date.present? ? (@event.start_date.to_date - Date.current).to_i : nil

if registrations.empty?
redirect_to remind_event_path(@event), alert: "Please select at least one recipient."
return
end

registrations.each do |event_registration|
EventMailer.event_registration_reminder(event_registration, days_until_event: days_until).deliver_later
end

redirect_to manage_event_path(@event), notice: "Reminder emails are being sent to #{registrations.size} registrant#{'s' if registrations.size != 1}."
end

def create
authorize!
@event = Event.new(event_params)
Expand Down
22 changes: 22 additions & 0 deletions app/mailers/event_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ def event_registration_confirmation(event_registration)
)
end

def event_registration_reminder(event_registration, days_until_event: nil)
@event_registration = event_registration
@event = event_registration.event.decorate
@person = event_registration.registrant
@days_until_event = days_until_event

@notification_type = "Event registration reminder"

@time_zone = @person.user&.time_zone || Time.zone.name
@event_url = @event_registration.slug.present? ? event_url(@event, reg: @event_registration.slug) : event_url(@event)
@organization_name = ENV.fetch("ORGANIZATION_NAME", "AWBW")
@organization_website = ENV.fetch("ORGANIZATION_WEBSITE", root_url)

subject = "Reminder: #{@event.title} – #{@event.start_date.in_time_zone(@time_zone).strftime('%B %-d, %Y')}"
mail(
to: @person.preferred_email,
from: ENV.fetch("REPLY_TO_EMAIL", "no-reply@awbw.org"),
reply_to: ENV.fetch("REPLY_TO_EMAIL", "programs@awbw.org"),
subject: "AWBW Portal: #{subject}"
)
end

def event_registration_cancelled(event_registration)
@event_registration = event_registration
@event = event_registration.event.decorate
Expand Down
8 changes: 8 additions & 0 deletions app/policies/event_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ def manage?
admin? || owner?
end

def remind?
manage?
end

def send_reminder?
manage?
end

alias_rule :preview?, to: :edit?

private
Expand Down
60 changes: 60 additions & 0 deletions app/views/event_mailer/event_registration_reminder.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<h1>Event reminder</h1>

<div style="margin-top: 36px; text-align: left;">
<p>
Hello <strong><%= @person.full_name %></strong>,
</p>

<p>
This is a reminder that you're registered for the following <%= @organization_name %> event<%= raw(" " + (@days_until_event == 0 ? "<strong>today</strong>" : @days_until_event == 1 ? "<strong>tomorrow</strong>" : "in <strong>#{@days_until_event} days</strong>")) if @days_until_event.is_a?(Integer) %>:
</p>

<div style="text-align: center; background-color: #f3f4f6; border-radius: 6px; padding: 24px; margin: 16px 0;">
<% if @event.respond_to?(:pre_title) && @event.pre_title.present? %>
<p style="font-size: 14px; font-weight: 600; color: #374151; margin: 0 0 4px; font-family: 'Telefon Bold', sans-serif;">
<%= @event.pre_title %>
</p>
<% end %>

<h2 style="font-size: 28px; font-weight: bold; color: #166534; margin: 0 0 16px; font-family: Lato, sans-serif;">
<%= @event.title %>
</h2>

<p style="font-size: 22px; font-weight: bold; color: #1e3a8c; text-transform: uppercase; margin: 0 0 8px; font-family: Lato, sans-serif;">
<% Time.use_zone(@time_zone) { %><%= @event.times(display_day: true, display_date: true) %><% } %>
</p>

<% if @event.labelled_cost.present? %>
<p style="font-size: 18px; font-weight: bold; color: #1e3a8c; text-transform: uppercase; margin: 0 0 8px; font-family: Lato, sans-serif;">
<%= @event.labelled_cost %>
</p>
<% end %>

<% if @event.location.present? %>
<p style="font-size: 16px; color: #374151; margin: 0 0 8px;">
<%= @event.location.name %>
</p>
<% end %>

<% if @event.videoconference_url.present? %>
<p style="font-size: 16px; color: #374151; margin: 0 0 8px;">
<a href="<%= @event.videoconference_url %>" style="color: #374151; text-decoration: underline;">Join us on <%= @event.decorate.videoconference_domain %></a>
</p>
<% end %>
</div>
</div>

<p>
Visit the event page for updates, directions, or calendar links:
</p>

<p>
<% if @event_registration.persisted? && @event_registration.slug.present? %>
<a href="<%= registration_ticket_url(@event_registration.slug) %>" class="button">View registration</a>
<% end %>
<a href="<%= @event_url %>" class="button">View event</a>
</p>

<p style="margin-top: 24px; font-size: 12px; color: #6b7280;">
This is an automated reminder from <%= @organization_name %>.
</p>
32 changes: 32 additions & 0 deletions app/views/event_mailer/event_registration_reminder.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Event reminder

Hello <%= @person.full_name %>,

This is a reminder that you're registered for the following event<%= @days_until_event.is_a?(Integer) ? (@days_until_event == 0 ? " today" : @days_until_event == 1 ? " tomorrow" : " in #{@days_until_event} days") : "" %>:

<% if @event.respond_to?(:pre_title) && @event.pre_title.present? %>
<%= @event.pre_title %>
<% end %><%= @event.title %>
<% Time.use_zone(@time_zone) { %><%= @event.times(display_day: true, display_date: true) %><% } %>

<% if @event.location.present? %>
Location: <%= @event.location.name %>
<% end %>

<% if @event.videoconference_url.present? %>
Videoconference URL: <%= @event.videoconference_url %>
<% end %>

<% if @event.labelled_cost.present? %>
<%= @event.labelled_cost %>
<% end %>

<% if @event_registration.slug.present? %>
View your registration:
<%= registration_ticket_url(@event_registration.slug) %>

<% end %>View the event page:
<%= @event_url %>

--
This is an automated reminder from <%= @organization_name %>.
3 changes: 3 additions & 0 deletions app/views/events/manage.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
</p>
</div>
<div class="flex gap-2">
<% if allowed_to?(:remind?, @event) %>
<%= link_to "Send reminder", remind_event_path(@event), class: "btn btn-secondary-outline btn-sm" %>
<% end %>
<%= link_to "Download CSV", manage_event_path(@event, format: :csv), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1", data: { turbo_frame: "_top" } %>
<%= link_to "View", event_path(@event), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
<% if allowed_to?(:edit?, @event) %>
Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@
resources :events do
member do
get :manage
get :remind
patch :preview
post :copy_registration_form
post :send_reminder
end
resource :registrations, only: %i[ create destroy ], module: :events, as: :registrant_registration
resource :public_registration, only: [ :new, :create, :show ], module: :events
Expand Down
70 changes: 70 additions & 0 deletions spec/mailers/event_mailer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,74 @@
end
end
end

describe "#event_registration_reminder" do
let(:event_registration) { create(:event_registration) }
let(:mail) { described_class.event_registration_reminder(event_registration, days_until_event: days_until_event) }
let(:days_until_event) { 7 }

it "renders without raising" do
expect { mail.deliver_now }.not_to raise_error
end

it "sends to the registrant" do
expect(mail.to).to eq([ event_registration.registrant.preferred_email ])
end

it "includes the event title in the subject" do
expect(mail.subject).to include(event_registration.event.title)
end

it "includes the event title in the body" do
expect(mail.body.encoded).to include(event_registration.event.title)
end

it "includes the registrant name in the body" do
expect(mail.body.encoded).to include(event_registration.registrant.full_name)
end

it "includes reminder wording in the body" do
expect(mail.body.encoded).to include("This is a reminder that you're registered for the following")
end

context "when days_until_event is 0" do
let(:days_until_event) { 0 }

it "includes today in the body" do
expect(mail.body.encoded).to include("today")
end
end

context "when days_until_event is 1" do
let(:days_until_event) { 1 }

it "includes tomorrow in the body" do
expect(mail.body.encoded).to include("tomorrow")
end
end

context "when days_until_event is 7" do
let(:days_until_event) { 7 }

it "includes the number of days in the body" do
expect(mail.body.encoded).to include("7 days")
end
end

context "when days_until_event is nil" do
let(:days_until_event) { nil }
let(:mail) { described_class.event_registration_reminder(event_registration) }

it "renders without raising" do
expect { mail.deliver_now }.not_to raise_error
end

it "does not include today, tomorrow, or in N days in the body" do
body = mail.body.encoded
expect(body).not_to include("today")
expect(body).not_to include("tomorrow")
expect(body).not_to match(/\bin \d+ days\b/)
end
end
end
end
5 changes: 5 additions & 0 deletions test/mailers/previews/event_mailer_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ def event_registration_confirmation
EventMailer.event_registration_confirmation(event_registration)
end

def event_registration_reminder
event_registration = sample_event_registration
EventMailer.event_registration_reminder(event_registration, days_until_event: 1)
end

def event_registration_cancelled
event_registration = sample_event_registration
event_registration.status = "cancelled"
Expand Down