[% 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" />
+<script>
+angular.module('egCoreMod').run(['egStrings', function(s) {
+ s.CLEAR_SHELF_ACTION_shelf = "[% l('Reshelve') %]";
+}])
+</script>
[% END %]
<div ng-view></div>
<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>
+ <eg-org-selector selected="pickup_ou" ng-disabled="is_clearing()"></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 class="col-md-3" ng-show="is_clearing()">
+ <progressbar max="clear_progress.max" value="clear_progress.value">
+ <span class="progressbar-text">{{clear_progress.value}} / {{clear_progress.max}}</span>
+ </progressbar>
</div>
</div>
+
<div class="pad-vert"></div>
<div ng-if="!detail_hold_id">
id-field="id"
features="-sort,-multisort"
items-provider="gridDataProvider"
+ grid-controls="gridControls"
persist-key="circ.holds.shelf">
<eg-grid-menu-item handler="detail_view"
label="[% l('Detail View') %]"></eg-grid-menu-item>
+ <eg-grid-menu-item handler="show_clearable"
+ hidden="clear_mode" disabled="is_clearing"
+ label="[% l('Show Clearable Holds') %]"></eg-grid-menu-item>
+
+ <eg-grid-menu-item handler="show_active"
+ hidden="active_mode" disabled="is_clearing"
+ label="[% l('Show All Holds') %]"></eg-grid-menu-item>
+
+ <eg-grid-menu-item handler="clear_holds" disabled="disable_clear"
+ label="[% l('Clear These Holds') %]"></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-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('Post-Clear') %]" path='post_clear'></eg-grid-field>
<eg-grid-field label="[% l('Title') %]" path='mvr.title'>
<a href="[% ctx.base_path %]/opac/record/{{item.mvr.doc_id()}}">
/* bootstrap alerts are heavily padded. use this to reduce */
.alert-less-pad {padding: 5px;}
+/* text displayed inside a <progressbar>, typically the max/progress values */
+.progressbar-text {
+ color:black;
+ white-space:nowrap;
+}
+
/* ----------------------------------------------------------------------
* Grid
* ---------------------------------------------------------------------- */
</button>
<ul class="dropdown-menu">
<li ng-repeat="item in menuItems" ng-class="{divider: item.divider}">
- <a ng-if="!item.divider" href
+ <a ng-if="!item.divider" href ng-disabled="item.disabled"
ng-click="item.handler()">{{item.label}}</a>
</li>
</ul>
<!-- if no menu label is present, present menu-items as a
horizontal row of buttons -->
<div class="btn-group" ng-if="!menuLabel">
- <button class="btn btn-default eg-grid-menu-item"
+ <button ng-if="!item.hidden()"
+ class="btn btn-default eg-grid-menu-item"
+ ng-disabled="item.disabled()"
ng-repeat="item in menuItems"
ng-click="item.handler(item, item.handlerData)">
{{item.label}}
var hold_ids = [];
var holds = [];
+ var clear_mode = false;
+ $scope.gridControls = {};
function fetch_holds(offset, count) {
var ids = hold_ids.slice(offset, offset + count);
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';
- */
+ if (clear_mode)
+ method = 'open-ils.circ.captured_holds.id_list.expired_on_shelf_or_wrong_shelf.retrieve.atomic';
egCore.net.request(
'open-ils.circ', method,
$scope.detail_hold_record_id = hold_data.mvr.doc_id();
}
+ // manage active vs. clearable holds display
+ var clearing = false; // true if actively clearing holds (below)
+ $scope.is_clearing = function() { return clearing };
+ $scope.active_mode = function() {return !clear_mode}
+ $scope.clear_mode = function() {return clear_mode}
+ $scope.show_clearable = function() { clear_mode = true; refresh_page() }
+ $scope.show_active = function() { clear_mode = false; refresh_page() }
+ $scope.disable_clear = function() { return clearing || !clear_mode }
+
+ // action handlers
+ // TODO: These are copied directly from patron/holds.js.
+ // Consider refactoring / consolidating.
+ $scope.cancel_hold = function(items) {
+ var hold_ids = items.filter(function(item) {
+ return !item.hold.cancel_time();
+ }).map(function(item) {return item.hold.id()});
+
+ return egHolds.cancel_holds(hold_ids).then(refresh_page);
+ }
+
+ // jump to circ list for either 1) the targeted copy or
+ // 2) the hold target copy for copy-level holds
+ $scope.show_recent_circs = function(items) {
+ if (items.length && (copy = items[0].copy)) {
+ var url = $location.path(
+ '/cat/item/' + copy.id() + '/circ_list').absUrl();
+ $window.open(url, '_blank').focus();
+ }
+ }
+
+ function generic_update(items, action) {
+ if (!items.length) return $q.when();
+ var hold_ids = items.map(function(item) {return item.hold.id()});
+ return egHolds[action](hold_ids).then(refresh_page);
+ }
+
+ $scope.set_copy_quality = function(items) {
+ generic_update(items, 'set_copy_quality'); }
+ $scope.edit_pickup_lib = function(items) {
+ generic_update(items, 'edit_pickup_lib'); }
+ $scope.edit_notify_prefs = function(items) {
+ generic_update(items, 'edit_notify_prefs'); }
+ $scope.edit_dates = function(items) {
+ generic_update(items, 'edit_dates'); }
+ $scope.suspend = function(items) {
+ generic_update(items, 'suspend_holds'); }
+ $scope.activate = function(items) {
+ generic_update(items, 'activate_holds'); }
+ $scope.set_top_of_queue = function(items) {
+ generic_update(items, 'set_top_of_queue'); }
+ $scope.clear_top_of_queue = function(items) {
+ generic_update(items, 'clear_top_of_queue'); }
+ $scope.transfer_to_marked_title = function(items) {
+ generic_update(items, 'transfer_to_marked_title'); }
+
+ $scope.mark_damaged = function(items) {
+ var copy_ids = items
+ .filter(function(item) { return Boolean(item.copy) })
+ .map(function(item) { return item.copy.id() });
+ if (copy_ids.length)
+ egCirc.mark_damaged(copy_ids).then(refresh_page);
+ }
+
+ $scope.mark_missing = function(items) {
+ var copy_ids = items
+ .filter(function(item) { return Boolean(item.copy) })
+ .map(function(item) { return item.copy.id() });
+ if (copy_ids.length)
+ egCirc.mark_missing(copy_ids).then(refresh_page);
+ }
+
+ $scope.retarget = function(items) {
+ var hold_ids = items.map(function(item) { return item.hold.id() });
+ egHolds.retarget(hold_ids).then(refresh_page);
+ }
+
+ $scope.clear_holds = function() {
+ clearing = true;
+ $scope.clear_progress = { max : 0, value : 0 };
+
+ // we want to see all processed holds, so (effectively) remove
+ // the grid limit.
+ $scope.gridControls.setLimit(1000);
+
+ // initiate clear shelf and grab cache key
+ egCore.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.hold.clear_shelf.process',
+ egCore.auth.token(), $scope.pickup_ou.id()
+
+ // request responses from the clear shelf cache
+ ).then(function(resp) {
+ console.debug('clear holds cache key is ' + resp.cache_key);
+ return egCore.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.hold.clear_shelf.get_cache',
+ egCore.auth.token(), resp.cache_key
+ )
+
+ // with each clear-shelf response, update the progress meter,
+ // hide it when done.
+ }).then(
+ function() {
+ clearing = false;
+ },
+ null,
+ function(resp) {
+ console.debug('clear shelf said: ' + js2JSON(resp));
+ if (!angular.isArray(resp)) resp = [resp];
+ angular.forEach(resp, function(info) {
+ if (info.maximum)
+ $scope.clear_progress.max = info.maximum;
+ if (info.progress)
+ $scope.clear_progress.value = info.progress;
+
+ if (info.action) {
+ var grid_item = holds.filter(function(item) {
+ return item.hold.id() == info.hold_details.id
+ })[0];
+
+ // there will be no grid item if the hold is off-page
+ if (grid_item) {
+ grid_item.post_clear =
+ egCore.strings['CLEAR_SHELF_ACTION_' + info.action];
+ }
+ }
+ });
+ }
+ );
+ }
+
+
refresh_page();
}]);
}
$scope.set_copy_quality = function(items) {
- generic_update(items, 'set_copy_quality');
- }
+ generic_update(items, 'set_copy_quality'); }
$scope.edit_pickup_lib = function(items) {
- generic_update(items, 'edit_pickup_lib');
- }
+ generic_update(items, 'edit_pickup_lib'); }
$scope.edit_notify_prefs = function(items) {
- generic_update(items, 'edit_notify_prefs');
- }
+ generic_update(items, 'edit_notify_prefs'); }
$scope.edit_dates = function(items) {
- generic_update(items, 'edit_dates');
- }
+ generic_update(items, 'edit_dates'); }
$scope.suspend = function(items) {
- generic_update(items, 'suspend_holds');
- }
+ generic_update(items, 'suspend_holds'); }
$scope.activate = function(items) {
- generic_update(items, 'activate_holds');
- }
+ generic_update(items, 'activate_holds'); }
$scope.set_top_of_queue = function(items) {
- generic_update(items, 'set_top_of_queue');
- }
+ generic_update(items, 'set_top_of_queue'); }
$scope.clear_top_of_queue = function(items) {
- generic_update(items, 'clear_top_of_queue');
- }
+ generic_update(items, 'clear_top_of_queue'); }
$scope.transfer_to_marked_title = function(items) {
- generic_update(items, 'transfer_to_marked_title');
- }
+ generic_update(items, 'transfer_to_marked_title'); }
$scope.mark_damaged = function(items) {
var copy_ids = items
grid.collect();
}
+ controls.setLimit = function(limit) {
+ grid.limit = limit;
+ }
+ controls.getLimit = function() {
+ return grid.limit;
+ }
+ controls.setOffset = function(offset) {
+ grid.offset = offset;
+ }
+ controls.getOffset = function() {
+ return grid.offset;
+ }
+
grid.dataProvider.refresh = controls.refresh;
grid.controls = controls;
}
label : '@',
handler : '=', // onclick handler function
divider : '=', // if true, show a divider only
- handlerData : '=' // if set, passed as second argument to handler
+ handlerData : '=', // if set, passed as second argument to handler
+ disabled : '=', // function
+ hidden : '=' // function
},
link : function(scope, element, attrs, egGridCtrl) {
egGridCtrl.addMenuItem({
label : scope.label,
handler : scope.handler,
divider : scope.divider,
+ disabled : scope.disabled,
+ hidden : scope.hidden,
handlerData : scope.handlerData
});
scope.$destroy();