web staff : patron search, grid integration experimenting
authorBill Erickson <berick@esilibrary.com>
Mon, 7 Apr 2014 21:07:06 +0000 (17:07 -0400)
committerBill Erickson <berick@esilibrary.com>
Mon, 7 Apr 2014 21:07:06 +0000 (17:07 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/circ/patron/t_search.tt2
Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2
Open-ILS/web/js/ui/default/staff/circ/patron/app.js

index 02d3a43..f6dcb0c 100644 (file)
 <br/>
 <div class="row">
   <div class="col-md-12">
-<!--
-    <div class="alert alert-info alert-dismissable" 
-      ng-hide="tips.dismissed('circ.patron.search')">
-      <button type="button" class="close" data-dismiss="alert" aria-hidden="true" 
-        ng-click="tips.dismiss('circ.patron.search')">&times;</button>
-      <ol>
-        <li>Click a row to select a patron.  This will activate action tabs for the patron.</li>
-        <li>Double-Click a row to focus the checkout tab for a patron.</li>
-        <li>Middle-click to open a patron in a new browser tab.</li>
-      </ol>
-    </div> 
--->
     [% INCLUDE 'staff/circ/patron/t_search_results.tt2' %]
   </div>
 </div>
index 1c5bf86..312055c 100644 (file)
@@ -1,28 +1,30 @@
 <eg-grid
   idl-class="au" sort="[]" id-field="id"
-  main-label="[% l('Patron Search Resuts') %]"
+  initial-offset="cachedSearchOffset"
+  features="-sort,-display"
+  main-label="[% l('Patron Search Results') %]"
   items-provider="patronSearchGridProvider"
   persist-key="eg.staff.circ.patron.search">
-  <eg-grid-field label="[% ('ID') %]" path='id'></eg-grid-field>
-  <eg-grid-field label="[% ('Card') %]" path='card.barcode'></eg-grid-field>
-  <eg-grid-field label="[% ('Last Name') %]" path='family_name'></eg-grid-field>
-  <eg-grid-field label="[% ('First Name') %]" path='first_given_name'></eg-grid-field>
-  <eg-grid-field label="[% ('Middle Name') %]" path='second_given_name'></eg-grid-field>
-  <eg-grid-field label="[% ('DoB') %]" path='dob'></eg-grid-field>
-  <eg-grid-field label="[% ('Home Library') %]" path='home_ou.shortname'></eg-grid-field>
-  <eg-grid-field label="[% ('Created On') %]" path='create_date'></eg-grid-field>
+  <eg-grid-field label="[% ('ID') %]" path='id' visible></eg-grid-field>
+  <eg-grid-field label="[% ('Card') %]" path='card.barcode' visible></eg-grid-field>
+  <eg-grid-field label="[% ('Last Name') %]" path='family_name' visible sortable></eg-grid-field>
+  <eg-grid-field label="[% ('First Name') %]" path='first_given_name' visible sortable></eg-grid-field>
+  <eg-grid-field label="[% ('Middle Name') %]" path='second_given_name' visible sortable></eg-grid-field>
+  <eg-grid-field label="[% ('DoB') %]" path='dob' visible sortable></eg-grid-field>
+  <eg-grid-field label="[% ('Home Library') %]" path='home_ou.shortname' visible></eg-grid-field>
+  <eg-grid-field label="[% ('Created On') %]" path='create_date' visible sortable></eg-grid-field>
 
-  <eg-grid-field label="[% ('Mailing:Street 1') %]" path='mailing_address.street1'></eg-grid-field>
-  <eg-grid-field label="[% ('Mailing:Street 2') %]" path='mailing_address.street2' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Mailing:City') %]" path='mailing_address.city' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Mailing:County') %]" path='mailing_address.county' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Mailing:State') %]" path='mailing_address.state' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Mailing:Zip') %]" path='mailing_address.post_code' display="false"></eg-grid-field>
+  <eg-grid-field label="[% ('Mailing:Street 1') %]" path='mailing_address.street1' visible></eg-grid-field>
+  <eg-grid-field label="[% ('Mailing:Street 2') %]" path='mailing_address.street2'></eg-grid-field>
+  <eg-grid-field label="[% ('Mailing:City') %]" path='mailing_address.city'></eg-grid-field>
+  <eg-grid-field label="[% ('Mailing:County') %]" path='mailing_address.county'></eg-grid-field>
+  <eg-grid-field label="[% ('Mailing:State') %]" path='mailing_address.state'></eg-grid-field>
+  <eg-grid-field label="[% ('Mailing:Zip') %]" path='mailing_address.post_code'></eg-grid-field>
 
