-[% INCLUDE 'staff/share/t_patron_search_form.tt2' %]
+<!-- TODO: inputs need sr-only labels
+ <label class="sr-only" for="input-id">label</label>
+-->
+
+<div class="row" id="patron-search-form-row">
+ <div class="col-md-11">
+ <form ng-submit="search(searchArgs)" id="patron-search-form"
+ role="form" class="form-horizontal">
+
+ <div class="form-group">
+
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ focus-me="focusMe"
+ ng-model="searchArgs.family_name" placeholder="[% l('Last Name') %]"/>
+ </div>
+
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.first_given_name" placeholder="[% l('First Name') %]"/>
+ </div>
+
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.second_given_name" placeholder="[% l('Middle Name') %]"/>
+ </div>
+
+ <div class="col-md-2" ng-mouseover="setLastFormElement()">
+ <input type="submit" class="btn btn-primary" value="[% l('Search') %]"/>
+ </div>
+
+ <div class="col-md-2" ng-mouseover="setLastFormElement()">
+ <input type="reset" class="btn btn-primary" ng-click="clearForm()"
+ value="[% l('Clear Form') %]"/>
+ </div>
+
+ <div class="col-md-2">
+ <button class="btn btn-default" ng-click="applyShowExtras($event, true)"
+ ng-mouseover="setLastFormElement()"
+ title="[% l('Show More Fields') %]" ng-show="!showExtras">
+ <span class="glyphicon glyphicon-circle-arrow-down"></span>
+ </button>
+ <button class="btn btn-default" ng-click="applyShowExtras($event, false)"
+ ng-mouseover="setLastFormElement()"
+ title="[% l('Show Fewer Fields') %]" ng-show="showExtras">
+ <span class="glyphicon glyphicon-circle-arrow-up"></span>
+ </button>
+ </div>
+ </div>
+
+ <div class="form-group" ng-show="showExtras">
+ <div class="col-md-2">
+ <input type="text" class="form-control" ng-model="searchArgs.card"
+ placeholder="[% l('Barcode') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.alias" placeholder="[% l('Alias') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.usrname" placeholder="[% l('Username') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.email" placeholder="[% l('Email') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.ident" placeholder="[% l('Identification') %]"/>
+ </div>
+ </div>
+
+ <div class="form-group" ng-show="showExtras">
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.id" placeholder="[% l('Database ID') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.phone" placeholder="[% l('Phone') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.street1" placeholder="[% l('Street 1') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.street2" placeholder="[% l('Street 2') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control"
+ ng-model="searchArgs.city" placeholder="[% l('City') %]"/>
+ </div>
+ </div>
+
+ <div class="form-group" ng-show="showExtras">
+ <div class="col-md-2">
+ <input type="text" class="form-control" ng-model="searchArgs.state"
+ placeholder="[% l('State') %]" title="[% l('State') %]"/>
+ </div>
+
+ <div class="col-md-2">
+ <input type="text" class="form-control" ng-model="searchArgs.post_code"
+ placeholder="[% l('Post Code') %]" title="[% l('Post Code') %]"/>
+ </div>
+
+ <div class="col-md-2">
+ <!--
+ <input type="text" class="form-control"
+ placeholder="[% l('Profile Group') %]"
+ ng-model="searchArgs.profile"
+ typeahead="grp as grp.name for grp in profiles | filter:$viewValue"
+ typeahead-editable="false" />
+ -->
+
+ <div class="btn-group patron-search-selector" uib-dropdown>
+ <button type="button" class="btn btn-default" uib-dropdown-toggle>
+ <span style="padding-right: 5px;">{{searchArgs.profile.name() || "[% l('Profile Group') %]"}}</span>
+ <span class="caret"></span>
+ </button>
+ <ul uib-dropdown-menu>
+ <li ng-repeat="grp in profiles">
+ <a href
+ style="padding-left: {{pgt_depth(grp) * 10 + 5}}px"
+ ng-click="searchArgs.profile = grp">{{grp.name()}}</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ <div class="col-md-2">
+ <eg-org-selector label="[% l('Home Library') %]"
+ selected="searchArgs.home_ou" sticky-setting="eg.circ.patron.search.ou">
+ </eg-org-selector>
+ </div>
+
+ <div class="col-md-2">
+ <div class="checkbox">
+ <label>
+ <input type="checkbox" ng-model="searchArgs.inactive"/>
+ [% l('Include Inactive?') %]
+ </label>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" ng-show="showExtras">
+ <div class="col-md-2">
+ <input type="text" class="form-control" ng-model="searchArgs.dob_year"
+ placeholder="[% l('DOB Year') %]" title="[% l('DOB Year') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control" ng-model="searchArgs.dob_month"
+ placeholder="[% l('DOB Month') %]" title="[% l('DOB Month') %]"/>
+ </div>
+ <div class="col-md-2">
+ <input type="text" class="form-control" ng-model="searchArgs.dob_day"
+ placeholder="[% l('DOB Day') %]" title="[% l('DOB Day') %]"/>
+ </div>
+ </div>
+ </form>
+ </div>
+</div>
<br/>
<div class="row">
true
);
- $scope.need_one_selected = function() {
- var items = $scope.gridControls.selectedItems();
- return (items.length > 0) ? false : true;
+ } else {
+ patronSvc.search_barcode = $scope.searchArgs.card;
+
+ var search = compileSearch($scope.searchArgs);
+ if (Object.keys(search) == 0) return $q.when();
+
+ var home_ou = search.home_ou;
+ delete search.home_ou;
+ var inactive = search.inactive;
+ delete search.inactive;
+
+ fullSearch = {
+ search : search,
+ sort : compileSort(),
+ inactive : inactive,
+ home_ou : home_ou,
+ };
+ }
+
+ fullSearch.count = count;
+ fullSearch.offset = offset;
+
+ if (patronSvc.lastSearch) {
+ // search repeated, return the cached results
+ if (angular.equals(fullSearch, patronSvc.lastSearch)) {
+ console.log('patron search returning ' +
+ patronSvc.patrons.length + ' cached results');
+
+ // notify has to happen after returning the promise
+ $timeout(
+ function() {
+ angular.forEach(patronSvc.patrons, function(user) {
+ deferred.notify(user);
+ });
+ deferred.resolve();
+ }
+ );
+ return deferred.promise;
+ }
+ }
+
+ patronSvc.lastSearch = fullSearch;
+
+ if (fullSearch.search.id) {
+ // search by user id performs a direct ID lookup
+ var userId = fullSearch.search.id.value;
+ $timeout(
+ function() {
+ egUser.get(userId).then(function(user) {
+ patronSvc.localFlesh(user);
+ patronSvc.patrons = [user];
+ deferred.notify(user);
+ deferred.resolve();
+ });
+ }
+ );
+ return deferred.promise;
+ }
+
+ if (!Object.keys(fullSearch.search).length) {
+ // Empty searches are rejected by the server. Avoid
+ // running the the empty search that runs on page load.
+ return $q.when();
+ }
+
+ egProgressDialog.open(); // Indeterminate
+
+ patronSvc.patrons = [];
+ var which_sound = 'success';
+ egCore.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.patron.search.advanced.fleshed',
+ egCore.auth.token(),
+ fullSearch.search,
+ fullSearch.count,
+ fullSearch.sort,
+ fullSearch.inactive,
+ fullSearch.home_ou,
+ egUser.defaultFleshFields,
+ fullSearch.offset
+
+ ).then(
+ function() {
+ deferred.resolve();
+ },
+ function() { // onerror
+ which_sound = 'error';
+ },
+ function(user) {
+ // hide progress bar as soon as the first result appears.
+ egProgressDialog.close();
+ patronSvc.localFlesh(user); // inline
+ patronSvc.patrons.push(user);
+ deferred.notify(user);
+ }
+ )['finally'](function() { // close on 0-hits or error
+ if (which_sound == 'success' && patronSvc.patrons.length == 0) {
+ which_sound = 'warning';
+ }
+ egCore.audio.play(which_sound + '.patron.by_search');
+ egProgressDialog.close();
+ });
+
+ return deferred.promise;
+ };
+
+ $scope.patronSearchGridProvider = provider;
+
+ // determine the tree depth of the profile group
+ $scope.pgt_depth = function(grp) {
+ var d = 0;
+ while (grp = egCore.env.pgt.map[grp.parent()]) d++;
+ return d;
+ }
+
+ $scope.clearForm = function () {
+ $scope.searchArgs={};
+ if (lastFormElement) lastFormElement.focus();
+ }
+
+ $scope.applyShowExtras = function($event, bool) {
+ if (bool) {
+ $scope.showExtras = true;
+ egCore.hatch.setItem('eg.circ.patron.search.show_extras', true);
+ } else {
+ $scope.showExtras = false;
+ egCore.hatch.removeItem('eg.circ.patron.search.show_extras');
+ }
+ if (lastFormElement) lastFormElement.focus();
+ $event.preventDefault();
+ }
+
+ egCore.hatch.getItem('eg.circ.patron.search.show_extras')
+ .then(function(val) {$scope.showExtras = val});
+
+ // map form arguments into search params
+ function compileSearch(args) {
+ var search = {};
+ angular.forEach(args, function(val, key) {
+ if (!val) return;
+ if (key == 'profile' && args.profile) {
+ search.profile = {value : args.profile.id(), group : 0};
+ } else if (key == 'home_ou' && args.home_ou) {
+ search.home_ou = args.home_ou.id(); // passed separately
+ } else if (key == 'inactive') {
+ search.inactive = val;
+ } else {
+ search[key] = {value : val, group : 0};
+ }
+ if (key.match(/phone|ident/)) {
+ search[key].group = 2;
+ } else {
+ if (key.match(/street|city|state|post_code/)) {
+ search[key].group = 1;
+ } else if (key == 'card') {
+ search[key].group = 3
+ } else if (key.match(/dob_/)) {
+ // DOB should always be numeric
+ search[key].value = search[key].value.replace(/\D/g,'');
+ if (search[key].value.length == 0) {
+ delete search[key];
+ }
+ else {
+ search[key].group = 4;
+ }
+ }
+ }
+ });
+
+ return search;
+ }
+
+ function compileSort() {
+
+ if (!provider.sort.length) {
+ return [ // default
+ "family_name ASC",
+ "first_given_name ASC",
+ "second_given_name ASC",
+ "dob DESC"
+ ];
+ }
+
+ var sort = [];
+ angular.forEach(
+ provider.sort,
+ function(sortdef) {
+ if (angular.isObject(sortdef)) {
+ var name = Object.keys(sortdef)[0];
+ var dir = sortdef[name];
+ sort.push(name + ' ' + dir);
+ } else {
+ sort.push(sortdef);
+ }
+ }
+ );
+
+ return sort;
+ }
+
+ $scope.setLastFormElement = function() {
+ lastFormElement = $document[0].activeElement;
+ }
+
+ // search form submit action; tells the results grid to
+ // refresh itself.
+ $scope.search = function(args) { // args === $scope.searchArgs
+ if (args && Object.keys(args).length)
+ $scope.gridControls.refresh();
+ if (lastFormElement) lastFormElement.focus();
+ }
+
+ // TODO: move this into the (forthcoming) grid row activate action
+ $scope.onPatronDblClick = function($event, user) {
+ $location.path('/circ/patron/' + user.id() + '/checkout');
+ }
+
+ if (patronSvc.urlSearch) {
+ // force the grid to load the url-based search on page load
+ provider.refresh();
}
$scope.need_two_selected = function() {
var items = $scope.gridControls.selectedItems();