effective_memberships 0.6.13 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/admin/applicant_reviews_controller.rb +19 -0
  3. data/app/controllers/effective/applicant_reviews_controller.rb +13 -0
  4. data/app/datatables/admin/effective_applicant_reviews_datatable.rb +24 -0
  5. data/app/datatables/effective_applicant_reviews_datatable.rb +25 -0
  6. data/app/datatables/effective_available_applicant_reviews_datatable.rb +40 -0
  7. data/app/mailers/effective/memberships_mailer.rb +43 -0
  8. data/app/models/concerns/effective_memberships_applicant.rb +23 -2
  9. data/app/models/concerns/effective_memberships_applicant_review.rb +34 -68
  10. data/app/models/concerns/effective_memberships_user.rb +6 -0
  11. data/app/views/admin/applicant_reviews/_applicant_review.html.haml +1 -0
  12. data/app/views/admin/applicants/_form_applicant.html.haml +4 -0
  13. data/app/views/admin/applicants/_status.html.haml +2 -2
  14. data/app/views/effective/applicant_reviews/_applicant_review.html.haml +4 -0
  15. data/app/views/effective/applicant_reviews/_content.html.haml +8 -0
  16. data/app/views/effective/applicant_reviews/_dashboard.html.haml +36 -0
  17. data/app/views/effective/applicant_reviews/_layout.html.haml +3 -0
  18. data/app/views/effective/applicant_reviews/_recommendation.html.haml +23 -0
  19. data/app/views/effective/applicant_reviews/_review.html.haml +1 -0
  20. data/app/views/effective/applicant_reviews/recommendation.html.haml +20 -0
  21. data/app/views/effective/applicant_reviews/review.html.haml +14 -0
  22. data/app/views/effective/applicant_reviews/start.html.haml +18 -0
  23. data/app/views/effective/applicant_reviews/submitted.html.haml +15 -0
  24. data/app/views/effective/applicants/_summary.html.haml +4 -3
  25. data/app/views/effective/memberships_mailer/applicant_review_submitted.liquid +19 -0
  26. data/config/routes.rb +6 -0
  27. data/lib/effective_memberships/version.rb +1 -1
  28. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 58da6947ec96b076bd125a190e31ade30e7964b6dc796423dc7387efa156a246
4
- data.tar.gz: 5805aa420a911f2abbb4d38ceaf1d6d743a3932e3b47a14b3b1e4a8796afd0bd
3
+ metadata.gz: 3972d4221b4931ed7f157f87ef8a7d9c9a1ad785679599a13c3d38e37fd48e56
4
+ data.tar.gz: 4a19e5c29db9c7ee41fd67177cda71197adb743f1f4eaaf97116192af82c562b
5
5
  SHA512:
