hackathon_manager 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/app/assets/javascripts/hackathon_manager/manage/application.js +1 -0
  4. data/app/assets/javascripts/hackathon_manager/manage/lib/datatables.js +1 -0
  5. data/app/assets/javascripts/hackathon_manager/manage/lib/debounce.js +19 -0
  6. data/app/assets/javascripts/hackathon_manager/manage/lib/forms.js +34 -0
  7. data/app/assets/javascripts/hackathon_manager/manage/lib/messageLivePreview.js +16 -0
  8. data/app/assets/javascripts/hackathon_manager/manage/lib/setupDataTables.js +10 -12
  9. data/app/assets/stylesheets/hackathon_manager/dashboard.css +6 -0
  10. data/app/controllers/manage/application_controller.rb +1 -1
  11. data/app/controllers/manage/messages_controller.rb +13 -2
  12. data/app/controllers/manage/questionnaires_controller.rb +0 -4
  13. data/app/controllers/questionnaires_controller.rb +0 -1
  14. data/app/controllers/rsvps_controller.rb +0 -5
  15. data/app/datatables/{message_datatable.rb → bulk_message_datatable.rb} +6 -4
  16. data/app/datatables/questionnaire_datatable.rb +6 -2
  17. data/app/mailers/mail_preview.rb +0 -17
  18. data/app/mailers/mailer.rb +2 -26
  19. data/app/models/message.rb +25 -1
  20. data/app/views/application/_triggered_email_summary.html.haml +14 -7
  21. data/app/views/layouts/manage/application.html.haml +4 -4
  22. data/app/views/mailer/bulk_templates/_default.html.erb +379 -1
  23. data/app/views/manage/bus_lists/index.html.haml +2 -2
  24. data/app/views/manage/configs/show.html.haml +2 -2
  25. data/app/views/manage/dashboard/map_data.tsv.erb +7 -0
  26. data/app/views/manage/messages/_form.html.haml +13 -4
  27. data/app/views/manage/messages/index.html.haml +5 -4
  28. data/app/views/manage/messages/show.html.haml +24 -24
  29. data/app/views/manage/questionnaires/_overview.html.haml +12 -2
  30. data/app/views/manage/questionnaires/index.html.haml +3 -1
  31. data/config/locales/en.yml +4 -0
  32. data/config/routes.rb +1 -0
  33. data/db/migrate/20180801144544_add_type_to_messages.rb +17 -0
  34. data/{app/views/mailer/accepted_email.html.erb → db/seed_messages/questionnaire--accepted.md} +4 -2
  35. data/db/seed_messages/questionnaire--denied.md +16 -0
  36. data/db/seed_messages/questionnaire--pending.md +13 -0
  37. data/db/seed_messages/questionnaire--rsvp_confirmed.md +9 -0
  38. data/db/seeds.rb +41 -0
  39. data/lib/hackathon_manager.rb +2 -2
  40. data/lib/hackathon_manager/engine.rb +1 -1
  41. data/lib/hackathon_manager/version.rb +1 -1
  42. data/test/factories/bus_list.rb +2 -2
  43. data/test/factories/fips.rb +3 -3
  44. data/test/factories/message.rb +7 -6
  45. data/test/factories/questionnaire.rb +18 -18
  46. data/test/factories/school.rb +4 -4
  47. data/test/factories/users.rb +5 -5
  48. metadata +11 -7
  49. data/app/views/mailer/application_confirmation_email.html.erb +0 -10
  50. data/app/views/mailer/denied_email.html.erb +0 -11
  51. data/app/views/mailer/rsvp_confirmation_email.html.erb +0 -5
@@ -1,7 +1,7 @@
1
1
  = render "layouts/manage/page_title", title: "Hackathon Configuration"
2
2
 
3
3
  .row
4
- .col-lg-6
4
+ .col-lg-6.mb-3
5
5
  .card
6
6
  .card-body
