catalog; view holds cont.
authorBill Erickson <berick@esilibrary.com>
Thu, 17 Jul 2014 11:55:43 +0000 (07:55 -0400)
committerBill Erickson <berick@esilibrary.com>
Thu, 17 Jul 2014 11:55:43 +0000 (07:55 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/cat/catalog/index.tt2
Open-ILS/src/templates/staff/cat/catalog/t_catalog.tt2
Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/share/print_templates/t_holds_for_bib.tt2 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
Open-ILS/web/js/ui/default/staff/services/ui.js

index 9a207d8..9e799d4 100644 (file)
@@ -5,8 +5,13 @@
 %]
 
 [% 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/eframe.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/cat/services/record.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/cat/catalog/app.js"></script>
 [% END %]
 
index a21e45c..398335e 100644 (file)
@@ -7,7 +7,8 @@
   </div>
   <div class="col-md-3">
     <div class="btn-group pull-right" dropdown>
-      <button type="button" class="btn btn-default dropdown-toggle" ng-disabled="!record_id">
+      <button type="button" 
+          class="btn btn-default dropdown-toggle" ng-disabled="!record_id">
         [% l('Actions for This Record') %] 
         <span class="caret"></span>
       </button>
@@ -15,7 +16,7 @@
         <li><a href dropdown-toggle ng-click="set_record_tab('catalog')">[% l('OPAC View') %]</a></li>
         <li><a href dropdown-toggle ng-click="set_record_tab('record_html')">[% l('MARC View') %]</a></li>
         <li class="divider"></li>
-        <li class="disabled"><a href dropdown-toggle>[% l('View Holds') %]</a></li>
+        <li><a href dropdown-toggle ng-click="set_record_tab('holds')">[% l('View Holds') %]</a></li>
         <li class="disabled"><a href dropdown-toggle>[% l('Mark as Title Hold Transfer Destination') %]</a></li>
         <li class="disabled"><a href dropdown-toggle>[% l('Transfer All Title Holds') %]</a></li>
       </ul>
@@ -23,7 +24,7 @@
   </div>
 </div>
 
-<div class="pad-vert">
+<div>
   <!-- ng-show allows the catalog iframe to stay loaded (unlike ng-if) -->
   <div ng-show="record_tab == 'catalog'">
     <eg-embed-frame url="catalog_url" handlers="handlers" onchange="handle_page"></eg-embed-frame>
@@ -32,5 +33,8 @@
   <div ng-if="record_tab == 'record_html'">
     <eg-record-html record-id="record_id"></eg-record-html>
   </div>
+  <div ng-if="record_tab == 'holds'">
+    [% INCLUDE 'staff/cat/catalog/t_holds.tt2' %]
+  </div>
 </div>
 
diff --git a/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2 b/Open-ILS/src/templates/staff/cat/catalog/t_holds.tt2
new file mode 100644 (file)
index 0000000..a420265
--- /dev/null
@@ -0,0 +1,108 @@
+
+<div ng-if="!detail_hold_id">
+  <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" onchange="pickup_ou_changed"></eg-org-selector>
+      </div>
+    </div>
+  </div>
+  <div class="pad-vert"></div>
+
+  <eg-grid
+    id-field="id"
+    features="-sort,-multisort"
+    items-provider="hold_grid_data_provider"
+    grid-controls="hold_grid_controls"
+    persist-key="cat.catalog.holds">
+
+    <eg-grid-menu-item handler="detail_view" 
+      label="[% l('Detail View') %]"></eg-grid-menu-item>
+
+    <eg-grid-action handler="grid_actions.show_recent_circs"
+      label="[% l('Show Last Few Circulations') %]"></eg-grid-action>
+    <eg-grid-action divider="true"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.set_copy_quality"
+      label="[% l('Set Desired Copy Quality') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.edit_pickup_lib"
+      label="[% l('Edit Pickup Library') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.edit_notify_prefs"
+      label="[% l('Edit Notification Settings') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.edit_dates"
+      label="[% l('Edit Hold Dates') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.activate"
+      label="[% l('Activate') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.suspend"
+      label="[% l('Suspend') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.set_top_of_queue"
+      label="[% l('Set Top of Queue') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.clear_top_of_queue"
+      label="[% l('Un-Set Top of Queue') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.transfer_to_marked_title"
+      label="[% l('Transfer To Marked Title') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.mark_damaged"
+      label="[% l('Mark Item Damaged') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.mark_missing"
+      label="[% l('Mark Item Missing') %]"></eg-grid-action>
+    <eg-grid-action divider="true"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.retarget"
+      label="[% l('Find Another Target') %]"></eg-grid-action>
+    <eg-grid-action handler="grid_actions.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>
+
+  <div class="flex-row pad-vert">
+    <div class="flex-cell"></div>
+    <div>
+      <button class="btn btn-default" ng-click="print_holds()">
+        [% l('Print') %]
+      </button>
+    </div>
+  </div>
+</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/share/print_templates/t_holds_for_bib.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_holds_for_bib.tt2
new file mode 100644 (file)
index 0000000..8d3061c
--- /dev/null
@@ -0,0 +1,29 @@
+<div>
+  <div>[% l('Holds for record: [_1]', '{{holds[0].title}}') %]</div>
+  <hr/>
+  <style>#holds-for-bib-table td { padding: 5px; }</style>
+  <table id="holds-for-bib-table">
+    <thead>
+      <tr>
+        <th>[% l('Request Date') %]</th>
+        <th>[% l('Patron Barcode') %]</th>
+        <th>[% l('Patron Last') %]</th>
+        <th>[% l('Patron Alias') %]</th>
+        <th>[% l('Current Copy') %]</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr ng-repeat="hold in holds">
+        <td>{{hold.hold.request_time | date:'short'}}</td>
+        <td>{{hold.patron_barcode}}</td>
+        <td>{{hold.patron_last}}</td>
+        <td>{{hold.patron_alias}}</td>
+        <td>{{hold.copy.barcode}}</td>
+      </tr>
+    </tbody>
+  </table>
+  <hr/>
+  <div>{{current_location.shortname}} {{today | date:'short'}}</div>
+  <div>[% l('Printed by [_1]', '{{staff.first_given_name}}') %]</div>
+<br/>
+
index e9bd6e0..b50d4f9 100644 (file)
@@ -1,8 +1,13 @@
 /**
  * TPAC Frame App
+ *
+ * currently, this app doesn't use routes for each sub-ui, because 
+ * reloading the catalog each time is sloooow.  better so far to 
+ * swap out divs w/ ng-if / ng-show / ng-hide as needed.
+ *
  */
 
-angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','egCoreMod'])
+angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','egCoreMod','egGridMod'])
 
 .config(function($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
@@ -17,6 +22,7 @@ angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','egCoreMod'])
         resolve : resolver
     });
 
+    // create some catalog page-specific mappings
     $routeProvider.when('/cat/catalog/record/:record_id', {
         templateUrl: './cat/catalog/t_catalog',
         controller: 'CatalogCtrl',
@@ -30,8 +36,10 @@ angular.module('egCatalogApp', ['ui.bootstrap','ngRoute','egCoreMod'])
 /**
  * */
 .controller('CatalogCtrl',
-       ['$scope','$routeParams','$location','egCore',
-function($scope , $routeParams , $location , egCore) {
+       ['$scope','$routeParams','$location','$q','egCore','egHolds',
+        'egGridDataProvider','egHoldGridActions',
+function($scope , $routeParams , $location , $q , egCore , egHolds, 
+         egGridDataProvider , egHoldGridActions) {
 
     // TODO: is start path configurable in the xul client?
     var url = $location.absUrl().replace(/\/staff.*/, '/opac/advanced');
@@ -46,15 +54,10 @@ function($scope , $routeParams , $location , egCore) {
     // default to catalog view of the record page
     $scope.record_tab = 'catalog';
 
-    $scope.set_record_tab = function(tab) {
-        $scope.record_tab = tab;
-    }
-
     $scope.handle_page = function(url) {
         var match = url.match(/\/+opac\/+record\/+(\d+)/);
         if (match) {
             $scope.record_id = match[1];
-            console.debug('loading record ' + $scope.record_id);
 
             // force the record_id to show up in the page.  
             // not sure why a $digest isn't occuring here.
@@ -64,6 +67,101 @@ function($scope , $routeParams , $location , egCore) {
         }
     }
 
+    // xulG handlers
     $scope.handlers = {};
+
+    // Holds bits -------------------------------
+    var provider = egGridDataProvider.instance({});
+    $scope.hold_grid_data_provider = provider;
+    $scope.grid_actions = egHoldGridActions;
+    $scope.hold_grid_controls = {};
+
+    var hold_ids = []; // current list of holds
+    function fetchHolds(offset, count) {
+        var ids = hold_ids.slice(offset, offset + count);
+        return egHolds.fetch_holds(ids).then(null, null,
+            function(hold_data) { 
+                //patronSvc.holds.push(hold_data);
+                console.log('returning hold ' + hold_data.mvr.title());
+                return hold_data;
+            }
+        );
+    }
+
+    provider.get = function(offset, count) {
+        if ($scope.record_tab != 'holds') return $q.when();
+        var deferred = $q.defer();
+        hold_ids = []; // no caching ATM
+
+        // fetch the IDs
+        egCore.net.request(
+            'open-ils.circ',
+            'open-ils.circ.holds.retrieve_all_from_title',
+            egCore.auth.token(), $scope.record_id, 
+            {pickup_lib : $scope.pickup_ou.id()}
+        ).then(
+            function(hold_data) {
+                angular.forEach(hold_data, function(list, type) {
+                    hold_ids = hold_ids.concat(list);
+                });
+                fetchHolds(offset, count).then(
+                    deferred.resolve, null, deferred.notify);
+            }
+        );
+
+        return deferred.promise;
+    }
+
+    $scope.detail_view = function(action, user_data, items) {
+        if (h = items[0]) {
+            $scope.detail_hold_id = h.hold.id();
+        }
+    }
+
+    $scope.list_view = function(items) {
+         $scope.detail_hold_id = null;
+    }
+
+    // refresh the list of record holds when the pickup lib is changed.
+    $scope.pickup_ou = egCore.org.get(egCore.auth.user().ws_ou());
+    $scope.pickup_ou_changed = function(org) {
+        $scope.pickup_ou = org;
+        provider.refresh();
+    }
+
+    $scope.set_record_tab = function(tab) {
+        $scope.record_tab = tab;
+
+        if (tab == 'holds') {
+            $scope.detail_hold_record_id = $scope.record_id; 
+
+            // refresh the holds grid
+            provider.refresh();
+        }
+    }
+
+    $scope.print_holds = function() {
+        var holds = [];
+        angular.forEach($scope.hold_grid_controls.allItems(), function(item) {
+            holds.push({
+                hold : egCore.idl.toHash(item.hold),
+                patron_last : item.patron_last,
+                patron_alias : item.patron_alias,
+                patron_barcode : item.patron_barcode,
+                copy : egCore.idl.toHash(item.copy),
+                volume : egCore.idl.toHash(item.volume),
+                title : item.mvr.title(),
+                author : item.mvr.author()
+            });
+        });
+
+        egCore.print.print({
+            context : 'receipt', 
+            template : 'holds_for_bib', 
+            scope : {holds : holds}
+        });
+    }
+
+
 }])
  
index 7017ae8..c960ffb 100644 (file)
@@ -198,6 +198,10 @@ function($modal, $interpolate) {
             // org unit will not be added to the selector.
             hiddenTest : '=',
 
+            // Caller can either $watch(selected, ..) or register an
+            // onchange handler.
+            onchange : '=',
+
             // optional primary drop-down button label
             label : '@'
         },
@@ -240,6 +244,7 @@ function($modal, $interpolate) {
 
             $scope.orgChanged = function(org) {
                 $scope.selected = egOrg.get(org.id);
+                if ($scope.onchange) $scope.onchange($scope.selected);
             }
 
             if (!$scope.selected)