-  <eg-grid-field label="[% ('Billing:Street 1') %]" path='billing_address.street1' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Billing:Street 2') %]" path='billing_address.street2' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Billing:City') %]" path='billing_address.city' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Billing:County') %]" path='billing_address.county' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Billing:State') %]" path='billing_address.state' display="false"></eg-grid-field>
-  <eg-grid-field label="[% ('Billing:Zip') %]" path='billing_address.post_code' display="false"></eg-grid-field>
+  <eg-grid-field label="[% ('Billing:Street 1') %]" path='billing_address.street1'></eg-grid-field>
+  <eg-grid-field label="[% ('Billing:Street 2') %]" path='billing_address.street2'></eg-grid-field>
+  <eg-grid-field label="[% ('Billing:City') %]" path='billing_address.city'></eg-grid-field>
+  <eg-grid-field label="[% ('Billing:County') %]" path='billing_address.county'></eg-grid-field>
+  <eg-grid-field label="[% ('Billing:State') %]" path='billing_address.state'></eg-grid-field>
+  <eg-grid-field label="[% ('Billing:Zip') %]" path='billing_address.post_code'></eg-grid-field>
 </eg-grid>
index 00ff7e9..3d82f6d 100644 (file)
@@ -12,7 +12,7 @@ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap',
 
 .config(function($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
-    $compileProvider.aHrefSanitizationWhitelist(/^\s*(blob):/); // grid export
+    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export
 
     // data loaded at startup which only requires an authtoken goes
     // here. this allows the requests to be run in parallel instead of
@@ -115,7 +115,7 @@ function($q,  egList,  egNet,  egAuth,  egUser,  egEnv,  egOrg,  egList) {
         checkout_overrides : {},
 
         // keep a cache of the patron search results
-        patrons : egList.create({indexFieldAsFunction : true}), // patron.id()
+        patrons : [],
         checkouts : egList.create(),
         items_out : egList.create({indexFieldAsFunction : true}), // circ.id()
         holds : egList.create(),
@@ -238,16 +238,82 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
 
     $scope.initTab('search');
     $scope.focusMe = true;
-    $scope.patrons = patronSvc.patrons;
+    $scope.args = $location.search();
+
+    console.log('PatronSearchCtrl');
     
     // our data provider is a modified flat data provider
     var provider = egGridFlatDataProvider.instance({});
-    provider.get = function(index, count, onitem) {
-        angular.forEach(
-            $scope.patrons.items.slice(index, index + count),
-            function(item) { onitem(item) }
+    provider.select = function(items) {
+        if (items[0]) {
+            var user = items[0];
+            patronSvc.setDefault(null, user);
+        }
+    }
+
+    provider.get = function(offset, count, onitem) {
+        console.log('get ' + $location.search() + ' : ' + provider._revision);
+        var deferred = $q.defer();
+
+            /*
+            if (args.id) {
+                retrieveUsers([args.id]);
+       egUser.get(id).then(function(user) {
+                patronSvc.localFlesh(user);
+                $scope.patrons.items[idx] = user;
+            });
+
+            } else {
+                sendSearch(args);
+            }
+            */
+
+        var search = compileSearch($location.search());
+
+        /*
+        if (Object.keys(search).length == 0 && 
+                offset == patronSvc.cachedSearchOffset && 
+                patronSvc.patrons.length) {
+            // accessing the page without a cached search
+            // see if we have a cached result set
+            angular.forEach(patronSvc.patrons, function(p) { onitem(p) });
+            return;
+        }
+        */
+
+        //patronSvc.cachedSearchOffset = offset;
+        patronSvc.patrons = [];
+
+        var home_ou = search.home_ou;
+        delete search.home_ou;
+        var inactive = search.inactive;
+        delete search.inactive;
+
+        console.debug('patron search ' + js2JSON(search));
+        egNet.request(
+            'open-ils.actor',
+            'open-ils.actor.patron.search.advanced.fleshed',
+            egAuth.token(), 
+            search, 
+            count,
+            compileSort(),
+            inactive,
+            home_ou,
+            egUser.defaultFleshFields,
+            offset
+
+        ).then(
+            function() { deferred.resolve() },
+            null, // onerror
+            function(user) {
+                patronSvc.localFlesh(user); // inline
+                deferred.notify(user);
+            }
         );
+
+        return deferred.promise;
     };
+
     provider.itemFieldValue = function(item, column) {
         return provider.nestedItemFieldValue(item, column);
     };
@@ -264,18 +330,6 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
     $scope.org_units = egEnv.aou.list.map(
         function(org) {return {shortname : org.shortname(), id : org.id() }})
 
-    // TODO: experiment
-    // if this is useful, it should be moved into a service.
-    $scope.tips = {
-        dismiss : function(tip) {
-            $window.localStorage.setItem('eg.tips.' + tip, 1);
-        },
-        dismissed : function(tip) {
-            return $window.localStorage.getItem('eg.tips.' + tip);
-        }
-        // TODO: function to reset all tips
-    };
-
     // TODO:
     // another experiment -- user prefs in localStorage -- this should
     // be a service, which can also check (certain) user/org settings
@@ -288,15 +342,17 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
     $scope.showExtras = JSON.parse($window.localStorage.getItem(
         'eg.prefs.circ.patron.search.showExtras')) || false;
 
+
     // map form arguments into search params
     function compileSearch(args) {
         var search = {};
         angular.forEach(args, function(val, key) {
             if (!val) return;
             if (key == 'profile') {
-                search.profile = {value : args.profile.id, group : 0};
+                //search.profile = {value : args.profile.id, group : 0};
+                search.profile = {value : search.profile, group : 0};
             } else if (key == 'home_ou') {
-                search.home_ou = args.home_ou.id; // passed separately
+                //search.home_ou = args.home_ou.id; // passed separately
             } else if (key == 'inactive') {
                 search.inactive = val;
             } else {
@@ -316,37 +372,37 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
         return search;
     }
 
-    // send compiled search; get user IDs
-    /*
-    function __sendSearch(args) {
-        search = compileSearch(args);
-
-        var home_ou = search.home_ou;
-        delete search.home_ou;
-        var inactive = search.inactive;
-        delete search.inactive;
+    function compileSort() {
 
-        console.debug('patron search ' + js2JSON(search));
-        egNet.request(
-            'open-ils.actor',
-            'open-ils.actor.patron.search.advanced',
-            egAuth.token(), search, 100,
-            [   // sort
+        if (!$scope.patronSearchGridProvider.sort.length) {
+            return [ // default
                 "family_name ASC",
                 "first_given_name ASC",
                 "second_given_name ASC",
                 "dob DESC"
-            ],
-            inactive,
-            home_ou
+            ];
+        }
 
-        ).then(function(ids) {
-            retrieveUsers(ids);
-        });
-    };
-    */
+        var sort = [];
+        angular.forEach(
+            $scope.patronSearchGridProvider.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);
+                }
+            }
+        );
+
+        console.log('sort = ' + js2JSON(sort));
+        return sort;
+    }
 
     // alt form which receives fleshed user objects
+    /*
     function sendSearch(args) {
         search = compileSearch(args);
 
@@ -359,13 +415,10 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
         egNet.request(
             'open-ils.actor',
             'open-ils.actor.patron.search.advanced.fleshed',
-            egAuth.token(), search, 50 /* limit */,
-            [   /* sort */
-                "family_name ASC",
-                "first_given_name ASC",
-                "second_given_name ASC",
-                "dob DESC"
-            ],
+            egAuth.token(), 
+            search, 
+            50 //
+            compileSort(),
             inactive,
             home_ou,
             egUser.defaultFleshFields
@@ -376,9 +429,10 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
             $scope.patronSearchGridProvider.increment();
         });
     };
-
+    */
 
     // fetch users by id and add them to the patrons list
+    /*
     function retrieveUsers(ids) {
         angular.forEach(ids, function(id, idx) {
             // capture idx to maintain search results order
@@ -388,20 +442,33 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
             });
         });
     }
+    */
 
     // collect form args fire patron search
     $scope.search = function(args) {
         if (args && Object.keys(args).length) {
+            //$scope.searchArgs = args;
+            //$scope.patronSearchGridProvider.increment();
+
+            if (args.profile) args.profile = args.profile.id;
+            if (args.home_ou) args.home_ou = args.home_ou.id;
+       
+            $location.search(args);
+
+            /*
+            $scope.
             $scope.patrons.reset();
             if (args.id) {
                 retrieveUsers([args.id]);
             } else {
                 sendSearch(args);
             }
+            */
         }
     }
 
     // manage table row selection
+    /*
     $scope.onPatronClick = function($event, user) {
         $scope.lastSelected = user;
 
@@ -425,6 +492,7 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
             patronSvc.setDefault(null, user);
         }                                                                      
     }
+    */
 
     $scope.onPatronDblClick = function($event, user) {
         $location.path('/circ/patron/' + user.id() + '/checkout');
@@ -437,6 +505,7 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
     // http://stackoverflow.com/questions/16749907/window-open-behaviour-in-chrome-tabs-windows
     // for now, skip this feature and support control-click to open 
     // multiple patrons instead.
+    /*
     $scope.openSelectedPatrons = function() {
         angular.forEach(
             $scope.patrons.selectedItems(),
@@ -448,6 +517,7 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
             }
         );
     }
+    */
 
     // handled up/down arrow events while the patrons results table is focused.
     // disabled for now, since there are some UI issues to work out first:
@@ -459,6 +529,8 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
     // hover in its original position, making the hovered row appear to be
     // selected (style-wise) even when it's not.  Disabling table-hover
     // CSS works, but table-hover is useful, so...
+
+    /* TODO: MOVE ME INTO GRID.js
     $scope.navigateResults = function($event) {
         // we can't select the next/previous user if we don't know 
         // which user was selected last.  this should never happen, though.
@@ -485,6 +557,7 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egEnv,
         
         if (user) $scope.onPatronClick($event, user);
     }
+    */
 
 }])