From ddb80fa61551c4e9af055f7b1a58d2b27835b361 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Tue, 27 May 2014 11:02:54 -0400 Subject: [PATCH] grid field wildcards; billing cont. Signed-off-by: Bill Erickson --- .../templates/staff/circ/patron/t_bills_list.tt2 | 5 + Open-ILS/src/templates/staff/css/style.css.tt2 | 13 +- Open-ILS/src/templates/staff/share/t_autogrid.tt2 | 106 ++++++------ .../web/js/ui/default/staff/circ/patron/app.js | 2 +- .../web/js/ui/default/staff/circ/patron/bills.js | 2 + Open-ILS/web/js/ui/default/staff/services/grid.js | 189 +++++++++++++++++---- 6 files changed, 236 insertions(+), 81 deletions(-) diff --git a/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 index 89d6252646..eb66017713 100644 --- a/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 +++ b/Open-ILS/src/templates/staff/circ/patron/t_bills_list.tt2 @@ -74,6 +74,11 @@ + + + + + diff --git a/Open-ILS/src/templates/staff/css/style.css.tt2 b/Open-ILS/src/templates/staff/css/style.css.tt2 index e848693935..8b51943b93 100644 --- a/Open-ILS/src/templates/staff/css/style.css.tt2 +++ b/Open-ILS/src/templates/staff/css/style.css.tt2 @@ -25,9 +25,13 @@ /* status bar along the bottom of the page ------------------------ */ /* decrease padding to decrease overall height */ +/** TODO:move status bar items into navbar config entry (top-right) + * to avoid body padding weirdness. Or if we want a permenently + * visible status bar, maybe put it just below the navbar.. */ + /* bottom padding ensures no body content is hidden behind the status * bar. When content reaches the status bar a scroll bar appears */ -body { padding-bottom: 26px; } +/*body { padding-bottom: 26px; }*/ #status-bar { min-height:1.8em !important; @@ -324,6 +328,13 @@ table.list tr.selected td { height: 600px; } */ +.eg-grid-column-picker { + height: auto; + max-height: 400px; + overflow: auto; + box-shadow: none; +} + /* ---------------------------------------------------------------------- * /Grid diff --git a/Open-ILS/src/templates/staff/share/t_autogrid.tt2 b/Open-ILS/src/templates/staff/share/t_autogrid.tt2 index 018d6dec99..883d3dd336 100644 --- a/Open-ILS/src/templates/staff/share/t_autogrid.tt2 +++ b/Open-ILS/src/templates/staff/share/t_autogrid.tt2 @@ -8,7 +8,8 @@
{{mainLabel}}
-
+
@@ -62,19 +63,20 @@ -
+
-
+
- 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 0020d9f4b3..861cf9a448 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 @@ -280,7 +280,7 @@ function($q , $timeout , $location , egCore, egUser) { service.fetchUserStats = function() { return egCore.net.request( 'open-ils.actor', - 'open-ils.actor.user.opac.vital_stats', + 'open-ils.actor.user.opac.vital_stats.authoritative', egCore.auth.token(), service.current.id() ).then( function(stats) { diff --git a/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js b/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js index 8aefec43a6..0e6b2fbc61 100644 --- a/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js +++ b/Open-ILS/web/js/ui/default/staff/circ/patron/bills.js @@ -238,6 +238,7 @@ function($scope, $q , $routeParams, $locale , egCore , egGridDataProvider , bi billSvc.applyPayment( $scope.payment_type, generatePayments(), note) .then(function() { + patronSvc.fetchUserStats(); billSvc.fetchSummary().then(function(s) {$scope.summary = s}); $scope.payment_amount = 0; $scope.gridRevision++; // tell the grid to refresh itself @@ -271,6 +272,7 @@ function($scope, $q , $routeParams, $locale , egCore , egGridDataProvider , bi // send the billing to the server using the arguments // provided in the billing dialog, then refresh billSvc.billPatron(args).then(function() { + patronSvc.fetchUserStats(); $scope.payment_amount = 0; $scope.gridRevision++; }); 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 7a9f147f60..2c816fb2f0 100644 --- a/Open-ILS/web/js/ui/default/staff/services/grid.js +++ b/Open-ILS/web/js/ui/default/staff/services/grid.js @@ -92,9 +92,9 @@ angular.module('egGridMod', }, controller : [ - '$scope','$q','egCore','egGridFlatDataProvider', + '$scope','$q','egCore','egGridFlatDataProvider','$location', 'egGridColumnsProvider','$filter','$window','$sce', - function($scope, $q , egCore, egGridFlatDataProvider, + function($scope, $q , egCore, egGridFlatDataProvider , $location, egGridColumnsProvider , $filter , $window , $sce) { var grid = this; @@ -117,10 +117,11 @@ angular.module('egGridMod', grid.addMenuItem = function(item) { $scope.menuItems.push(item); var handler = item.handler; - if (handler) { - item.handler = function() { - handler(item, item.handlerData, - grid.getSelectedItems()); + item.handler = function() { + $scope.gridMenuIsOpen = false; // close menu + if (handler) { + handler(item, + item.handlerData, grid.getSelectedItems()); } } } @@ -156,10 +157,21 @@ angular.module('egGridMod', $scope.showAllColumns = function() { grid.columnsProvider.showAllColumns(); } + $scope.hideAllColumns = function() { grid.columnsProvider.hideAllColumns(); } + $scope.toggleColumnVisibility = function(col) { + $scope.gridColumnPickerIsOpen = false; + col.visible = !col.visible; + + // egGridFlatDataProvider only retrieves data to be + // displayed. When column visibility changes, it's + // necessary to fetch the newly visible column data. + if (grid.selfManagedData) grid.collect(); + } + if (!grid.indexField && grid.idlClass) grid.indexField = egCore.idl.classes[grid.idlClass].pkey; @@ -248,9 +260,23 @@ angular.module('egGridMod', }); } + // remove the stored column configuration preferenc, then recover + // the column visibility information from the initial page load. + $scope.resetColumns = function() { + $scope.gridColumnPickerIsOpen = false; + egCore.hatch.removeItem('eg.grid.' + grid.persistKey) + .then(function() { + grid.columnsProvider.reset(); + if (grid.selfManagedData) grid.collect(); + }); + } + + // save the columns configuration (position, sort, width) to // eg.grid. $scope.saveConfig = function() { + $scope.gridColumnPickerIsOpen = false; + if (!grid.persistKey) { console.warn( "Cannot save settings without a grid persist-key"); @@ -404,6 +430,7 @@ angular.module('egGridMod', // fires the action handler function $scope.actionLauncher = function(action) { + $scope.gridActionsIsOpen = false; action.handler(grid.getSelectedItems()); } @@ -619,6 +646,8 @@ angular.module('egGridMod', } else { $scope.showGridConf = true; } + + $scope.gridColumnPickerIsOpen = false; } // called when a dragged column is dropped onto itself @@ -664,6 +693,7 @@ angular.module('egGridMod', // sets the download file name and inserts the current CSV // into a Blob URL for browser download. $scope.generateCSVExportURL = function() { + $scope.gridColumnPickerIsOpen = false; // let the file name describe the grid $scope.csvExportFileName = @@ -678,6 +708,7 @@ angular.module('egGridMod', } $scope.printCSV = function() { + $scope.gridColumnPickerIsOpen = false; egCore.hatch.print('text/plain', grid.generateCSV()) .then(function() { console.debug('print complete') }); } @@ -738,6 +769,12 @@ angular.module('egGridMod', // asks the dataProvider for a page of data grid.collect = function() { if (grid.collecting) return; // avoid parallel collects() + + // ensure all of our dropdowns are closed + $scope.gridColumnPickerIsOpen = false; + $scope.gridRowCountIsOpen = false; + $scope.gridPageSelectIsOpen = false; + $scope.items = []; $scope.selected = {}; grid.collecting = true; @@ -800,7 +837,6 @@ angular.module('egGridMod', if (tmpl && !tmpl.match(/^\s*$/)) scope.template = tmpl - 'datatype', // for non-IDL fields, can be used to apply filters egGridCtrl.columnsProvider.add(scope); scope.$destroy(); } @@ -835,11 +871,27 @@ angular.module('egGridMod', function ColumnsProvider(args) { var cols = this; cols.columns = []; + cols.stockVisible = []; cols.idlClass = args.idlClass; cols.defaultToHidden = args.defaultToHidden; cols.defaultToNoSort = args.defaultToNoSort; cols.defaultToNoMultiSort = args.defaultToNoMultiSort; + // resets column width, visibility, and sort behavior + // Visibility resets to the visibility settings defined in the + // template (i.e. the original egGridField values). + cols.reset = function() { + angular.forEach(cols.columns, function(col) { + col.flex = 2; + col.sort = 0; + if (cols.stockVisible.indexOf(col.name) > -1) { + col.visible = true; + } else { + col.visible = false; + } + }); + } + // returns true if any columns are sortable cols.hasSortableColumn = function() { return cols.columns.filter( @@ -850,15 +902,19 @@ angular.module('egGridMod', } cols.showAllColumns = function() { + $scope.gridColumnPickerIsOpen = false; angular.forEach(cols.columns, function(column) { column.visible = true; }); + if (grid.selfManagedData) grid.collect(); } cols.hideAllColumns = function() { + $scope.gridColumnPickerIsOpen = false; angular.forEach(cols.columns, function(col) { delete col.visible; }); + // note: no need to fetch new data if no columns are visible } cols.indexOf = function(name) { @@ -900,12 +956,62 @@ angular.module('egGridMod', ); } - // Add a column to the columns collection. - // Columns may come from a slim eg-columns-field or - // directly from the IDL. - cols.add = function(colSpec, fromIDL) { + // if a column definition has a path with a wildcard, create + // columns for all non-virtual fields at the specified + // position in the path. + cols.expandPath = function(colSpec) { + + var dotpath = colSpec.path.replace(/\.\*$/,''); + var class_obj = egCore.idl.classes[cols.idlClass]; + var path_parts = dotpath.split(/\./); + + // find the IDL class definition for the last element in the + // path before the .* + var class_obj; + for (var path_idx in path_parts) { + var part = path_parts[path_idx]; + var idl_field = class_obj.field_map[part]; - var column = { + // unless we're at the end of the list, this field should + // link to another class. + if (idl_field && idl_field['class'] && ( + idl_field.datatype == 'link' || + idl_field.datatype == 'org_unit')) { + class_obj = egCore.idl.classes[idl_field['class']]; + } else { + if (path_idx < (path_parts.length - 1)) { + // we ran out of classes to hop through before + // we ran out of path components + console.error("egGrid: invalid IDL path: " + path); + } + } + } + + if (class_obj) { + angular.forEach(class_obj.fields, function(field) { + + // Only show wildcard fields where we have data to show + // Virtual and un-fleshed links will not have any data. + if (field.virtual || ( + field.datatype == 'link' || field.datatype == 'org_unit')) + return; + + var col = cols.cloneFromScope(colSpec); + col.path = dotpath + '.' + field.name; + cols.add(col, null, true); + }); + + } else { + console.error( + "egGrid: wildcard path does not resolve to an object: " + + dotpath); + } + } + + // angular.clone(scopeObject) is not permittable. Manually copy + // the fields over that we need (so the scope object can go away). + cols.cloneFromScope = function(colSpec) { + return { name : colSpec.name, label : colSpec.label, path : colSpec.path, @@ -922,6 +1028,27 @@ angular.module('egGridMod', multisortable : colSpec.multisortable, nonmultisortable : colSpec.nonmultisortable }; + } + + + // Add a column to the columns collection. + // Columns may come from a slim eg-columns-field or + // directly from the IDL. + cols.add = function(colSpec, fromIDL, fromExpand) { + + // First added column with the specified path takes precedence. + // This allows for specific definitions followed by wildcard + // definitions. If a match is found, back out. + if (cols.columns.filter(function(c) { + return (c.path == colSpec.path) })[0]) { + console.debug('skipping column ' + colSpec.path); + return; + } + + var column = fromExpand ? colSpec : cols.cloneFromScope(colSpec); + + if (column.path && column.path.match(/\*/)) + return cols.expandPath(colSpec); if (!column.name) column.name = column.path; if (!column.path) column.path = column.name; @@ -938,22 +1065,32 @@ angular.module('egGridMod', cols.columns.push(column); + // Track which columns are visible by default in case we + // need to reset column visibility + if (column.visible) + cols.stockVisible.push(column.name); + if (fromIDL) return; if (!cols.idlClass) return; // ad-hoc object // lookup the matching IDL field - var idl_field = cols.idlFieldFromPath(column.path); + var idl_info = cols.idlFieldFromPath(column.path); - if (!idl_field) { + if (!idl_info) { // column is not represented within the IDL column.adhoc = true; return; } - column.datatype = idl_field.datatype; + column.datatype = idl_info.idl_field.datatype; if (!column.label) { - column.label = idl_field.label || column.name; + column.label = idl_info.idl_field.label || column.name; + if (fromExpand) { + var label = + idl_info.idl_class.label || idl_info.idl_class.name; + column.label = label + '::' + column.label; + } } }, @@ -963,21 +1100,10 @@ angular.module('egGridMod', var class_obj = egCore.idl.classes[cols.idlClass]; var path_parts = dotpath.split(/\./); - // for() == early exit var idl_field; for (var path_idx in path_parts) { var part = path_parts[path_idx]; - - // find the field object matching the path component - for (var field_idx in class_obj.fields) { - if (class_obj.fields[field_idx].name == part) { - idl_field = class_obj.fields[field_idx]; - break; - } - } - - // unless we're at the end of the list, this field should - // link to another class. + idl_field = class_obj.field_map[part]; if (idl_field && idl_field['class'] && ( idl_field.datatype == 'link' || @@ -992,7 +1118,12 @@ angular.module('egGridMod', } } - return idl_field; + if (!idl_field) return null; + + return { + idl_field :idl_field, + idl_class : class_obj + }; } } -- 2.11.0