6
- metadata.gz: ea84383f2dbd0763c31e4c8a1c13f16d474d96e749502717dec4825cdbd50eee9fe3dbf3572650dcccedc3f05785bc283c0451fd9ac51fee5f17fd7fa738e7b6
7
- data.tar.gz: f0d0dad74b75782b25fc76f786b6e1d042faa591941ebc747ac85d24301dda54e8d3fc5be080ef5827ac251de0f60d23eafea443106565fb872fd2ccdb9bf1a6
6
+ metadata.gz: 9b5b013d0f0c7eddfbef6ed4b75c93169bfd4a48eebb07af26bb768d39f57285efb725266b26f6a75f05b14657ddb1ba9490594568b0deb65126caef3b873f99
7
+ data.tar.gz: cd37dded19f0ef05190120efe7749a6c0c3e71c5c53b53690c5d5e7e234a8b2b00e12db26480ee3e9f49617c505ca52499254d37b4ebd03c7e0918866e7af6d4
@@ -0,0 +1,19 @@
1
+ module Admin
2
+ class ApplicantReviewsController < 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
+ resource_scope -> { EffectiveMemberships.ApplicantReview.deep.all }
9
+ datatable -> { Admin::EffectiveApplicantReviewsDatatable.new }
10
+
11
+ private
12
+
13
+ def permitted_params
14
+ model = (params.key?(:effective_applicant_review) ? :effective_applicant_review: :applicant_review)
15
+ params.require(model).permit!
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Effective
2
+ class ApplicantReviewsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+
5
+ include Effective::WizardController
6
+
7
+ resource_scope -> {
8
+ applicant = EffectiveMemberships.Applicant.find(params[:applicant_id])
9
+ EffectiveMemberships.ApplicantReview.deep.where(reviewer: current_user, applicant: applicant)
10
+ }
11
+
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Admin
2
+ class EffectiveApplicantReviewsDatatable < Effective::Datatable
3
+ datatable do
4
+ order :submitted_at
5
+
6
+ col :created_at, visible: false
7
+ col :id, visible: false
8
+
9
+ col :submitted_at, label: 'Reviewed', as: :date
10
+ col :reviewer
11
+ col :applicant
12
+
13
+ col :recommendation
14
+ col :comments
15
+
16
+ actions_col
17
+ end
18
+
19
+ collection do
20
+ EffectiveMemberships.ApplicantReview.deep.submitted
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # Dashboard ApplicantReviews
2
+ class EffectiveApplicantReviewsDatatable < Effective::Datatable
3
+ datatable do
4
+ order :id, :desc
5
+
6
+ col :created_at, visible: false
7
+ col :id, visible: false
8
+
9
+ col :applicant
10
+ col :reviewer, visible: false
11
+
12
+ col :submitted_at, label: 'Reviewed', as: :date
13
+ col :recommendation
14
+ col :comments
15
+
16
+ actions_col(show: false) do |applicant_review|
17
+ dropdown_link_to('Show', effective_memberships.applicant_applicant_review_path(applicant_review.applicant, applicant_review))
18
+ end
19
+ end
20
+
21
+ collection do
22
+ EffectiveMemberships.ApplicantReview.deep.submitted.where(reviewer: current_user)
23
+ end
24
+
25
+ end
@@ -0,0 +1,40 @@
1
+ # Dashboard Available Applicant Reviews
2
+ class EffectiveAvailableApplicantReviewsDatatable < Effective::Datatable
3
+ datatable do
4
+ order :created_at
5
+
6
+ col :id, visible: false
7
+
8
+ col :created_at, label: 'Created', as: :date, visible: false
9
+ col :updated_at, label: 'Updated', as: :date, visible: false
10
+ col :submitted_at, label: 'Submitted', as: :date, visible: false
11
+
12
+ # This is when they become available for review
13
+ col :completed_at, label: 'Date', as: :date
14
+
15
+ col :user
16
+ col :category, label: 'Category'
17
+ col :to_status, label: 'Status'
18
+
19
+ actions_col(show: false) do |applicant|
20
+ applicant_review = applicant.applicant_review(reviewer: current_user)
21
+
22
+ if applicant_review.blank?
23
+ dropdown_link_to('Start Review', effective_memberships.new_applicant_applicant_review_path(applicant), 'data-turbolinks' => false)
24
+ elsif applicant_review.in_progress?
25
+ dropdown_link_to('Continue', effective_memberships.applicant_applicant_review_build_path(applicant, applicant_review, applicant_review.next_step), 'data-turbolinks' => false)
26
+ dropdown_link_to('Delete', effective_memberships.applicant_applicant_review_path(applicant, applicant_review), 'data-confirm': "Really delete #{applicant_review}?", 'data-method': :delete)
27
+ else
28
+ dropdown_link_to('Show', effective_memberships.applicant_applicant_review_path(applicant, applicant_review), 'data-turbolinks' => false)
29
+ end
30
+ end
31
+ end
32
+
33
+ collection do
34
+ # These ones have already been submitted by this reviewer
35
+ submitted = EffectiveMemberships.ApplicantReview.deep.submitted.where(reviewer: current_user).select('applicant_id')
36
+
37
+ EffectiveMemberships.Applicant.deep.reviewable.where.not(id: submitted)
38
+ end
39
+
40
+ end
@@ -64,6 +64,17 @@ module Effective
64
64
  mail(to: resource.email, subject: subject, **headers)
65
65
  end
66
66
 
67
+ def applicant_review_submitted(resource, opts = {})
68
+ @assigns = assigns_for(resource)
69
+ @applicant_review = resource
70
+ @applicant = resource.applicant
71
+
72
+ subject = subject_for(__method__, "Applicant Review for #{resource.applicant} submitted", resource, opts)
73
+ headers = headers_for(resource, opts)
74
+
75
+ mail(to: mailer_admin, subject: subject, **headers)
76
+ end
77
+
67
78
  protected
68
79
 
