Skip to content

Commit 4b37087

Browse files
ShamiTomitagithub username
authored and
github username
committedSep 1, 2023
Co-authored-by: Eric Halverson <[email protected]>
styling and controller updates Add request test for bulk assignment add datatable checkboxes to volunteer#index view for bulk assignment update styles for volunteer#index table update volunteers model to include the bulk column
1 parent dd993fd commit 4b37087

16 files changed

+347
-33
lines changed
 

‎app/assets/stylesheets/application.scss

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
);
66
@use "bootstrap-datepicker/dist/css/bootstrap-datepicker";
77
@use "datatables.net-dt/css/jquery.dataTables";
8+
@use "jquery-datatables-checkboxes/css/dataTables.checkboxes";
89
@use "@fortawesome/fontawesome-free/scss/fontawesome";
910
@use "@fortawesome/fontawesome-free/scss/solid";
1011
@use "select2/dist/css/select2";

‎app/assets/stylesheets/pages/volunteers.scss

+13
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,16 @@ body.volunteers {
1818
}
1919
}
2020
}
21+
22+
23+
table#volunteers.dataTable.hover tbody tr.selected > * {
24+
background-color: rgba(54, 92, 245, 0.1);
25+
box-shadow: none;
26+
color: inherit;
27+
}
28+
29+
table#volunteers.dataTable.hover > tbody > tr.selected:hover > * {
30+
background-color: rgba(54, 92, 245, 0.15);
31+
box-shadow: none !important;
32+
color: inherit;
33+
}

‎app/controllers/supervisor_volunteers_controller.rb

+55-1
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,67 @@ def unassign
2525
redirect_to request.referer, notice: flash_message
2626
end
2727

28+
def bulk_assignment
29+
authorize :supervisor_volunteer
30+
if mass_assign_volunteers?
31+
volunteer_ids = supervisor_volunteer_params[:volunteer_ids]
32+
supervisor = supervisor_volunteer_params[:supervisor_id]
33+
vol = "Volunteer".pluralize(volunteer_ids.length)
34+
35+
if supervisor == "unassign"
36+
name_array = bulk_unassign!(volunteer_ids)
37+
flash_message = "#{vol} #{name_array.to_sentence} successfully unassigned"
38+
else
39+
supervisor = supervisor_volunteer_parent
40+
name_array = bulk_assign!(supervisor, volunteer_ids)
41+
flash_message = "#{vol} #{name_array.to_sentence} successfully reassigned to #{supervisor.display_name}"
42+
end
43+
44+
redirect_to volunteers_path, notice: flash_message
45+
else
46+
redirect_to volunteers_path, notice: "Please select at least one volunteer and one supervisor."
47+
end
48+
end
49+
2850
private
2951

3052
def supervisor_volunteer_params
31-
params.require(:supervisor_volunteer).permit(:supervisor_id, :volunteer_id)
53+
params.require(:supervisor_volunteer).permit(:supervisor_id, :volunteer_id, volunteer_ids: [])
3254
end
3355

3456
def supervisor_volunteer_parent
3557
Supervisor.find(params[:supervisor_id] || supervisor_volunteer_params[:supervisor_id])
3658
end
59+
60+
def mass_assign_volunteers?
61+
supervisor_volunteer_params[:volunteer_ids] && supervisor_volunteer_params[:supervisor_id] ? true : false
62+
end
63+
64+
def bulk_assign!(supervisor, volunteer_ids)
65+
created_volunteers = []
66+
volunteer_ids.each do |vol_id|
67+
if (supervisor_volunteer = SupervisorVolunteer.find_by(volunteer_id: vol_id.to_i))
68+
supervisor_volunteer.update!(supervisor_id: supervisor.id)
69+
else
70+
supervisor_volunteer = supervisor.supervisor_volunteers.create(volunteer_id: vol_id.to_i)
71+
end
72+
supervisor_volunteer.is_active = true
73+
volunteer = supervisor_volunteer.volunteer
74+
supervisor_volunteer.save
75+
created_volunteers << volunteer.display_name.to_s
76+
end
77+
created_volunteers
78+
end
79+
80+
def bulk_unassign!(volunteer_ids)
81+
unassigned_volunteers = []
82+
volunteer_ids.each do |vol_id|
83+
supervisor_volunteer = SupervisorVolunteer.find_by(volunteer_id: vol_id.to_i)
84+
supervisor_volunteer.update(is_active: false)
85+
volunteer = supervisor_volunteer.volunteer
86+
supervisor_volunteer.save
87+
unassigned_volunteers << volunteer.display_name.to_s # take into account single assignments and give multiple assignments proper format
88+
end
89+
unassigned_volunteers
90+
end
3791
end

‎app/controllers/volunteers_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class VolunteersController < ApplicationController
66

77
def index
88
authorize Volunteer
9+
@supervisors = policy_scope(current_organization.supervisors)
910
end
1011

1112
def show