7
7
  %h5.card-title Hackathon Manager Config
@@ -15,7 +15,7 @@
15
15
  %b #{key}:
16
16
  %pre= Rails.configuration.hackathon[key]
17
17
 
18
- .col-lg-6
18
+ .col-lg-6.mb-3
19
19
  .card
20
20
  .card-body
21
21
  %h5.card-title Environment Variables
@@ -3,6 +3,8 @@
3
3
  <%
4
4
  counties = {}
5
5
  amount = 1
6
+ redo_count = 0
7
+ redo_limit = 10
6
8
  @schools.each do |school|
7
9
  if school.fips_code.blank?
8
10
  next if school.city.blank? || school.state.blank?
@@ -10,8 +12,13 @@ amount = 1
10
12
  resp = HTTParty.get("https://maps.googleapis.com/maps/api/geocode/json?address=#{CGI.escape(school.city)}+#{CGI.escape(school.state)}&sensor=true")
11
13
  results = resp.parsed_response["results"][0]
12
14
  if results.blank?
15
+ if redo_count >= redo_limit
16
+ raise 'Exceeded maximum number of retries: No results from Google Maps API.'
17
+ end
18
+ redo_count += 1
13
19
  redo
14
20
  end
21
+ redo_count = 0
15
22
 
16
23
  lat = results["geometry"]["location"]["lat"]
17
24
  lng = results["geometry"]["location"]["lng"]
@@ -3,15 +3,24 @@
3
3
  = f.error_notification
4
4
 
5
5
  .form-inputs
6
+ = f.input :type, as: :select, collection: Message::POSSIBLE_TYPES.map { |x| [x.titleize, x] }, include_blank: false
7
+ = f.input :trigger, as: :select, collection: Message::POSSIBLE_TRIGGERS.invert, include_blank: "(disabled)"
6
8
  = f.input :name
7
9
  = f.input :subject, hint: "All emails are from <pre class=\"d-inline\">#{html_escape(Rails.configuration.hackathon['email_from'])}</pre>".html_safe
8
10
  = f.input :template, as: :select, collection: Message::POSSIBLE_TEMPLATES.map { |x| [x.titleize, x] }, include_blank: false
9
11
  - if @message.status == "drafted"
10
- = f.input :recipients, as: :select, collection: @message.possible_recipients, input_html: { class: "selectize", multiple: true, }, hint: "Sent manually, in bulk", placeholder: "Type to search by school or type..."
12
+ = f.input :recipients, as: :select, collection: @message.possible_recipients, input_html: { class: "selectize", multiple: true, }, placeholder: "Type to search by school or type..."
11
13
  - else
12
14
  = f.input :recipients, as: :select, collection: @message.possible_recipients, input_html: { class: "selectize", multiple: true }, hint: "Cannot be edited once a message has been delivered", disabled: true
13
- = f.input :trigger, as: :select, collection: Message::POSSIBLE_TRIGGERS.invert, include_blank: "(no automatic trigger)", hint: "Sent automatically, to an individual applicant"
14
- = f.input :body, input_html: { rows: 10 }, hint: "Supports markdown and HTML"
15
+ %br
15
16
 
16
- .form-actions
17
+ .row
18
+ .col-lg-6.mb-3
19
+ %h5.mb-3 Body
20
+ = f.input :body, input_html: { rows: 23, 'data-message-live-preview' => 'textarea', class: 'text-monospace', style: 'font-size: 14px' }, hint: "Supports markdown and HTML", label: false, wrapper: :bootstrap_inline_form
21
+ .col-lg-6.mb-3
22
+ %h5.mb-3 Preview
23
+ %iframe.email-preview{src: live_preview_manage_messages_path(body: @message.body), 'data-message-live-preview' => 'iframe', 'data-message-live-preview-base-src' => live_preview_manage_messages_path}
24
+
25
+ .form-actions.mb-3
17
26
  = f.button :submit, class: 'btn-primary'
