Skip to content

Commit

Permalink
Organizer can email individual and groups of attendees
Browse files Browse the repository at this point in the history
* feature tests for emailing individuals and groups from the organizer console
* refactor of the EventEmailAddresser JS
* select2 recipient multiselect on the event email view
* presenter objects for EventEmail and PastEventEmails
* refactors on Events::EmailsController and spec
  • Loading branch information
Rae Bonfanti committed Oct 14, 2015
1 parent b70042d commit 29340ed
Show file tree
Hide file tree
Showing 14 changed files with 494 additions and 314 deletions.
4 changes: 3 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ GEM
bullet (4.14.7)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.9.0)
byebug (6.0.2)
byebug (5.0.0)
columnize (= 0.9.0)
capybara (2.5.0)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
Expand All @@ -83,6 +84,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.9.1.1)
columnize (0.9.0)
crack (0.4.2)
safe_yaml (~> 1.0.0)
crass (1.0.2)
Expand Down
48 changes: 0 additions & 48 deletions app/assets/javascripts/email.js.coffee

This file was deleted.

107 changes: 107 additions & 0 deletions app/assets/javascripts/event_email_addresser.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
Bridgetroll.EventEmailAddresser = {
$recipientMultiSelect: ->
$('select#recipients')

init: ->
this.$recipientMultiSelect().change => this.recalculateRecipients()
$('.cc-organizers').change => this.toggleOrganizerCopy()

this.initRecipientSelect();
this.recalculateRecipients();
this.toggleOrganizerCopy();

initRecipientSelect: ->
$recipientMultiSelect = this.$recipientMultiSelect()
$recipientMultiSelect.select2({
placeholder: 'Select recipients',
width: '100%'
})

$('#recipients-set-to-all').on 'click', (e) ->
e.preventDefault()
recipientIds = _.map attendees, (attendee) ->
attendee.user_id
$recipientMultiSelect.val(recipientIds).trigger 'change'

$('#recipients-add-volunteers').on 'click', (e) =>
e.preventDefault()
roleId = $(e.target).data('role-id')
this.addRecipientGroup(roleId)

$('#recipients-open-students-dropdown').on 'click', (e) ->
e.preventDefault()
$(this).toggleClass('open')

$('#recipients-add-accepted-students').on 'click', (e) =>
e.preventDefault()
roleId = $(e.target).data('role-id')
this.addRecipientGroup(roleId, 'accepted')

$('#recipients-add-waitlisted-students').on 'click', (e) =>
e.preventDefault()
roleId = $(e.target).data('role-id')
this.addRecipientGroup(roleId, 'waitlisted')

$('#recipients-add-all-students').on 'click', (e) =>
e.preventDefault()
roleId = $(e.target).data('role-id')
this.addRecipientGroup(roleId)

$('#recipients-remove-dropdown').on 'click', (e) ->
e.preventDefault()
$(this).toggleClass('open')

$('#recipients-remove-no-shows').on 'click', (e) =>
e.preventDefault()
this.removeNoShows()

$('#recipients-remove-all').on 'click', (e) ->
e.preventDefault()
$recipientMultiSelect.val(null).trigger 'change'

addRecipientGroup: (roleId, roleState) ->
recipientGroup = _.filter attendees, (attendee) ->
attendee.role_id == roleId

if (roleState == 'accepted')
recipientGroup = _.filter recipientGroup, (recipient) ->
!recipient.waitlisted
else if (roleState == 'waitlisted')
recipientGroup = _.filter recipientGroup, (recipient) ->
recipient.waitlisted

recipientIds = _.map recipientGroup, (recipient) ->
recipient.user_id

this.$recipientMultiSelect().val((i, currentVal) ->
_.union(currentVal, recipientIds)
).trigger 'change'

removeNoShows: ->
if this.$recipientMultiSelect().val()
currentRecipients = _.map this.$recipientMultiSelect().val(), (recipientId) ->
_.find attendees, (attendee) -> attendee.user_id == recipientId

recipientsWhoAttended = _.filter currentRecipients, (recipient) ->
recipient.checkins_count > 0

recipientIds = _.map recipientsWhoAttended, (recipient) ->
recipient.user_id

this.$recipientMultiSelect().val(recipientIds).trigger 'change'

recalculateRecipients: ->
recipients = this.$recipientMultiSelect().val()
count = if recipients then recipients.length else 0

noun = if (count == 1) then "person." else "people."
$('.num').html("<b>#{count}</b> #{noun}")

toggleOrganizerCopy: () ->
checked = $('.cc-organizers').prop('checked')

if (checked)
$('.organizer-copy').show()
else
$('.organizer-copy').hide()
}
23 changes: 5 additions & 18 deletions app/assets/stylesheets/events/_email.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
margin: 0;
}

input[type=text] {
input[type=text], .select2-container {
margin-bottom: 20px;
}

Expand All @@ -21,22 +21,9 @@
margin: 5px 5px 5px 0;
}

.recipients-popover {
color: dodgerblue;
cursor: pointer;
}