69
80
  def assigns_for(resource)
@@ -71,6 +82,10 @@ module Effective
71
82
  return applicant_assigns(resource).merge(owner_assigns(resource.owner)).merge(membership_assigns(resource.owner.membership))
72
83
  end
73
84
 
85
+ if resource.class.respond_to?(:effective_memberships_applicant_review?)
86
+ return applicant_review_assigns(resource).merge(applicant_assigns(resource.applicant)).merge(owner_assigns(resource.applicant.owner)).merge(membership_assigns(resource.applicant.owner.membership))
87
+ end
88
+
74
89
  if resource.kind_of?(Effective::ApplicantEndorsement)
75
90
  return endorsement_assigns(resource).merge(owner_assigns(resource.applicant.owner))
76
91
  end
@@ -103,6 +118,23 @@ module Effective
103
118
  { applicant: values }
104
119
  end
105
120
 
121
+ def applicant_review_assigns(applicant_review)
122
+ raise('expected an applicant review') unless applicant_review.class.respond_to?(:effective_memberships_applicant_review?)
123
+
124
+ values = {
125
+ date: (applicant_review.submitted_at || Time.zone.now).strftime('%F'),
126
+ submitted_at: (applicant_review.submitted_at&.strftime('%F') || 'not submitted'),
127
+
128
+ url: effective_memberships.applicant_applicant_review_url(applicant_review.applicant, applicant_review),
129
+ admin_url: effective_memberships.admin_applicant_review_url(applicant_review),
130
+
131
+ recommendation: applicant_review.recommendation.presence,
132
+ comments: applicant_review.comments.presence
133
+ }.compact
134
+
135
+ { applicant_review: values }
136
+ end
137
+
106
138
  def membership_assigns(membership)
107
139
  return {} if membership.blank?
108
140
  raise('expected a membership') unless membership.kind_of?(Effective::Membership)
@@ -149,5 +181,16 @@ module Effective
149
181
  { user: values }
150
182
  end
151
183
 
184
+ def reviewer_assigns(owner)
185
+ raise('expected a owner') unless owner.class.respond_to?(:effective_memberships_owner?)
186
+
187
+ values = {
188
+ name: owner.to_s,
189
+ email: owner.email
190
+ }
191
+
192
+ { reviewer: values }
193
+ end
194
+
152
195
  end
153
196
  end
@@ -187,6 +187,8 @@ module EffectiveMembershipsApplicant
187
187
 
188
188
  scope :not_draft, -> { where.not(status: :draft) }
189
189
 
