Angular selfcheck WIP
authorBill Erickson <berickxx@gmail.com>
Thu, 27 Oct 2016 19:44:47 +0000 (15:44 -0400)
committerBill Erickson <berickxx@gmail.com>
Fri, 11 Aug 2017 19:20:25 +0000 (15:20 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/templates/staff/circ/selfcheck/index.tt2
Open-ILS/src/templates/staff/circ/selfcheck/t_items.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/selfcheck/t_login.tt2
Open-ILS/web/js/ui/default/staff/circ/selfcheck/app.js

index 04be105..c6c2261 100644 (file)
@@ -24,13 +24,16 @@ angular.module('egCoreMod').run(['egStrings', function(s) {
 </script>
 [% END %]
 
+
+<!-- TODO: CSS, header image, other styling...-->
+
+<!-- header bar : main banner image and scan box -->
 <div class="row" ng-if="!is_login_page()">
   <div class="col-md-12">
     <div class="row">
       <div>[% l('Welcome, [_1]', '{{patron().first_given_name()}}') %]</div>
     </div>
     <div class="row">
-      <!-- TODO: header image stuff -->
       <div class="col-md-5"></div>
       <div class="col-md-2">
         <input 
@@ -45,32 +48,29 @@ angular.module('egCoreMod').run(['egStrings', function(s) {
   </div>
 </div>
 
+<!-- dynamic content pane (left) and navigation pane (right) -->
 <div class="row">
-  <div class="col-md-8">
+  <div class="{{dyn_content_class()}}">
     <div ng-view></div>
   </div>
   <div class="col-md-4" ng-if="!is_login_page()">
     <fieldset>
       <legend>[% l('Items Checked Out') %]</legend>
-      <div>[% l('Total items this session: [_1]', 
-        '{{counts("session_circ")}}') %]</div>
-      <div>[% l('Total items on account: [_1]', 
-        '{{counts("total_circ")}}') %]</div>
-      <div><button class="btn btn-success">
+      <div>[% l('Total items this session: [_1]', '{{counts("session_circ")}}') %]</div>
+      <div>[% l('Total items on account: [_1]', '{{counts("total_circ")}}') %]</div>
+      <div><button class="btn btn-success" ng-click="show_pane('items')">
         [% l('View Items Out') %]</button></div>
     </fieldset>
     <fieldset>
       <legend>[% l('Holds') %]</legend>
-      <div>[% l('You have [_1] item(s) ready for pickup', 
-        '{{counts("hold_ready")}}') %]</div>
-      <div>[% l('You have [_1] total holds', 
-        '{{counts("total_hold")}}') %]</div>
-      <div><button class="btn btn-success">
+      <div>[% l('You have [_1] item(s) ready for pickup', '{{counts("hold_ready")}}') %]</div>
+      <div>[% l('You have [_1] total holds', '{{counts("total_hold")}}') %]</div>
+      <div><button class="btn btn-success" ng-click="show_pane('holds')">
         [% l('View Holds') %]</button></div>
     </fieldset>
     <fieldset>
       <legend>[% l('Fines') %]</legend>
-      <div><button class="btn btn-success">
+      <div><button class="btn btn-success" ng-click="show_pane('fines')">
         [% l('View Details') %]</button>
       </div>
     </fieldset>
diff --git a/Open-ILS/src/templates/staff/circ/selfcheck/t_items.tt2 b/Open-ILS/src/templates/staff/circ/selfcheck/t_items.tt2
new file mode 100644 (file)
index 0000000..55511c6
--- /dev/null
@@ -0,0 +1,19 @@
+
+<div class="row">
+  <div class="col-md-1"></div>
+  <div class="col-md-2">[% l('Barcode') %]</div>
+  <div class="col-md-3">[% l('Title') %]</div>
+  <div class="col-md-3">[% l('Author') %]</div>
+  <div class="col-md-2">[% l('Due Date') %]</div>
+</div>
+
+<div class="row" ng-repeat="circ in circs track by circ.circ.id()">
+  <div class="col-md-1">
+    <img src="/opac/extras/ac/jacket/small/r/{{circ.record.doc_id()}}"/>
+  </div>
+  <div class="col-md-2">{{circ.copy.barcode()}}</div>
+  <div class="col-md-3">{{circ.record.title()}}</div>
+  <div class="col-md-3">{{circ.record.author()}}</div>
+  <div class="col-md-2">{{circ.circ.due_date() | date:'short'}}</div>
+</div>
+
index 3e15c44..411a526 100644 (file)
@@ -1,41 +1,39 @@
-<div class="container">
-  <div class="row">
-    <div class="col-md-3"></div>
-      <div class="col-md-6">
-        <fieldset>
-          <legend>[% l('Please log in with your username or library barcode.') %]</legend>
+<div class="row">
+  <div class="col-md-3"></div>
+    <div class="col-md-6">
+      <fieldset>
+        <legend>[% l('Please log in with your username or library barcode.') %]</legend>
 
-          <form ng-submit="login(args)" name="sc-login-form" 
-            autocomplete="off" class="form-horizontal" role="form">
-            <div class="form-group">
-              <label class="col-md-4 control-label" for="login-username">[% l('Username or Barcode') %]</label>
-              <div class="col-md-8">
-                <input type="text" id="login-username" class="form-control" 
-                  focus-me="focus_username" select-me="focus_username"
-                  placeholder="Username" ng-model="args.username"/>
-              </div>
+        <form ng-submit="login(args)" name="sc-login-form" 
+          autocomplete="off" class="form-horizontal" role="form">
+          <div class="form-group">
+            <label class="col-md-4 control-label" for="login-username">[% l('Username or Barcode') %]</label>
+            <div class="col-md-8">
+              <input type="text" id="login-username" class="form-control" 
+                focus-me="focus_username" select-me="focus_username"
+                placeholder="Username" ng-model="args.username"/>
             </div>
+          </div>
 
-            <div class="form-group">
-              <label class="col-md-4 control-label" for="login-password">[% l('Password') %]</label>
-              <div class="col-md-8">
-                <input type="password" id="login-password" class="form-control"
-                  placeholder="Password" ng-model="args.password"/>
-              </div>
+          <div class="form-group" ng-if="password_required">
+            <label class="col-md-4 control-label" for="login-password">[% l('Password') %]</label>
+            <div class="col-md-8">
+              <input type="password" id="login-password" class="form-control"
+                placeholder="Password" ng-model="args.password"/>
             </div>
+          </div>
 
-            <div class="form-group">
-              <div class="col-md-offset-4 col-md-2">
-                <button type="submit" class="btn btn-default">[% l('Log in') %]</button>
-              </div>
-              <div class="col-md-2">
-                <span ng-show="login_failed" class="label label-warning">[% l('Login Failed') %]</span>
-              </div>
+          <div class="form-group">
+            <div class="col-md-offset-4 col-md-2">
+              <button type="submit" class="btn btn-default">[% l('Log in') %]</button>
             </div>
+            <div class="col-md-2">
+              <span ng-show="login_failed" class="label label-warning">[% l('Login Failed') %]</span>
+            </div>
+          </div>
 
-          </form>
-        </fieldset>
-      </div>
-    <div class="col-md-3"></div>
-  </div>
+        </form>
+      </fieldset>
+    </div>
+  <div class="col-md-3"></div>
 </div>
index d6c631a..beb3a21 100644 (file)
@@ -2,6 +2,12 @@
  * Self-Checkout App
  */
 
+/*
+ * TODO: add support auth.js for forcing a re-login when navigating
+ * away from the selfcheck UI.  Consider sessionStorage item
+ * that restricts access.
+ */
+
 angular.module('egSelfCheckApp', 
     ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'ngToast'])
 
@@ -60,6 +66,12 @@ angular.module('egSelfCheckApp',
         resolve : resolver
     });
 
+    $routeProvider.when('/circ/selfcheck/items', {
+        templateUrl: './circ/selfcheck/t_items',
+        controller: 'ItemsCtrl',
+        resolve : resolver
+    });
+
     $routeProvider.otherwise({redirectTo : '/circ/selfcheck/login'});
 })
 
@@ -90,8 +102,14 @@ function($q , $timeout , $window , $location , $timeout , egCore , egConfirmDial
 
     };
 
+    // called with each path load
+    service.new_path_init = function() {
+        if (!service.patron) 
+            return service.logout_patron();
+        service.reset_login_timer();
+    }
+
     service.show_timeout_warning = function() {
-        // TODO: force logout after warning timeout
 
         egConfirmDialog.open(
             egCore.strings.CONFIRM_TIMEOUT_TITLE, 
@@ -103,6 +121,8 @@ function($q , $timeout , $window , $location , $timeout , egCore , egConfirmDial
             egCore.strings.CONFIRM_TIMEOUT_LOGOUT
         );
 
+        // If the patron choses no action from the above dialog,
+        // force a logout after the configured amount of time.
         service.login_warning_timer = $timeout(
             service.logout_patron, service.login_timeout_warning
         );
@@ -160,12 +180,31 @@ function($q , $timeout , $window , $location , $timeout , egCore , egConfirmDial
         });
     }
 
+    /* 
+     * Returns true if the username string looks like a barcode. 
+     */
+    service.username_is_barcode = function(username) {
+        var regstr = 
+            egCore.env.aous['circ.selfcheck.patron_password_required'];
+        if (regstr) {
+            var reg = new RegExp(regstr);
+            return reg && username.match(reg);
+        }
+        return false;
+    }
+
+    /*
+     * Test password if required, then fetch the user data.
+     */
     service.login_patron = function(username, password) {
 
-        // TODO: test barcode regex
-        var barcode = null;
+        var barcode;
+        if (service.username_is_barcode(username)) {
+           barcode = username;
+           username = null;
+        }
 
-        if (true) { // TODO: test password required
+        if (egCore.env.aous['circ.selfcheck.patron_password_required']) {
             return egCore.auth.verify({
                 username : username, 
                 barcode  : barcode,
@@ -178,12 +217,31 @@ function($q , $timeout , $window , $location , $timeout , egCore , egConfirmDial
         }
     }
 
+    /*
+     * Return to the login page via full page load to clear
+     * stored user data.
+     */
     service.logout_patron = function() {
-        // force a page reload to clear all cached data.
         $window.location.href = $location.absUrl().replace(
             /\/selfcheck\/.*/, '/selfcheck/login');
     }
 
+    /* 
+     * Returns a notify-stream of open circ blobs.
+     * Resets total circ count to match the server.
+     */
+    service.get_items_out = function() {
+        service.total_circ_count = 0;
+        return egCore.net.request(
+            'open-ils.circ',
+            'open-ils.circ.actor.user.checked_out',
+            egCore.auth.token(), service.patron.id()
+        ).then(null, null, function(circ_blob) {
+            service.total_circ_count++;
+            return circ_blob;
+        });
+    }
+
     return service;
 }])
 
@@ -205,6 +263,10 @@ function($scope,  $q,  $location , egCore,  scSvc) {
         return $location.path() == '/circ/selfcheck/login';
     }
 
+    $scope.dyn_content_class = function() {
+        return $scope.is_login_page() ? 'col-md-12' : 'col-md-8';
+    }
+
     $scope.scanbox = {
         text : '',
         focus : false,
@@ -216,6 +278,13 @@ function($scope,  $q,  $location , egCore,  scSvc) {
 
     $scope.patron = function() { return scSvc.patron }
 
+    $scope.show_pane = function(name) {
+        if (name && name.match(/(login|checkout|items|holds|fines)$/)) { 
+            console.log('navigating to pane ' + name);
+            $location.path('/circ/selfcheck/' + name);
+        }
+    }
+
 }])
 
 
@@ -228,10 +297,15 @@ function($scope,  $q,  $location , egCore,  scSvc) {
        ['$scope','$q','$location','egCore','scSvc',
 function($scope,  $q,  $location , egCore,  scSvc) {
 
+    $scope.focus_username = true;
+    $scope.password_required = 
+        egCore.env.aous['circ.selfcheck.patron_password_required'];
+
     $scope.login = function(args) {
         $scope.login_failed = false;
 
         if (!args.username) return;
+        if ($scope.password_required && !args.password) return;
 
         scSvc.login_patron(args.username, args.password).then(
             function() {
@@ -249,14 +323,20 @@ function($scope,  $q,  $location , egCore,  scSvc) {
 .controller('CheckoutCtrl',
        ['$scope','$q','$location','egCore','scSvc',
 function($scope,  $q,  $location , egCore,  scSvc) {
+    scSvc.new_path_init();
+    $scope.scanbox.focus = true;
 
-    if (!scSvc.patron) {
-        $location.path('/circ/selfcheck/login');
-        return;
-    }
+}])
 
-    scSvc.reset_login_timer();
+.controller('ItemsCtrl',
+       ['$scope','$q','$location','egCore','scSvc',
+function($scope,  $q,  $location , egCore,  scSvc) {
+    scSvc.new_path_init();
     $scope.scanbox.focus = true;
+    $scope.circs = [];
+
+    scSvc.get_items_out().then(null, null, 
+        function(circ_blob) { $scope.circs.push(circ_blob) });
 
 }])