From: Bill Erickson Date: Sun, 6 Apr 2014 19:57:02 +0000 (-0400) Subject: web staff : initial patron search grid integration X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=837ebde22db1bce412d20e48377ba7b82e03cb38;p=working%2FEvergreen.git web staff : initial patron search grid integration Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/templates/staff/circ/patron/index.tt2 b/Open-ILS/src/templates/staff/circ/patron/index.tt2 index 076ba8e607..c5fab21dc2 100644 --- a/Open-ILS/src/templates/staff/circ/patron/index.tt2 +++ b/Open-ILS/src/templates/staff/circ/patron/index.tt2 @@ -6,7 +6,8 @@ %] [% BLOCK APP_JS %] - + + diff --git a/Open-ILS/src/templates/staff/circ/patron/t_search.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_search.tt2 index 601af0db5d..02d3a434f3 100644 --- a/Open-ILS/src/templates/staff/circ/patron/t_search.tt2 +++ b/Open-ILS/src/templates/staff/circ/patron/t_search.tt2 @@ -131,15 +131,13 @@ -
- [% INCLUDE 'staff/circ/patron/t_search_actions.tt2' %] -

+ [% INCLUDE 'staff/circ/patron/t_search_results.tt2' %]
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 index 22003167be..1c5bf86533 100644 --- a/Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 +++ b/Open-ILS/src/templates/staff/circ/patron/t_search_results.tt2 @@ -1,77 +1,28 @@ -[% -# Default / available display columns -# Since there will be demand for configurable columns in this UI, -# experiment with automagic column creation. -# -# We could autogenerate much of this from the IDL. However, since there -# are special cases to handle (e.g. billing vs mailing address) and -# because table autogeneration will likely evolve over time, go ahead -# and list the columns explicitly for now. -# -# the 'name' field doubles as the path to the value and as a unique -# key for the column picker -COLUMNS = [ + + + + + + + + + -{label => l('ID'), name => 'id', display => 1}, -{label => l('Card'), name => 'card.barcode', display => 1}, -{label => l('Last Name'), name => 'family_name', display => 1}, -{label => l('First Name'), name => 'first_given_name', display => 1}, -{label => l('Middle Name'), name => 'second_given_name',display => 1}, -{label => l('DoB'), name => 'dob', display => 1}, -{label => l('Home Library'),name => 'home_ou.shortname',display => 1}, -{label => l('Created On'), name => 'create_date', display => 1}, - -{label => l('Mailing:Street 1'), name => 'mailing_address.street1', display => 1}, -{label => l('Mailing:Street 2'), name => 'mailing_address.street2'}, -{label => l('Mailing:City'), name => 'mailing_address.city'}, -{label => l('Mailing:County'), name => 'mailing_address.county'}, -{label => l('Mailing:State'), name => 'mailing_address.state'}, -{label => l('Mailing:Zip'), name => 'mailing_address.post_code'}, - -{label => l('Billing:Street 1'), name => 'billing_address.street1'}, -{label => l('Billing:Street 2'), name => 'billing_address.street2'}, -{label => l('Billing:City'), name => 'billing_address.city'}, -{label => l('Billing:County'), name => 'billing_address.county'}, -{label => l('Billing:State'), name => 'billing_address.state'}, -{label => l('Billing:Zip'), name => 'billing_address.post_code'} - -] -%] - - -
-
- - - - - - - - - - - - - - - - -
# - {{col.label}} -
{{$index + 1}} - - {{patrons.fieldValue(user, col.name)}} -
+ + + + + + + + + + + + +
diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/app.js b/Open-ILS/web/js/ui/default/staff/circ/patron/app.js index 0d8c02bc53..00ff7e99f7 100644 --- a/Open-ILS/web/js/ui/default/staff/circ/patron/app.js +++ b/Open-ILS/web/js/ui/default/staff/circ/patron/app.js @@ -8,10 +8,11 @@ */ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap', - 'egCoreMod', 'egUiMod', 'egListMod', 'egUserMod']) + 'egCoreMod', 'egUiMod', 'egGridMod', 'egListMod', 'egUserMod']) -.config(function($routeProvider, $locationProvider) { +.config(function($routeProvider, $locationProvider, $compileProvider) { $locationProvider.html5Mode(true); + $compileProvider.aHrefSanitizationWhitelist(/^\s*(blob):/); // grid export // data loaded at startup which only requires an authtoken goes // here. this allows the requests to be run in parallel instead of @@ -230,12 +231,27 @@ function($scope, $q, $filter, egNet, egAuth, egUser, patronSvc, egEnv, e .controller('PatronSearchCtrl', ['$scope','$q','$routeParams','$timeout','$window','$location','egEnv', '$filter','egIDL','egNet','egAuth','egEvent','egList','egUser','patronSvc', + 'egGridFlatDataProvider', function($scope, $q, $routeParams, $timeout, $window, $location, egEnv, - $filter, egIDL, egNet, egAuth, egEvent, egList, egUser, patronSvc) { + $filter, egIDL, egNet, egAuth, egEvent, egList, egUser, patronSvc, + egGridFlatDataProvider) { $scope.initTab('search'); $scope.focusMe = true; $scope.patrons = patronSvc.patrons; + + // our data provider is a modified flat data provider + var provider = egGridFlatDataProvider.instance({}); + provider.get = function(index, count, onitem) { + angular.forEach( + $scope.patrons.items.slice(index, index + count), + function(item) { onitem(item) } + ); + }; + provider.itemFieldValue = function(item, column) { + return provider.nestedItemFieldValue(item, column); + }; + $scope.patronSearchGridProvider = provider; // typeahead doesn't filter correctly with full hash objects, so // trim them down to just name and id. This would allow us to use @@ -357,6 +373,7 @@ function($scope, $q, $routeParams, $timeout, $window, $location, egEnv, ).then(null, null, function(user) { patronSvc.localFlesh(user); $scope.patrons.items[$scope.patrons.items.length] = user; + $scope.patronSearchGridProvider.increment(); }); }; diff --git a/Open-ILS/web/js/ui/default/staff/services/grid.js b/Open-ILS/web/js/ui/default/staff/services/grid.js index c9ef91b7e2..a76d6f67fd 100644 --- a/Open-ILS/web/js/ui/default/staff/services/grid.js +++ b/Open-ILS/web/js/ui/default/staff/services/grid.js @@ -29,8 +29,8 @@ angular.module('egGridMod', // egList containting our tabular data is provided for us // and managed externally. - dataProvider : '=', - + itemsProvider : '=', + // if true, hide the sortPriority options in the // grid configuration UI. This is primarily used by // UIs where the data is ephemeral and can only be @@ -52,12 +52,10 @@ angular.module('egGridMod', }, controller : [ - '$scope','egIDL','egAuth','egNet', - 'egGridFlatDataProvider','egGridColumnsProvider', - '$filter','$window', - function($scope, egIDL, egAuth, egNet, - egGridFlatDataProvider, egGridColumnsProvider, - $filter, $window) { + '$scope','egIDL','egAuth','egNet', 'egGridFlatDataProvider', + 'egGridColumnsProvider', '$filter','$window', + function($scope, egIDL, egAuth, egNet, egGridFlatDataProvider, + egGridColumnsProvider, $filter, $window) { var grid = this; @@ -67,7 +65,7 @@ angular.module('egGridMod', grid.items = []; grid.selected = {}; // idField-based grid.totalCount = -1; - grid.dataProvider = $scope.dataProvider; + grid.dataProvider = $scope.itemsProvider; grid.idlClass = $scope.idlClass; grid.mainLabel = $scope.mainLabel; grid.indexField = $scope.idField; @@ -88,7 +86,17 @@ angular.module('egGridMod', grid.columnsProvider.compileAutoColumns(); } - if (!grid.dataProvider) { + if (grid.dataProvider) { + + // refresh the grid contents each time the data + // provider's revision changes + $scope.$watch( + function() { return grid.dataProvider.revision() }, + function() { grid.collect() } + ); + + } else { + grid.selfManagedData = true; grid.dataProvider = egGridFlatDataProvider.instance({ idlClass : grid.idlClass, @@ -157,9 +165,9 @@ angular.module('egGridMod', grid.indexValue = function(item) { if (angular.isObject(item)) { if (item !== null) { - if (grid.indexFieldAsFunction) + if (angular.isFunction(item[grid.indexField])) return item[grid.indexField](); - return item[grid.indexField]; + return item[grid.indexField]; // flat data } } // passed a non-object; assume it's an index @@ -446,11 +454,12 @@ angular.module('egGridMod', name : '@', // required; unique name path : '@', // optional; flesh path label : '@', // optional; display label - flex : '@', // optoinal; default flex width + flex : '@', // optional; default flex width + display : '=' // optional; hide column by default }, template : '
', // NOOP template link : function(scope, element, attrs, egGridCtrl) { - egGridCtrl.addColumn(scope); + egGridCtrl.columnsProvider.add(scope); } }; }) @@ -595,8 +604,8 @@ angular.module('egGridMod', // Factory service for egGridDataManager instances, which are // responsible for collecting flattened grid data. .factory('egGridFlatDataProvider', - ['egNet','egAuth', - function(egNet, egAuth) { + ['$filter','egNet','egAuth','egIDL', + function($filter , egNet , egAuth , egIDL) { function FlatDataProvider(args) { var gridData = this; @@ -605,6 +614,15 @@ angular.module('egGridMod', gridData.query = args.query; gridData.columnsProvider = args.columnsProvider; gridData.sort = []; + gridData._revision = 0; + + gridData.revision = function() { + return gridData._revision; + } + + gridData.increment = function() { + gridData._revision++; + } gridData.get = function(index, count, onresponse) { @@ -631,9 +649,49 @@ angular.module('egGridMod', } gridData.itemFieldValue = function(item, column) { - // all of our data is flattened + // all of our data is flat return item[column.name]; } + + // utility function which may be useful for other grid data + // providers. + // given an object and a dot-separated path to a field, + // extract the value of the field. The path can refer + // to function names or object attributes. If the final + // value is an IDL field, run the value through its + // corresponding output filter. + gridData.nestedItemFieldValue = function(obj, column) { + if (obj === null || obj === undefined || obj === '') return ''; + if (!column.path) return obj; + + var idlField, cls, clsobj; + var parts = column.path.split('.'); + + angular.forEach(parts, function(step, idx) { + // object is not fleshed to the expected extent + if (!obj || typeof obj != 'object') { + obj = ''; + return; + } + + cls = obj.classname; + if (cls && (clsobj = egIDL.classes[cls])) { + idlField = clsobj.fields.filter( + function(f) { return f.name == step })[0]; + obj = obj[step](); + } else { + if (angular.isFunction(obj[step])) { + obj = obj[step](); + } else { + obj = obj[step]; + } + } + }); + + if (obj === null || obj === undefined || obj === '') return ''; + if (!idlField) return obj; + return $filter('egGridValueFilter')(obj, column); + } } return {