holds shelf
authorBill Erickson <berick@esilibrary.com>
Tue, 8 Jul 2014 21:14:04 +0000 (17:14 -0400)
committerBill Erickson <berick@esilibrary.com>
Tue, 8 Jul 2014 21:14:04 +0000 (17:14 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/circ/holds/index.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/holds/t_shelf.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/navbar.tt2
Open-ILS/web/js/ui/default/staff/circ/holds/app.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/circ/patron/holds.js
Open-ILS/web/js/ui/default/staff/circ/services/holds.js
Open-ILS/web/js/ui/default/staff/services/ui.js

diff --git a/Open-ILS/src/templates/staff/circ/holds/index.tt2 b/Open-ILS/src/templates/staff/circ/holds/index.tt2
new file mode 100644 (file)
index 0000000..01d07b1
--- /dev/null
@@ -0,0 +1,21 @@
+[%
+  WRAPPER "staff/base.tt2";
+  ctx.page_title = l("Holds Shelf"); 
+  ctx.page_app = "egHoldsApp";
+%]
+
+[% BLOCK APP_JS %]
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/grid.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/user.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/circ.js"></script>
+[% INCLUDE 'staff/circ/share/circ_strings.tt2' %]
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/services/holds.js"></script>
+[% INCLUDE 'staff/circ/share/hold_strings.tt2' %]
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/holds/app.js"></script>
+<link rel="stylesheet" href="[% ctx.base_path %]/staff/css/circ.css" />
+[% END %]
+
+<div ng-view></div>
+
+[% END %]
diff --git a/Open-ILS/src/templates/staff/circ/holds/t_shelf.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_shelf.tt2
new file mode 100644 (file)
index 0000000..a465a6c
--- /dev/null
@@ -0,0 +1,36 @@
+<div class="row">
+  <div class="col-md-3">
+    <div class="input-group">
+      <span class="input-group-addon">[% l('Pickup Library') %]</span>
+      <eg-org-selector selected="pickup_ou"></eg-org-selector>
+    </div>
+  </div>
+  <div class="col-md-2">
+    <div class="checkbox">
+      <label>
+        <input ng-model="show_cleared" type="checkbox"/>
+        [% l('View Clearable Holds') %]
+      </label>
+    </div>
+  </div>
+</div>
+<div class="pad-vert"></div>
+
+<div ng-if="!detail_hold_id">
+[% INCLUDE 'staff/circ/holds/t_shelf_list.tt2' %]
+</div>
+
+<!-- hold details -->
+<div ng-if="detail_hold_id">
+  <div class="row">
+    <div class="col-md-2">
+      <button class="btn btn-default" ng-click="list_view()">
+        [% l('List View') %]
+      </button>
+    </div>
+  </div>
+  <div class="pad-vert"></div>
+  <eg-record-summary record='detail_hold_record' 
+    record-id="detail_hold_record_id"></eg-record-summary>
+  <eg-hold-details hold-retrieved="set_hold" hold-id="detail_hold_id"></eg-hold-details>
+</div>
diff --git a/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2 b/Open-ILS/src/templates/staff/circ/holds/t_shelf_list.tt2
new file mode 100644 (file)
index 0000000..0494b85
--- /dev/null
@@ -0,0 +1,72 @@
+
+<eg-grid
+  id-field="id"
+  features="-sort,-multisort"
+  items-provider="gridDataProvider"
+  persist-key="circ.holds.shelf">
+
+  <eg-grid-menu-item handler="detail_view" 
+    label="[% l('Detail View') %]"></eg-grid-menu-item>
+
+  <eg-grid-action handler="show_recent_circs"
+    label="[% l('Show Last Few Circulations') %]"></eg-grid-action>
+  <eg-grid-action divider="true"></eg-grid-action>
+  <eg-grid-action handler="set_copy_quality"
+    label="[% l('Set Desired Copy Quality') %]"></eg-grid-action>
+  <eg-grid-action handler="edit_pickup_lib"
+    label="[% l('Edit Pickup Library') %]"></eg-grid-action>
+  <eg-grid-action handler="edit_notify_prefs"
+    label="[% l('Edit Notification Settings') %]"></eg-grid-action>
+  <eg-grid-action handler="edit_dates"
+    label="[% l('Edit Hold Dates') %]"></eg-grid-action>
+  <eg-grid-action handler="activate"
+    label="[% l('Activate') %]"></eg-grid-action>
+  <eg-grid-action handler="suspend"
+    label="[% l('Suspend') %]"></eg-grid-action>
+  <eg-grid-action handler="set_top_of_queue"
+    label="[% l('Set Top of Queue') %]"></eg-grid-action>
+  <eg-grid-action handler="clear_top_of_queue"
+    label="[% l('Un-Set Top of Queue') %]"></eg-grid-action>
+  <eg-grid-action handler="transfer_to_marked_title"
+    label="[% l('Transfer To Marked Title') %]"></eg-grid-action>
+  <eg-grid-action handler="mark_damaged"
+    label="[% l('Mark Item Damaged') %]"></eg-grid-action>
+  <eg-grid-action handler="mark_missing"
+    label="[% l('Mark Item Missing') %]"></eg-grid-action>
+  <eg-grid-action divider="true"></eg-grid-action>
+  <eg-grid-action handler="retarget"
+    label="[% l('Find Another Target') %]"></eg-grid-action>
+  <eg-grid-action handler="cancel_hold"
+    label="[% l('Cancel Hold') %]"></eg-grid-action>
+
+  <eg-grid-field label="[% l('Hold ID') %]" path='hold.id'></eg-grid-field>
+  <eg-grid-field label="[% l('Current Copy') %]" 
+    path='hold.current_copy.barcode'>
+    <a href="./cat/item/{{item.hold.current_copy().id()}}/summary" target="_self">
+      {{item.hold.current_copy().barcode()}}
+    </a>
+  </eg-grid-field>
+
+  <eg-grid-field label="[% l('Request Date') %]" path='hold.request_time'></eg-grid-field>
+  <eg-grid-field label="[% l('Capture Date') %]" path='hold.capture_time'></eg-grid-field>
+  <eg-grid-field label="[% l('Available Date') %]" path='hold.shelf_time'></eg-grid-field>
+  <eg-grid-field label="[% l('Hold Type') %]" path='hold.hold_type'></eg-grid-field>
+  <eg-grid-field label="[% l('Pickup Library') %]" path='hold.pickup_lib.shortname'></eg-grid-field>
+
+  <eg-grid-field label="[% l('Title') %]" path='mvr.title'>
+    <a href="[% ctx.base_path %]/opac/record/{{item.mvr.doc_id()}}">
+      {{item.mvr.title()}}
+    </a>
+  </eg-grid-field>
+
+  <eg-grid-field label="[% l('Author') %]" path='mvr.author'></eg-grid-field>
+  <eg-grid-field label="[% l('Potential Copies') %]" path='potential_copies'></eg-grid-field>
+  <eg-grid-field label="[% l('Status') %]" path='status_string'></eg-grid-field>
+
+  <eg-grid-field label="[% l('Queue Position') %]" path='queue_position' hidden></eg-grid-field>
+  <eg-grid-field path='hold.*' parent-idl-class="ahr" hidden></eg-grid-field>
+  <eg-grid-field path='copy.*' parent-idl-class="acp" hidden></eg-grid-field>
+  <eg-grid-field path='volume.*' parent-idl-class="acn" hidden></eg-grid-field>
+  <eg-grid-field path='mvr.*' parent-idl-class="mvr" hidden></eg-grid-field>
+</eg-grid>
+
index f9082f0..b51509e 100644 (file)
               <span>[% l('Record In-House Use') %]</span>
             </a>
           </li>
+          <li>
+            <a href="./circ/holds/shelf" target="_self">
+              <span class="glyphicon glyphicon-tasks"></span>
+              <span>[% l('View Holds Shelf') %]</span>
+            </a>
+          </li>
           <li class="divider"></li>
           <li>
             <a href="./cat/item/replace_barcode/index" target="_self">
diff --git a/Open-ILS/web/js/ui/default/staff/circ/holds/app.js b/Open-ILS/web/js/ui/default/staff/circ/holds/app.js
new file mode 100644 (file)
index 0000000..b558358
--- /dev/null
@@ -0,0 +1,120 @@
+angular.module('egHoldsApp', 
+    ['ngRoute', 'ui.bootstrap', 'egCoreMod', 'egUiMod', 'egGridMod'])
+
+.config(function($routeProvider, $locationProvider, $compileProvider) {
+    $locationProvider.html5Mode(true);
+    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob):/); // grid export
+
+    var resolver = {delay : 
+        ['egStartup', function(egStartup) {return egStartup.go()}]}
+
+    $routeProvider.when('/circ/holds/shelf', {
+        templateUrl: './circ/holds/t_shelf',
+        controller: 'HoldsShelfCtrl',
+        resolve : resolver
+    });
+
+    $routeProvider.when('/circ/holds/shelf/:hold_id', {
+        templateUrl: './circ/holds/t_shelf',
+        controller: 'HoldsShelfCtrl',
+        resolve : resolver
+    });
+
+    $routeProvider.otherwise({redirectTo : '/circ/holds/shelf'});
+})
+
+.controller('HoldsShelfCtrl',
+       ['$scope','$q','$routeParams','$window','$location','egCore','egHolds','egCirc','egGridDataProvider',
+function($scope , $q , $routeParams , $window , $location , egCore , egHolds , egCirc , egGridDataProvider)  {
+    $scope.detail_hold_id = $routeParams.hold_id;
+
+    var hold_ids = [];
+    var holds = [];
+
+    function fetch_holds(offset, count) {
+        var ids = hold_ids.slice(offset, offset + count);
+        return egHolds.fetch_holds(ids).then(null, null,
+            function(hold_data) { 
+                holds.push(hold_data);
+                return hold_data; // to the grid
+            }
+        );
+    }
+
+    var provider = egGridDataProvider.instance({});
+    $scope.gridDataProvider = provider;
+
+    provider.get = function(offset, count) {
+
+        // see if we have the requested range cached
+        if (holds[offset]) {
+            return provider.arrayNotifier(patronSvc.holds, offset, count);
+        }
+
+        // see if we have the holds IDs for this range already loaded
+        if (hold_ids[offset]) {
+            return fetch_holds(offset, count);
+        }
+
+        var deferred = $q.defer();
+        hold_ids = [];
+        holds = [];
+
+        var method = 'open-ils.circ.captured_holds.id_list.on_shelf.retrieve.authoritative.atomic';
+        /*
+        if ($scope.holds_display == 'alt')
+            method = 'open-ils.circ.holds.canceled.id_list.retrieve.authoritative';
+        */
+
+        egCore.net.request(
+            'open-ils.circ', method,
+            egCore.auth.token(), $scope.pickup_ou.id()
+
+        ).then(function(ids) {
+            if (!ids.length) { 
+                deferred.resolve(); 
+                return; 
+            }
+
+            hold_ids = ids;
+            fetch_holds(offset, count)
+            .then(deferred.resolve, null, deferred.notify);
+        });
+
+        return deferred.promise;
+    }
+
+    function refresh_page() {
+        holds = [];
+        hold_ids = [];
+        provider.refresh();
+    }
+
+
+    // re-draw the grid when user changes the org selector
+    $scope.pickup_ou = egCore.org.get(egCore.auth.user().ws_ou());
+    $scope.$watch('pickup_ou', function(newVal, oldVal) {
+        if (newVal && newVal != oldVal) 
+            refresh_page();
+    });
+
+    $scope.detail_view = function(action, user_data, items) {
+        if (h = items[0]) {
+            $location.path('/circ/holds/shelf/' + h.hold.id());
+        }
+    }
+
+    $scope.list_view = function(items) {
+        $location.path('/circ/holds/shelf');
+    }
+
+    // when the detail hold is fetched (and updated), update the bib
+    // record summary display record id.
+    $scope.set_hold = function(hold_data) {
+        $scope.detail_hold_record_id = hold_data.mvr.doc_id();
+    }
+
+    refresh_page();
+
+}]);
+
index a77fbd2..39ec836 100644 (file)
@@ -40,24 +40,12 @@ function($scope,  $q,  $routeParams,  egCore,  egUser,  patronSvc,
     $scope.gridDataProvider = provider;
 
     function fetchHolds(offset, count) {
-
         var ids = patronSvc.hold_ids.slice(offset, offset + count);
-
-        return egCore.net.request(
-            'open-ils.circ',
-            'open-ils.circ.hold.details.batch.retrieve.authoritative',
-            egCore.auth.token(), ids
-
-        ).then(null, null, function(hold_data) {
-            $scope.loading = false;
-
-            var hold = hold_data.hold;
-            hold_data.id = hold.id();
-            egHolds.local_flesh(hold_data);
-
-            patronSvc.holds.push(hold_data);
-            return hold_data;
-        });
+        return egHolds.fetch_holds(ids).then(
+            function() { $scope.loading = false; },
+            null,
+            function(hold_data) { patronSvc.holds.push(hold_data) }
+        );
     }
 
     provider.get = function(offset, count) {
index 347c687..82d795a 100644 (file)
@@ -11,6 +11,22 @@ function($modal , $q , egCore , egAlertDialog , egConfirmDialog , egAlertDialog)
 
     var service = {};
 
+    service.fetch_holds = function(hold_ids) {
+
+        return egCore.net.request(
+            'open-ils.circ',
+            'open-ils.circ.hold.details.batch.retrieve.authoritative',
+            egCore.auth.token(), hold_ids
+
+        ).then(null, null, function(hold_data) {
+            var hold = hold_data.hold;
+            hold_data.id = hold.id();
+            service.local_flesh(hold_data);
+            return hold_data;
+        });
+    }
+
+
     service.cancel_holds = function(hold_ids) {
        
         return $modal.open({
index 02ca8d8..7017ae8 100644 (file)
@@ -270,7 +270,7 @@ It also allows us to abstract away some browser finickiness.
                 // user's expectations.  Note this allows us to retain
                 // the timezone.
                 function strip_time(date) {
-                    if (!date) return null;
+                    if (!date) date = new Date();
                     date.setHours(0);
                     date.setMinutes(0);
                     date.setSeconds(0);