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