Bug 25614: Move ILL request keyword filter to sidebar
[koha.git] / koha-tmpl / intranet-tmpl / prog / js / ill-list-table.js
1 $(document).ready(function() {
2
3     // Illview Datatable setup
4
5     var table;
6
7     // Filters that are active
8     var activeFilters = {};
9
10     // Get any prefilters
11     var prefilters = $('table#ill-requests').data('prefilters');
12
13     // Fields we need to expand (flatten)
14     var expand = [
15         'metadata',
16         'patron',
17         'library'
18     ];
19
20     // Expanded fields
21     // This is auto populated
22     var expanded = {};
23
24     // Filterable columns
25     var filterable = {
26         status: {
27             prep: function(tableData, oData) {
28                 var uniques = {};
29                 tableData.forEach(function(row) {
30                     var resolvedName;
31                     if (row.status_alias) {
32                         resolvedName = row.status_alias.lib;
33                     } else {
34                         resolvedName = getStatusName(
35                             row.capabilities[row.status].name,
36                             row
37                         );
38                     }
39                     uniques[resolvedName] = 1
40                 });
41                 Object.keys(uniques).sort().forEach(function(unique) {
42                     $('#illfilter_status').append(
43                         '<option value="' + unique  +
44                         '">' + unique +  '</option>'
45                     );
46                 });
47             },
48             listener: function() {
49                 var me = 'status';
50                 $('#illfilter_status').change(function() {
51                     var sel = $('#illfilter_status option:selected').val();
52                     if (sel && sel.length > 0) {
53                         activeFilters[me] = function() {
54                             table.api().column(13).search(sel);
55                         }
56                     } else {
57                         if (activeFilters.hasOwnProperty(me)) {
58                             delete activeFilters[me];
59                         }
60                     }
61                 });
62             },
63             clear: function() {
64                 $('#illfilter_status').val('');
65             }
66         },
67         pickupBranch: {
68             prep: function(tableData, oData) {
69                 var uniques = {};
70                 tableData.forEach(function(row) {
71                     uniques[row.library_branchname] = 1
72                 });
73                 Object.keys(uniques).sort().forEach(function(unique) {
74                     $('#illfilter_branchname').append(
75                         '<option value="' + unique  +
76                         '">' + unique +  '</option>'
77                     );
78                 });
79             },
80             listener: function() {
81                 var me = 'pickupBranch';
82                 $('#illfilter_branchname').change(function() {
83                     var sel = $('#illfilter_branchname option:selected').val();
84                     if (sel && sel.length > 0) {
85                         activeFilters[me] = function() {
86                             table.api().column(12).search(sel);
87                         }
88                     } else {
89                         if (activeFilters.hasOwnProperty(me)) {
90                             delete activeFilters[me];
91                         }
92                     }
93                 });
94             },
95             clear: function() {
96                 $('#illfilter_branchname').val('');
97             }
98         },
99         patron: {
100             listener: function() {
101                 var me = 'patron';
102                 $('#illfilter_patron').change(function() {
103                     var val = $('#illfilter_patron').val();
104                     if (val && val.length > 0) {
105                         activeFilters[me] = function() {
106                             table.api().column(10).search(val);
107                         }
108                     } else {
109                         if (activeFilters.hasOwnProperty(me)) {
110                             delete activeFilters[me];
111                         }
112                     }
113                 });
114             },
115             clear: function() {
116                 $('#illfilter_patron').val('');
117             }
118         },
119         keyword: {
120             listener: function () {
121                 var me = 'keyword';
122                 $('#illfilter_keyword').change(function () {
123                     var val = $('#illfilter_keyword').val();
124                     if (val && val.length > 0) {
125                         activeFilters[me] = function () {
126                             table.api().search(val);
127                         }
128                     } else {
129                         if (activeFilters.hasOwnProperty(me)) {
130                             delete activeFilters[me];
131                         }
132                     }
133                 });
134             },
135             clear: function () {
136                 $('#illfilter_keyword').val('');
137             }
138         },
139         dateModified: {
140             clear: function() {
141                 $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
142             }
143         },
144         datePlaced: {
145             clear: function() {
146                 $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
147             }
148         }
149     }; //END Filterable columns
150
151     // Expand any fields we're expanding
152     var expandExpand = function(row) {
153         expand.forEach(function(thisExpand) {
154             if (row.hasOwnProperty(thisExpand)) {
155                 if (!expanded.hasOwnProperty(thisExpand)) {
156                     expanded[thisExpand] = [];
157                 }
158                 var expandObj = row[thisExpand];
159                 Object.keys(expandObj).forEach(
160                     function(thisExpandCol) {
161                         var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
162                         // Keep a list of fields that have been expanded
163                         // so we can create toggle links for them
164                         if (expanded[thisExpand].indexOf(expColName) == -1) {
165                             expanded[thisExpand].push(expColName);
166                         }
167                         expandObj[expColName] =
168                             expandObj[thisExpandCol];
169                         delete expandObj[thisExpandCol];
170                     }
171                 );
172                 $.extend(true, row, expandObj);
173                 delete row[thisExpand];
174             }
175         });
176     };
177     //END Expand
178
179     // Strip the expand prefix if it exists, we do this for display
180     var stripPrefix = function(value) {
181         expand.forEach(function(thisExpand) {
182             var regex = new RegExp(thisExpand + '_', 'g');
183             value = value.replace(regex, '');
184         });
185         return value;
186     };
187
188     // Our 'render' function for borrowerlink
189     var createPatronLink = function(data, type, row) {
190         var patronLink = '<a title="' + ill_borrower_details + '" ' +
191             'href="/cgi-bin/koha/members/moremember.pl?' +
192             'borrowernumber='+row.borrowernumber+'">';
193         if ( row.patron_firstname ) {
194             patronLink = patronLink + row.patron_firstname + ' ';
195         }
196         patronLink = patronLink + row.patron_surname +
197             ' (' + row.patron_cardnumber + ')' + '</a>';
198         return patronLink;
199     };
200
201     // Our 'render' function for biblio_id
202     var createBiblioLink = function(data, type, row) {
203         return (row.biblio_id) ?
204             '<a title="' + ill_biblio_details + '" ' +
205             'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
206             row.biblio_id + '">' +
207             row.biblio_id +
208             '</a>' : '';
209     };
210
211     // Our 'render' function for title
212     var createTitle = function(data, type, row) {
213         return (
214             row.hasOwnProperty('metadata_container_title') &&
215             row.metadata_container_title
216         ) ? row.metadata_container_title : row.metadata_title;
217     };
218
219     // Render function for request ID
220     var createRequestId = function(data, type, row) {
221         return row.id_prefix + row.illrequest_id;
222     };
223
224     // Render function for type
225     var createType = function(data, type, row) {
226         if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
227             if (row.hasOwnProperty('medium') && row.medium) {
228                 row.metadata_Type = row.medium;
229             } else {
230                 row.metadata_Type = null;
231             }
232         }
233         return row.metadata_Type;
234     };
235
236     // Render function for request status
237     var createStatus = function(data, type, row, meta) {
238         if (row.status_alias) {
239             return row.status_alias.lib
240                 ? row.status_alias.lib
241                 : row.status_alias.authorised_value;
242         } else {
243             var status_name = row.capabilities[row.status].name;
244             return getStatusName(status_name, row);
245         }
246     };
247
248     var getStatusName = function(origName, row) {
249         switch( origName ) {
250             case "New request":
251                 return ill_statuses.new;
252             case "Requested":
253                 return ill_statuses.req;
254             case "Requested from partners":
255                 var statStr = ill_statuses.genreq;
256                 if (
257                     row.hasOwnProperty('requested_partners') &&
258                     row.requested_partners &&
259                     row.requested_partners.length > 0
260                 ) {
261                     statStr += ' (' + row.requested_partners + ')';
262                 }
263                 return statStr;
264             case "Request reverted":
265                 return ill_statuses.rev;
266             case "Queued request":
267                 return ill_statuses.que;
268             case "Cancellation requested":
269                 return ill_statuses.canc;
270             case "Completed":
271                 return ill_statuses.comp;
272             case "Delete request":
273                 return ill_statuses.del;
274             default:
275                 return origName;
276         }
277     };
278
279     // Render function for creating a row's action link
280     var createActionLink = function(data, type, row) {
281         return '<a class="btn btn-default btn-sm" ' +
282             'href="/cgi-bin/koha/ill/ill-requests.pl?' +
283             'method=illview&amp;illrequest_id=' +
284             row.illrequest_id +
285             '">' + ill_manage + '</a>';
286     };
287
288     // Columns that require special treatment
289     var specialCols = {
290         action: {
291             func: createActionLink,
292             skipSanitize: true
293         },
294         illrequest_id: {
295             func: createRequestId
296         },
297         status: {
298             func: createStatus
299         },
300         biblio_id: {
301             name: ill_columns.biblio_id,
302             func: createBiblioLink,
303             skipSanitize: true
304         },
305         metadata_title: {
306             func: createTitle
307         },
308         metadata_Type: {
309             func: createType
310         },
311         updated: {
312             name: ill_columns.updated
313         },
314         patron: {
315             skipSanitize: true,
316             func: createPatronLink
317         }
318     };
319
320     // Display the modal containing request supplier metadata
321     $('#ill-request-display-log').on('click', function(e) {
322         e.preventDefault();
323         $('#requestLog').modal({show:true});
324     });
325
326     // Toggle request attributes in Illview
327     $('#toggle_requestattributes').on('click', function(e) {
328         e.preventDefault();
329         $('#requestattributes').toggleClass('content_hidden');
330     });
331
332     // Toggle new comment form in Illview
333     $('#toggle_addcomment').on('click', function(e) {
334         e.preventDefault();
335         $('#addcomment').toggleClass('content_hidden');
336     });
337
338     // Filter partner list
339     // Record the list of all options
340     var ill_partner_options = $('#partners > option');
341     $('#partner_filter').keyup(function() {
342         var needle = $('#partner_filter').val();
343         var regex = new RegExp(needle, 'i');
344         var filtered = [];
345         ill_partner_options.each(function() {
346             if (
347                 needle.length == 0 ||
348                 $(this).is(':selected') ||
349                 $(this).text().match(regex)
350             ) {
351                 filtered.push($(this));
352             }
353         });
354         $('#partners').empty().append(filtered);
355     });
356
357     // Display the modal containing request supplier metadata
358     $('#ill-request-display-metadata').on('click', function(e) {
359         e.preventDefault();
360         $('#dataPreview').modal({show:true});
361     });
362
363     // Allow us to chain Datatable render helpers together, so we
364     // can use our custom functions and render.text(), which
365     // provides us with data sanitization
366     $.fn.dataTable.render.multi = function(renderArray) {
367         return function(d, type, row, meta) {
368             for(var r = 0; r < renderArray.length; r++) {
369                 var toCall = renderArray[r].hasOwnProperty('display') ?
370                     renderArray[r].display :
371                     renderArray[r];
372                 d = toCall(d, type, row, meta);
373             }
374             return d;
375         }
376     }
377
378     // Get our data from the API and process it prior to passing
379     // it to datatables
380     var filterParam = prefilters ? '&' + prefilters : '';
381     // Only fire the request if we're on an appropriate page
382     if (
383         (
384             // ILL list requests page
385             window.location.href.match(/ill\/ill-requests\.pl/) &&
386             window.location.search.length == 0
387         ) ||
388         // Patron profile page
389         window.location.href.match(/members\/ill-requests\.pl/)
390     ) {
391         var ajax = $.ajax(
392             '/api/v1/illrequests?embed=metadata,patron,capabilities,library,status_alias,comments,requested_partners'
393             + filterParam
394         ).done(function() {
395             var data = JSON.parse(ajax.responseText);
396             // Make a copy, we'll be removing columns next and need
397             // to be able to refer to data that has been removed
398             var dataCopy = $.extend(true, [], data);
399             // Expand columns that need it and create an array
400             // of all column names
401             $.each(dataCopy, function(k, row) {
402                 expandExpand(row);
403             });
404
405             // Assemble an array of column definitions for passing
406             // to datatables
407             var colData = [];
408             columns_settings.forEach(function(thisCol) {
409                 var colName = thisCol.columnname;
410                 // Create the base column object
411                 var colObj = $.extend({}, thisCol);
412                 colObj.name = colName;
413                 colObj.className = colName;
414                 colObj.defaultContent = '';
415
416                 // We may need to process the data going in this
417                 // column, so do it if necessary
418                 if (
419                     specialCols.hasOwnProperty(colName) &&
420                     specialCols[colName].hasOwnProperty('func')
421                 ) {
422                     var renderArray = [
423                         specialCols[colName].func
424                     ];
425                     if (!specialCols[colName].skipSanitize) {
426                         renderArray.push(
427                             $.fn.dataTable.render.text()
428                         );
429                     }
430
431                     colObj.render = $.fn.dataTable.render.multi(
432                         renderArray
433                     );
434                 } else {
435                     colObj.data = colName;
436                     colObj.render = $.fn.dataTable.render.text()
437                 }
438                 // Make sure properties that aren't present in the API
439                 // response are populated with null to avoid Datatables
440                 // choking on their absence
441                 dataCopy.forEach(function(thisData) {
442                     if (!thisData.hasOwnProperty(colName)) {
443                         thisData[colName] = null;
444                     }
445                 });
446                 colData.push(colObj);
447             });
448
449             // Initialise the datatable
450             table = KohaTable("ill-requests", {
451                 'aoColumnDefs': [
452                     { // Last column shouldn't be sortable or searchable
453                         'aTargets': [ 'actions' ],
454                         'bSortable': false,
455                         'bSearchable': false
456                     },
457                     { // When sorting 'placed', we want to use the
458                         // unformatted column
459                         'aTargets': [ 'placed_formatted'],
460                         'iDataSort': 14
461                     },
462                     { // When sorting 'updated', we want to use the
463                         // unformatted column
464                         'aTargets': [ 'updated_formatted'],
465                         'iDataSort': 16
466                     },
467                     { // When sorting 'completed', we want to use the
468                         // unformatted column
469                         'aTargets': [ 'completed_formatted'],
470                         'iDataSort': 19
471                     }
472                 ],
473                 'aaSorting': [[ 16, 'desc' ]], // Default sort, updated descending
474                 'processing': true, // Display a message when manipulating
475                 'sPaginationType': "full_numbers", // Pagination display
476                 'deferRender': true, // Improve performance on big datasets
477                 'data': dataCopy,
478                 "dom": '<"top pager"<"table_entries"ilp><"table_controls"B>>tr<"bottom pager"ip>',
479                 'columns': colData,
480                 'originalData': data, // Enable render functions to access
481                                         // our original data
482                 'initComplete': function() {
483
484                     // Prepare any filter elements that need it
485                     for (var el in filterable) {
486                         if (filterable.hasOwnProperty(el)) {
487                             if (filterable[el].hasOwnProperty('prep')) {
488                                 filterable[el].prep(dataCopy, data);
489                             }
490                             if (filterable[el].hasOwnProperty('listener')) {
491                                 filterable[el].listener();
492                             }
493                         }
494                     }
495
496                 }
497             }, columns_settings);
498
499             // Custom date range filtering
500             $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
501                 var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
502                 var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
503                 var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
504                 var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
505                 var rowPlaced = data[14] ? new Date(data[14]) : null;
506                 var rowModified = data[16] ? new Date(data[16]) : null;
507                 var placedPassed = true;
508                 var modifiedPassed = true;
509                 if (placedStart && rowPlaced && rowPlaced < placedStart) {
510                     placedPassed = false
511                 };
512                 if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
513                     placedPassed = false;
514                 }
515                 if (modifiedStart && rowModified && rowModified < modifiedStart) {
516                     modifiedPassed = false
517                 };
518                 if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
519                     modifiedPassed = false;
520                 }
521
522                 return placedPassed && modifiedPassed;
523
524             });
525
526         });
527     } //END if window.location.search.length == 0
528
529     var clearSearch = function() {
530         table.api().search('').columns().search('');
531         activeFilters = {};
532         for (var filter in filterable) {
533             if (
534                 filterable.hasOwnProperty(filter) &&
535                 filterable[filter].hasOwnProperty('clear')
536             ) {
537                 filterable[filter].clear();
538             }
539         }
540         table.api().draw();
541     };
542
543     // Apply any search filters, or clear any previous
544     // ones
545     $('#illfilter_form').submit(function(event) {
546         event.preventDefault();
547         table.api().search('').columns().search('');
548         for (var active in activeFilters) {
549             if (activeFilters.hasOwnProperty(active)) {
550                 activeFilters[active]();
551             }
552         }
553         table.api().draw();
554     });
555
556     // Clear all filters
557     $('#clear_search').click(function() {
558         clearSearch();
559     });
560
561 });