effective_memberships 0.4.9 → 0.4.10

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/effective_memberships/applicant_experiences.js +0 -5
  3. data/app/controllers/admin/applicant_endorsements_controller.rb +9 -0
  4. data/app/controllers/effective/applicant_endorsements_controller.rb +48 -0
  5. data/app/controllers/effective/applicant_references_controller.rb +1 -1
  6. data/app/datatables/admin/effective_applicant_endorsements_datatable.rb +35 -0
  7. data/app/datatables/effective_applicant_endorsements_datatable.rb +37 -0
  8. data/app/datatables/effective_applicant_references_datatable.rb +1 -1
  9. data/app/mailers/effective/memberships_mailer.rb +25 -0
  10. data/app/models/concerns/effective_memberships_applicant.rb +34 -6
  11. data/app/models/concerns/effective_memberships_fee_payment.rb +0 -1
  12. data/app/models/effective/applicant_endorsement.rb +100 -0
  13. data/app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml +1 -0
  14. data/app/views/admin/applicants/_form.html.haml +4 -0
  15. data/app/views/admin/categories/_form_applicant_steps.html.haml +6 -0
  16. data/app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml +46 -0
  17. data/app/views/effective/applicant_endorsements/_datatable_actions.html.haml +4 -0
  18. data/app/views/effective/applicant_endorsements/_form.html.haml +9 -0
  19. data/app/views/effective/applicant_endorsements/_form_declaration.html.haml +22 -0
  20. data/app/views/effective/applicant_endorsements/complete.html.haml +3 -0
  21. data/app/views/effective/applicant_endorsements/edit.html.haml +8 -0
  22. data/app/views/effective/applicants/_endorsements.html.haml +15 -0
  23. data/app/views/effective/applicants/endorsements.html.haml +25 -0
  24. data/app/views/effective/applicants/experience.html.haml +1 -1
  25. data/app/views/effective/applicants/select.html.haml +6 -1
  26. data/app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid +15 -0
  27. data/config/effective_memberships.rb +3 -0
  28. data/config/routes.rb +9 -0
  29. data/db/migrate/01_create_effective_memberships.rb.erb +30 -0
  30. data/lib/effective_memberships/version.rb +1 -1
  31. data/lib/effective_memberships.rb +1 -1
  32. metadata +17 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60e4351c09c9d070ea622ed6e7770df2f14d53e177a4ea74e04294d00b91af6f
4
- data.tar.gz: 65436fdfda6a93aa08e1846d44b13131eedcc9d92e5831742dd6892d5ed87a32
3
+ metadata.gz: b7d09ef68f61058a8ee29567caa3a008d887482edab0fa3e8d251ea69f9ea3cb
4
+ data.tar.gz: 10a88e200464a6d2d7dbb8fbdcaa3aed77337b067e12fc3577b31a8032cbd2ce
5
5
  SHA512:
6
- metadata.gz: bffdf6c0614f8f36ee84b38426b1a8ea9aae37a7b1385fe9ce746584196bb043d2c835f1414942f8dc504b53090e6725649657fe348feab736a9b00453aa13c7
7
- data.tar.gz: 845130d67ea3500c634f2f9c96684af9ca1785dc50133ac94efda8087cb80e2e50bf05ea071540106d7d0250c557a0267ead9ed5b3ea2f98ca59cb99858b197a
6
+ metadata.gz: 3adbdb1ab5f18df509ebc6631f39a142f2857213ce9ee0a23b83eec2fa44dda3d8b77c42afe7e4e4b494bb938dac7aee672cc26dacbd398fd9f34cc3c55ed222
7
+ data.tar.gz: ab80c7ff8b01736429ecabc774b81c45702d21ae521e2e7db276191802fa212171f8e81e448a06e452fb97745c5aec3723fe37537669f3a3ed87b9cd30d03746
@@ -31,8 +31,3 @@ $(document).on('change dp.change keyup', "[data-applicant-experiences-month]", f
31
31
  const $form = $(event.currentTarget).closest('form');
32
32
  sumApplicantExperiences($form);
33
33
  });
