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