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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f40107fc2cf80fe072926e73ff051505f8c6aa2631a7ff4f4aa780d4ca65536
4
- data.tar.gz: b233ea4f63e164baafccdef3a4d924bc3d9b0f896f84d64ad80c25ffae47c6ea
3
+ metadata.gz: 42bb8fbade8f26eef5e53334f66e8ca79e763df6e69759dd0bc52cf7fd6308ad
4
+ data.tar.gz: 197276de127e1213e5741734a19cfeae498aed81016dc6eb907a454102ebd34b
5
5
  SHA512:
6
- metadata.gz: 503a9996dce1f5ac31061896b6c941f5be460af220f7467f2e23d171178d188f3e61cad9e3938c9169c7b9aeabb99f62bb73eb2907e62837c99da4a7199eeb75
7
- data.tar.gz: 6fc6587f1559941d51179bb41caed46cfd7883709d3d46c3671ac425485513203b164fd640a0d2ee6315d7b40c877aba462050b2aae8b0467420b357d8202299
6
+ metadata.gz: 302c4d953876425e55b177e234ccbdbdf1a496825a06f92b87899f74208ef8eec7575c749144cd8629b9e6bf45859567c85e499da6ea2f443ec4104deeb8933e
7
+ data.tar.gz: a1dd14af8ad7fd29a116a8c4ad778b80591073718cba15608a4b0fc2550f7af48d133a256e5e2aab83d3b7bb81302642dbcbaabb62103a1027be3d4df8731c91
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
14
  rdoc.rdoc_files.include('lib/**/*.rb')
15
15
  end
16
16
 
17
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
17
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
18
18
  load 'rails/tasks/engine.rake'
19
19
 
20
20
  load 'rails/tasks/statistics.rake'
@@ -16,4 +16,5 @@ $(document).ready(function() {
16
16
  setupDataTables();
17
17
  setupHighcharts();
18
18
  $('[data-toggle="popover"]').popover();
19
+ $('[data-message-live-preview="textarea"]').messageLivePreview();
19
20
  });