34
-
35
-
36
- // $(document).on 'cocoon:after-remove', (event) ->
37
- // $form = $(event.target).closest('form')
38
- // sumMonths($form) if $form.find('[data-experience-month]').length > 0
@@ -0,0 +1,9 @@
1
+ module Admin
2
+ class ApplicantEndorsementsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_memberships) }
5
+
6
+ include Effective::CrudController
7
+
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ module Effective
2
+ class ApplicantEndorsementsController < ApplicationController
3
+ include Effective::CrudController
4
+ include Effective::Select2AjaxController
5
+
6
+ page_title 'Confidential Endorsement Form'
7
+
8
+ # The show and update actions are public routes but can only be reached by the token.
9
+ # The endorser must declare and submit the form
10
+ # To move an applicant endorsement from submitted to completed
11
+
12
+ submit :notify, 'Resend email notification',
13
+ success: -> { "Sent email notification to #{resource.email}" }
14
+
15
+ submit :complete, 'Complete Endorsement'
16
+
17
+ def show
18
+ @applicant_endorsement = ApplicantEndorsement.submitted.find(params[:id])
19
+ EffectiveResources.authorize!(self, :show, @applicant_endorsement)
20
+
21
+ render 'edit'
22
+ end
23
+
24
+ # Must be signed in
25
+ def select2_ajax_endorser
26
+ authenticate_user! if defined?(Devise)
27
+
28
+ applicant = EffectiveMemberships.Applicant.find(params[:applicant_id])
29
+ collection = Effective::ApplicantEndorsement.endorser_collection(applicant)
30
+
31
+ respond_with_select2_ajax(collection)
32
+ end
33
+
34
+ protected
35
+
36
+ def permitted_params
37
+ permitted = params.require(:effective_applicant_endorsement).permit!.except(:token, :last_notified_at, :status, :status_steps, :applicant_id, :endorser_id)
38
+
39
+ if current_user && current_user.memberships_owners.include?(resource.applicant&.owner)
40
+ permitted.except(:notes, :accept_declaration)
41
+ else
42
+ permitted
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -23,7 +23,7 @@ module Effective
23
23
  protected
24
24
 
25
25
  def permitted_params
26
- permitted = params.require(:effective_applicant_reference).permit!.except(:token, :last_notified_at, :status, :status_steps)
26
+ permitted = params.require(:effective_applicant_reference).permit!.except(:token, :last_notified_at, :status, :status_steps, :applicant_id)
27
27
 
28
28
  if current_user && current_user.memberships_owners.include?(resource.applicant&.owner)
29
29
  permitted.except(:reservations, :reservations_reason, :work_history, :accept_declaration)
@@ -0,0 +1,35 @@
1
+ module Admin
2
+ class EffectiveApplicantEndorsementsDatatable < Effective::Datatable
3
+
4
+ datatable do
5
+ order :name
6
+
7
+ col :applicant
8
+
9
+ col :endorser
10
+ col :email
11
+ col :name
12
+ col :phone
13
+ col :unknown_member, visible: false
14
+
15
+ col :status do |endorsement|
16
+ if endorsement.submitted?
17
+ 'Waiting on response'
18
+ elsif endorsement.completed?
19
+ 'Completed'
20
+ end
21
+ end
22
+
23
+ col :last_notified_at do |endorsement|
24
+ endorsement.last_notified_at&.strftime('%F') unless endorsement.completed?
25
+ end
26
+
27
+ actions_col
28
+ end
29
+
30
+ collection do
31
+ Effective::ApplicantEndorsement.deep.all
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ # For the applicant complete step to add additional endorsements
2
+ class EffectiveApplicantEndorsementsDatatable < Effective::Datatable
3
+
4
+ datatable do
5
+ order :name
6
+
7
+ col :name do |endorsement|
8
+ endorser&.to_s || name
9
+ end
10
+
11
+ col :email
12
+ col :phone
13
+
14
+ col :status do |reference|
15
+ if reference.submitted?
16
+ 'Waiting on response'
17
+ elsif reference.completed?
18
+ 'Completed'
19
+ end
20
+ end
21
+
22
+ col :last_notified_at do |endorsement|
23
+ endorsement.last_notified_at&.strftime('%F') unless endorsement.completed?
24
+ end
25
+
26
+ actions_col partial: 'effective/applicant_endorsements/datatable_actions', partial_as: :applicant_endorsement
27
+ end
28
+
29
+ collection do
30
+ Effective::ApplicantEndorsement.deep.where(applicant: applicant)
31
+ end
32
+
33
+ def applicant
34
+ @applicant ||= EffectiveMemberships.Applicant.where(id: attributes[:applicant_id]).first!
35
+ end
36
+
37
+ end
@@ -1,4 +1,4 @@
1
- # For the applicant complete step to add additional applicants
1
+ # For the applicant complete step to add additional references
2
2
  class EffectiveApplicantReferencesDatatable < Effective::Datatable
3
3
 
4
4
  datatable do
@@ -44,6 +44,16 @@ module Effective
44
44
  mail(to: resource.user.email, subject: subject, **headers)
45
45
  end
46
46
 