‎app/helpers/volunteers_helper.rb

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module VolunteersHelper
2+
def show_supervisors(supervisors)
3+
list = "<option value='unassign'>-- No Supervisor --</option>"
4+
if supervisors
5+
supervisors.active.each do |supervisor|
6+
list += "<option value='#{supervisor.id}'>#{supervisor.display_name}</option>"
7+
end
8+
end
9+
list.html_safe
10+
end
11+
end

‎app/javascript/application.js

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import 'trix'
88
import '@rails/actiontext'
99

1010
require('datatables.net-dt')(null, window.jQuery) // First parameter is the global object. Defaults to window if null
11+
require('datatables.net-select')(null, window.jQuery)
12+
require('jquery-datatables-checkboxes')(null, window.jQuery)
1113
require('select2')(window.jQuery)
1214
require('@rails/ujs').start()
1315
require('@rails/activestorage').start()

‎app/javascript/src/dashboard.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,21 @@ $(() => { // JQuery's callback for the DOM loading
134134
}
135135
})
136136
},
137-
order: [[6, 'desc']],
137+
order: [[7, 'desc']],
138+
select: {
139+
style: 'multi'
140+
},
138141
columns: [
142+
{
143+
data: 'id',
144+
targets: 0,
145+
searchable: false,
146+
orderable: false,
147+
checkboxes: {
148+
selectRow: true,
149+
stateSave: false
150+
}
151+
},
139152
{
140153
name: 'display_name',
141154
render: (data, type, row, meta) => {
@@ -293,6 +306,29 @@ $(() => { // JQuery's callback for the DOM loading
293306
}
294307
})
295308

309+
$('#form-bulk-assignment').on('submit', function (e) {
310+
const form = this
311+
const rowsSelected = volunteersTable.column(0).checkboxes.selected()
312+
313+
$.each(rowsSelected, function (index, rowId) {
314+
$(form).append(
315+
$('<input>')
316+
.attr('type', 'hidden')
317+
.attr('name', 'supervisor_volunteer[volunteer_ids][]')
318+
.val(rowId)
319+
)
320+
})
321+
})
322+
323+
volunteersTable.column(0).on('change', function () {
324+
const rowsSelected = volunteersTable.column(0).checkboxes.selected()
325+
if (rowsSelected.count() === 0) {
326+
$('#volunteers-selected').html('')
327+
} else {
328+
$('#volunteers-selected').html('s (' + rowsSelected.count() + ')')
329+
}
330+
})
331+
296332
// Because the table saves state, we have to check/uncheck modal inputs based on what
297333
// columns are visible
298334
volunteersTable.columns().every(function (index) {

‎app/models/volunteer.rb

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
class Volunteer < User
44
devise :invitable, invite_for: 1.year
55

6+
BULK_COLUMN = "bulk"
67
NAME_COLUMN = "name"
78
EMAIL_COLUMN = "email"
89
SUPERVISOR_COLUMN = "supervisor"
@@ -16,6 +17,7 @@ class Volunteer < User
1617
EXTRA_LANGUAGES_COLUMN = "has_any_extra_languages"
1718
ACTIONS_COLUMN = "actions"
1819
TABLE_COLUMNS = [
20+
BULK_COLUMN,
1921
NAME_COLUMN,
2022
EMAIL_COLUMN,
2123
SUPERVISOR_COLUMN,

‎app/policies/supervisor_volunteer_policy.rb

+4
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@ def create?
66
def unassign?
77
user.casa_admin? || user.supervisor?
88
end
9+
10+
def bulk_assignment?
11+
user.casa_admin? || user.supervisor?
12+
end
913
end

‎app/views/supervisors/index.html.erb

+1-5
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,14 @@
129129
<table id="active_volunteers" class="table">
130130
<thead>
131131
<tr>
132-
<th></th>
133132
<th><h6>Active volunteers not assigned to supervisors</h6></th>
134133
<th><h6>Assigned to Case(s)</h6></th>
135134
</tr>
136135
<!-- end table row-->
137136
</thead>
138137
<tbody>
139-
<% @available_volunteers.each_with_index do |volunteer, index| %>
138+
<% @available_volunteers.each do |volunteer| %>
140139
<tr>
141-
<td>
142-
<%= index + 1 %>
143-
</td>
144140
<td>
145141
<%= link_to volunteer.display_name, edit_volunteer_path(volunteer) %>
146142
</td>

‎app/views/volunteers/index.html.erb

+75-23
Original file line numberDiff line numberDiff line change
@@ -162,33 +162,85 @@
162162
<div class="col-lg-12">
163163
<div class="card-style mb-30">
164164
<h6 class="mb-10">Responsive Data Table</h6>
165+
<span class="mb-3">
166+
<button type="button" class="main-btn dark-btn btn-sm mb-2 mb-md-0" data-bs-toggle="modal" data-bs-target="#bulk-assigment-modal">
167+
<i class="lni lni-users mr-10"></i>
168+
Manage Volunteer<span class="d-inline" id="volunteers-selected"></span>
169+
</button>
170+
</span>
165171
<div class="table-responsive">
166-
<table
167-
class="table"
168-
id="volunteers"
169-
data-source="<%= datatable_volunteers_path format: :json %>">
170-
<thead>
171-
<tr>
172-
<th>Name</th>
173-
<th>Email</th>
174-
<th>Supervisor</th>
175-
<th>Status</th>
176-
<th>Assigned To Transition Aged Youth</th>
177-
<th>Case Number(s)</th>
178-
<th>Last Attempted Contact</th>
179-
<th>Contacts Made in Past 60 Days</th>
180-
<th>Hours spent in last 30 days</th>
181-
<th>Extra Languages</th>
182-
<th>Actions</th>
183-
</tr>
184-
</thead>
185-
<tbody>
186-
</tbody>
187-
</table>
172+
<%= form_with(url: bulk_assignment_supervisor_volunteers_path, id: "form-bulk-assignment") do |form| %>
173+
<table
174+
class="table hover"
175+
id="volunteers"
176+
data-source="<%= datatable_volunteers_path format: :json %>">
177+
<thead>
178+
<tr>
179+
<th></th>
180+
<th>Name</th>
181+
<th>Email</th>
182+
<th>Supervisor</th>
183+
<th>Status</th>
184+
<th>Assigned To Transition Aged Youth</th>
185+
<th>Case Number(s)</th>
186+
<th>Last Attempted Contact</th>
187+
<th>Contacts Made in Past 60 Days</th>
188+
<th>Hours spent in last 30 days</th>
189+
<th>Extra Languages</th>
190+
<th>Actions</th>
191+
</tr>
192+
</thead>
193+
<tbody>
194+
</tbody>
195+
</table>
196+
197+
<div class="warning-modal">
198+
<div class="modal fade" id="bulk-assigment-modal" tabindex="-1" aria-hidden="true">
199+
<div class="modal-dialog modal-dialog-centered">
200+
<div class="modal-content card-style">
201+
<div class="modal-header px-0 border-0">
202+
<h5 class="text-bold">Bulk Volunteer Assignment</h5>
203+
<button
204+
class="border-0 bg-transparent h1"
205+
data-bs-dismiss="modal"
206+
type="button">
207+
<i class="lni lni-cross-circle"></i>
208+
</button>
209+
</div>
210+
<div class="modal-body px-0">
211+
<div class="mb-30">
212+
<div class="select-style-1">
213+
<label for='supervisor_volunteer_supervisor_id'>
214+
Choose a new supervisor from the dropdown below to reassign selected volunteers, or select “No Supervisor” to unassign volunteers. When you're ready, go ahead and click the "Confirm" button to apply the changes.
215+
</label>
216+
<select name='supervisor_volunteer[supervisor_id]' class='supervisor-bulk-assignment form-control select2' required="required">
217+
<option value="default">Choose A Supervisor</option>
218+
<%= show_supervisors(@supervisors) %>
219+
</select>
220+
</div>
221+
</div>
222+
<div class="action d-flex flex-wrap justify-content-end">
223+
<button
224+
data-bs-dismiss="modal"
225+
class="main-btn danger-btn-outline btn-hover m-1"
226+
type="button"><i class="lni lni-ban mr-10"></i>
227+
Close
228+
</button>
229+
<%= form.button type: :submit, class: "submit-bulk-assignment main-btn dark-btn btn-hover m-1" do %>
230+
Confirm
231+
<% end %>
232+
</div>
233+
</div>
234+
</div>
235+
</div>
236+
</div>
237+
</div>
238+
<% end %>
188239
</div>
189240
</div>
190-
</div>
191241
</div>
242+
</div>
243+
192244
</div>
193245
</div>
194246

‎config/routes.rb

+3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@
125125
end
126126
end
127127
resources :supervisor_volunteers, only: %i[create] do
128+
collection do
129+
post :bulk_assignment
130+
end
128131
member do
129132
patch :unassign
130133
end

‎db/schema.rb

+6
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@
362362
t.index ["casa_org_id"], name: "index_judges_on_casa_org_id"
363363
end
364364

365+
create_table "jwt_denylist", force: :cascade do |t|
366+
t.string "jti", null: false
367+
t.datetime "exp", null: false
368+
t.index ["jti"], name: "index_jwt_denylist_on_jti"
369+
end
370+
365371
create_table "languages", force: :cascade do |t|
366372
t.string "name"
367373
t.bigint "casa_org_id", null: false

‎package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@
2828
"bootstrap-select": "^1.13.18",
2929
"chart.js": "^4.4.0",
3030
"chartjs-adapter-luxon": "^1.3.1",
31-
"datatables.net-dt": "^1.13.6",
32-
"esbuild": "^0.19.2",
31+
"datatables.net-dt": "^1.13.5",
32+
"datatables.net-select": "^1.7.0",
33+
"esbuild": "^0.18.13",
3334
"faker": "^5.5.3",
3435
"jquery": "^3.6.4",
36+
"jquery-datatables-checkboxes": "^1.2.13",
3537
"js-cookie": "^3.0.5",
3638
"jstz": "^2.1.1",
3739
"lodash": "^4.17.21",

0 commit comments

Comments
 (0)