Bug 28937: Use Flatpickr on circulation and patron pages
[koha.git] / koha-tmpl / intranet-tmpl / prog / js / holds.js
1 /* global __ dataTablesDefaults borrowernumber SuspendHoldsIntranet */
2 $(document).ready(function() {
3     var holdsTable;
4
5     // Don't load holds table unless it is clicked on
6     $("#holds-tab").on( "click", function(){ load_holds_table() } );
7
8     // If the holds tab is preselected on load, we need to load the table
9     if ( $("#holds-tab").parent().hasClass('ui-state-active') ) { load_holds_table() }
10
11     function load_holds_table() {
12         var holds = new Array();
13         if ( ! holdsTable ) {
14             var title;
15             holdsTable = $("#holds-table").dataTable($.extend(true, {}, dataTablesDefaults, {
16                 "bAutoWidth": false,
17                 "sDom": "rt",
18                 "columns": [
19                     {
20                         "data": { _: "reservedate_formatted", "sort": "reservedate" }
21                     },
22                     {
23                         "mDataProp": function ( oObj ) {
24                             title = "<a href='/cgi-bin/koha/reserve/request.pl?biblionumber="
25                                   + oObj.biblionumber
26                                   + "'>"
27                                   + oObj.title.escapeHtml();
28
29                             $.each(oObj.subtitle, function( index, value ) {
30                                 title += " " + value.escapeHtml();
31                             });
32
33                             title += " " + oObj.part_number + " " + oObj.part_name;
34
35                             if ( oObj.enumchron ) {
36                                 title += " (" + oObj.enumchron.escapeHtml() + ")";
37                             }
38
39                             title += "</a>";
40
41                             if ( oObj.author ) {
42                                 title += " " + __("by _AUTHOR_").replace("_AUTHOR_", oObj.author.escapeHtml());
43                             }
44
45                             if ( oObj.itemnotes ) {
46                                 var span_class = "";
47                                 if ( flatpickr.formatDate( new Date(oObj.issuedate), "Y-m-d" ) == ymd ){
48                                     span_class = "circ-hlt";
49                                 }
50                                 title += " - <span class='" + span_class + "'>" + oObj.itemnotes.escapeHtml() + "</span>"
51                             }
52
53                             return title;
54                         }
55                     },
56                     {
57                         "mDataProp": function( oObj ) {
58                             return oObj.itemcallnumber && oObj.itemcallnumber.escapeHtml() || "";
59                         }
60                     },
61                     {
62                         "mDataProp": function( oObj ) {
63                             var data = "";
64                             if ( oObj.barcode ) {
65                                 data += " <a href='/cgi-bin/koha/catalogue/moredetail.pl?biblionumber="
66                                   + oObj.biblionumber
67                                   + "&itemnumber="
68                                   + oObj.itemnumber
69                                   + "#item"
70                                   + oObj.itemnumber
71                                   + "'>"
72                                   + oObj.barcode.escapeHtml()
73                                   + "</a>";
74                             }
75                             return data;
76                         }
77                     },
78                     {
79                         "mDataProp": function( oObj ) {
80                             if( oObj.branches.length > 1 && oObj.found !== 'W' && oObj.found !== 'T' ){
81                                 var branchSelect='<select priority='+oObj.priority+' class="hold_location_select" data-hold-id="'+oObj.reserve_id+'" reserve_id="'+oObj.reserve_id+'" name="pick-location">';
82                                 for ( var i=0; i < oObj.branches.length; i++ ){
83                                     var selectedbranch;
84                                     var setbranch;
85                                     if( oObj.branches[i].selected ){
86
87                                         selectedbranch = " selected='selected' ";
88                                         setbranch = __(" (current) ");
89                                     } else if ( oObj.branches[i].pickup_location == 0 ) {
90                                         continue;
91                                     } else{
92                                         selectedbranch = '';
93                                         setbranch = '';
94                                     }
95                                     branchSelect += '<option value="'+ oObj.branches[i].branchcode.escapeHtml() +'"'+selectedbranch+'>'+oObj.branches[i].branchname.escapeHtml()+setbranch+'</option>';
96                                 }
97                                 branchSelect +='</select>';
98                                 return branchSelect;
99                             }
100                             else { return oObj.branchcode.escapeHtml() || ""; }
101                         }
102                     },
103                     { "data": { _: "expirationdate_formatted", "sort": "expirationdate" } },
104                     {
105                         "mDataProp": function( oObj ) {
106                             if ( oObj.priority && parseInt( oObj.priority ) && parseInt( oObj.priority ) > 0 ) {
107                                 return oObj.priority;
108                             } else {
109                                 return "";
110                             }
111                         }
112                     },
113                     {
114                         "bSortable": false,
115                         "mDataProp": function( oObj ) {
116                             return "<select name='rank-request'>"
117                                  +"<option value='n'>" + __("No") + "</option>"
118                                  +"<option value='del'>" + __("Yes") + "</option>"
119                                  + "</select>"
120                                  + "<input type='hidden' name='biblionumber' value='" + oObj.biblionumber + "'>"
121                                  + "<input type='hidden' name='borrowernumber' value='" + borrowernumber + "'>"
122                                  + "<input type='hidden' name='reserve_id' value='" + oObj.reserve_id + "'>";
123                         }
124                     },
125                     {
126                         "bSortable": false,
127                         "visible": SuspendHoldsIntranet,
128                         "mDataProp": function( oObj ) {
129                             holds[oObj.reserve_id] = oObj; //Store holds for later use
130
131                             if ( oObj.found ) {
132                                 return "";
133                             } else if ( oObj.suspend == 1 ) {
134                                 return "<a class='hold-resume btn btn-default btn-xs' id='resume" + oObj.reserve_id + "'>"
135                                      +"<i class='fa fa-play'></i> " + __("Resume") + "</a>";
136                             } else {
137                                 return "<a class='hold-suspend btn btn-default btn-xs' id='suspend" + oObj.reserve_id + "'>"
138                                      +"<i class='fa fa-pause'></i> " + __("Suspend") + "</a>";
139                             }
140                         }
141                     },
142                     {
143                         "mDataProp": function( oObj ) {
144                             var data = "";
145
146                             if ( oObj.suspend == 1 ) {
147                                 data += "<p>" + __("Hold is <strong>suspended</strong>");
148                                 if ( oObj.suspend_until ) {
149                                     data += " " + __("until %s").format(oObj.suspend_until_formatted);
150                                 }
151                                 data += "</p>";
152                             }
153
154                             if ( oObj.itemtype_limit ) {
155                                 data += __("Next available %s item").format(oObj.itemtype_limit);
156                             }
157
158                             if ( oObj.barcode ) {
159                                 data += "<em>";
160                                 if ( oObj.found == "W" ) {
161
162                                     if ( oObj.waiting_here ) {
163                                         data += __("Item is <strong>waiting here</strong>");
164                                         if (oObj.desk_name) {
165                                             data += ", " + __("at %s").format(oObj.desk_name.escapeHtml());
166                                         }
167                                     } else {
168                                         data += __("Item is <strong>waiting</strong>");
169                                         data += " " + __("at %s").format(oObj.waiting_at);
170                                         if (oObj.desk_name) {
171                                             data += ", " + __("at %s").format(oObj.desk_name.escapeHtml());
172                                         }
173
174                                     }
175
176                                 } else if ( oObj.transferred ) {
177                                     data += __("Item is <strong>in transit</strong> from %s since %s").format(oObj.from_branch, oObj.date_sent);
178                                 } else if ( oObj.not_transferred ) {
179                                     data += __("Item hasn't been transferred yet from %s").format(oObj.not_transferred_by);
180                                 }
181                                 data += "</em>";
182                             }
183                             return data;
184                         }
185                     }
186                 ],
187                 "bPaginate": false,
188                 "bProcessing": true,
189                 "bServerSide": false,
190                 "ajax": {
191                     "url": '/cgi-bin/koha/svc/holds',
192                     "data": function ( d ) {
193                         d.borrowernumber = borrowernumber;
194                     }
195                 },
196             }));
197
198             $('#holds-table').on( 'draw.dt', function () {
199                 $(".hold-suspend").on( "click", function() {
200                     var id = $(this).attr("id").replace("suspend", "");
201                     var hold = holds[id];
202                     $("#suspend-modal-title").html( hold.title );
203                     $("#suspend-modal-reserve_id").val( hold.reserve_id );
204                     $('#suspend-modal').modal('show');
205                 });
206
207                 $(".hold-resume").on( "click", function() {
208                     var id = $(this).attr("id").replace("resume", "");
209                     var hold = holds[id];
210                     $.post('/cgi-bin/koha/svc/hold/resume', { "reserve_id": hold.reserve_id }, function( data ){
211                       if ( data.success ) {
212                           holdsTable.api().ajax.reload();
213                       } else {
214                         if ( data.error == "HOLD_NOT_FOUND" ) {
215                             alert( __("Unable to resume, hold not found") );
216                             holdsTable.api().ajax.reload();
217                         }
218                       }
219                     });
220                 });
221
222                 function display_pickup_location (state) {
223                     var $text;
224                     if ( state.needs_override === true ) {
225                         $text = $(
226                             '<span>' + state.text + '</span> <span style="float:right;" title="' +
227                             _("This pickup location is not allowed according to circulation rules") +
228                             '"><i class="fa fa-exclamation-circle" aria-hidden="true"></i></span>'
229                         );
230                     }
231                     else {
232                         $text = $('<span>'+state.text+'</span>');
233                     }
234
235                     return $text;
236                 };
237
238                 $(".hold_location_select").each( function () {
239                     var this_dropdown = $(this);
240                     var hold_id = $(this).data('hold-id');
241
242                     this_dropdown.select2({
243                         allowClear: false,
244                         ajax: {
245                             url: '/api/v1/holds/' + encodeURIComponent(hold_id) + '/pickup_locations',
246                             delay: 300, // wait 300 milliseconds before triggering the request
247                             dataType: 'json',
248                             cache: true,
249                             data: function (params) {
250                                 var search_term = (params.term === undefined) ? '' : params.term;
251                                 var query = {
252                                     "q": JSON.stringify({"name":{"-like":search_term+'%'}}),
253                                     "_order_by": "name"
254                                 };
255                                 return query;
256                             },
257                             processResults: function (data) {
258                                 var results = [];
259                                 data.forEach( function ( pickup_location ) {
260                                     results.push(
261                                         {
262                                             "id": pickup_location.library_id.escapeHtml(),
263                                             "text": pickup_location.name.escapeHtml(),
264                                             "needs_override": pickup_location.needs_override
265                                         }
266                                     );
267                                 });
268                                 return { "results": results };
269                             }
270                         },
271                         templateResult: display_pickup_location
272                     });
273                     $('.select2-container').css('width','100%');
274                 });
275
276                 $(".hold_location_select").on("change", function(){
277                     $(this).prop("disabled",true);
278                     var cur_select = $(this);
279                     var res_id = $(this).attr('reserve_id');
280                     $(this).after('<div id="updating_reserveno'+res_id+'" class="waiting"><img src="/intranet-tmpl/prog/img/spinner-small.gif" alt="" /><span class="waiting_msg"></span></div>');
281                     var api_url = '/api/v1/holds/' + encodeURIComponent(res_id) + '/pickup_location';
282                     $.ajax({
283                         method: "PUT",
284                         url: api_url,
285                         data: JSON.stringify({ "pickup_library_id": $(this).val() }),
286                         headers: { "x-koha-override": "any" },
287                         success: function( data ){ holdsTable.api().ajax.reload(); },
288                         error: function( jqXHR, textStatus, errorThrown) {
289                             alert('There was an error:'+textStatus+" "+errorThrown);
290                             cur_select.prop("disabled",false);
291                             $("#updating_reserveno"+res_id).remove();
292                             cur_select.val( cur_select.children('option[selected="selected"]').val() );
293                         },
294                     });
295                 });
296
297             });
298
299             if ( $("#holds-table").length ) {
300                 $("#holds-table_processing").position({
301                     of: $( "#holds-table" ),
302                     collision: "none"
303                 });
304             }
305         }
306     }
307
308     $("body").append("\
309         <div id='suspend-modal' class='modal fade' role='dialog' aria-hidden='true'>\
310             <div class='modal-dialog'>\
311             <div class='modal-content'>\
312             <form id='suspend-modal-form' class='form-inline'>\
313                 <div class='modal-header'>\
314                     <button type='button' class='closebtn' data-dismiss='modal' aria-hidden='true'>×</button>\
315                     <h3 id='suspend-modal-label'>" + __("Suspend hold on") + " <i><span id='suspend-modal-title'></span></i></h3>\
316                 </div>\
317 \
318                 <div class='modal-body'>\
319                     <input type='hidden' id='suspend-modal-reserve_id' name='reserve_id' />\
320 \
321                     <label for='suspend-modal-until'>" + __("Suspend until:") + "</label>\
322                     <input name='suspend_until' id='suspend-modal-until' class='suspend-until' size='10' />\
323 \
324                     <p><a class='btn btn-link' id='suspend-modal-clear-date' >" + __("Clear date to suspend indefinitely") + "</a></p>\
325 \
326                 </div>\
327 \
328                 <div class='modal-footer'>\
329                     <button id='suspend-modal-submit' class='btn btn-primary' type='submit' name='submit'>" + __("Suspend") + "</button>\
330                     <a href='#' data-dismiss='modal' aria-hidden='true' class='cancel'>" + __("Cancel") + "</a>\
331                 </div>\
332             </form>\
333             </div>\
334             </div>\
335         </div>\
336     ");
337
338     $("#suspend-modal-until").flatpickr({
339         minDate: new Date().fp_incr(1) // Require that "until date" be in the future
340     });
341     $("#suspend-modal-clear-date").on( "click", function() { $("#suspend-modal-until").val(""); } );
342
343     $("#suspend-modal-submit").on( "click", function( e ) {
344         e.preventDefault();
345         $.post('/cgi-bin/koha/svc/hold/suspend', $('#suspend-modal-form').serialize(), function( data ){
346           $('#suspend-modal').modal('hide');
347           if ( data.success ) {
348               holdsTable.api().ajax.reload();
349           } else {
350             if ( data.error == "INVALID_DATE" ) {
351                 alert( __("Unable to suspend hold, invalid date") );
352             }
353             else if ( data.error == "HOLD_NOT_FOUND" ) {
354                 alert( __("Unable to suspend hold, hold not found") );
355                 holdsTable.api().ajax.reload();
356             }
357           }
358         });
359     });
360 });