47
+ def applicant_endorsement_notification(resource, opts = {})
48
+ @assigns = assigns_for(resource)
49
+ @applicant_endorsement = resource
50
+
51
+ subject = subject_for(__method__, 'Endorsement Requested', resource, opts)
52
+ headers = headers_for(resource, opts)
53
+
54
+ mail(to: resource.email, subject: subject, **headers)
55
+ end
56
+
47
57
  def applicant_reference_notification(resource, opts = {})
48
58
  @assigns = assigns_for(resource)
49
59
  @applicant_reference = resource
@@ -61,6 +71,10 @@ module Effective
61
71
  return applicant_assigns(resource).merge(owner_assigns(resource.owner))
62
72
  end
63
73
 
74
+ if resource.kind_of?(Effective::ApplicantEndorsement)
75
+ return endorsement_assigns(resource).merge(owner_assigns(resource.applicant.owner))
76
+ end
77
+
64
78
  if resource.kind_of?(Effective::ApplicantReference)
65
79
  return reference_assigns(resource).merge(owner_assigns(resource.applicant.owner))
66
80
  end
@@ -88,6 +102,17 @@ module Effective
88
102
  { applicant: values }
89
103
  end
90
104
 
105
+ def endorsement_assigns(applicant_endorsement)
106
+ raise('expected a endorsement') unless applicant_endorsement.kind_of?(Effective::ApplicantEndorsement)
107
+
108
+ values = {
109
+ name: (applicant_endorsement.endorser&.to_s || applicant_endorsement.name),
110
+ url: effective_memberships.applicant_endorsement_url(applicant_endorsement)
111
+ }
112
+
113
+ { endorsement: values }
114
+ end
115
+
91
116
  def reference_assigns(applicant_reference)
92
117
  raise('expected a reference') unless applicant_reference.kind_of?(Effective::ApplicantReference)
93
118
 
@@ -93,6 +93,9 @@ module EffectiveMembershipsApplicant
93
93
  has_many :applicant_educations, -> { order(:id) }, class_name: 'Effective::ApplicantEducation', as: :applicant, inverse_of: :applicant, dependent: :destroy
94
94
  accepts_nested_attributes_for :applicant_educations, reject_if: :all_blank, allow_destroy: true
95
95
 
96
+ has_many :applicant_endorsements, -> { order(:id) }, class_name: 'Effective::ApplicantEndorsement', as: :applicant, inverse_of: :applicant, dependent: :destroy
97
+ accepts_nested_attributes_for :applicant_endorsements, reject_if: :all_blank, allow_destroy: true
98
+
96
99
  has_many :applicant_experiences, -> { order(:id) }, class_name: 'Effective::ApplicantExperience', as: :applicant, inverse_of: :applicant, dependent: :destroy
97
100
  accepts_nested_attributes_for :applicant_experiences, reject_if: :all_blank, allow_destroy: true
98
101
 
@@ -238,6 +241,16 @@ module EffectiveMembershipsApplicant
238
241
  end
239
242
  end
240
243
 
244
+ # Applicant Endorsements Step
245
+ with_options(if: -> { current_step == :endorsements }) do
246
+ validate do
247
+ required = min_applicant_endorsements()
248
+ existing = applicant_endorsements().reject(&:marked_for_destruction?).length
249
+
250
+ self.errors.add(:applicant_endorsements, "please include #{required} or more endorsements") if existing < required
251
+ end
252
+ end
253
+
241
254
  # Applicant References Step
242
255
  with_options(if: -> { current_step == :references }) do
243
256
  validate do
@@ -361,6 +374,7 @@ module EffectiveMembershipsApplicant
361
374
  stamps.each { |stamp| stamp.submit! }
362
375
 
363
376
  after_commit do
377
+ applicant_endorsements.each { |endorsement| endorsement.notify! if endorsement.submitted? }
364
378
  applicant_references.each { |reference| reference.notify! if reference.submitted? }
365
379
  end
366
380
 
@@ -521,6 +535,11 @@ module EffectiveMembershipsApplicant
521
535
  category&.min_applicant_references.to_i
522
536
  end
523
537
 
538
+ # Endorsements Step
539
+ def min_applicant_endorsements
540
+ category&.min_applicant_endorsements.to_i
541
+ end
542
+
524
543
  # Files Step
525
544
  def min_applicant_files
526
545
  category&.min_applicant_files.to_i
@@ -536,6 +555,10 @@ module EffectiveMembershipsApplicant
536
555
  )
537
556
  end
538
557
 
558
+ def applicant_endorsements_required?
559
+ min_applicant_endorsements > 0
560
+ end
561
+
539
562
  def applicant_references_required?
540
563
  min_applicant_references > 0
541
564
  end
@@ -543,13 +566,18 @@ module EffectiveMembershipsApplicant
543
566
  # When an application is submitted, these must be done to go to completed.