@@ -3,6 +3,7 @@
3
3
  $.extend( $.fn.dataTable.defaults, {
4
4
  processing : true,
5
5
  serverSide : true,
6
+ scrollX : false,
6
7
  ajax : {
7
8
  url : $(this).data('source'),
8
9
  type : "POST"
@@ -0,0 +1,19 @@
1
+ // Source: https://davidwalsh.name/javascript-debounce-function
2
+ // Returns a function, that, as long as it continues to be invoked, will not
3
+ // be triggered. The function will be called after it stops being called for
4
+ // N milliseconds. If `immediate` is passed, trigger the function on the
5
+ // leading edge, instead of the trailing.
6
+ function debounce(func, wait, immediate) {
7
+ var timeout;
8
+ return function() {
9
+ var context = this, args = arguments;
10
+ var later = function() {
11
+ timeout = null;
12
+ if (!immediate) func.apply(context, args);
13
+ };
14
+ var callNow = immediate && !timeout;
15
+ clearTimeout(timeout);
16
+ timeout = setTimeout(later, wait);
17
+ if (callNow) func.apply(context, args);
18
+ };
19
+ };
@@ -0,0 +1,34 @@
1
+ $(document).ready(function() {
2
+ var disable = function($element) {
3
+ $element.hide();
4
+ $element.find('input').prop('disabled', true);
5
+ };
6
+ var enable = function($element) {
7
+ $element.show();
8
+ $element.find('input').prop('disabled', false);
9
+ };
10
+
11
+ var updateMessageForm = function() {
12
+ var $type = $('[name="message[type]"]');
13
+ if (typeof $type === 'undefined') {
14
+ return;
15
+ }
16
+
17
+ $recipients = $('.message_recipients');
18
+ $trigger = $('.message_trigger');
19
+
20
+ var type = $type.val();
21
+ if (type === "automated") {
22
+ disable($recipients);
23
+ enable($trigger);
24
+ } else {
25
+ disable($trigger);
26
+ enable($recipients);
27
+ }
28
+ };
29
+
30
+ updateMessageForm();
31
+ $('[name="message[type]"]').on('change', function() {
32
+ updateMessageForm();
33
+ });
34
+ });
@@ -0,0 +1,16 @@
1
+ $.fn.messageLivePreview = function() {
2
+
3
+ var updateLivePreview = function() {
4
+ var textarea = $('[data-message-live-preview="textarea"]');
5
+ var iframe = $('[data-message-live-preview="iframe"]');
6
+ var baseSrc = iframe.data('message-live-preview-base-src') ;
7
+ var newSrc = baseSrc + '?body=' + encodeURIComponent(textarea.val());
8
+ console.log(newSrc);
9
+ iframe.attr('src', newSrc);
10
+ };
11
+
12
+ var debouncedUpdateLivePreview = debounce(updateLivePreview, 1000);
13
+
14
+ $('[data-message-live-preview="textarea"]').on('change', debouncedUpdateLivePreview);
15
+ $('[data-message-live-preview="textarea"]').on('input', debouncedUpdateLivePreview);
16
+ };
@@ -2,11 +2,10 @@ var setupDataTables = function() {
2
2
 
3
3
  window.questionnairesDataTable = $('.datatable.questionnaires').DataTable({
4
4
  order : [3, 'desc'],
5
- scrollX : false,
6
5
  columns : [
7
- { orderable : false, data: 'bulk' },
8
- { orderable : false, data: 'link' },
9
- { orderable : false, data: 'note' },
6
+ { orderable: false, data: 'bulk' },
7
+ { orderable: false, data: 'link' },
8
+ { orderable: false, data: 'note' },
10
9
  { orderable: true, data: 'id', visible: false },
11
10
  { orderable: true, data: 'first_name' },
12
11
  { orderable: true, data: 'last_name' },
@@ -17,13 +16,14 @@ var setupDataTables = function() {
17
16
  { orderable: true, data: 'acc_status' },
18
17
  { orderable: true, data: 'checked_in' },
19
18
  { orderable: true, data: 'school' },
20
- { orderable: true, data: 'created_at' }
19
+ { orderable: true, data: 'created_at' },
20
+ { orderable: true, data: 'dietary_restrictions', visible: false },
21
+ { orderable: true, data: 'special_needs', visible: false },
21
22
  ]
22
23
  });
23
24
 
24
25
  $('.datatable.users').DataTable({
25
26
  order : [1, 'asc'],
26
- scrollX : false,
27
27
  columns : [
28
28
  { orderable: true, data: 'id', visible: false },
29
29
  { orderable: true, data: 'email' },
@@ -31,22 +31,21 @@ var setupDataTables = function() {
31
31
  ]
32
32
  });
33
33
 
34
- $('.datatable.messages').DataTable({
35
- order : [0, 'desc'],
36
- scrollX : false,
34
+ $('.datatable.bulk-messages').DataTable({
35
+ order : [4, 'desc'],
37
36
  columns : [
38
37
  { orderable: true, data: 'id', visible: false},
39
38
  { orderable: true, data: 'name'},
40
39
  { orderable: true, data: 'subject'},
41
- { orderable: true, data: 'trigger'},
42
40
  { orderable: false, data: 'status'},
41
+ { orderable: true, data: 'created_at'},
42
+ { orderable: true, data: 'updated_at', visible: false},
43
43
  { orderable: true, data: 'delivered_at'}
44
44
  ]
45
45
  });
46
46
 
47
47
  $('.datatable.schools').DataTable({
48
48
  order : [4, 'desc'],
49
- scrollX : false,
50
49
  columns : [
51
50
  { orderable: true, data: 'id', visible: false },
52
51
  { orderable: true, data: 'name' },
@@ -58,7 +57,6 @@ var setupDataTables = function() {
58
57
  });
59
58
 
60
59
  $('.datatable.stats').DataTable({
61
- scrollX : false,
62
60
  processing : false,
63
61
  serverSide : false
64
62
  });
@@ -35,6 +35,9 @@ body {
35
35
  .sidebar .nav-link {
36
36
  font-weight: 500;
37
37
  color: #333;
38
+ overflow: hidden;
39
+ white-space: nowrap;
40
+ text-overflow: ellipsis;
38
41
  }
39
42
 
40
43
  .sidebar .nav-link .fa {
@@ -62,6 +65,7 @@ body {
62
65
  margin-left: 26px;
63
66
  color: #999;
64
67
  line-height: 1.2;
68
+ white-space: normal;
65
69
  }
66
70
 
67
71
  /*
@@ -82,6 +86,8 @@ body {
82
86
  font-size: 1rem;
83
87
  background-color: rgba(0, 0, 0, .25);
84
88
  box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
89
+ text-overflow: ellipsis;
90
+ overflow: hidden;
85
91
  }
86
92
 
87
93
  .navbar .form-control {
@@ -1,6 +1,6 @@
1
1
  class Manage::ApplicationController < ApplicationController
2
2
  before_action :logged_in
3
- before_action :limit_admin_access, only: ["edit", "update", "new", "create", "destroy", "convert_to_admin", "deliver", "merge", "perform_merge", "toggle_bus_captain", "duplicate", "update_acc_status", "send_update_email"]
3
+ before_action :limit_admin_access, only: ["edit", "update", "new", "create", "destroy", "convert_to_admin", "deliver", "merge", "perform_merge", "toggle_bus_captain", "duplicate", "update_acc_status", "send_update_email", "live_preview"]
4
4
 
5
5
  def logged_in
6
6
  authenticate_user!
@@ -8,7 +8,7 @@ class Manage::MessagesController < Manage::ApplicationController
8
8
  end
9
9
 
10
10
  def datatable
11
- render json: MessageDatatable.new(view_context)
11
+ render json: BulkMessageDatatable.new(view_context)
12
12
  end
13
13
 
14
14
  def show
@@ -40,6 +40,10 @@ class Manage::MessagesController < Manage::ApplicationController
40
40
  end
41
41
 
42
42
  def deliver
43
+ if @message.automated?
44
+ flash[:notice] = "Automated messages cannot be manually delivered. Only bulk messages can."
45
+ return redirect_to manage_message_path(@message)
46
+ end
43
47
  if @message.status != "drafted"
44
48
  flash[:notice] = "Message cannot be re-delivered"
45
49
  return redirect_to manage_messages_path
@@ -55,6 +59,13 @@ class Manage::MessagesController < Manage::ApplicationController
55
59
  render html: email.body.raw_source.html_safe
56
60
  end
57
61
 
62
+ def live_preview
63
+ body = params[:body] || ''
64
+ message = Message.new(body: body)
65
+ email = Mailer.bulk_message_email(nil, current_user.id, message)
66
+ render html: email.body.raw_source.html_safe
67
+ end
68
+
58
69
  def duplicate
59
70
  new_message = @message.dup
60
71
  new_message.update_attributes(
@@ -71,7 +82,7 @@ class Manage::MessagesController < Manage::ApplicationController
71
82
 
72
83
  def message_params
73
84
  params.require(:message).permit(
74
- :name, :subject, :template, :body, :trigger, recipients: []
85
+ :type, :name, :subject, :template, :body, :trigger, recipients: []
75
86
  )
76
87
  end
77
88
 
@@ -165,10 +165,6 @@ class Manage::QuestionnairesController < Manage::ApplicationController
165
165
  end
166
166
 
167
167
  def process_acc_status_notifications(questionnaire, new_status)
168
- Mailer.delay.accepted_email(questionnaire.id) if new_status == "accepted"
169
- Mailer.delay.rsvp_confirmation_email(questionnaire.id) if new_status == "rsvp_confirmed"
170
- Mailer.delay.denied_email(questionnaire.id) if new_status == "denied"
171
-
172
168
  questionnaire.invite_to_slack if ENV['INVITE_TO_SLACK_UPON_RSVP'] == 'true' && new_status == 'rsvp_confirmed'
173
169
  end
174
170
  end
@@ -70,7 +70,6 @@ class QuestionnairesController < ApplicationController
70
70
  if @questionnaire.save
71
71
  current_user.questionnaire = @questionnaire
72
72
  @questionnaire.update_attribute(:acc_status, default_acc_status)
73
- Mailer.delay.application_confirmation_email(@questionnaire.id)
74
73
  format.html { redirect_to questionnaires_path }
75
74
  format.json { render json: @questionnaire, status: :created, location: @questionnaire }
76
75
  else
@@ -22,7 +22,6 @@ class RsvpsController < ApplicationController
22
22
  if @questionnaire.save
23
23
  flash[:notice] = "Thank you for confirming your attendance! "
24
24
  flash[:notice] += @questionnaire.eligible_for_a_bus? ? "See below for additional bus information." : "You're all set to attend."
25
- Mailer.delay.rsvp_confirmation_email(@questionnaire.id)
26
25
  else
27
26
  flash[:notice] = rsvp_error_notice
28
27
  end
@@ -72,16 +71,12 @@ class RsvpsController < ApplicationController
72
71
  @questionnaire.bus_captain_interest = params[:questionnaire][:bus_captain_interest]
73
72
  end
74
73
 
75
- acc_status_changed = @questionnaire.acc_status_changed?
76
-
77
74
  unless @questionnaire.save
78
75
  flash[:notice] = @questionnaire.errors.full_message.join(", ")
79
76
  redirect_to rsvp_path
80
77
  return
81
78
  end
82
79
 
83
- Mailer.delay.rsvp_confirmation_email(@questionnaire.id) if acc_status_changed && @questionnaire.acc_status == "rsvp_confirmed"
84
-
85
80
  flash[:notice] = "Your RSVP has been updated." if flash[:notice].blank?
86
81
 
87
82
  redirect_to rsvp_path
@@ -1,4 +1,4 @@
1
- class MessageDatatable < AjaxDatatablesRails::Base
1
+ class BulkMessageDatatable < AjaxDatatablesRails::Base
2
2
  def_delegators :@view, :link_to, :manage_message_path, :display_datetime
3
3
 
4
4
  def view_columns
@@ -6,7 +6,8 @@ class MessageDatatable < AjaxDatatablesRails::Base
6
6
  id: { source: "Message.id" },
7
7
  name: { source: "Message.name" },
8
8
  subject: { source: "Message.subject" },
9
- trigger: { source: "Message.trigger" },
9
+ created_at: { source: "Message.created_at", searchable: false },
10
+ updated_at: { source: "Message.updated_at", searchable: false },
10
11
  delivered_at: { source: "Message.delivered_at", searchable: false }
11
12
  }
12
13
  end
@@ -19,8 +20,9 @@ class MessageDatatable < AjaxDatatablesRails::Base
19
20
  id: record.id,
20
21
  name: link_to(record.name, manage_message_path(record)),
21
22
  subject: record.subject,
22
- trigger: Message::POSSIBLE_TRIGGERS[record.trigger],
23
23
  status: record.status.titleize,
24
+ created_at: display_datetime(record.created_at),
25
+ updated_at: record.updated_at.present? ? display_datetime(record.updated_at) : '',
24
26
  delivered_at: record.delivered_at.present? ? display_datetime(record.delivered_at) : ''
25
27
  }
26
28
  end
@@ -28,7 +30,7 @@ class MessageDatatable < AjaxDatatablesRails::Base
28
30
 
29
31
  # rubocop:disable Naming/AccessorMethodName
30
32
  def get_raw_records
31
- Message.unscoped
33
+ Message.where(type: 'bulk')
32
34
  end
33
35
  # rubocop:enable Naming/AccessorMethodName
34
36
  end
@@ -14,7 +14,9 @@ class QuestionnaireDatatable < AjaxDatatablesRails::Base
14
14
  acc_status: { source: 'Questionnaire.acc_status', searchable: true },
15
15
  checked_in: { source: 'Questionnaire.checked_in_at', searchable: false },
16
16
  school: { source: 'School.name' },
17
- created_at: { source: 'Questionnaire.created_at', searchable: false }
17
+ created_at: { source: 'Questionnaire.created_at', searchable: false },
18
+ dietary_restrictions: { source: 'Questionnaire.dietary_restrictions', searchable: true },
19
+ special_needs: { source: 'Questionnaire.special_needs', searchable: true }
18
20
  }
19
21
  end
20
22
 
@@ -36,7 +38,9 @@ class QuestionnaireDatatable < AjaxDatatablesRails::Base
36
38
  acc_status: "<span class=\"text-#{acc_status_class(record.acc_status)}\">#{record.acc_status.titleize}</span>".html_safe,
37
39
  checked_in: record.checked_in? ? '<span class="text-success">Yes</span>'.html_safe : 'No',
38
40
  school: link_to(record.school.name, manage_school_path(record.school)),
39
- created_at: record.created_at.present? ? display_datetime(record.created_at) : ''
41
+ created_at: record.created_at.present? ? display_datetime(record.created_at) : '',
42
+ dietary_restrictions: record.dietary_restrictions,
43
+ special_needs: record.special_needs
40
44
  }
41
45
  end
42
46
  end
@@ -1,22 +1,5 @@
1
1
  if defined?(ActionMailer::Preview)
2
2
  class MailPreview < ActionMailer::Preview
3
- def application_confirmation_email
4
- questionnaire = Questionnaire.first
5
- Mailer.application_confirmation_email(questionnaire.id)
6
- end
7
-
8
- def rsvp_confirmation_email
9
- Mailer.rsvp_confirmation_email(Questionnaire.first.id)
10
- end
11
-
12
- def accepted_email
13
- Mailer.accepted_email(Questionnaire.first.id)
14
- end
15
-
16
- def denied_email
17
- Mailer.denied_email(Questionnaire.first.id)
18
- end
19
-
20
3
  def bulk_message_email
21
4
  message = Message.first
22
5
  Mailer.bulk_message_email(message, User.first.id)
@@ -4,32 +4,8 @@ class Mailer < ApplicationMailer
4
4
 
5
5
  default from: Rails.configuration.hackathon['email_from']
6
6
 
7
- def application_confirmation_email(questionnaire_id)
8
- @questionnaire = Questionnaire.find_by_id(questionnaire_id)
9
- return unless @questionnaire.present? && @questionnaire.user.present?
10
- mail_questionnaire("Application Received", transactional: true)
11
- end
12
-
13
- def rsvp_confirmation_email(questionnaire_id)
14
- @questionnaire = Questionnaire.find_by_id(questionnaire_id)
15
- return unless @questionnaire.present? && @questionnaire.user.present?
16
- mail_questionnaire("RSVP Confirmation", transactional: true)
17
- end
18
-
19
- def accepted_email(questionnaire_id)
20
- @questionnaire = Questionnaire.find_by_id(questionnaire_id)
21
- return unless @questionnaire.present? && @questionnaire.user.present?
22
- mail_questionnaire("You've been accepted!")
23
- end
24
-
25
- def denied_email(questionnaire_id)
26
- @questionnaire = Questionnaire.find_by_id(questionnaire_id)
27
- return unless @questionnaire.present? && @questionnaire.user.present?
28
- mail_questionnaire("Your application status")
29
- end
30
-
31
- def bulk_message_email(message_id, user_id)
32
- @message = Message.find_by_id(message_id)
7
+ def bulk_message_email(message_id, user_id, message = nil)
8
+ @message = message || Message.find_by_id(message_id)
33
9
  @user = User.find_by_id(user_id)
34
10
  return if @user.blank? || @message.blank?
35
11
  mail(
@@ -6,6 +6,8 @@ class Message < ApplicationRecord
6
6
 
7
7
  POSSIBLE_TEMPLATES = ["default"].freeze
8
8
 
9
+ POSSIBLE_TYPES = ["bulk", "automated"].freeze
10
+
9
11
  POSSIBLE_SIMPLE_RECIPIENTS = {
10
12
  "all" => "Everyone",
11
13
  "incomplete" => "Incomplete Applications",
@@ -34,6 +36,7 @@ class Message < ApplicationRecord
34
36
  serialize :recipients, Array
35
37
 
36
38
  validates_inclusion_of :template, in: POSSIBLE_TEMPLATES
39
+ validates_inclusion_of :type, in: POSSIBLE_TYPES
37
40
 
38
41
  def recipients=(values)
39
42
  values.present? ? super(values.reject(&:blank?)) : super(values)
@@ -52,6 +55,14 @@ class Message < ApplicationRecord
52
55
  labels
53
56
  end
54
57
 
58
+ def bulk?
59
+ type == "bulk"
60
+ end
61
+
62
+ def automated?
63
+ type == "automated"
64
+ end
65
+
55
66
  def delivered?
56
67
  delivered_at.present?
57
68
  end
@@ -65,6 +76,7 @@ class Message < ApplicationRecord
65
76
  end
66
77
 
67
78
  def status
79
+ return "automated" if automated?
68
80
  return "delivered" if delivered?
69
81
  return "started" if started?
70
82
  return "queued" if queued?
@@ -72,7 +84,7 @@ class Message < ApplicationRecord
72
84
  end
73
85
 
74
86
  def can_edit?
75
- status == "drafted" || trigger.present?
87
+ automated? || status == "drafted"
76
88
  end
77
89
 
78
90
  def can_queue?
@@ -127,4 +139,16 @@ class Message < ApplicationRecord
127
139
  messages_to_queue = Message.where(trigger: trigger)
128
140
  messages_to_queue.map { |message| Mailer.delay.bulk_message_email(message.id, user_id) }
129
141
  end
142
+
143
+ def self.bulk
144
+ where(type: 'bulk')
145
+ end
146
+
147
+ def self.automated
148
+ where(type: 'automated')
149
+ end
150
+
151
+ def self.inheritance_column
152
+ "class_type"
153
+ end
130
154
  end