Bug 29002: Fix async bookings load issues
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / modules / bookings / list.tt
1 [% USE raw %]
2 [% USE Asset %]
3 [% USE KohaDates %]
4 [% SET footerjs = 1 %]
5 [% INCLUDE 'doc-head-open.inc' %]
6 <title>Bookings for [% INCLUDE 'biblio-title-head.inc' %] &rsaquo; Bookings &rsaquo; Circulation &rsaquo; Koha</title>
7 [% INCLUDE 'doc-head-close.inc' %]
8 </head>
9
10 <body id="circ_request" class="catalog">
11
12 [% WRAPPER 'header.inc' %]
13     [% INCLUDE 'cat-search.inc' %]
14 [% END %]
15
16 [% WRAPPER 'sub-header.inc' %]
17     [% WRAPPER breadcrumbs %]
18         [% WRAPPER breadcrumb_item %]
19             <a href="/cgi-bin/koha/catalogue/search.pl">Catalog</a>
20         [% END %]
21         [% WRAPPER breadcrumb_item %]
22             [% INCLUDE 'biblio-title.inc' link = 1 %]
23         [% END %]
24         [% WRAPPER breadcrumb_item bc_active= 1 %]
25             <span>Bookings</span>
26         [% END %]
27     [% END #/ WRAPPER breadcrumbs %]
28 [% END #/ WRAPPER sub-header.inc %]
29
30 <div class="main container-fluid">
31     <div class="row">
32         <div class="col-sm-10 col-sm-push-2">
33             <main>
34                 <div class="row">
35                     [% INCLUDE 'cat-toolbar.inc' %]
36                     <h1>Bookings for [% INCLUDE 'biblio-title-head.inc' %]</h1>
37                     <div class="page-section" id="bookings-timeline"></div>
38                     <div class="page-section">
39                         <table id="bookings_table"></table>
40                     </div>
41                 </div>
42             </main>
43         </div> <!-- /.col-sm-10.col-sm-push-2 -->
44
45         <div class="col-sm-2 col-sm-pull-10">
46             <aside>
47                 [% INCLUDE 'biblio-view-menu.inc' %]
48             </aside>
49         </div> <!-- /.col-sm-2.col-sm-pull-10 -->
50     </div> <!-- /.row -->
51
52     [% INCLUDE modals/cancel_booking.inc %]
53
54     [% MACRO jsinclude BLOCK %]
55     [% Asset.js("lib/dayjs/dayjs.min.js") | $raw %]
56     [% Asset.js("lib/vis-timeline/vis-timeline-graph2d.min.js") | $raw %]
57     [% INCLUDE 'datatables.inc' %]
58     [% INCLUDE 'columns_settings.inc' %]
59     [% INCLUDE 'calendar.inc' %]
60     [% INCLUDE 'select2.inc' %]
61     [% INCLUDE 'js-patron-format.inc' %]
62     [% INCLUDE 'js-date-format.inc' %]
63     [% Asset.js("js/place_booking_modal.js") | $raw %]
64     [% Asset.js("js/cancel_booking_modal.js") | $raw %]
65 <script>
66     var cancel_success = 0;
67     var update_success = 0;
68     var bookings_table;
69     var timeline;
70     $(document).ready(function(){
71
72         var items = $.ajax({
73             url: '/api/v1/biblios/[% biblionumber | uri %]/items?bookable=1',
74             dataType: 'json',
75             type: 'GET'
76         });
77         var bookings = $.ajax({
78             url: '/api/v1/biblios/[% biblionumber | uri %]/bookings',
79             headers: { 'x-koha-embed': 'patron' },
80             dataType: 'json',
81             type: 'GET',
82         });
83
84         $.when(items, bookings).then(
85             function(items, bookings){
86                 var itemsSet = new vis.DataSet([{
87                     id: 0,
88                     content: "Biblio level"
89                 }]);
90                 for (item of items[0]) {
91                     itemsSet.add({
92                         id: item.item_id,
93                         content: "Item " + item.external_id,
94                     });
95                 }
96
97                 var bookingsSet = new vis.DataSet();
98                 for (booking of bookings[0]){
99                     bookingsSet.add({
100                         id: booking.booking_id,
101                         booking: booking.booking_id,
102                         patron: booking.patron_id,
103                         start: dayjs(booking.start_date).toDate(),
104                         end: dayjs(booking.end_date).toDate(),
105                         content: $patron_to_html(booking.patron, {
106                             display_cardnumber: true,
107                             url: false
108                         }),
109                         editable: { remove: true, updateTime: true },
110                         type: 'range',
111                         group: booking.item_id ? booking.item_id : 0
112                     });
113                 }
114
115                 var container = document.getElementById('bookings-timeline');
116                 var options = {
117                     stack: true,
118                     verticalScroll: true,
119                     orientation: {
120                         axis: "both",
121                         item: "top"
122                     },
123                     timeAxis: {scale: 'day', step: 1},
124                     dataAttributes: [ 'booking' ],
125
126                     // always snap to full days, independent of the scale
127                     snap: function (date, scale, step) {
128                         let offset = date.getTimezoneOffset() * 60 * 1000;
129                         let day = 24 * 60 * 60 * 1000;
130                         let seconds = Math.round(date / day) * day;
131                         return seconds + offset;
132                     },
133
134                     // prevent overlapping bookings
135                     onMoving: function (item, callback) {
136                         var overlapping = bookingsSet.get({
137                             filter: function(testItem) {
138                                 if (testItem.id == item.id) {
139                                     return false;
140                                 }
141                                 if (testItem.group != item.group) {
142                                     return false;
143                                 }
144                                 return ((item.start < testItem.end) && (item.end > testItem.start));
145                             }
146                         });
147
148                         if (overlapping.length == 0) {
149                             callback(item);
150                         }
151                     },
152
153                     // action events
154                     onMove: function (data, callback) {
155                         let startDate = dayjs(data.start);
156                         
157                         // set end datetime hours and minutes to the end of the day
158                         let endDate = dayjs(data.end).endOf('day');
159
160                         $('#placeBookingModal').modal('show', $('<button data-booking="'+data.id+'"  data-biblionumber="[% biblionumber | uri %]"  data-itemnumber="'+data.group+'" data-patron="'+data.patron+'" data-start_date="'+startDate.toISOString()+'" data-end_date="'+endDate.toISOString()+'">'));
161                         $('#placeBookingModal').on('hide.bs.modal', function(e) {
162                             if (update_success) {
163                                 update_success = 0;
164                                 callback(data);
165                             }
166                         });
167                     },
168                     onRemove: function (item, callback) {
169                         $('#cancelBookingModal').modal('show', $('<button data-booking="'+item.id+'">'));
170                         $('#cancelBookingModal').on('hide.bs.modal', function(e) {
171                             if (cancel_success) {
172                                 cancel_success = 0;
173                                 callback(item);
174                             }
175                         });
176                     }
177                 };
178                 timeline = new vis.Timeline(container, bookingsSet, itemsSet, options);
179             },
180             function(jqXHR, textStatus, errorThrown){
181                 console.log("Fetch failed");
182             }
183         );
184
185         var bookings_table_url = "/api/v1/biblios/[% biblionumber | uri %]/bookings";
186         bookings_table = $('#bookings_table').kohaTable({
187             "ajax": {
188                 "url": bookings_table_url
189             },
190             "embed": [
191                 "item",
192                 "patron"
193             ],
194             "columns": [{
195                 "data": "booking_id",
196                 "title": "Booking ID",
197                 "visible": false
198             },
199             {
200                 "data": "item.external_id",
201                 "title": "Item",
202                 "searchable": true,
203                 "orderable": true,
204                 "defaultContent": "Any item",
205                 "render": function(data,type,row,meta) {
206                     if ( row.item ) {
207                         return row.item.external_id + " (" + row.booking_id + ")";
208                     } else {
209                         return null;
210                     }
211                 }
212             },
213             {
214                 "data": "patron.firstname:patron.surname",
215                 "title": "Patron",
216                 "searchable": true,
217                 "orderable": true,
218                 "render": function(data, type, row, meta) {
219                     return $patron_to_html(row.patron, {
220                         display_cardnumber: true,
221                         url: true
222                     });
223                 }
224             },
225             {
226                 "data": "start_date",
227                 "title": "Start date",
228                 "searchable": true,
229                 "orderable": true,
230                 "render": function(data, type, row, meta) {
231                     return $date(row.start_date);
232                 }
233             },
234             {
235                 "data": "end_date",
236                 "title": "End date",
237                 "searchable": true,
238                 "orderable": true,
239                 "render": function(data, type, row, meta) {
240                     return $date(row.end_date);
241                 }
242             },
243             {
244                 "data": "",
245                 "title": "Actions",
246                 "class": "actions",
247                 "searchable": false,
248                 "orderable": false,
249                 "render": function(data, type, row, meta) {
250                     var result = '<button type="button" class="btn btn-default btn-xs edit-action" data-toggle="modal" data-target="#placeBookingModal" data-booking="'+row.booking_id+'" data-biblionumber="[% biblionumber | uri %]" data-itemnumber="'+row.item_id+'" data-patron="'+row.patron_id+'" data-start_date="'+row.start_date+'" data-end_date="'+row.end_date+'"><i class="fa fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</button>';
251                     result += '<button type="button" class="btn btn-default btn-xs cancel-action" data-toggle="modal" data-target="#cancelBookingModal" data-booking="'+row.booking_id+'"><i class="fa fa-trash" aria-hidden="true"></i> '+_("Cancel")+'</button>';
252                     return result;
253                 }
254             }]
255         }, [], 0);
256     });
257 </script>
258     [% END %]
259
260 [% INCLUDE 'intranet-bottom.inc' %]