544
567
  # An Admin can override this and just set them to completed.
545
568
  def completed_requirements
546
- if category&.applicant_wizard_steps&.include?(:references) || applicant_references_required?
547
- {
548
- 'Applicant References' => (!applicant_references_required? || applicant_references.count(&:completed?) >= min_applicant_references)
549
- }
550
- else
551
- {}
569
+ requirements = {}
570
+ return requirements unless category.present?
571
+
572
+ if category.applicant_wizard_steps.include?(:references) || applicant_references_required?
573
+ requirements['Applicant References'] = (!applicant_references_required? || applicant_references.count(&:completed?) >= min_applicant_references)
552
574
  end
575
+
576
+ if category.applicant_wizard_steps.include?(:endorsements) || applicant_endorsements_required?
577
+ requirements['Applicant Endorsements'] = (!applicant_endorsements_required? || applicant_endorsements.count(&:completed?) >= min_applicant_endorsements)
578
+ end
579
+
580
+ requirements
553
581
  end
554
582
 
555
583
  def complete!
@@ -129,7 +129,6 @@ module EffectiveMembershipsFeePayment
129
129
 
130
130
  fee_payment_steps.delete(:organization) unless organization?
131
131
 
132
-
133
132
  wizard_steps.select do |step|
134
133
  required_steps.include?(step) || category.blank? || fee_payment_steps.include?(step)
135
134
  end
@@ -0,0 +1,100 @@
1
+ # An ApplicantEndorsement is similar to a reference, except it must be an existing member
2
+
3
+ module Effective
4
+ class ApplicantEndorsement < ActiveRecord::Base
5
+ acts_as_tokened
6
+
7
+ acts_as_statused(
8
+ :submitted, # Was submitted by the applicant
9
+ :completed # Was completed by the endorser.
10
+ )
11
+
12
+ log_changes(to: :applicant) if respond_to?(:log_changes)
13
+
14
+ belongs_to :applicant, polymorphic: true
15
+ belongs_to :endorser, polymorphic: true, optional: true
16
+
17
+ effective_resource do
18
+ # These fields are submitted by the applicant
19
+ # They should try to select an endorser from the dropdown
20
+ # But we also allow a fallback
21
+ unknown_member :boolean
22
+ endorser_email :string
23
+ name :string
24
+ phone :string
25
+
26
+ # As per acts_as_statused. For tracking the state machine.
27
+ status :string
28
+ status_steps :text
29
+
30
+ # Endorser Declaration
31
+ notes :text
32
+ accept_declaration :boolean
33
+
34
+ # Tracking the submission
35
+ token :string
36
+ last_notified_at :datetime
37
+
38
+ timestamps
39
+ end
40
+
41
+ scope :deep, -> { all }
42
+
43
+ # All step validations
44
+ validates :applicant, presence: true
45
+ validates :endorser, presence: true, unless: -> { unknown_member? }
46
+
47
+ with_options(if: -> { unknown_member? }) do
48
+ validates :endorser_email, presence: true, email: true
49
+ validates :name, presence: true
50
+ validates :phone, presence: true
51
+ end
52
+
53
+ # When being submit by the reference
54
+ with_options(if: -> { completed? }) do
55
+ validates :accept_declaration, acceptance: true
56
+ end
57
+
58
+ after_commit(on: :create, if: -> { applicant.was_submitted? }) { notify! }
59
+
60
+ def self.endorser_collection(applicant)
61
+ raise('expected an effective memberships applicant') unless applicant.class.try(:effective_memberships_applicant?)
62
+
63
+ collection_method = EffectiveMemberships.applicant_endorsements_endorser_collection()
64
+
65
+ if collection_method.blank?
66
+ return (applicant.user.class.members)
67
+ end
68
+
69
+ collection = instance_exec(applicant, &collection_method)
70
+
71
+ unless collection.kind_of?(ActiveRecord::Relation)
72
+ raise("expected EffectiveMemberships.applicant_endorsements_endorsers_collection to return an ActiveRecord::Relation.")
73
+ end
74
+
75
+ collection
76
+ end
77
+
78
+ def to_s
79
+ 'endorsement'
80
+ end
81
+
82
+ def email
83
+ endorser&.email || endorser_email
84
+ end
85
+
86
+ def notify!
87
+ raise('expected endorsement email') unless email.present?
88
+
89
+ EffectiveMemberships.send_email(:applicant_endorsement_notification, self)
90
+ update!(last_notified_at: Time.zone.now)
91
+ end
92
+
93
+ def complete!
94
+ completed!
95
+ applicant.save!
96
+ end
97
+
98
+ end
99
+
100
+ end
@@ -0,0 +1 @@
1
+ = render '/effective/applicant_endorsements/applicant_endorsement', applicant_endorsement: applicant_endorsement, skip_actions: true
@@ -25,6 +25,10 @@
25
25
  = tab 'References' do
