Bug 29002: Trigger table reload on timeline move
[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             dataType: 'json',
80             type: 'GET',
81         });
82
83         $.when(items, bookings).then(
84             function(items, bookings){
85                 var itemsSet = new vis.DataSet([{
86                     id: 0,
87                     content: "Biblio level"
88                 }]);
89                 for (item of items[0]) {
90                     itemsSet.add({
91                         id: item.item_id,
92                         content: "Item " + item.external_id,
93                     });
94                 }
95
96                 var bookingsSet = new vis.DataSet();
97                 for (booking of bookings[0]){
98                     bookingsSet.add({
99                         id: booking.booking_id,
100                         booking: booking.booking_id,
101                         patron: booking.patron_id,
102                         start: dayjs(booking.start_date).toDate(),
103                         end: dayjs(booking.end_date).toDate(),
104                         content: 'Booking: ' + booking.booking_id,
105                         editable: { remove: true, updateTime: true },
106                         type: 'range',
107                         group: booking.item_id ? booking.item_id : 0
108                     });
109                 }
110
111                 var container = document.getElementById('bookings-timeline');
112                 var options = {
113                     stack: true,
114                     verticalScroll: true,
115                     orientation: {
116                         axis: "both",
117                         item: "top"
118                     },
119                     timeAxis: {scale: 'day', step: 1},
120                     dataAttributes: [ 'booking' ],
121
122                     // always snap to full days, independent of the scale
123                     snap: function (date, scale, step) {
124                         let offset = date.getTimezoneOffset() * 60 * 1000;
125                         let day = 24 * 60 * 60 * 1000;
126                         let seconds = Math.round(date / day) * day;
127                         return seconds + offset;
128                     },
129
130                     // prevent overlapping bookings
131                     onMoving: function (item, callback) {
132                         var overlapping = bookingsSet.get({
133                             filter: function(testItem) {
134                                 if (testItem.id == item.id) {
135                                     return false;
136                                 }
137                                 if (testItem.group != item.group) {
138                                     return false;
139                                 }
140                                 return ((item.start < testItem.end) && (item.end > testItem.start));
141                             }
142                         });
143
144                         if (overlapping.length == 0) {
145                             callback(item);
146                         }
147                     },
148
149                     // action events
150                     onMove: function (item, callback) {
151
152                         let startDate = new Date(item.start);
153                         
154                         // set end datetime hours and minutes to the end of the day
155                         let endDate = new Date(item.end);
156                         endDate.setHours(endDate.getHours()+23);
157                         endDate.setMinutes(endDate.getMinutes()+59);
158
159                         let url = '/api/v1/bookings/'+item.booking;
160                         var putting = $.ajax({
161                             method: 'PUT',
162                             url: url,
163                             data: JSON.stringify({
164                                 "booking_id": item.booking,
165                                 "start_date": startDate.toISOString(),
166                                 "end_date": endDate.toISOString(),
167                                 "biblio_id": [% biblionumber | uri %],
168                                 "item_id": item.group != 0 ? item.group : null,
169                                 "patron_id": item.patron
170                             })
171                         });
172
173                         putting.done(function(data) {
174                             bookings_table.api().ajax.reload();
175                             callback(item);
176                         });
177
178                         putting.fail(function(data) {
179                             $('#booking_result').replaceWith('<div id="booking_result" class="alert alert-danger">Failure</div>');
180                         });
181                     },
182                     onRemove: function (item, callback) {
183                         $('#cancelBookingModal').modal('show', $('<button data-booking="'+item.id+'">'));
184                         $('#cancelBookingModal').on('hide.bs.modal', function(e) {
185                             if (cancel_success) {
186                                 cancel_success = 0;
187                                 callback(item);
188                             }
189                         });
190                     }
191                 };
192                 timeline = new vis.Timeline(container, bookingsSet, itemsSet, options);
193             },
194             function(jqXHR, textStatus, errorThrown){
195                 console.log("Fetch failed");
196             }
197         );
198
199         var bookings_table_url = "/api/v1/biblios/[% biblionumber | uri %]/bookings";
200         bookings_table = $('#bookings_table').kohaTable({
201             "ajax": {
202                 "url": bookings_table_url
203             },
204             "embed": [
205                 "item",
206                 "patron"
207             ],
208             "columns": [{
209                 "data": "booking_id",
210                 "title": "Booking ID",
211                 "visible": false,
212                 "searchable": false,
213                 "orderable": false
214             },
215             {
216                 "data": "item.external_id",
217                 "title": "Item",
218                 "searchable": true,
219                 "orderable": true,
220                 "defaultContent": "Any item",
221                 "render": function(data,type,row,meta) {
222                     if ( row.item ) {
223                         return row.item.external_id;
224                     } else {
225                         return null;
226                     }
227                 }
228             },
229             {
230                 "data": "patron.firstname:patron.surname",
231                 "title": "Patron",
232                 "searchable": true,
233                 "orderable": true,
234                 "render": function(data, type, row, meta) {
235                     return $patron_to_html(row.patron, {
236                         display_cardnumber: false,
237                         url: true
238                     });
239                 }
240             },
241             {
242                 "data": "start_date",
243                 "title": "Start date",
244                 "searchable": true,
245                 "orderable": true,
246                 "render": function(data, type, row, meta) {
247                     return $date(row.start_date);
248                 }
249             },
250             {
251                 "data": "end_date",
252                 "title": "End date",
253                 "searchable": true,
254                 "orderable": true,
255                 "render": function(data, type, row, meta) {
256                     return $date(row.end_date);
257                 }
258             },
259             {
260                 "data": "",
261                 "title": "Actions",
262                 "searchable": false,
263                 "orderable": false,
264                 "render": function(data, type, row, meta) {
265                     var 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>';
266                     return result;
267                 }
268             }]
269         }, [], 1);
270     });
271 </script>
272     [% END %]
273
274 [% INCLUDE 'intranet-bottom.inc' %]