@@ -2,15 +2,16 @@
2
2
  = link_to "New Message", new_manage_message_path, class: "btn btn-sm btn-outline-secondary"
3
3
 
4
4
  %div
5
- %table.messages.datatable.table.table-striped.table-hover{ "data-source" => datatable_manage_messages_path(format: :json) }
5
+ %table.bulk-messages.datatable.table.table-striped.table-hover{ "data-source" => datatable_manage_messages_path(format: :json) }
6
6
  %thead
7
7
  %tr
8
8
  %th ID
9
9
  %th Name
10
10
  %th Subject
11
- %th Trigger
12
- %th Bulk Status
13
- %th Bulk Delivered
11
+ %th Status
12
+ %th Created
13
+ %th Last modified
14
+ %th Delivered
14
15
  %tbody
15
16
 
16
17
  %hr
@@ -28,29 +28,29 @@
28
28
  %dt.col-md-3 Template
29
29
  %dd.col-md-9= @message.template.titleize
30
30
  .col-lg-6
31
- %p.mb-1
32
- %b Recipients
33
- %small
34
- %em Sent manually, in bulk
35
- %ul.pl-4.mb-1
36
- - @message.recipients_list.each do |recipient|
37
- %li= recipient
38
- - if @message.recipients.blank?
39
- %p.text-muted (none)
40
- - if @message.recipients.present?
41
- %p.small
42
- - if recipient_error.present?
43
- %strong{style: 'color: red;'} Error parsing recipients:
44
- = recipient_error
45
- %br To resolve this, edit & remove the offending recipient.
46
- - else
47
- %em #{recipient_count} currently match this query
48
- %p.mb-1
49
- %b Trigger
50
- %small
51
- %em Sent automatically, to an applicant once they match this criteria
52
- %p
53
- = Message::POSSIBLE_TRIGGERS[@message.trigger] || "<span class=\"text-muted\">(no automatic trigger)</span>".html_safe
31
+ - if @message.bulk?
32
+ %p.mb-1
33
+ %b Recipients
34
+ %ul.pl-4.mb-1
35
+ - @message.recipients_list.each do |recipient|
36
+ %li= recipient
37
+ - if @message.recipients.blank?
38
+ %p.text-muted (none)
39
+ - if @message.recipients.present?
40
+ %p.small
41
+ - if recipient_error.present?
42
+ %strong{style: 'color: red;'} Error parsing recipients:
43
+ = recipient_error
44
+ %br To resolve this, edit & remove the offending recipient.
45
+ - else
46
+ %em #{recipient_count} currently match this query
47
+ - if @message.automated?
48
+ %p.mb-1
49
+ %b Trigger
50
+ %small
51
+ %em Sent automatically to an applicant upon meeting this criteria
52
+ %p
53
+ = Message::POSSIBLE_TRIGGERS[@message.trigger] || "<span class=\"text-muted\">(disabled)</span>".html_safe
54
54
 
55
55
  %div
56
56
  %p
@@ -61,7 +61,7 @@
61
61
 
62
62
  %iframe.email-preview{src: preview_manage_message_path(@message)}
63
63
 
64
- - if @message.recipients_list.present?
64
+ - if @message.bulk?
65
65
  .card.mt-3.mb-3
66
66
  .card-body
67
67
  %h5.card-title Bulk delivery details
@@ -82,9 +82,19 @@
82
82
  .card-body
83
83
  .row
84
84
  %dt.col-md-4 Dietary restrictions
85
- %dd.col-md-8= @questionnaire.dietary_restrictions || "<span class=\"text-muted\">(none)</span>".html_safe
85
+ %dd.col-md-8
86
+ - if @questionnaire.dietary_restrictions.present?
87
+ %span.fa.fa-exclamation-triangle.text-warning.icon-space-r-half
88
+ = @questionnaire.dietary_restrictions
89
+ - else
90
+ %span.text-muted (none)
86
91
  %dt.col-md-4 Special needs