26
26
  .mb-4= render_inline_datatable(Admin::EffectiveApplicantReferencesDatatable.new(applicant: applicant))
27
27
 
28
+ - if applicant.applicant_endorsements.present?
29
+ = tab 'Endorsements' do
30
+ .mb-4= render_inline_datatable(Admin::EffectiveApplicantEndorsementsDatatable.new(applicant: applicant))
31
+
28
32
  - if applicant.fees.present? || applicant.orders.present?
29
33
  = tab 'Fees' do
30
34
  .mb-4
@@ -32,6 +32,12 @@
32
32
  %td= f.number_field :min_applicant_experiences_months, label: false
33
33
  %td minimimum months of work experience
34
34
 
35
+ - if applicant.wizard_step_keys.include?(:endorsements)
36
+ %tr
37
+ %td= applicant.wizard_step_title(:endorsements)
38
+ %td= f.number_field :min_applicant_endorsements, label: false
39
+ %td minimimum number of endorsements
40
+
35
41
  - if applicant.wizard_step_keys.include?(:references)
36
42
  %tr
37
43
  %td= applicant.wizard_step_title(:references)
@@ -0,0 +1,46 @@
1
+ - endorsement = applicant_endorsement
2
+
3
+ %table.table.table-sm
4
+ %tbody
5
+ %tr
6
+ %th Endorser
7
+ %td
8
+ - if endorsement.endorser.present?
9
+ - endorser = endorsement.endorser
10
+ = link_to("#{endorser.to_s} <#{endorser.email}>", "mailto:#{endorser.email}")
11
+ - else
12
+ = link_to("#{endorsement.name} <#{endorsement.email}>", "mailto:#{endorsement.email}")
13
+ %td
14
+
15
+ - if endorsement.phone.present?
16
+ %tr
17
+ %th Phone
18
+ %td= endorsement.phone
19
+
20
+ - if endorsement.applicant.was_submitted?
21
+ %tr
22
+ %th Status
23
+ %td
24
+ - if endorsement.submitted?
25
+ Waiting on response
26
+ - elsif endorsement.completed?
27
+ Response completed
28
+
29
+ - unless endorsement.applicant.was_approved?
30
+ %tr
31
+ %th Last Notified at
32
+ %td= endorsement.last_notified_at&.strftime('%F') || 'Never'
33
+
34
+ - if can?(:notify, endorsement) && !local_assigns[:skip_actions]
35
+ %tr
36
+ %th Notification
37
+ %td= link_to('Resend email notification', effective_memberships.notify_applicant_endorsement_path(endorsement), 'data-method': :post, 'data-confirm': "Really resend email notification to #{endorsement.email}?")
38
+
39
+ - if endorsement.completed? && can?(:review, endorsement)
40
+ %tr
41
+ %th Notes
42
+ %td= endorsement.notes
43
+
44
+ %tr
45
+ %th Endorsed On
46
+ %td= endorsement.completed_at.strftime('%F')
@@ -0,0 +1,4 @@
1
+ = dropdown(variation: :dropleft) do
2
+ - unless applicant_endorsement.completed?
3
+ = dropdown_link_to 'Notify', effective_memberships.notify_applicant_endorsement_path(applicant_endorsement),
4
+ data: { method: :post, confirm: "Really notify #{applicant_endorsement.email}?" }
@@ -0,0 +1,9 @@
1
+ = effective_form_with(model: applicant_endorsement, engine: true) do |f|
2
+ = f.hidden_field :applicant_id
3
+ = f.hidden_field :applicant_type
4
+
5
+ - if f.object.new_record?
6
+ %p An email will be sent asking them to complete a endorsement declaration form.
7
+ = f.submit('Create and Notify endorser')
8
+ - else
9
+ = f.submit
@@ -0,0 +1,22 @@
1
+ = effective_form_with(model: applicant_endorsement, engine: true) do |f|
2
+ %h2 Applicant endorsement
3
+
4
+ %p
5
+ I endorse and sponser
6
+ = applicant_endorsement.applicant.owner
7
+ as a candidate for
8
+ = applicant_endorsement.applicant.category
9
+ membership and certify that I have personal knowledge of the professional ability
10
+ and character, methods of practice, and experience of the applicant.
11
+
12
+ %p
13
+ The candidate has satisfied the requirements for membership in this category,
14
+ and to the best of my personal knowledge,
15
+ the applicant observes and upholds the Code of Ethics and Professional Practice.
16
+
17
+ .mt-4
18
+
19
+ = f.text_area :notes, label: "Additional comments"
20
+ = f.check_box :accept_declaration, label: 'Yes, I declare that the above information is true and accurate'
21
+
22
+ = f.submit 'Complete Endorsement'
@@ -0,0 +1,3 @@
1
+ .card
2
+ .card-body
3
+ %p Thank you! The confidential endorsement has been completed.
@@ -0,0 +1,8 @@
1
+ .card
2
+ .card-body
3
+ %p
4
+ Please provide a endorsement for the applicant
5
+ = succeed('.') do
6
+ %strong= @applicant_endorsement.applicant.owner
7
+
8
+ = render('form_declaration', applicant_endorsement: @applicant_endorsement)
@@ -0,0 +1,15 @@
1
+ .card
2
+ .card-body
3
+ .row
4
+ .col-sm
5
+ %h5.card-title= applicant.wizard_step_title(:endorsements)
6
+ .col-sm-auto.text-right
7
+ = link_to('Edit', wizard_path(:endorsements)) if edit_effective_wizard?
8
+
9
+ - applicant.applicant_endorsements.each_with_index do |applicant_endorsement, index|
10
+ - if index > 0
11
+ .mb-4
12
+
13
+ %h6 Endorsement ##{index + 1}
14
+
15
+ = render('effective/applicant_endorsements/applicant_endorsement', applicant_endorsement: applicant_endorsement)
@@ -0,0 +1,25 @@
1
+ = render 'layout' do
2
+ = render 'effective/applicants/content', resource: resource
3
+
4
+ .card
5
+ .card-body
6
+ - if resource.min_applicant_endorsements > 0
7
+ %p You must include #{resource.min_applicant_endorsements} or more endorsements.
8
+
9
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
10
+ = f.hidden_field :id
11
+
12
+ = f.has_many(:applicant_endorsements, cards: true) do |aef|
13
+ %h4 Endorsement
14
+
15
+ = aef.select :endorser, Effective::ApplicantEndorsement.endorser_collection(resource), required: false,
16
+ ajax_url: effective_memberships.select2_ajax_endorser_applicant_endorsement_path(applicant_id: resource)
17
+
18
+ = aef.check_box :unknown_member, label: 'I cant find my endorser in the above list'
19
+
20
+ = aef.show_if(:unknown_member, true) do
21
+ = aef.email_field :endorser_email, required: true
22
+ = aef.text_field :name, required: true
23
+ = aef.phone_field :phone, required: true
24
+
25
+ = f.save 'Save and Continue'
@@ -12,7 +12,7 @@
12
12
  = f.error :applicant_experiences_months
