Lines 12-24
Link Here
|
12 |
[% END %]</title |
12 |
[% END %]</title |
13 |
> |
13 |
> |
14 |
[% INCLUDE 'doc-head-close.inc' %] |
14 |
[% INCLUDE 'doc-head-close.inc' %] |
15 |
<style> |
15 |
[% Asset.css("css/bookings.css") | $raw %] |
16 |
#bookings-timeline .vis-item.vis-range { |
|
|
17 |
&.cancelled { |
18 |
background: rgba(128, 128, 128, 0.3); |
19 |
} |
20 |
} |
21 |
</style> |
22 |
</head> |
16 |
</head> |
23 |
|
17 |
|
24 |
<body id="circ_request" class="catalog"> |
18 |
<body id="circ_request" class="catalog"> |
Lines 47-56
Link Here
|
47 |
<h1>Bookings for [% INCLUDE 'biblio-title-head.inc' %]</h1> |
41 |
<h1>Bookings for [% INCLUDE 'biblio-title-head.inc' %]</h1> |
48 |
<div class="page-section" id="bookings-timeline"></div> |
42 |
<div class="page-section" id="bookings-timeline"></div> |
49 |
<div class="page-section"> |
43 |
<div class="page-section"> |
50 |
<fieldset class="action filters d-flex gap-2" style="cursor: pointer;"> |
44 |
[% |
51 |
<a id="expired_filter" data-filter="expired"><i class="fa fa-bars"></i> Show expired</a> |
45 |
INCLUDE 'table-filters.inc' |
52 |
<a id="cancelled_filter" data-filter="cancelled"><i class="fa fa-bars"></i> Show cancelled</a> |
46 |
filters = [ |
53 |
</fieldset> |
47 |
{ id = 'expired', label_show = t('Include expired'), label_hide = t('Exclude expired') }, |
|
|
48 |
{ id = 'cancelled', label_show = t('Include cancelled'), label_hide = t('Exclude cancelled') }, |
49 |
] |
50 |
filter_class = 'bookings' |
51 |
%] |
54 |
|
52 |
|
55 |
<table id="bookings_table"></table> |
53 |
<table id="bookings_table"></table> |
56 |
</div> |
54 |
</div> |
Lines 124-129
Link Here
|
124 |
start: dayjs(booking.start_date).toDate(), |
122 |
start: dayjs(booking.start_date).toDate(), |
125 |
end: dayjs(booking.end_date).toDate(), |
123 |
end: dayjs(booking.end_date).toDate(), |
126 |
content: !isActive ? `<s>${patronContent}</s>` : patronContent, |
124 |
content: !isActive ? `<s>${patronContent}</s>` : patronContent, |
|
|
125 |
className: booking.status === "cancelled" ? "cancelled" : "", |
127 |
[% IF CAN_user_circulate_manage_bookings %] |
126 |
[% IF CAN_user_circulate_manage_bookings %] |
128 |
editable: booking.status !== "cancelled" ? { remove: true, updateTime: true } : false, |
127 |
editable: booking.status !== "cancelled" ? { remove: true, updateTime: true } : false, |
129 |
[% ELSE %] |
128 |
[% ELSE %] |
Lines 134-150
Link Here
|
134 |
}); |
133 |
}); |
135 |
} |
134 |
} |
136 |
|
135 |
|
137 |
const cancelledItems = document.querySelectorAll('#bookings-timeline .vis-item.vis-range.cancelled'); |
|
|
138 |
cancelledItems.forEach(item => { |
139 |
item.style.background = 'repeating-linear-gradient(' + |
140 |
'135deg,' + |
141 |
'rgba(211, 211, 211, 0.5) 0,' + |
142 |
'rgba(211, 211, 211, 0.5) 10px,' + |
143 |
'transparent 10px,' + |
144 |
'transparent 20px' + |
145 |
');'; |
146 |
}); |
147 |
|
148 |
var container = document.getElementById('bookings-timeline'); |
136 |
var container = document.getElementById('bookings-timeline'); |
149 |
var options = { |
137 |
var options = { |
150 |
stack: true, |
138 |
stack: true, |
Lines 201-207
Link Here
|
201 |
}); |
189 |
}); |
202 |
}, |
190 |
}, |
203 |
onRemove: function (item, callback) { |
191 |
onRemove: function (item, callback) { |
204 |
$('#cancelBookingModal').modal('show', $('<button data-booking="'+item.id+'">')); |
192 |
const button = document.createElement('button'); |
|
|
193 |
button.dataset.booking = item.booking || item.id; |
194 |
$('#cancelBookingModal').modal('show', button); |
205 |
$('#cancelBookingModal').on('hide.bs.modal', function(e) { |
195 |
$('#cancelBookingModal').on('hide.bs.modal', function(e) { |
206 |
if (cancel_success) { |
196 |
if (cancel_success) { |
207 |
cancel_success = 0; |
197 |
cancel_success = 0; |
Lines 219-243
Link Here
|
219 |
} |
209 |
} |
220 |
); |
210 |
); |
221 |
|
211 |
|
222 |
const filterStates = { expired: true, cancelled: false }; |
212 |
const filters = ["expired", "cancelled"].reduce((acc, filter) => { |
|
|
213 |
acc[filter] = document.getElementById(`filter-${filter}`); |
214 |
return acc; |
215 |
}, {}); |
216 |
|
217 |
Object.values(filters).forEach(filter => { |
218 |
filter.addEventListener("click", handleFilter); |
219 |
}); |
220 |
|
221 |
const isFilterActive = (filter) => "filtered" in filter.dataset; |
222 |
const isShowingItems = (filter) => !isFilterActive(filter); |
223 |
const additional_filters = { |
223 |
const additional_filters = { |
224 |
end_date: () => { |
224 |
end_date: () => { |
225 |
if (filterStates.expired) { |
225 |
if (isShowingItems(filters.expired)) { |
226 |
let today = new Date(); |
226 |
let today = new Date(); |
227 |
return { ">=": today.toISOString() }; |
227 |
return { ">=": today.toISOString() }; |
228 |
} |
228 |
} |
229 |
}, |
229 |
}, |
230 |
status: () => { |
230 |
status: () => { |
231 |
const defaults = ["new", "pending", "active"]; |
231 |
const defaults = ["new", "pending", "active"]; |
232 |
if (filterStates.cancelled) { |
232 |
const filtered = [...defaults]; |
233 |
const filtered = [...defaults, "cancelled"]; |
233 |
if (isShowingItems(filters.cancelled)) { |
234 |
return { "-in": filtered }; |
234 |
filtered.push("cancelled"); |
235 |
} |
235 |
} |
236 |
|
236 |
return { "-in": filtered }; |
237 |
return { "-in": defaults }; |
|
|
238 |
}, |
237 |
}, |
239 |
}; |
238 |
}; |
240 |
|
239 |
|
|
|
240 |
function handleFilter(e) { |
241 |
const target = e.target; |
242 |
const anchor = target.closest("a"); |
243 |
|
244 |
if (isFilterActive(anchor)) { |
245 |
delete anchor.dataset.filtered; |
246 |
} else { |
247 |
anchor.dataset.filtered = ""; |
248 |
} |
249 |
|
250 |
bookings_table.DataTable().ajax.reload(() => { |
251 |
const anyFiltersActive = Object.values(filters).some(isShowingItems); |
252 |
bookings_table |
253 |
.DataTable() |
254 |
.column("status:name") |
255 |
.visible(anyFiltersActive, false); |
256 |
}); |
257 |
} |
258 |
|
241 |
var bookings_table_url = "/api/v1/biblios/%s/bookings".format(biblionumber); |
259 |
var bookings_table_url = "/api/v1/biblios/%s/bookings".format(biblionumber); |
242 |
bookings_table = $('#bookings_table').kohaTable({ |
260 |
bookings_table = $('#bookings_table').kohaTable({ |
243 |
"ajax": { |
261 |
"ajax": { |
Lines 374-384
Link Here
|
374 |
"orderable": false, |
392 |
"orderable": false, |
375 |
"render": function(data, type, row, meta) { |
393 |
"render": function(data, type, row, meta) { |
376 |
let result = ""; |
394 |
let result = ""; |
377 |
let is_cancelled = row.status === "cancelled"; |
395 |
const is_readonly = row.status === "cancelled"; |
378 |
[% IF CAN_user_circulate_manage_bookings %] |
396 |
[% IF CAN_user_circulate_manage_bookings %] |
379 |
if (!is_cancelled) { |
397 |
if (!is_readonly) { |
380 |
result += '<button type="button" class="btn btn-default btn-xs edit-action" data-bs-toggle="modal" data-bs-target="#placeBookingModal" data-booking="%s" data-biblionumber="%s" data-itemnumber="%s" data-patron="%s" data-pickup_library="%s" data-start_date="%s" data-end_date="%s" data-item_type_id="%s"><i class="fa fa-pencil" aria-hidden="true"></i> %s</button>'.format(row.booking_id, biblionumber, row.item_id, row.patron_id, row.pickup_library_id, row.start_date, row.end_date, row.item.item_type_id, _("Edit")); |
398 |
result += ` |
381 |
result += '<button type="button" class="btn btn-default btn-xs cancel-action" data-bs-toggle="modal" data-bs-target="#cancelBookingModal" data-booking="%s"><i class="fa fa-trash" aria-hidden="true"></i> %s</button>'.format(row.booking_id, _("Cancel")); |
399 |
<button type="button" class="btn btn-default btn-xs edit-action" |
|
|
400 |
data-bs-toggle="modal" |
401 |
data-bs-target="#placeBookingModal" |
402 |
data-booking="%s" |
403 |
data-biblionumber="%s" |
404 |
data-itemnumber="%s" |
405 |
data-patron="%s" |
406 |
data-pickup_library="%s" |
407 |
data-start_date="%s" |
408 |
data-end_date="%s" |
409 |
data-item_type_id="%s" |
410 |
> |
411 |
<i class="fa fa-pencil" aria-hidden="true"></i> %s |
412 |
</button> |
413 |
`.format( |
414 |
escape_str(row.booking_id), |
415 |
escape_str(biblionumber), |
416 |
escape_str(row.item_id), |
417 |
escape_str(row.patron_id), |
418 |
escape_str(row.pickup_library_id), |
419 |
escape_str(row.start_date), |
420 |
escape_str(row.end_date), |
421 |
escape_str(row.item.item_type_id), |
422 |
_("Edit") |
423 |
); |
424 |
result += ` |
425 |
<button type="button" class="btn btn-default btn-xs cancel-action" |
426 |
data-bs-toggle="modal" |
427 |
data-bs-target="#cancelBookingModal" |
428 |
data-booking="%s" |
429 |
> |
430 |
<i class="fa fa-trash" aria-hidden="true"></i> %s |
431 |
</button> |
432 |
`.format( |
433 |
escape_str(row.booking_id), |
434 |
_("Cancel") |
435 |
); |
382 |
} |
436 |
} |
383 |
[% END %] |
437 |
[% END %] |
384 |
return result; |
438 |
return result; |
Lines 386-436
Link Here
|
386 |
}] |
440 |
}] |
387 |
}, undefined, 0, additional_filters); |
441 |
}, undefined, 0, additional_filters); |
388 |
|
442 |
|
389 |
document |
|
|
390 |
.getElementById("expired_filter") |
391 |
.addEventListener("click", (e) => |
392 |
handleFilter( |
393 |
e, |
394 |
{ active: _("Hide expired"), inactive: _("Show expired") }, |
395 |
filterStates, |
396 |
), |
397 |
); |
398 |
|
399 |
document |
400 |
.getElementById("cancelled_filter") |
401 |
.addEventListener("click", (e) => |
402 |
handleFilter( |
403 |
e, |
404 |
{ active: _("Hide cancelled"), inactive: _("Show cancelled") }, |
405 |
filterStates, |
406 |
), |
407 |
); |
408 |
|
409 |
$("#cancellation-reason").comboBox({ |
443 |
$("#cancellation-reason").comboBox({ |
410 |
displayProperty: 'name', |
444 |
displayProperty: 'name', |
411 |
placeholder: _("Select or type a reason"), |
445 |
placeholder: _("Select or type a reason"), |
412 |
}); |
446 |
}); |
413 |
|
447 |
|
414 |
function handleFilter(e, text, filterStates) { |
|
|
415 |
const target = e.target; |
416 |
const { filter } = target.dataset; |
417 |
if (target.classList.contains("filtered")) { |
418 |
filterStates[filter] = false; |
419 |
target.innerHTML = `<i class="fa fa-bars"></i> ${text.inactive}`; |
420 |
} else { |
421 |
filterStates[filter] = true; |
422 |
target.innerHTML = `<i class="fa fa-filter"></i> ${text.active}`; |
423 |
} |
424 |
|
425 |
bookings_table.DataTable().ajax.reload(() => { |
426 |
bookings_table |
427 |
.DataTable() |
428 |
.column("status:name") |
429 |
.visible(filterStates[filter], false); |
430 |
}); |
431 |
|
432 |
target.classList.toggle("filtered"); |
433 |
} |
434 |
}); |
448 |
}); |
435 |
</script> |
449 |
</script> |
436 |
[% END %] |
450 |
[% END %] |
437 |
- |
|
|