87
- %dd.col-md-8= @questionnaire.special_needs || "<span class=\"text-muted\">(none)</span>".html_safe
92
+ %dd.col-md-8
93
+ - if @questionnaire.special_needs.present?
94
+ %span.fa.fa-exclamation-triangle.text-warning.icon-space-r-half
95
+ = @questionnaire.special_needs
96
+ - else
97
+ %span.text-muted (none)
88
98
  %dt.col-md-4 Traveling from
89
99
  %dd.col-md-8
90
100
  = @questionnaire.travel_not_from_school ? "Somewhere else (#{@questionnaire.travel_location})" : "<span class=\"text-muted\">My school</span>".html_safe
@@ -23,11 +23,13 @@
23
23
  %th Email
24
24
  %th Phone
25
25
  %th Gender
26
- %th Date of Birth
26
+ %th Date of birth
27
27
  %th Status
28
28
  %th Checked In
29
29
  %th School
30
30
  %th Applied on
31
+ %th Dietary restrictions
32
+ %th Special needs
31
33
  %tbody
32
34
 
33
35
  .row
@@ -40,6 +40,10 @@ en:
40
40
  notes: Notes are shared with applicants. Supports Markdown and HTML.
41
41
  user:
42
42
  admin_limited_access: Limited access prevents the admin from adding, modifying, or deleting any records. Modifications through the check-in process are allowed.
43
+ message:
44
+ type: Bulk emails are sent once, manually. Automated emails are sent upon a desired trigger/event.
45
+ name: A friendly name to recognize this email. Applicants won't see this.
46
+ trigger: Sent automatically when a new or updated applicant matches this criteria. Does not send to anyone already matching this criteria.
43
47
  placeholders:
44
48
  bus_list:
45
49
  notes: |
data/config/routes.rb CHANGED
@@ -53,6 +53,7 @@ Rails.application.routes.draw do
53
53
  end
54
54
  resources :messages do
55
55
  get :preview, on: :member
56
+ get :live_preview, on: :collection
56
57
  post :datatable, on: :collection
57
58
  patch :deliver, on: :member
58
59
  patch :duplicate, on: :member
@@ -0,0 +1,17 @@
1
+ class AddTypeToMessages < ActiveRecord::Migration[5.1]
2
+ def up
3
+ add_column :messages, :type, :string
4
+
5
+ Message.all.each do |message|
6
+ if message.trigger.present?
7
+ message.update_attribute(:type, 'automated')
8
+ else
9
+ message.update_attribute(:type, 'bulk')
10
+ end
11
+ end
12
+ end
13
+
14
+ def down
15
+ remove_column :messages, :type, :string
16
+ end
17
+ end
@@ -1,5 +1,7 @@
1
- <h2>Congratulations <%= @questionnaire.first_name %>, you're in!</h2>
2
- <p>You have been accepted to attend <%= Rails.configuration.hackathon['name'] %>! Please RSVP:</p>
1
+ ## Congratulations <%= @questionnaire.first_name %>, you're in!
2
+
3
+ You have been accepted to attend <%= Rails.configuration.hackathon['name'] %>! **Please RSVP:**
4
+
3
5
  <p>
4
6
  <a href="https://brickhack.io/rsvp/accept" class="button" target="_blank">Yes, I will Attend &raquo;</a>
5
7
  <a href="https://brickhack.io/rsvp/deny" class="button" target="_blank">No, I Can't Attend &raquo;</a>