.popover {
max-width: 600px;

.recipients {
height: 300px;
overflow-y: auto;
overflow-x: hidden;

ul {
margin: 0;
}
.recipients-selection {
label {
display: block;
}
}
}
}
131 changes: 48 additions & 83 deletions app/controllers/events/emails_controller.rb
Original file line number Diff line number Diff line change
@@ -1,97 +1,62 @@
class Events::EmailsController < ApplicationController
before_action :authenticate_user!, :validate_organizer!, :find_event
module Events
class EmailsController < ApplicationController
before_action :authenticate_user!, :validate_organizer!, :find_event

def new
new_email = @event.event_emails.build(attendee_group: 'All')
@email = EventEmailPresenter.new(new_email)
@past_emails = PastEventEmailsPresenter.new(@event)
end

def new
@email = @event.event_emails.build(
attendee_group: 'All',
)
assign_ivars
end
def create
recipient_ids = email_params[:recipients] ? email_params[:recipients].map(&:to_i) : []
recipient_rsvps = @event.rsvps.where(user_id: recipient_ids).includes(:user)

cc_recipients = email_params[:cc_organizers] ? @event.organizers.map(&:email) : [current_user.email]

@email = @event.event_emails.build(
subject: email_params[:subject],
body: email_params[:body],
sender: current_user,
recipient_rsvp_ids: recipient_rsvps.map(&:id),
attendee_group: email_params[:attendee_group].to_i
)

unless @email.valid?
@email = EventEmailPresenter.new(@email)
@past_emails = PastEventEmailsPresenter.new(@event)
flash[:alert] = "We were unable to send your email."
return render :new
end

def create
role_ids = if email_params[:attendee_group] == 'All'
Role.attendee_role_ids
else
email_params[:attendee_group]
end
recipient_rsvps = @event.rsvps.where(role_id: role_ids).includes(:user)
EventMailer.from_organizer(
sender: current_user,
recipients: recipient_rsvps.map { |rsvp| rsvp.user.email },
cc: cc_recipients,
subject: email_params[:subject],
body: email_params[:body],
event: @event
).deliver_now

unless email_params[:include_waitlisted]
recipient_rsvps = recipient_rsvps.confirmed
end
@email.save!

if email_params[:only_checked_in]
recipient_rsvps = recipient_rsvps.where('checkins_count > 0')
redirect_to event_organizer_tools_path(@event), notice: <<-EOT
Your email has been sent. Woo!
EOT
end

if email_params[:cc_organizers]
cc_recipients = @event.organizers.map(&:email) - [current_user.email]
def show
@email = @event.event_emails.find(params[:id])
end

@email = @event.event_emails.build(
subject: email_params[:subject],
body: email_params[:body],
sender: current_user,
recipient_rsvp_ids: recipient_rsvps.map(&:id),
attendee_group: email_params[:attendee_group].to_i,
include_waitlisted: email_params[:include_waitlisted]
)
private

unless @email.valid?
assign_ivars
return render :new
def email_params
params.require(:event_email)
end

EventMailer.from_organizer(
sender: current_user,
recipients: recipient_rsvps.map { |rsvp| rsvp.user.email } + [current_user.email],
cc: cc_recipients,
subject: email_params[:subject],
body: email_params[:body],
event: @event
).deliver_now

@email.save!

redirect_to event_organizer_tools_path(@event), notice: <<-EOT
Your mail has been sent. Woo!
You will also get a copy of the email in your inbox to prove that email sending is working.
EOT
end

def show
@email = @event.event_emails.find(params[:id])
end

private

def email_params
params.require(:event_email)
end

def find_event
@event = Event.find_by_id(params[:event_id])
end

def assign_ivars
@rsvps = @event.rsvps.where(role_id: Role.attendee_role_ids).includes(:user)
@emails = @event.event_emails.order(:created_at)

@email_recipients = {}
@emails.each do |email|
@email_recipients[email.id] = {
total: email.recipient_rsvps.length,
volunteers: 0,
students: 0
}
email.recipient_rsvps.each do |rsvp|
if rsvp.role_volunteer?
@email_recipients[email.id][:volunteers] += 1
end
if rsvp.role_student?
@email_recipients[email.id][:students] += 1
end
end
def find_event
@event = Event.find_by_id(params[:event_id])
end
end
end
1 change: 0 additions & 1 deletion app/models/event_email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ class EventEmail < ActiveRecord::Base
belongs_to :sender, class_name: 'User'

has_many :event_email_recipients, dependent: :destroy

has_many :recipient_rsvps, through: :event_email_recipients
has_many :recipients, through: :recipient_rsvps, source: :user, source_type: 'User'

Expand Down
31 changes: 31 additions & 0 deletions app/presenters/event_email_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class EventEmailPresenter
attr_reader :event_email

delegate :model_name, :to_key, :to_model, :persisted?,
:errors, :attendee_group, :include_waitlisted, :only_checked_in,
:cc_organizers, :subject, :body, to: :event_email

def initialize(event_email)
@event_email = event_email
end

def rsvps
@rsvps ||= event.rsvps.where(role_id: Role.attendee_role_ids).includes(:user)
end

def volunteers_rsvps
rsvps.where(role_id: Role::VOLUNTEER.id)
end

def students_accepted_rsvps
rsvps.where(role_id: Role::STUDENT.id).confirmed
end

def students_waitlisted_rsvps
rsvps.where(role_id: Role::STUDENT.id).where.not("waitlist_position IS NULL")
end

def event
event_email.event
end
end
Loading

0 comments on commit 29340ed

Please sign in to comment.