13
13
 
14
14
  = f.has_many(:applicant_experiences, cards: true) do |aef|
15
- %h4 Work Experience
15
+ %h4.mb-4 Work Experience
16
16
 
17
17
  .row
18
18
  .col= aef.text_field :employer
@@ -27,9 +27,14 @@
27
27
  = f.show_if(:category_id, mc.id) do
28
28
  .mb-4
29
29
  %h3= mc.to_s
30
- %small.text-muted #{mc.category} #{mc.category_type} Membership
30
+
31
+ - if organization_categories.present?
32
+ %small.text-muted #{mc.category} #{mc.category_type} Membership
33
+
31
34
  = mc.rich_text_body
32
35
 
36
+ = render_if_exists("effective/applicants/select/#{mc.to_s.parameterize.underscore}", f: f, category: mc)
37
+
33
38
  - if organization_categories.present?
34
39
  = f.show_if_any(:category_id, organization_categories.map(&:id)) do
35
40
  = render('effective/applicants/select_organization', f: f)
@@ -0,0 +1,15 @@
1
+ ---
2
+ subject: 'Your endorsement has been requested'
3
+ from: 'admin@example.com'
4
+ ---
5
+ Hello {{ endorsement.name }},
6
+
7
+ An applicant, {{ user.name }}, has requested your endorsement.
8
+
9
+ Would you please complete the endorsement declaration form at this url:
10
+
11
+ {{ endorsement.url }}
12
+
13
+ Thank you.
14
+
15
+ Please contact us for assistance.
@@ -24,6 +24,9 @@ EffectiveMemberships.setup do |config|
24
24
  # The defaults include: Applicant, Prorated, Renewal, Late, Admin
25
25
  # config.additional_fee_types = []
26
26
 
27
+ # Applicant Endorsements
28
+ # config.applicant_endorsements_endorser_collection = Proc.new { |applicant| applicant.user.class.members }
29
+
27
30
  # Applicant Reviews
28
31
  # When true, display the reviewed state and require Category.min_applicant_reviews