@@ -0,0 +1,16 @@
1
+ <!--
2
+
3
+ IMPLEMENT THIS!
4
+
5
+ You'll probably want something along the lines of:
6
+
7
+ ### Dear @questionnaire.first_name,
8
+
9
+ It is with our sincerest regret to inform you that our admissions committee has chosen to not accept your application to BrickHack at this time. We were overjoyed with the number of applicants we received, but unfortunately we can not accept everyone due to capacity for only 400.
10
+
11
+ We invite you to apply again next year. There are plenty of other hackathons this season, and it may not be too late to apply for those. Checkout <a href="https://mlh.io" target="_blank">https://mlh.io</a> to find out more information.
12
+
13
+ Thank you for applying,<br>
14
+ - The BrickHack Team
15
+
16
+ -->
@@ -0,0 +1,13 @@
1
+ <h2 class="center">
2
+ Thanks for Applying!
3
+ </h2>
4
+
5
+ Hey <%= @questionnaire.first_name %>,
6
+
7
+ We've received your application to <%= Rails.configuration.hackathon['name'] %>!
8
+
9
+ If needed, you can edit your information by clicking the button below.
10
+
11
+ <%= link_to questionnaires_url, class: 'button' do %>
12
+ My Profile
13
+ <% end %>
@@ -0,0 +1,9 @@
1
+ ## You are confirmed!
2
+
3
+ This is your confirmation for <%= Rails.configuration.hackathon['name'] %>! We can't wait to see you there. In the meantime, follow us on [Facebook](https://www.facebook.com/brickhackrit) and [Twitter](https://twitter.com/brickhackrit) to get news and updates.
4
+
5
+ **If you can no longer attend, please let us know so we can open the spot to someone else. [I Can No Longer Attend &raquo;](https://brickhack.io/rsvp/deny)**
6
+
7
+ ### Meet Other Hackers
8
+
9
+ Join our [Facebook Group](https://www.facebook.com/groups/brickhack3attendees/) to get in touch with other attendees. This will be the best way to find teammates if you don't have a team already!
data/db/seeds.rb CHANGED
@@ -8,6 +8,45 @@
8
8
 
9
9
  require 'csv'
10
10
 
11
+ puts "Seeding messages..."
12
+
13
+ subject_mapping = {
14
+ "questionnaire.pending" => "Application Received",
15
+ "questionnaire.accepted" => "You've been accepted!",
16
+ "questionnaire.denied" => "Your application status",
17
+ "questionnaire.rsvp_confirmed" => "RSVP Confirmation"
18
+ }
19
+
20
+ name_mapping = {
21
+ "questionnaire.pending" => "Application Received",
22
+ "questionnaire.accepted" => "Accepted email",
23
+ "questionnaire.denied" => "Denied email",
24
+ "questionnaire.rsvp_confirmed" => "RSVP confirmed email"
25
+ }
26
+
27
+ path = File.join(File.dirname(__FILE__), 'seed_messages/*.md')
28
+ files = FileList.glob(path)
29
+ files.each do |filepath|
30
+ filename = File.basename(filepath)
31
+ trigger = filename.sub('.md', '').sub('--', '.')
32
+
33
+ if Message.automated.where(trigger: trigger).count >= 1
34
+ puts "Skipping seed for automated message #{trigger}: existing automated email found"
35
+ next
36
+ end
37
+
38
+ body = File.read(filepath)
39
+ Message.create(
40
+ type: 'automated',
41
+ name: name_mapping[trigger],
42
+ subject: subject_mapping[trigger],
43
+ trigger: trigger,
44
+ body: body
45
+ )
46
+
47
+ puts "Seeded automated message for #{trigger}"
48
+ end
49
+
11
50
  puts "Seeding school list..."
12
51
 
13
52
  csv_file = File.join(File.dirname(__FILE__), 'schools.csv')
@@ -16,3 +55,5 @@ csv = CSV.parse(csv_text, headers: true)
16
55
  csv.each do |row|
17
56
  School.create(row.to_hash)
18
57
  end
58
+
59
+ puts "Done"
@@ -5,9 +5,9 @@ module HackathonManager
5
5
  hackathon = app.config_for(:hackathon)
6
6
 
7
7
  # Applications without a specified config.time_zone will parse
8
- # this as a string instead of a DateTime
8
+ # this as a string instead of a Time
9
9
  if hackathon['last_day_to_apply'].is_a?(String)
10
- hackathon['last_day_to_apply'] = DateTime.parse(hackathon['last_day_to_apply'])
10
+ hackathon['last_day_to_apply'] = Time.parse(hackathon['last_day_to_apply'])
11
11
  end
12
12
 
13
13
  app.config.hackathon = hackathon
@@ -33,7 +33,7 @@ module HackathonManager
33
33
  end
34
34
 
35
35
  initializer 'hackathon_manager.factories', after: 'factory_bot.set_factory_paths' do
36
- FactoryBot.definition_file_paths << File.expand_path('../../../test/factories', __FILE__) if defined?(FactoryBot)
36
+ FactoryBot.definition_file_paths << File.expand_path('../../test/factories', __dir__) if defined?(FactoryBot)
37
37
  end
38
38
 
39
39
  ActionController::Base.class_eval do
@@ -1,3 +1,3 @@
1
1
  module HackathonManager
2
- VERSION = '0.7.1'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
@@ -4,7 +4,7 @@ FactoryBot.define do
4
4
  sequence :name do |n|
5
5
  "Bus List #{n}"
6
6
  end
7
- capacity 50
8
- notes "Notes!"
7
+ capacity { 50 }
8
+ notes { "Notes!" }
9
9
  end
10
10
  end
@@ -1,7 +1,7 @@
1
1
  FactoryBot.define do
2
2
  factory :fips do
3
- fips_code "36055"
4
- city "Rochester"
5
- state "NY"
3
+ fips_code { "36055" }
4
+ city { "Rochester" }
5
+ state { "NY" }
6
6
  end
7
7
  end
@@ -1,10 +1,11 @@
1
1
  FactoryBot.define do
2
2
  factory :message do
3
- name "Message Name"
4
- subject "Message Subject"
5
- recipients ["all"]
6
- body "Hello world!"
7
- queued_at nil
8
- delivered_at nil
3
+ type { "bulk" }
4
+ name { "Message Name" }
5
+ subject { "Message Subject" }
6
+ recipients { ["all"] }
7
+ body { "Hello world!" }
8
+ queued_at { nil }
9
+ delivered_at { nil }
9
10
  end
10
11
  end
@@ -1,24 +1,24 @@
1
1
  FactoryBot.define do
2
2
  factory :questionnaire do
3
- first_name "John"
4
- last_name "Doe"
5
- phone "(123) 456-7890"
6
- international false
7
- date_of_birth Date.today - 20.years
8
- experience "first"
9
- interest "design"
3
+ first_name { "John" }
4
+ last_name { "Doe" }
5
+ phone { "(123) 456-7890" }
6
+ international { false }
7
+ date_of_birth { Date.today - 20.years }
8
+ experience { "first" }
9
+ interest { "design" }
10
10
  school_id { create(:school).id }
11
- shirt_size "Unisex - M"
12
- dietary_restrictions ""
13
- special_needs ""
14
- agreement_accepted true
15
- code_of_conduct_accepted true
16
- data_sharing_accepted true
17
- can_share_info true
18
- gender "Male"
19
- major "Computer Science"
20
- level_of_study "University (Undergraduate)"
21
- why_attend "This sounds cool"
11
+ shirt_size { "Unisex - M" }
12
+ dietary_restrictions { "" }
13
+ special_needs { "" }
14
+ agreement_accepted { true }
15
+ code_of_conduct_accepted { true }
16
+ data_sharing_accepted { true }
17
+ can_share_info { true }
18
+ gender { "Male" }
19
+ major { "Computer Science" }
20
+ level_of_study { "University (Undergraduate)" }
21
+ why_attend { "This sounds cool" }
22
22
 
23
23
  association :user
24
24
  end