190
+ scope :reviewable, -> { where(status: :completed) }
191
+
190
192
  scope :for, -> (user) {
191
193
  raise('expected a effective memberships user') unless user.class.try(:effective_memberships_user?)
192
194
  where(user: user).or(where(organization: user.organizations))
@@ -750,6 +752,10 @@ module EffectiveMembershipsApplicant
750
752
  end
751
753
 
752
754
  # Completed -> Reviewed requirements
755
+ def reviewable?
756
+ completed?
757
+ end
758
+
753
759
  def applicant_reviews_required?
754
760
  (min_applicant_reviews > 0 || applicant_reviews.present?)
755
761
  end
@@ -758,6 +764,16 @@ module EffectiveMembershipsApplicant
758
764
  category&.min_applicant_reviews.to_i
759
765
  end
760
766
 
767
+ # Find
768
+ def applicant_review(reviewer:)
769
+ applicant_reviews.find { |ar| ar.reviewer_id == reviewer.id && ar.reviewer_type == reviewer.class.name }
770
+ end
771
+
772
+ # Find or build
773
+ def build_applicant_review(reviewer:)
774
+ applicant_review(reviewer: reviewer) || applicant_reviews.build(reviewer: reviewer)
775
+ end
776
+
761
777
  # When an application is completed, these must be done to go to reviewed
762
778
  # An Admin can override this and just set them to reviewed.
763
779
  def reviewed_requirements
@@ -765,17 +781,22 @@ module EffectiveMembershipsApplicant
765
781
  return requirements unless category.present?
766
782
 
767
783
  if applicant_reviews_required?
768
- requirements['Applicant Reviews'] = (applicant_reviews.count(&:completed?) >= min_applicant_reviews)
784
+ requirements['Applicant Reviews'] = (applicant_reviews.count(&:done?) >= min_applicant_reviews)
769
785
  end
770
786
 
771
787
  requirements
772
788
  end
773
789
 
790
+ # Called when an applicant_review was submitted
791
+ def try_reviewed!
792
+ return false unless completed? && reviewed_requirements.values.all?
793
+ reviewed!
794
+ end
795
+
774
796
  def review!
775
797
  raise('applicant must have been submitted to review!') unless was_submitted?
776
798
 
777
799
  # Let an admin ignore these requirements if need be
778
- # return false unless completed? && reviewed_requirements.values.all?
779
800
  reviewed!
780
801
  end
781
802
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # EffectiveMembershipsApplicantReview
4
4
  #
5
- # Mark your category model with effective_memberships_applicant_review to get all the includes
5
+ # Mark your owner model with effective_memberships_applicant_review to get all the includes
6
6
 
7
7
  module EffectiveMembershipsApplicantReview
8
8
  extend ActiveSupport::Concern
@@ -22,82 +22,60 @@ module EffectiveMembershipsApplicantReview
22
22
  end
23
23
 
24
24
  def recommendations
25
- ['Accept', 'Reject']
25
+ ['Recommend Approve', 'Recommend Decline', 'Missing Information']
26
26
  end
27
27
 
28
28
  end
29
29
 
30
30
  included do
31
- log_changes if respond_to?(:log_changes)
31
+ log_changes(to: :applicant) if respond_to?(:log_changes)
32
32
 
33
33
  acts_as_tokened
34
34
 
35
35
  acts_as_statused(
36
- :draft, # Just Started
37
- :conflicted, # Conflict of Interest
38
- :accepted, # Accepted
39
- :rejected # Rejected
36
+ :draft, # Just Started
37
+ :submitted # All Done
40
38
  )
41
39
 
42
40
  acts_as_wizard(
43
41
  start: 'Start',
44
- conflict: 'Conflict of Interest',
45
- education: 'Education',
46
- course_amounts: 'Courses',
47
- experience: 'Work Experience',
48
- references: 'References',
49
- files: 'Attach Files',
50
- declarations: 'Declarations',
42
+ review: 'Review Applicant',
51
43
  recommendation: 'Recommendation',
52
44
  submitted: 'Submitted'
53
45
  )
54
46
 
47
+ scope :in_progress, -> { where(status: :draft) }
48
+ scope :done, -> { where(status: :submitted) }
49
+
55
50
  belongs_to :applicant
56
51
  belongs_to :reviewer, polymorphic: true
57
52
 
58
53
  effective_resource do
59
54
  submitted_at :datetime
60
- recommendation :string
61
55
 
56
+ recommendation :string
62
57
  comments :text # Rolling comments
63
58
 
64
- # Conflict Step
65
- conflict_of_interest :boolean
66
-
67
- # Education Step
68
- education_accepted :boolean
69
-
70
- # Course Amounts
71
- course_amounts_accepted :boolean
72
-
73
- # Courses
74
- courses_accepted :boolean
75
-
76
- # Experience Step
77
- experience_accepted :boolean
78
-
79
- # References Step
80
- references_accepted :boolean
81
-
82
- # References Step
83
- files_accepted :boolean
84
-
85
59
  timestamps
86
60
  end
87
61
 
88
- scope :deep, -> { includes(:reviewer, applicant: :owner) }
62
+ scope :deep, -> { includes(:reviewer, applicant: :user) }
89
63
 
90
- with_options(if: -> { current_step == :conflict }) do
91
- validates :conflict_of_interest, inclusion: { in: [true, false] }
92
- validates :comments, presence: true, if: -> { conflict_of_interest? }
93
- end
64
+ scope :for, -> (user) {
65
+ raise('expected a effective memberships user') unless user.class.try(:effective_memberships_user?)
66
+ where(reviewer: user)
67
+ }
94
68
 
95
- validates :recommendation, absence: true, if: -> { done? && conflict_of_interest? }
69
+ with_options(if: -> { submitted? }) do
70
+ validates :recommendation, presence: true
96
71
 
97
- after_commit(on: :create, if: -> { applicant.completed? }) { notify! }
72
+ validate do
73
+ self.errors.add(:recommendation, 'is invalid') unless EffectiveMemberships.ApplicantReview.recommendations.include?(recommendation)
74
+ end
75
+ end
98
76
 
99
77
  def to_s
100
- 'applicant review'
78
+ "Review of #{applicant}"
101
79
  end
102
80
 
103
81
  def in_progress?
@@ -105,43 +83,31 @@ module EffectiveMembershipsApplicantReview
105
83
  end
106
84
 
107
85
  def done?
108
- !draft?
86
+ submitted?
109
87
  end
110
88
 
111
- def conflict_of_interest!
112
- update!(conflict_of_interest: true, recommendation: nil)
113
- conflicted!
114
-
115
- applicant.save!
116
-
117
- after_commit { send_email(:applicant_review_conflict_of_interest) }
118
- true
89
+ def can_visit_step?(step)
90
+ return (step == :submitted) if was_submitted?
91
+ can_revisit_completed_steps(step)
119
92
  end
120
93
 
121
- def accept!
122
- assign_attributes(recommendation: 'Accept')
123
- accepted!
124
-
125
- applicant.save!
126
-
127
- after_commit { send_email(:applicant_review_completed) }
128
- true
94
+ # Called by the recommendation wizard step
95
+ def recommendation!
96
+ submit!
129
97
  end
130
98
 
131
- def reject!
132
- assign_attributes(recommendation: 'Reject')
133
- rejected!
134
-
135
- applicant.save!
99
+ def submit!
100
+ submitted!
101
+ applicant.try_reviewed!
136
102
 
137
- after_commit { send_email(:applicant_review_completed) }
103
+ after_commit { send_email(:applicant_review_submitted) }
138
104
  true
139
105
  end
140
106
 
141
107
  private
142
108
 
143
109
  def send_email(email)
144
- EffectiveMemberships.send_email(email, self, email_form_params) unless email_form_skip?
110
+ EffectiveMemberships.send_email(email, self)
145
111
  end
146
112
 
147
113
  end
@@ -21,6 +21,7 @@ module EffectiveMembershipsUser
21
21
  # App scoped
22
22
  has_many :applicants, -> { order(:id) }, inverse_of: :user, as: :user
23
23
  has_many :fee_payments, -> { order(:id) }, inverse_of: :user, as: :user
24
+ has_many :applicant_reviews, -> { order(:id) }, inverse_of: :reviewer, as: :reviewer
24
25
 
25
26
  # Effective Scoped
26
27
  has_many :representatives, -> { Effective::Representative.sorted },
@@ -85,4 +86,9 @@ module EffectiveMembershipsUser
85
86
  membership_history_on(date).try(:membership_categories)
86
87
  end
87
88
 
89
+ # Roles
90
+ def applicant_reviewer?
91
+ false
92
+ end
93
+
88
94
  end
@@ -0,0 +1 @@
1
+ = render 'effective/applicant_reviews/applicant_review', applicant_review: applicant_review
@@ -17,6 +17,10 @@
17
17
  = render 'effective/applicants/applicant', applicant: applicant, namespace: :admin
18
18
 
19
19
  - # Just normal tabs now
20
+ - if applicant.applicant_reviews.present?
21
+ = tab 'Reviews' do
22
+ .mb-4= render_inline_datatable(Admin::EffectiveApplicantReviewsDatatable.new(applicant: applicant))
23
+
20
24
  - if applicant.was_submitted? && !applicant.was_approved?
21
25
  = tab 'Process' do
22
26
  = render 'admin/applicants/form_process', applicant: applicant
@@ -107,10 +107,10 @@
107
107
  - if applicant.reviewed_requirements['Applicant Reviews']
108
108
  = icon('check', class: 'small-1')
109
109
 
110
- = applicant.applicant_reviews.count(&:completed?)
110
+ = applicant.applicant_reviews.count(&:submitted?)
111
111
  = '/'
112
112
  = applicant.min_applicant_reviews
113
- Reviews Completed
113
+ Reviews Submitted
114
114
  - else
115
115
  %p
116
116
  = icon('check', class: 'small-1')
@@ -0,0 +1,4 @@
1
+ .effective-applicant-review
2
+ - applicant_review.render_steps.each do |partial|
3
+ - applicant_review.render_step = partial
4
+ = render "effective/applicant_reviews/#{partial}", applicant_review: applicant_review
@@ -0,0 +1,8 @@
1
+ - all_steps_content = resource.applicant&.category&.rich_text_applicant_review_all_steps_content
2
+ - step_content = resource.applicant&.category&.send("rich_text_applicant_review_#{step}_content")
3
+
4
+ - if all_steps_content.present?
5
+ .mb-2= all_steps_content
6
+
7
+ - if step_content.present?
8
+ .mb-2= step_content
@@ -0,0 +1,36 @@
1
+ - authorized = EffectiveResources.authorized?(self, :new, EffectiveMemberships.ApplicantReview)
2
+
3
+ - available = EffectiveResources.best('EffectiveAvailableApplicantReviewsDatatable').new(self)
4
+ - datatable = EffectiveResources.best('EffectiveApplicantReviewsDatatable').new(self)
5
+
6
+ - # In progress
7
+ - applicant_review = current_user.applicant_reviews.in_progress.first
8
+
9
+ - if applicant_review.present? && applicant_review.draft?
10
+ %h2 In-Progress Applicant Review
11
+
12
+ %p
13
+ Your review of #{applicant_review} is incomplete.
14
+
15
+ %p
16
+ Please
17
+ = link_to("Continue review", effective_memberships.applicant_applicant_review_build_path(applicant_review.applicant, applicant_review, applicant_review.next_step), 'data-turbolinks' => false, class: 'btn btn-primary')
18
+ or you can
19
+ = link_to('Abandon review', effective_memberships.applicant_applicant_review_path(applicant_review.applicant, applicant_review), 'data-confirm': "Really delete #{applicant_review}?", 'data-method': :delete, class: 'btn btn-danger')
20
+ to review again.
21
+
22
+ %hr
23
+
24
+ %h2 Applicant Reviews
25
+
26
+ - if available.present?
27
+ %p The following applicants are available for your review:
28
+
29
+ = render_simple_datatable(available)
30
+ - else
31
+ %p There are no applicants available for your review. When there are, we'll show them here.
32
+
33
+ - if datatable.present?
34
+ .mt-4
35
+ %h2 Review History
36
+ = render_datatable(datatable, simple: true)
@@ -0,0 +1,3 @@
1
+ .row
2
+ .col-lg-3.mb-3= render_wizard_sidebar(resource)
3
+ .col-lg-9= yield
@@ -0,0 +1,23 @@
1
+ = wizard_card(applicant_review) do
2
+ %table.table
3
+ - if request.path.start_with?('/admin')
4
+ %tr
5
+ %th Reviewer
6
+ %td
7
+ - url = (polymorphic_admin_path(applicant_review.reviewer) rescue "/admin/users/#{applicant_review.reviewer.to_param}/edit")
8
+ = link_to(applicant_review.reviewer, url)
9
+ - else
10
+ %tr
11
+ %th Reviewer
12
+ %td
13
+ = applicant_review.reviewer.to_s
14
+ %br
15
+ = mail_to(applicant_review.reviewer.email)
16
+
17
+ %tr
18
+ %th Recommendation
19
+ %td= applicant_review.recommendation
20
+
21
+ %tr
22
+ %th Comments
23
+ %td= simple_format(applicant_review.comments.presence || 'none')
@@ -0,0 +1 @@
1
+ - # Intentionally blank
@@ -0,0 +1,20 @@
1
+ = render 'layout' do
2
+ = render 'effective/applicant_reviews/content', resource: resource
3
+
4
+ = card('Summary') do
5
+ = render 'effective/applicants/summary', applicant: resource.applicant
6
+
7
+ .mb-2
8
+ = collapse('Show application...', card_class: 'my-2') do
9
+ = render 'effective/applicants/applicant', applicant: resource.applicant
10
+
11
+ = card('Recommendation') do
12
+
13
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
14
+ = f.hidden_field :id
15
+
16
+ %p My recommendation is
17
+ = f.radios :recommendation, EffectiveMemberships.ApplicantReview.recommendations, label: false
18
+ = f.text_area :comments, label: 'Reviewer comments'
19
+
20
+ = f.save 'Submit Recommendation'
@@ -0,0 +1,14 @@
1
+ = render 'layout' do
2
+ = render 'effective/applicant_reviews/content', resource: resource
3
+
4
+ = card do
5
+ = render 'effective/applicants/summary', applicant: resource.applicant
6
+ = render 'effective/applicants/applicant', applicant: resource.applicant
7
+
8
+ = card do
9
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
10
+ = f.hidden_field :id
11
+
12
+ = f.text_area :comments, label: 'Reviewer comments'
13
+
14
+ = f.save 'Save and Continue'
@@ -0,0 +1,18 @@
1
+ = render 'layout' do
2
+ = render 'effective/applicant_reviews/content', resource: resource
3
+
4
+ = card do
5
+ %p Welcome #{current_user}!
6
+
7
+ %p You are starting an applicant review for #{resource.applicant}.
8
+
9
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
10
+ = f.hidden_field :id
11
+
12
+ = f.hidden_field :applicant_type, value: f.object.applicant.class.name
13
+ = f.hidden_field :applicant_id
14
+
15
+ = f.hidden_field :reviewer_type
16
+ = f.hidden_field :reviewer_id
17
+
18
+ = f.save 'Save and Continue'
@@ -0,0 +1,15 @@
1
+ = render 'layout' do
2
+ = render 'effective/applicant_reviews/content', resource: resource
3
+
4
+ - raise('expected a submitted applicant review') unless resource.was_submitted?
5
+
6
+ .alert.alert-success.mb-4
7
+ This review was submitted on #{resource.submitted_at.strftime('%F')}
8
+
9
+ .mb-4
10
+ = collapse('Show application...', card_class: 'my-2') do
11
+ = render 'effective/applicants/applicant', applicant: resource.applicant
12
+
13
+ = render 'effective/applicant_reviews/applicant_review', applicant_review: resource
14
+
15
+ = link_to "Return to Dashboard", root_path, class: 'btn btn-lg btn-primary btn-block'
@@ -54,11 +54,12 @@
54
54
  %th Stream
55
55
  %td= applicant.stream
56
56
 
57
- - if applicant.orders.present?
57
+ - orders = applicant.orders.select { |order| EffectiveResources.authorized?(self, :show, order) }
58
+ - if orders.present?
58
59
  %tr
59
- %th Order#{'s' if applicant.orders.length > 1}
60
+ %th Order#{'s' if orders.length > 1}
60
61
  %td
61
- - applicant.orders.each do |order|
62
+ - orders.each do |order|
62
63
  - if request.path.start_with?('/admin')
63
64
  = link_to(order, effective_orders.edit_admin_order_path(order))
64
65
  - else
@@ -0,0 +1,19 @@
1
+ ---
2
+ subject: 'An applicant review has been submitted'
3
+ from: 'admin@example.com'
4
+ ---
5
+ Hello admin,
6
+
7
+ The application for {{ user.name }} for {{ applicant.to_category }} has been reviewed by #{{ reviewer.name }}
8
+
9
+ They recommended {{ applicant_review.recommendation }} with the following comments:
10
+
11
+ {{ applicant_review.comments }}
12
+
13
+ For more details please visit
14
+
15
+ {{ applicant.admin_url }}
16
+
17
+ Thanks,
18
+
19
+ Have a great day!
data/config/routes.rb CHANGED
@@ -7,6 +7,10 @@ EffectiveMemberships::Engine.routes.draw do
7
7
  scope module: 'effective' do
8
8
  resources :applicants, only: [:new, :show, :destroy] do
9
9
  resources :build, controller: :applicants, only: [:show, :update]
10
+
11
+ resources :applicant_reviews, only: [:new, :show, :destroy] do
12
+ resources :build, controller: :applicant_reviews, only: [:show, :update]
13
+ end
10
14
  end
11
15
 
12
16
  resources :applicant_endorsements, only: [:new, :create, :show, :update] do
@@ -45,6 +49,8 @@ EffectiveMemberships::Engine.routes.draw do
45
49
  post :notify, on: :member
46
50
  end
47
51
 
52
+ resources :applicant_reviews, only: [:index, :show]
53
+
48
54
  resources :fees
49
55
  resources :categories, only: [:index, :edit, :update]
50
56
 
@@ -1,3 +1,3 @@
1
1
  module EffectiveMemberships
2
- VERSION = '0.6.13'
2
+ VERSION = '0.7.0'
3
3
  end
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.6.13
4
+ version: 0.7.0
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-09-14 00:00:00.000000000 Z
11
+ date: 2022-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -258,6 +258,7 @@ files:
258
258
  - app/controllers/admin/applicant_course_names_controller.rb
259
259
  - app/controllers/admin/applicant_endorsements_controller.rb
260
260
  - app/controllers/admin/applicant_references_controller.rb
261
+ - app/controllers/admin/applicant_reviews_controller.rb
261
262
  - app/controllers/admin/applicants_controller.rb
262
263
  - app/controllers/admin/categories_controller.rb
263
264
  - app/controllers/admin/fee_payments_controller.rb
@@ -270,6 +271,7 @@ files:
270
271
  - app/controllers/admin/statuses_controller.rb
271
272
  - app/controllers/effective/applicant_endorsements_controller.rb
272
273
  - app/controllers/effective/applicant_references_controller.rb
274
+ - app/controllers/effective/applicant_reviews_controller.rb
273
275
  - app/controllers/effective/applicants_controller.rb
274
276
  - app/controllers/effective/fee_payments_controller.rb
275
277
  - app/controllers/effective/membership_cards_controller.rb
@@ -280,6 +282,7 @@ files:
280
282
  - app/datatables/admin/effective_applicant_course_names_datatable.rb
281
283
  - app/datatables/admin/effective_applicant_endorsements_datatable.rb
282
284
  - app/datatables/admin/effective_applicant_references_datatable.rb
285
+ - app/datatables/admin/effective_applicant_reviews_datatable.rb
283
286
  - app/datatables/admin/effective_applicants_datatable.rb
284
287
  - app/datatables/admin/effective_categories_datatable.rb
285
288
  - app/datatables/admin/effective_fee_payments_datatable.rb
@@ -296,7 +299,9 @@ files:
296
299
  - app/datatables/effective_applicant_equivalences_datatable.rb
297
300
  - app/datatables/effective_applicant_experiences_datatable.rb
298
301
  - app/datatables/effective_applicant_references_datatable.rb
302
+ - app/datatables/effective_applicant_reviews_datatable.rb
299
303
  - app/datatables/effective_applicants_datatable.rb
304
+ - app/datatables/effective_available_applicant_reviews_datatable.rb
300
305
  - app/datatables/effective_fee_payments_datatable.rb
301
306
  - app/datatables/effective_memberships_directory_datatable.rb
302
307
  - app/datatables/effective_organizations_datatable.rb
@@ -342,6 +347,7 @@ files:
342
347
  - app/views/admin/applicant_course_name/_form.html.haml
343
348
  - app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml
344
349
  - app/views/admin/applicant_references/_applicant_reference.html.haml
350
+ - app/views/admin/applicant_reviews/_applicant_review.html.haml
345
351
  - app/views/admin/applicants/_form.html.haml
346
352
  - app/views/admin/applicants/_form_applicant.html.haml
347
353
  - app/views/admin/applicants/_form_approve.html.haml
@@ -395,6 +401,16 @@ files:
395
401
  - app/views/effective/applicant_references/_form_declaration.html.haml
396
402
  - app/views/effective/applicant_references/complete.html.haml
397
403
  - app/views/effective/applicant_references/edit.html.haml
404
+ - app/views/effective/applicant_reviews/_applicant_review.html.haml
405
+ - app/views/effective/applicant_reviews/_content.html.haml
406
+ - app/views/effective/applicant_reviews/_dashboard.html.haml
407
+ - app/views/effective/applicant_reviews/_layout.html.haml
408
+ - app/views/effective/applicant_reviews/_recommendation.html.haml
409
+ - app/views/effective/applicant_reviews/_review.html.haml
410
+ - app/views/effective/applicant_reviews/recommendation.html.haml
411
+ - app/views/effective/applicant_reviews/review.html.haml
412
+ - app/views/effective/applicant_reviews/start.html.haml
413
+ - app/views/effective/applicant_reviews/submitted.html.haml
398
414
  - app/views/effective/applicants/_applicant.html.haml
399
415
  - app/views/effective/applicants/_content.html.haml
400
416
  - app/views/effective/applicants/_course_amounts.html.haml
@@ -470,6 +486,7 @@ files:
470
486
  - app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid
471
487
  - app/views/effective/memberships_mailer/applicant_missing_info.liquid
472
488
  - app/views/effective/memberships_mailer/applicant_reference_notification.liquid
489
+ - app/views/effective/memberships_mailer/applicant_review_submitted.liquid
473
490
  - app/views/effective/organizations/_dashboard.html.haml
474
491
  - app/views/effective/organizations/_form.html.haml
475
492
  - app/views/effective/organizations/_form_organization.html.haml