29
32
  # When false, hide the reviewed state entirely
data/config/routes.rb CHANGED
@@ -9,6 +9,11 @@ EffectiveMemberships::Engine.routes.draw do
9
9
  resources :build, controller: :applicants, only: [:show, :update]
10
10
  end
11
11
 
12
+ resources :applicant_endorsements, only: [:new, :create, :show, :update] do
13
+ get :select2_ajax_endorser, on: :member
14
+ post :notify, on: :member
15
+ end
16
+
12
17
  resources :applicant_references, only: [:new, :create, :show, :update] do
13
18
  post :notify, on: :member
14
19
  end
@@ -32,6 +37,10 @@ EffectiveMemberships::Engine.routes.draw do
32
37
  namespace :admin do
33
38
  resources :applicants, except: [:new, :create, :show]
34
39
 
40
+ resources :applicant_endorsements do
41
+ post :notify, on: :member
42
+ end
43
+
35
44
  resources :applicant_references do
36
45
  post :notify, on: :member
37
46
  end
@@ -24,6 +24,7 @@ class CreateEffectiveMemberships < ActiveRecord::Migration[6.0]
24
24
  t.integer :min_applicant_educations
25
25
  t.integer :min_applicant_experiences_months
26
26
  t.integer :min_applicant_references
27
+ t.integer :min_applicant_endorsements
27
28
  t.integer :min_applicant_courses
28
29
  t.integer :min_applicant_files
29
30
 
@@ -295,6 +296,35 @@ class CreateEffectiveMemberships < ActiveRecord::Migration[6.0]
295
296
  add_index :applicant_references, :applicant_id
296
297
  add_index :applicant_references, :token
297
298
 
299
+ # Applicant Endorsements
300
+ create_table :applicant_endorsements do |t|
301
+ t.integer :applicant_id
302
+ t.string :applicant_type
303
+
304
+ t.integer :endorser_id
305
+ t.string :endorser_type
306
+
307
+ t.boolean :unknown_member, default: false
308
+ t.string :endorser_email
309
+ t.string :name
310
+ t.string :phone
311
+
312
+ t.string :status
313
+ t.text :status_steps
314
+
315
+ t.text :notes
316
+ t.boolean :accept_declaration
317
+
318
+ t.string :token
319
+ t.datetime :last_notified_at
320
+
321
+ t.datetime :created_at
322
+ t.datetime :updated_at
323
+ end
324
+
325
+ add_index :applicant_endorsements, :applicant_id
326
+ add_index :applicant_endorsements, :token
327
+
298
328
  # Applicant Courses
299
329
  create_table :applicant_course_areas do |t|
300
330
  t.string :title
@@ -1,3 +1,3 @@
1
1
  module EffectiveMemberships
2
- VERSION = '0.4.9'
2
+ VERSION = '0.4.10'
3
3
  end
