From 77d1154f956cc3f4548da25b1206396d7c8570a8 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 6 Nov 2013 09:44:30 -0500 Subject: [PATCH] ff ui / column sorting and more Signed-off-by: Bill Erickson --- .../templates/staff/fulfillment/t_item_table.tt2 | 8 +- .../web/js/ui/default/staff/fulfillment/app.js | 543 ++++++++++----------- 2 files changed, 275 insertions(+), 276 deletions(-) diff --git a/Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 b/Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 index eb7ff3a143..481c5d1d69 100644 --- a/Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 +++ b/Open-ILS/src/templates/staff/fulfillment/t_item_table.tt2 @@ -36,9 +36,13 @@ COLUMNS = [ - + [% FOREACH col IN COLUMNS %] - + [% END %] diff --git a/Open-ILS/web/js/ui/default/staff/fulfillment/app.js b/Open-ILS/web/js/ui/default/staff/fulfillment/app.js index c475d0aaa2..6c9e00bd4a 100644 --- a/Open-ILS/web/js/ui/default/staff/fulfillment/app.js +++ b/Open-ILS/web/js/ui/default/staff/fulfillment/app.js @@ -1,21 +1,14 @@ /** - * TODO: - * Instead of making pcrud calls, followed by per-item server calls, - * consider a server-side API for all of this stuff for speed, etc. - * OR consider using all flattener-based calls. - * - * Consolidate the various item structures into one common, - * authoritative structure for display and print templates. + * FulfILLment application. + * Includes pending items, inbound/outbound transits, on shelf, + * circulating, item status, and bib record file upload. */ - angular.module('ffMain', ['ngRoute', 'egCoreMod', 'egUiMod', 'egUserMod', 'egListMod']) .config(function($routeProvider, $locationProvider) { - - // The route-specified controller will not get instantiated - // until the promise returned by this function is resolved + $locationProvider.html5Mode(true); var resolver = {delay : function(egStartup) {return egStartup.go()}}; // record management UI @@ -25,18 +18,21 @@ angular.module('ffMain', resolve : resolver }); + // item status by barcode $routeProvider.when('/fulfillment/status/:barcode', { templateUrl: './fulfillment/t_ill', controller: 'ILLCtrl', resolve : resolver }); + // item status, barcode pending $routeProvider.when('/fulfillment/status', { templateUrl: './fulfillment/t_ill', controller: 'ILLCtrl', resolve : resolver }); + // transaction-focused tabs $routeProvider.when('/fulfillment/:orientation/:tab', { templateUrl: './fulfillment/t_ill', controller: 'ILLCtrl', @@ -47,8 +43,6 @@ angular.module('ffMain', $routeProvider.otherwise({ redirectTo : '/fulfillment/borrower/pending' }); - - $locationProvider.html5Mode(true); }) /** @@ -143,82 +137,92 @@ function ($scope, $q, $compile, $timeout, $rootScope, $location, // so our child controllers can access our route info $scope.illRouteParams = $routeParams; - $scope.itemList = egList.create({limit : 10}); // limit TBD + $scope.itemList = egList.create({limit : 10}); // UI? $scope.columns = []; $scope.addColumn = function(col) { $scope.columns.push(col); } + // sort by column name. if already sorting on the selected column, + // sort 'desc' instead. + $scope.sort = function(colname) { + if (typeof $scope.itemList.sort == 'string' && + $scope.itemList.sort == colname) { + $scope.itemList.sort = {}; + $scope.itemList.sort[colname] = 'desc'; + } else { + $scope.itemList.sort = colname; + } + $scope.collect(); + } + // map of flattener fields to retrieve for each query type + // TODO: there's some duplication here, since the fields + // are also defined in the template. $scope.flatFields = { ahr : { - id : {path : 'id'}, - hold_id : {path : 'id'}, - request_time : {path : 'request_time'}, - expire_time : {path : 'expire_time'}, - patron_id : {path : 'usr.id'}, - patron_barcode : {path : 'usr.card.barcode'}, - patron_given_name : {path : 'usr.first_given_name'}, - patron_family_name : {path : 'usr.family_name'}, - hold_request_lib : {path : 'request_lib.shortname'}, // TODO: causes query problem, wha? - hold_pickup_lib : {path : 'pickup_lib.shortname'}, - title : {path : 'bib_rec.bib_record.simple_record.title'}, - author : {path : 'bib_rec.bib_record.simple_record.author'}, - copy_id : {path : 'current_copy.id'}, - copy_status : {path : 'current_copy.status.name'}, - copy_barcode : {path : 'current_copy.barcode'}, - copy_circ_lib : {path : 'current_copy.circ_lib.shortname'}, - call_number : {path : 'current_copy.call_number.label'} + id : 'id', + hold_id : 'id', + request_time : 'request_time', + expire_time : 'expire_time', + patron_id : 'usr.id', + patron_barcode : 'usr.card.barcode', + patron_given_name : 'usr.first_given_name', + patron_family_name : 'usr.family_name', + hold_request_lib : 'request_lib.shortname', + hold_pickup_lib : 'pickup_lib.shortname', + title : 'bib_rec.bib_record.simple_record.title', + author : 'bib_rec.bib_record.simple_record.author', + copy_id : 'current_copy.id', + copy_status : 'current_copy.status.name', + copy_barcode : 'current_copy.barcode', + copy_circ_lib : 'current_copy.circ_lib.shortname', + call_number : 'current_copy.call_number.label' }, circ : { - id : {path : 'id'}, - circ_id : {path : 'id'}, - patron_id : {path : 'usr.id'}, - patron_barcode : {path : 'usr.card.barcode'}, - patron_given_name : {path : 'usr.first_given_name'}, - patron_family_name : {path : 'usr.family_name'}, - title : {path : 'target_copy.call_number.record.simple_record.title'}, - author : {path : 'target_copy.call_number.record.simple_record.author'}, - copy_id : {path : 'target_copy.id'}, - copy_status : {path : 'target_copy.status.name'}, - copy_barcode : {path : 'target_copy.barcode'}, - copy_circ_lib : {path : 'target_copy.circ_lib.shortname'}, - call_number : {path : 'target_copy.call_number.label'}, - circ_circ_lib : {path : 'circ_lib.shortname'}, - due_date : {path : 'due_date'}, - xact_start : {path : 'xact_start'}, - checkin_time : {path : 'checkin_time'} + id : 'id', + circ_id : 'id', + patron_id : 'usr.id', + patron_barcode : 'usr.card.barcode', + patron_given_name : 'usr.first_given_name', + patron_family_name : 'usr.family_name', + title : 'target_copy.call_number.record.simple_record.title', + author : 'target_copy.call_number.record.simple_record.author', + copy_id : 'target_copy.id', + copy_status : 'target_copy.status.name', + copy_barcode : 'target_copy.barcode', + copy_circ_lib : 'target_copy.circ_lib.shortname', + call_number : 'target_copy.call_number.label', + circ_circ_lib : 'circ_lib.shortname', + due_date : 'due_date', + xact_start : 'xact_start', + checkin_time : 'checkin_time' }, atc : { - id : {path : 'id'}, - transit_id : {path : 'id'}, - hold_id : {path : 'hold_transit_copy.hold.id'}, - hold_request_lib : {path : 'hold_transit_copy.hold.request_lib.shortname'}, - hold_pickup_lib : {path : 'hold_transit_copy.hold.pickup_lib.shortname'}, - patron_id : {path : 'hold_transit_copy.hold.usr.id'}, - patron_barcode : {path : 'hold_transit_copy.hold.usr.card.barcode'}, - patron_given_name : {path : 'hold_transit_copy.hold.usr.first_given_name'}, - patron_family_name : {path : 'hold_transit_copy.hold.usr.family_name'}, - title : {path : 'target_copy.call_number.record.simple_record.title'}, - author : {path : 'target_copy.call_number.record.simple_record.author'}, - copy_status : {path : 'target_copy.status.name'}, - copy_id : {path : 'target_copy.id'}, - copy_barcode : {path : 'target_copy.barcode'}, - copy_circ_lib : {path : 'target_copy.circ_lib.shortname'}, - call_number : {path : 'target_copy.call_number.label'}, - transit_time : {path : 'source_send_time'}, - transit_source : {path : 'source.shortname'}, - transit_dest : {path : 'dest.shortname'} + id : 'id', + transit_id : 'id', + hold_id : 'hold_transit_copy.hold.id', + hold_request_lib : 'hold_transit_copy.hold.request_lib.shortname', + hold_pickup_lib : 'hold_transit_copy.hold.pickup_lib.shortname', + patron_id : 'hold_transit_copy.hold.usr.id', + patron_barcode : 'hold_transit_copy.hold.usr.card.barcode', + patron_given_name : 'hold_transit_copy.hold.usr.first_given_name', + patron_family_name : 'hold_transit_copy.hold.usr.family_name', + title : 'target_copy.call_number.record.simple_record.title', + author : 'target_copy.call_number.record.simple_record.author', + copy_status : 'target_copy.status.name', + copy_id : 'target_copy.id', + copy_barcode : 'target_copy.barcode', + copy_circ_lib : 'target_copy.circ_lib.shortname', + call_number : 'target_copy.call_number.label', + transit_time : 'source_send_time', + transit_source : 'source.shortname', + transit_dest : 'dest.shortname' } }; - // set display=true for each flattener field - angular.forEach($scope.flatFields, function(val) { - angular.forEach(val, function(val2) { - val2.display = true; - }); - }); - + // apply the route-specific data loading class/query + // query == flattener query $scope.setCollector = function(class_, query) { $scope.collector = { query : query, @@ -226,13 +230,31 @@ function ($scope, $q, $compile, $timeout, $rootScope, $location, } } + // called on each item as it's received $scope.setMunger = function(func) { $scope.munger = func; } + // table paging... + $scope.firstPage = function() { + $scope.itemList.offset = 0; + $scope.collect(); + }; + + $scope.nextPage = function() { + $scope.itemList.incrementPage(); + $scope.collect(); + }; + + $scope.prevPage = function() { + $scope.itemList.decrementPage(); + $scope.collect(); + }; + + // fire the flattened search query to collect items $scope.collect = function() { $scope.lookupComplete = false; - $scope.itemList.items = []; + $scope.itemList.resetPageData(); egNet.request( 'open-ils.fielder', 'open-ils.fielder.flattened_search', @@ -261,198 +283,183 @@ function ($scope, $q, $compile, $timeout, $rootScope, $location, } - /* Actions - * Performed on flattened items (see above) - */ - $scope.actions = { + // Actions + // Performed on flattened items + $scope.actions = {}; - checkin : function(item) { - $scope.action_pending = true; - var deferred = $q.defer(); - egNet.request( - 'open-ils.circ', - 'open-ils.circ.checkin.override', - egAuth.token(), { - circ_lib : orgSelector.current().id(), - copy_id: item.copy_id, - ff_action: item.next_action - } - ).then(function(response) { - $scope.action_pending = false; - // do some basic sanity checking before passing - // the response to the caller. - if (response) { - if (angular.isArray(response)) - response = response[0]; - // TODO: check for failure events - deferred.resolve(response); - } else { - // warn that checkin failed - deferred.reject(); - } - }); - return deferred.promise; - }, + $scope.actions.checkin = function(item) { + $scope.action_pending = true; + var deferred = $q.defer(); + egNet.request('open-ils.circ', + 'open-ils.circ.checkin.override', + egAuth.token(), { + circ_lib : orgSelector.current().id(), + copy_id: item.copy_id, ff_action: item.next_action + } + ).then(function(response) { + $scope.action_pending = false; + // do some basic sanity checking before passing + // the response to the caller. + if (response) { + if (angular.isArray(response)) + response = response[0]; + // TODO: check for failure events + deferred.resolve(response); + } else { + // warn that checkin failed + deferred.reject(); + } + }); + return deferred.promise; + } - checkout : function(item) { - $scope.action_pending = true; - var deferred = $q.defer(); - egNet.request( - 'open-ils.circ', - 'open-ils.circ.checkout.full.override', - egAuth.token(), { - circ_lib : orgSelector.current().id(), - patron_id : item.user_id, - copy_id: item.copy_id, - ff_action: item.next_action - } - ).then(function(response) { - $scope.action_pending = false; - // do some basic sanity checking before passing - // the response to the caller. - if (response) { - if (angular.isArray(response)) - response = response[0]; - // TODO: check for failure events - deferred.resolve(response); - } else { - // warn that checkin failed - deferred.reject(); - } - }); - return deferred.promise; - }, + $scope.actions.checkout = function(item) { + $scope.action_pending = true; + var deferred = $q.defer(); + egNet.request('open-ils.circ', + 'open-ils.circ.checkout.full.override', + egAuth.token(), { + circ_lib : orgSelector.current().id(), + patron_id : item.user_id, + copy_id: item.copy_id, + ff_action: item.next_action + } + ).then(function(response) { + $scope.action_pending = false; + // do some basic sanity checking before passing + // the response to the caller. + if (response) { + if (angular.isArray(response)) + response = response[0]; + // TODO: check for failure events + deferred.resolve(response); + } else { + // warn that checkin failed + deferred.reject(); + } + }); + return deferred.promise; + } - cancel : function(item) { - var deferred = $q.defer(); - $scope.action_pending = true; - egNet.request( - 'open-ils.circ', 'open-ils.circ.hold.cancel', - egAuth.token(), item.hold_id - ).then(function() { - $scope.action_pending = false; - deferred.resolve(); - }); - return deferred.promise; - }, + $scope.actions.cancel = function(item) { + var deferred = $q.defer(); + $scope.action_pending = true; + egNet.request('open-ils.circ', + 'open-ils.circ.hold.cancel', + egAuth.token(), item.hold_id + ).then(function() { + $scope.action_pending = false; + deferred.resolve(); + }); + return deferred.promise; + } - retarget : function(item) { - var deferred = $q.defer(); - $scope.action_pending = true; - egNet.request( - 'open-ils.circ', 'open-ils.circ.hold.reset', - egAuth.token(), item.hold_id - ).then(function() { - $scope.action_pending = false; - deferred.resolve(); - }); - return deferred.promise; - }, + $scope.actions.retarget = function(item) { + var deferred = $q.defer(); + $scope.action_pending = true; + egNet.request('open-ils.circ', + 'open-ils.circ.hold.reset', + egAuth.token(), item.hold_id + ).then(function() { + $scope.action_pending = false; + deferred.resolve(); + }); + return deferred.promise; + } - abort_transit : function(item) { - var deferred = $q.defer(); - $scope.action_pending = true; - egNet.request( - 'open-ils.circ', 'open-ils.circ.transit.abort', - egAuth.token(), {transitid : item.transit_id} - ).then(function() { - $scope.action_pending = false; - deferred.resolve(); - }); - return deferred.promise; - }, + $scope.actions.abort_transit = function(item) { + var deferred = $q.defer(); + $scope.action_pending = true; + egNet.request('open-ils.circ', + 'open-ils.circ.transit.abort', + egAuth.token(), {transitid : item.transit_id} + ).then(function() { + $scope.action_pending = false; + deferred.resolve(); + }); + return deferred.promise; + } - mark_lost : function(item) { - var deferred = $q.defer(); - $scope.action_pending = true; - egNet.request( - 'open-ils.circ', 'open-ils.circ.circulation.set_lost', - egAuth.token(), {barcode : item.item_barcode} - ).then(function(resp) { - $scope.action_pending = false; - if (resp == 1) { - deferred.resolve(); - } else { - console.error('mark lost failed: ' + js2JSON(resp)); - deferred.reject(); - } - }); - return deferred.promise; - }, + $scope.actions.mark_lost = function(item) { + var deferred = $q.defer(); + $scope.action_pending = true; + egNet.request('open-ils.circ', + 'open-ils.circ.circulation.set_lost', + egAuth.token(), {barcode : item.item_barcode} + ).then(function(resp) { + $scope.action_pending = false; + if (resp == 1) { + deferred.resolve(); + } else { + console.error('mark lost failed: ' + js2JSON(resp)); + deferred.reject(); + } + }); + return deferred.promise; + } - print : function(item) { - var deferred = $q.defer(); - $scope.action_pending = true; - var focus = item.hold ? 'hold' : - (item.circ ? 'circ' : - (item.transit ? 'transit' : 'copy')); + $scope.actions.print = function(item) { + var deferred = $q.defer(); + $scope.action_pending = true; + var focus = item.hold ? 'hold' : + (item.circ ? 'circ' : (item.transit ? 'transit' : 'copy')); - // TODO: line up print template variables with - // local data structures - item.barcode = item.copy_barcode || item.item_barcode; - item.status = item.status_str || item.copy_status; - item.item_circ_lib = item.copy_circ_lib || item.circ_lib; + // TODO: line up print template variables with + // local data structures + item.barcode = item.copy_barcode || item.item_barcode; + item.status = item.status_str || item.copy_status; + item.item_circ_lib = item.copy_circ_lib || item.circ_lib; - egNet.request( - 'open-ils.actor', - 'open-ils.actor.web_action_print_template.fetch', - orgSelector.current().id(), focus - ).then(function(template) { - - if (!template || !(template = template.template())) { // assign - console.warn('unable to find template for ' + - item.copy_barcode + ' : ' + focus); - return; - } + egNet.request( + 'open-ils.actor', + 'open-ils.actor.web_action_print_template.fetch', + orgSelector.current().id(), focus + ).then(function(template) { + + if (!template || !(template = template.template())) { // assign + console.warn('unable to find template for ' + + item.copy_barcode + ' : ' + focus); + return; + } - // NOTE: templates stored for now as dojo-style - // template. mangle to angular templates manually. - template = template.replace(/\${([^}]+)}/g, '{{$1}}'); + // TODO: templates stored for now as dojo-style + // template. mangle to angular templates manually. + template = template.replace(/\${([^}]+)}/g, '{{$1}}'); - // compile the template w/ a temporary print scope - var printScope = $rootScope.$new(); - angular.forEach(item, function(val, key) { - printScope[key] = val; - }); - var element = angular.element(template); - $compile(element)(printScope); - - // append the compiled element to the new window and print - var w = window.open(); - $(w.document.body).append(element); - w.document.close(); - - // $timeout needed in some environments (Mac confirmed) - // to allow the new window to fully digest before printing. - $timeout( - function() { - w.print(); - w.close(); - $scope.action_pending = false; - deferred.resolve(); - } - ); + // compile the template w/ a temporary print scope + var printScope = $rootScope.$new(); + angular.forEach(item, function(val, key) { + printScope[key] = val; }); - return deferred.promise; - } - }; + var element = angular.element(template); + $compile(element)(printScope); + + // append the compiled element to the new window and print + var w = window.open(); + $(w.document.body).append(element); + w.document.close(); + + // $timeout needed in some environments (Mac confirmed) + // to allow the new window to fully $digest() before printing. + $timeout( + function() { + w.print(); + w.close(); + $scope.action_pending = false; + deferred.resolve(); + } + ); + }); + return deferred.promise; + } // default batch action handlers. - // when a batch is done, reload the route, unless printing. - - function performAction(action, item) { + function performOneAction(action, item) { console.debug(item.index + ' => ' + action); - $scope.actions[action](item).then( - // when all items are done processing, reload the route - function(resp) { - console.debug(item.index + ' => ' + action + ' : done'); - if (--total == 0 && action != 'print') - $route.reload() - }, - function(resp) { - console.error("error in " + action + ": " + resp); - } + return $scope.actions[action](item).then( + function(resp) {console.debug(item.index + ' => ' + action + ' : done')}, + function(resp) {console.error("error in " + action + " : " + resp)} ); } @@ -460,33 +467,21 @@ function ($scope, $q, $compile, $timeout, $rootScope, $location, Object.keys($scope.actions), function(action) { $scope[action] = function() { - var total = Object.keys($scope.itemList.selected).length; - angular.forEach($scope.itemList.selected, - function(val, idx) { - var item = $scope.itemList.items.filter( - function(i) {return i.index == idx})[0]; - performAction(action, item) + var promises = []; + angular.forEach( + $scope.itemList.selectedItems(), + function(item) { + promises.push(performOneAction(action, item)) } ); - }; + // when a batch has successfully completed, + // reload the route, unless printing. + $q.all(promises).then(function() { + if (action != 'print') $route.reload(); + }); + } } ); - - $scope.firstPage = function() { - $scope.itemList.offset = 0; - $scope.collect(); - }; - - $scope.nextPage = function() { - $scope.itemList.incrementPage(); - $scope.collect(); - }; - - $scope.prevPage = function() { - $scope.itemList.decrementPage(); - $scope.collect(); - }; - }]) .controller('PendingRequestsCtrl', -- 2.11.0
[% col.label %] + [% IF col.name == 'index'; col.label; ELSE %] + [% col.label %] + [% END %] +