Bug 30093: Fix QA failures
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / includes / patron-search.inc
1 [% USE Koha %]
2 [% USE Branches %]
3 [% USE raw %]
4 [% USE To %]
5
6 [% BLOCK patron_search_filters_simple  %]
7     <form id="patron_search_form">
8         <div class="hint">Enter patron card number or partial name:</div>
9         <input type="text" size="40" id="search_patron_filter" class="focus" autocomplete="off" />
10         <input type="submit" value="Search" />
11     </form>
12 [% END %]
13
14 [% BLOCK patron_search_filters %]
15     <form id="patron_search_form">
16         <fieldset class="brief">
17             <h3>Search for patron</h3>
18             <ol>
19                 <li>
20                     <label for="search_patron_filter">Search:</label>
21                     <div class="hint">Enter patron card number or partial name:</div>
22                     <input type="text" id="search_patron_filter" value="[% search_filter | html %]" class="focus" />
23                 </li>
24
25                 [% FOR column IN columns %]
26                     [% SWITCH column %]
27                     [% CASE 'branch' %]
28                         <li>
29                             <label for="branchcode_filter">Library:</label>
30                             <select id="branchcode_filter">
31                                 [% SET libraries = Branches.all( only_from_group => 1 ) %]
32                                 [% IF libraries.size != 1 %]
33                                     <option value="">Any</option>
34                                 [% END %]
35                                 [% FOREACH l IN libraries %]
36                                     <option value="[% l.branchcode | html %]">[% l.branchname | html %]</option>
37                                 [% END %]
38                             </select>
39                         </li>
40                     [% CASE 'category' %]
41                         <li>
42                             <label for="categorycode_filter">Category:</label>
43                             <select id="categorycode_filter">
44                                 <option value="">Any</option>
45                                 [% FOREACH category IN categories %]
46                                     <option value="[% category.categorycode | html %]">[% category.description | html %]</option>
47                                 [% END %]
48                             </select>
49                         </li>
50                     [% END %]
51                 [% END %]
52             </ol>
53             <fieldset class="action">
54                 <input type="submit" value="Search" />
55             </fieldset>
56         </fieldset>
57     </form>
58 [% END %]
59
60 [% BLOCK patron_search_table %]
61
62     [% IF filter == 'suggestions_managers' %]
63         <div class="hint">Only staff with superlibrarian or suggestions_manage permissions are returned in the search results</div>
64     [% ELSIF filter == 'orders_managers' %]
65         <div class="hint">Only staff with superlibrarian or acquisitions permissions (or order_manage permission if granular permissions are enabled) are returned in the search results</div>
66     [% ELSIF filter == 'funds_owners' OR filter == 'funds_users' %]
67         <div class="hint">Only staff with superlibrarian or acquisitions permissions (or budget_modify permission if granular permissions are enabled) are returned in the search results</div>
68     [% END %]
69
70     <div class="browse">
71         Browse by last name:
72         [% SET alphabet = Koha.Preference('alphabet').split(' ') %]
73         [% UNLESS alphabet.size %]
74             [% alphabet = ['A' .. 'Z'] %]
75         [% END %]
76         [% FOREACH letter IN alphabet %]
77             <a href="#" class="filterByLetter">[% letter | html %]</a>
78         [% END %]
79     </div>
80
81     <div id="[% table_id | html %]_search_results" style="display:none;">
82
83         <div id="info" class="dialog message" style="display: none;"></div>
84         <div id="error" class="dialog alert" style="display: none;"></div>
85
86
87         <input type="hidden" id="firstletter_filter" value="" />
88         [% IF open_on_row_click %]
89         <table id="[% table_id | html %]" class="selections-table">
90         [% ELSE %]
91         <table id="[% table_id | html %]">
92         [% END %]
93             <thead>
94                 <tr>
95                     [% FOR column IN columns %]
96                         [% SWITCH column %]
97                             [% CASE 'cardnumber' %]<th>Card</th>
98                             [% CASE 'dateofbirth' %]<th>Date of birth</th>
99                             [% CASE 'name' %]<th>Name</th>
100                             [% CASE 'name-address' %]<th>Name</th>
101                             [% CASE 'address' %]<th>Address</th>
102                             [% CASE 'address-library' %]<th>Address</th>
103                             [% CASE 'branch' %]<th data-filter="libraries">Library</th>
104                             [% CASE 'category' %]<th data-filter="categories">Category</th>
105                             [% CASE 'dateexpiry' %]<th>Expires on</td>
106                             [% CASE 'borrowernotes' %]<th>Notes</th>
107                             [% CASE 'phone' %]<th>Phone</th>
108                             [% CASE 'action' %]<th>&nbsp;</th>
109                         [% END %]
110                     [% END %]
111                 </tr>
112               </thead>
113             <tbody></tbody>
114         </table>
115     </div>
116
117 <!-- Patron preview modal -->
118 <div class="modal" id="patronPreview" tabindex="-1" role="dialog" aria-labelledby="patronPreviewLabel">
119     <div class="modal-dialog" role="document">
120         <div class="modal-content">
121             <div class="modal-header">
122                 <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
123                 <h4 class="modal-title" id="patronPreviewLabel"></h4>
124             </div>
125             <div class="modal-body">
126                 <div id="loading">
127                     <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" /> Loading
128                 </div>
129             </div>
130             <div class="modal-footer">
131                 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
132             </div>
133         </div>
134     </div>
135 </div>
136
137 [% END %]
138
139 [% BLOCK patron_search_js %]
140
141     [% IF redirect_if_one_result && !redirect_url %]
142         <script>console.log("Wrong call of patron_searh_js - missing redirect_url");</script>
143     [% END %]
144     <script>
145         let categories = [% To.json(categories) | $raw %];
146         let categories_map = categories.reduce((map, c) => {
147             map[c.categorycode] = c;
148             return map;
149         }, {});
150         let libraries  = [% To.json(libraries) | $raw %];
151         let libraries_map = libraries.reduce((map, l) => {
152             map[l.branchcode] = l;
153             return map;
154         }, {});
155
156         [% IF Koha.Preference('ExtendedPatronAttributes') %]
157             let extended_attribute_types = [% To.json(attribute_type_codes || []) | $raw %];
158         [% END %]
159
160     </script>
161
162     [% INCLUDE 'datatables.inc' %]
163     [% INCLUDE 'js-date-format.inc' %]
164     [% INCLUDE 'js-patron-get-age.inc' %]
165     [% INCLUDE 'js-patron-format.inc' %]
166     [% INCLUDE 'js-patron-format-address.inc' %]
167
168     <script>
169         var first_draw = 0;
170         let patrons_table;
171
172         $(document).ready(function(){
173
174
175             $("#info").hide();
176             $("#error").hide();
177
178             let additional_filters = {
179                 surname: function(){
180                     let start_with = $("#firstletter_filter").val()
181                     if (!start_with) return "";
182                     return { "like": start_with + "%" }
183                 },
184                 "-and": function(){
185                     let filter = $("#search_patron_filter").val();
186                     if (!filter) return "";
187                     [% SET search_fields = Koha.Preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid' %]
188                     return [
189                         [% FOR search_field IN search_fields.split(',') %]
190                         {"me.[% search_field | html %]":{"like":"%"+filter+"%"}},
191                         [% END %]
192                         [% IF Koha.Preference('ExtendedPatronAttributes') %]
193                         {
194                             "extended_attributes.value": { "like": "%" + filter + "%" },
195                             "extended_attributes.code": extended_attribute_types
196                         }
197                         [% END %]
198                     ];
199                 }
200             };
201             [% UNLESS default_sort_column %]
202                 [% default_sort_column = "name" %]
203             [% END %]
204             [% SET order_column_index = 0 %]
205             patrons_table = $("#[% table_id | html %]").kohaTable({
206                 "ajax": {
207                     [% SWITCH filter %]
208                     [% CASE 'suggestions_managers' %]
209                         "url": '/api/v1/suggestions/managers',
210                     [% CASE 'baskets_managers' %]
211                         "url": '/api/v1/acquisitions/baskets/managers',
212                     [% CASE 'funds_owners' %]
213                         "url": '/api/v1/acquisitions/funds/owners',
214                     [% CASE 'funds_users' %]
215                         "url": '/api/v1/acquisitions/funds/users',
216                     [% CASE %]
217                         "url": '/api/v1/patrons',
218                     [% END %]
219                     "dataSrc": function ( json ) {
220                         [% IF redirect_if_one_result %]
221                             // redirect if there is only 1 result.
222                             if ( first_draw && json.recordsFiltered == 1 ) {
223                                 document.location.href = '[% redirect_url | url %]&borrowernumber=' + json.data[0].patron_id;
224                                 return false;
225                             }
226                             first_draw = 0;
227                         [% END %]
228                         return json.data;
229                     }
230                 },
231                 embed: ['extended_attributes'],
232                 "drawCallback": function( settings ) {
233                     var api = this.api();
234                     var data = api.data();
235                     if ( data.length == 0 ) return;
236
237                     [% IF open_on_row_click %]
238                     $.each($(this).find("tbody tr"), function(index, tr) {
239                         let url = "[% on_click_url | url %]&borrowernumber=" + data[index].patron_id;
240                         $(tr).off('click').on('click', function() {
241                             document.location.href = url;
242                         }).addClass('clickable');
243                         $(tr).find("a.patron_name").attr('href', url);
244                     });
245                     [% ELSE %]
246                     $.each($(this).find("tbody tr"), function(index, tr) {
247                         $(tr).find("a.patron_name").addClass("patron_preview");
248                     });
249                     [% END %]
250                 },
251                 "iDeferLoading": 0,
252                 "columns": [
253                     [% FOR column IN columns %]
254                         [% IF default_sort_column == column %]
255                             [% order_column_index = loop.count - 1%]
256                         [% END %]
257                         [% SWITCH column %]
258                             [% CASE 'cardnumber' %]
259                             {
260                                 "data": "cardnumber",
261                                 "searchable": true,
262                                 "orderable": true
263                             }
264                             [% CASE 'dateofbirth' %]
265                             {
266                                 "data": "date_of_birth",
267                                 "searchable": true,
268                                 "orderable": true,
269                                 "render": function( data, type, row, meta ) {
270                                     return data ? escape_str($date(data) + " (" + _("%s years").format($get_age(data)) + ")") : "";
271                                 }
272                             }
273                             [% CASE 'address' %]
274                             {
275                                 "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
276                                 "searchable": true,
277                                 "orderable": true,
278                                  "render": function( data, type, row, meta ) {
279                                     let r = '<div class="address"><ul>';
280                                     r += $format_address(row, { no_line_break: 1 });
281                                     r += '</div></ul>';
282                                     return r;
283                                 }
284                             }
285                             [% CASE 'address-library' %]
286                             {
287                                 "data": "me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
288                                 "searchable": true,
289                                 "orderable": true,
290                                 "render": function( data, type, row, meta ) {
291                                     let r = '<div class="address"><ul>';
292                                     r += $format_address(row, { no_line_break: 1 });
293                                     r += '</div></ul>';
294                                     r += " " + escape_str(libraries_map[row.library_id].branchname);
295                                     return r;
296                                 }
297                             }
298                             [% CASE 'name-address' %]
299                             {
300                                 "data": "me.firstname:me.surname:me.othernames:me.street_number:me.address:me.address2:me.city:me.state:me.postal_code:me.country",
301                                 "searchable": true,
302                                 "orderable": true,
303                                 "render": function( data, type, row, meta ) {
304                                     let patron_id = encodeURIComponent(row.patron_id);
305                                     let r = '';
306                                     [% IF ! open_on_row_click %]
307                                     r += "<a href=\"/cgi-bin/koha/members/moremember.pl?borrowernumber=" + patron_id + "\" class=\"patron_name\" data-borrowernumber=\"" + patron_id + "\" style=\"white-space:nowrap\">" + $patron_to_html(row, { invert_name: 1 }) + "</a>";
308                                     [% ELSE %]
309                                     r += $patron_to_html(row, { invert_name: 1 });
310                                     [% END %]
311                                     r += '<br/>';
312                                     r += '<div class="address"><ul>';
313                                     r += $format_address(row, { no_line_break: 1 });
314
315                                     if ( row.email ) {
316                                         r += "<li>" + _("Email: ") + "<a href='mailto:" + encodeURIComponent(row.email) + "'>" + escape_str(row.email) + "</a></li>";
317                                     }
318                                     r += '</ul></div>'
319
320                                     return r;
321                                 }
322                             }
323                             [% CASE 'name' %]
324                             {
325                                 "data": "me.firstname:me.surname:me.othernames",
326                                 "searchable": true,
327                                 "orderable": true,
328                                 "render": function( data, type, row, meta ) {
329                                     let patron_id = encodeURIComponent(row.patron_id);
330                                     return "<a href=\"/cgi-bin/koha/members/moremember.pl?borrowernumber=" + patron_id + "\" class=\"patron_name\" data-borrowernumber=\"" + patron_id + "\" style=\"white-space:nowrap\">" + $patron_to_html(row, { invert_name: 1 }) + "</a>";
331                                 }
332                             }
333                             [% CASE 'branch' %]
334                             {
335                                 "data": "library_id",
336                                 "searchable": true,
337                                 "orderable": true,
338                                 "render": function( data, type, row, meta ) {
339                                     return escape_str(libraries_map[data].branchname);
340                                 }
341                             }
342                             [% CASE 'category' %]
343                             {
344                                 "data": "category_id",
345                                 "searchable": true,
346                                 "orderable": true,
347                                 "render": function( data, type, row, meta ) {
348                                     return escape_str(categories_map[data].description);
349                                 }
350                             }
351                             [% CASE 'dateexpiry' %]
352                             {
353                                 "data": "expiry_date",
354                                 "searchable": true,
355                                 "orderable": true,
356                                 "render": function( data, type, row, meta ) {
357                                     return data ? escape_str($date(data)) : "";
358                                 }
359                             }
360                             [% CASE 'borrowernotes' %]
361                             {
362                                 "data": "staff_notes",
363                                 "searchable": true,
364                                 "orderable": true,
365                                 [%# We don't escape here, we allow html tag in staff notes %]
366                             }
367                             [% CASE 'phone' %]
368                             {
369                                 "data": "phone",
370                                 "searchable": true,
371                                 "orderable": true,
372                                 "render": function( data, type, row, meta ) {
373                                     return escape_str(data);
374                                 }
375                             }
376                             [% CASE 'action' %]
377                             {
378                                 "data": function( row, type, val, meta ) {
379
380                                     let patron_id = encodeURIComponent(row.patron_id);
381                                     [% IF selection_type == 'select' %]
382                                         return '<a href="#" class="btn btn-default btn-xs select_user" data-borrowernumber="' + patron_id + '">Select</a><input type="hidden" id="borrower_data' + patron_id + '" name="borrower_data'+ patron_id + '" value=\''+JSON.stringify(row)+'\' />';
383                                     [% ELSE %]
384                                         return '<a href="#" class="btn btn-default btn-xs add_user" data-borrowernumber="' + patron_id + '" data-firstname="' + encodeURIComponent(row.firstname) + '" data-surname="' + encodeURIComponent(row.surname) + '">Add</a><input type="hidden" id="borrower_data' + patron_id + '" name="borrower_data'+ patron_id + '" />';
385                                     [% END %]
386                                 },
387                                 "searchable": false,
388                                 "orderable": false
389                             }
390                         [% END %]
391                         [% UNLESS loop.last %],[% END %]
392                     [% END %]
393                 ],
394                 "order": [[ [% order_column_index | html %], "asc" ]],
395                 'bAutoWidth': false,
396                 'sPaginationType': 'full_numbers',
397                 "iDisplayLength": [% Koha.Preference('PatronsPerPage') | html %],
398             }, typeof table_settings !== 'undefined' ? table_settings : null, 1, additional_filters);
399
400             $("#patron_search_form").on('submit', filter);
401             $(".filterByLetter").on("click",function(e){
402                 e.preventDefault();
403                 filterByFirstLetterSurname($(this).text());
404             });
405             $("body").on("click",".add_user",function(e){
406                 e.preventDefault();
407                 var borrowernumber = $(this).data("borrowernumber");
408                 var firstname = $(this).data("firstname");
409                 var surname = $(this).data("surname");
410                 add_user( borrowernumber, firstname + " " + surname );
411             });
412
413             $("body").on("click",".select_user",function(e){
414                 e.preventDefault();
415                 var borrowernumber = $(this).data("borrowernumber");
416                 var borrower_data = $("#borrower_data"+borrowernumber).val();
417                 select_user( borrowernumber, JSON.parse(borrower_data) );
418             });
419
420             $("body").on("click",".patron_preview", function( e ){
421                 e.preventDefault();
422                 var borrowernumber = $(this).data("borrowernumber");
423                 var page = "/cgi-bin/koha/members/moremember.pl?print=brief&borrowernumber=" + borrowernumber;
424                 $("#patronPreview .modal-body").load( page + " div.container-fluid" );
425                 $('#patronPreview').modal({show:true});
426             });
427
428             $("#patronPreview").on('hidden.bs.modal', function (e) {
429                 $("#patronPreview .modal-body").html("<img src=\"[% interface | html %]/[% theme | html %]/img/spinner-small.gif\" alt=\"\" /> Loading");
430             });
431
432         });
433
434         function filter() {
435             $("#firstletter_filter").val('');
436             $("#[% table_id | html %]_search_results").show();
437
438             let table_dt = patrons_table.DataTable();
439             [% FOR c IN columns %]
440                 [% SWITCH c %]
441                 [% CASE 'branch' %]
442                     library_id = $("#branchcode_filter").val() || "";
443                     patrons_table.find('thead tr:eq(1) th[data-filter="libraries"] select').val(library_id);
444                     table_dt.column([% loop.count - 1 %]).search(library_id ? '^'+library_id+'$' : '');
445                 [% CASE 'category' %]
446                     let category_id = $("#categorycode_filter").val() || "";
447                     patrons_table.find('thead tr:eq(1) th[data-filter="categories"] select').val(category_id);
448                     table_dt.column([% loop.count - 1 %]).search(category_id ? '^'+category_id+'$' : '');
449                 [% END %]
450             [% END %]
451             table_dt.search("");
452             first_draw = 1; // Only redirect if we are coming from here
453             table_dt.draw();
454             return false;
455         }
456
457         // User has clicked on a letter
458         function filterByFirstLetterSurname(letter) {
459             $("#firstletter_filter").val(letter);
460             $("#[% table_id | html %]_search_results").show();
461             patrons_table.DataTable().draw();
462         }
463
464         // modify parent window owner element
465         [% IF selection_type == 'add' %]
466             function add_user(borrowernumber, borrowername) {
467                 var p = window.opener;
468                 // In one place (serials/routing.tt), the page is reload on every add
469                 // We have to wait for the page to be there
470                 function wait_for_opener () {
471                     if ( ! $(opener.document).find('body').size() ) {
472                         setTimeout(wait_for_opener, 500);
473                     } else {
474                         [%# Note that add_user could sent data instead of borrowername too %]
475                         $("#info").hide();
476                         $("#error").hide();
477                         if ( p.add_user(borrowernumber, borrowername) < 0 ) {
478                             $("#error").html(_("Patron '%s' is already in the list.").format(borrowername));
479                             $("#error").show();
480                         } else {
481                             $("#info").html(_("Patron '%s' added.").format(borrowername));
482                             $("#info").show();
483                         }
484                     }
485                 }
486                 wait_for_opener();
487             }
488         [% ELSIF selection_type == 'select' %]
489             function select_user(borrowernumber, data) {
490                 var p = window.opener;
491                 [%  IF callback %]
492                     p.[% callback | html %](borrowernumber, data);
493                 [%  ELSE %]
494                     p.select_user(borrowernumber, data);
495                 [%  END %]
496                 window.close();
497             }
498         [% END %]
499     </script>
500 [% END %]