@@ -8,7 +8,7 @@ module EffectiveMemberships
8
8
  [
9
9
  :categories_table_name, :applicants_table_name, :applicant_reviews_table_name, :fee_payments_table_name, :organizations_table_name, :representatives_table_name,
10
10
  :category_class_name, :organization_class_name, :applicant_class_name, :applicant_review_class_name, :fee_payment_class_name, :registrar_class_name, :membership_card_class_name,
11
- :additional_fee_types, :applicant_reviews,
11
+ :additional_fee_types, :applicant_reviews, :applicant_endorsements_endorser_collection,
12
12
  :layout,
13
13
  :mailer, :parent_mailer, :deliver_method, :mailer_layout, :mailer_sender, :mailer_admin, :mailer_subject, :use_effective_email_templates
14
14
  ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_memberships
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.9
4
+ version: 0.4.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-13 00:00:00.000000000 Z
11
+ date: 2022-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -256,6 +256,7 @@ files:
256
256
  - app/assets/stylesheets/effective_memberships/base.scss
257
257
  - app/controllers/admin/applicant_course_areas_controller.rb
258
258
  - app/controllers/admin/applicant_course_names_controller.rb
259
+ - app/controllers/admin/applicant_endorsements_controller.rb
259
260
  - app/controllers/admin/applicant_references_controller.rb
260
261
  - app/controllers/admin/applicants_controller.rb
261
262
  - app/controllers/admin/categories_controller.rb
@@ -266,6 +267,7 @@ files:
266
267
  - app/controllers/admin/organizations_controller.rb
267
268
  - app/controllers/admin/registrar_actions_controller.rb
268
269
  - app/controllers/admin/representatives_controller.rb
270
+ - app/controllers/effective/applicant_endorsements_controller.rb
269
271
  - app/controllers/effective/applicant_references_controller.rb
270
272
  - app/controllers/effective/applicants_controller.rb
271
273
  - app/controllers/effective/fee_payments_controller.rb
@@ -275,6 +277,7 @@ files:
275
277
  - app/controllers/effective/representatives_controller.rb
276
278
  - app/datatables/admin/effective_applicant_course_areas_datatable.rb
277
279
  - app/datatables/admin/effective_applicant_course_names_datatable.rb
280
+ - app/datatables/admin/effective_applicant_endorsements_datatable.rb
278
281
  - app/datatables/admin/effective_applicant_references_datatable.rb
279
282
  - app/datatables/admin/effective_applicants_datatable.rb
280
283
  - app/datatables/admin/effective_categories_datatable.rb
@@ -286,6 +289,7 @@ files:
286
289
  - app/datatables/admin/effective_representatives_datatable.rb
287
290
  - app/datatables/effective_applicant_courses_datatable.rb
288
291
  - app/datatables/effective_applicant_educations_datatable.rb
292
+ - app/datatables/effective_applicant_endorsements_datatable.rb
289
293
  - app/datatables/effective_applicant_experiences_datatable.rb
290
294
  - app/datatables/effective_applicant_references_datatable.rb
291
295
  - app/datatables/effective_applicants_datatable.rb
@@ -309,6 +313,7 @@ files:
309
313
  - app/models/effective/applicant_course_area.rb
310
314
  - app/models/effective/applicant_course_name.rb
311
315
  - app/models/effective/applicant_education.rb
316
+ - app/models/effective/applicant_endorsement.rb
312
317
  - app/models/effective/applicant_experience.rb
313
318
  - app/models/effective/applicant_reference.rb
314
319
  - app/models/effective/applicant_review.rb
@@ -327,6 +332,7 @@ files:
327
332
  - app/views/admin/applicant_course_areas/_form_applicant_course_area.html.haml
328
333
  - app/views/admin/applicant_course_areas/index.html.haml
329
334
  - app/views/admin/applicant_course_name/_form.html.haml
335
+ - app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml
330
336
  - app/views/admin/applicant_references/_applicant_reference.html.haml
331
337
  - app/views/admin/applicants/_form.html.haml
332
338
  - app/views/admin/applicants/_form_approve.html.haml
@@ -367,6 +373,12 @@ files:
367
373
  - app/views/admin/representatives/_form.html.haml
368
374
  - app/views/admin/representatives/_user_fields.html.haml
369
375
  - app/views/admin/users/_col.html.haml
376
+ - app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml
377
+ - app/views/effective/applicant_endorsements/_datatable_actions.html.haml
378
+ - app/views/effective/applicant_endorsements/_form.html.haml
379
+ - app/views/effective/applicant_endorsements/_form_declaration.html.haml
380
+ - app/views/effective/applicant_endorsements/complete.html.haml
381
+ - app/views/effective/applicant_endorsements/edit.html.haml
370
382
  - app/views/effective/applicant_references/_applicant_reference.html.haml
371
383
  - app/views/effective/applicant_references/_datatable_actions.html.haml
372
384
  - app/views/effective/applicant_references/_form.html.haml
@@ -380,6 +392,7 @@ files:
380
392
  - app/views/effective/applicants/_declarations.html.haml
381
393
  - app/views/effective/applicants/_demographics.html.haml
382
394
  - app/views/effective/applicants/_education.html.haml
395
+ - app/views/effective/applicants/_endorsements.html.haml
383
396
  - app/views/effective/applicants/_experience.html.haml
384
397
  - app/views/effective/applicants/_files.html.haml
385
398
  - app/views/effective/applicants/_layout.html.haml
@@ -397,6 +410,7 @@ files:
397
410
  - app/views/effective/applicants/declarations.html.haml
398
411
  - app/views/effective/applicants/demographics.html.haml
399
412
  - app/views/effective/applicants/education.html.haml
413
+ - app/views/effective/applicants/endorsements.html.haml
400
414
  - app/views/effective/applicants/experience.html.haml
401
415
  - app/views/effective/applicants/files.html.haml
402
416
  - app/views/effective/applicants/organization.html.haml
@@ -432,6 +446,7 @@ files:
432
446
  - app/views/effective/memberships_mailer/applicant_approved.liquid
433
447
  - app/views/effective/memberships_mailer/applicant_completed.liquid
434
448
  - app/views/effective/memberships_mailer/applicant_declined.liquid
449
+ - app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid
435
450
  - app/views/effective/memberships_mailer/applicant_missing_info.liquid
436
451
  - app/views/effective/memberships_mailer/applicant_reference_notification.liquid
437
452
  - app/views/effective/organizations/_dashboard.html.haml