web staff: autogrid experiments
authorBill Erickson <berick@esilibrary.com>
Mon, 10 Mar 2014 20:12:52 +0000 (16:12 -0400)
committerBill Erickson <berick@esilibrary.com>
Mon, 10 Mar 2014 20:12:52 +0000 (16:12 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/parts/t_autogrid.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/parts/t_autogrid_field.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/test/index.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/test/t_autogrid.tt2 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/services/autogrid.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/test/app.js [new file with mode: 0644]

diff --git a/Open-ILS/src/templates/staff/parts/t_autogrid.tt2 b/Open-ILS/src/templates/staff/parts/t_autogrid.tt2
new file mode 100644 (file)
index 0000000..70ea9ae
--- /dev/null
@@ -0,0 +1,19 @@
+
+<style>
+  /* TODO: move me */
+  .eg-grid-header-row { font-weight: bold; }
+  .eg-grid div.row {padding: 3px; border-right: 2px solid rgb(248, 248, 248);}
+  .eg-grid div.row:nth-child(odd) {background-color: rgb(248, 248, 248);}
+</style>
+
+<div class="container-fluid eg-grid">
+  <!-- absorb the grid header -->
+  <div class="row eg-grid-header-row" ng-transclude></div>
+  <div class="row eg-grid-content-row" ng-repeat="item in dataList.items">
+    <div class="col-md-1" ng-repeat="field in dataList.allColumns">
+        {{dataList.fieldValue(item, field.name)}}
+    </div>
+  </div>
+</div>
+
+
diff --git a/Open-ILS/src/templates/staff/parts/t_autogrid_field.tt2 b/Open-ILS/src/templates/staff/parts/t_autogrid_field.tt2
new file mode 100644 (file)
index 0000000..4dc5b9d
--- /dev/null
@@ -0,0 +1 @@
+<div class="col-md-1" ng-show="dataList.displayColumns[column.name]">{{column.label}}</div>
diff --git a/Open-ILS/src/templates/staff/test/index.tt2 b/Open-ILS/src/templates/staff/test/index.tt2
new file mode 100644 (file)
index 0000000..68d4802
--- /dev/null
@@ -0,0 +1,17 @@
+[%
+  WRAPPER "staff/t_base.tt2";
+  ctx.page_title = l("Test"); 
+  ctx.page_app = "egTestApp";
+  ctx.page_ctrl = "egTestCtrl";
+%]
+
+[% BLOCK APP_JS %]
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/list.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/ui.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/services/autogrid.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/test/app.js"></script>
+[% END %]
+
+<div ng-view></div>
+
+[% END %]
diff --git a/Open-ILS/src/templates/staff/test/t_autogrid.tt2 b/Open-ILS/src/templates/staff/test/t_autogrid.tt2
new file mode 100644 (file)
index 0000000..2b0d102
--- /dev/null
@@ -0,0 +1,14 @@
+
+<h1>AutoGrid Test</h1>
+<div eg-grid 
+  idl-class="aou"
+  sort="testGridSort"
+  query="testGridQuery"
+  >
+  <div eg-grid-field name="shortname" label="[% l('Shortname Manual Label') %]"></div>
+  <div eg-grid-field name="name"></div>
+  <div eg-grid-field name="id"></div>
+  <div eg-grid-field name="depth"  path="ou_type.depth"></div>
+  <div eg-grid-field name="parent_ou_id"  path="parent_ou.id"></div>
+  <div eg-grid-field name="parent_ou_name" path="parent_ou.name" label="[% l('Parent Org') %]"></div>
+</div>
diff --git a/Open-ILS/web/js/ui/default/staff/services/autogrid.js b/Open-ILS/web/js/ui/default/staff/services/autogrid.js
new file mode 100644 (file)
index 0000000..cee91ca
--- /dev/null
@@ -0,0 +1,144 @@
+
+angular.module('egGridMod', ['egCoreMod', 'egListMod'])
+
+.directive('egGrid', function() {
+    return {
+        restrict : 'A',
+        transclude : true,
+        scope : {
+            idlClass : '@',
+            query : '=',
+            sort : '=',
+        },
+        templateUrl : '/eg/staff/parts/t_autogrid', // TODO: abs url
+        controller : function($scope, $timeout, egIDL, egAuth, egNet, egList) { // TODO: reqs list
+            var self = this;
+            $scope.dataList = egList.create();
+
+            this.addField = function(fieldScope) {
+                var field = {
+                    name : fieldScope.name,
+                    label : fieldScope.label,
+                    path : fieldScope.path,
+                    display : true
+                };
+                self.applyFieldLabel(field);
+                $scope.dataList.addColumn(field);
+                return {dataList : $scope.dataList, column : field};
+            }
+
+            this.applyFieldLabel = function(field) {
+                if (field.label) return; // label already applied
+
+                var class_obj = egIDL.classes[$scope.idlClass];
+                if (!field.path) field.path = field.name;
+                var path_parts = field.path.split(/\./);
+
+                // note: use of for() is intentional for early exit
+                var field_obj;
+                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) {
+                            field_obj = class_obj.fields[field_idx];
+                            break;
+                        }
+                    }
+
+                    // unless we're at the end of the list, this field should
+                    // link to another class.
+
+                    if (field_obj && field_obj['class'] && (
+                        field_obj.datatype == 'link' || 
+                        field_obj.datatype == 'org_unit')) {
+                        class_obj = egIDL.classes[field_obj['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: " + field.path);
+                        }
+                    }
+                }
+
+                field.label = field_obj ? field_obj.label : field.name;
+            }
+
+            this.fetchData = function() {
+
+                if (!$scope.query) {
+                    console.error("egGrid requires a query");
+                    return;
+                }
+
+                if (!$scope.idlClass) {
+                    console.error("egGrid requires an idlClass");
+                    return;
+                }
+
+                var queryFields = {}
+                angular.forEach($scope.dataList.allColumns, function(field) {
+                    if ($scope.dataList.displayColumns[field.name])
+                        queryFields[field.name] = field.path || field.name;
+                });
+
+                egNet.request(
+                    'open-ils.fielder',
+                    'open-ils.fielder.flattened_search',
+                    egAuth.token(), $scope.idlClass, queryFields,
+                    $scope.query,
+                    {   sort : $scope.sort,
+                        limit : 10, // TODO
+                        offset : 0 // TODO
+                    }
+                  ).then(null, null, function(item) {
+                      $scope.dataList.items.push(item);
+                    }
+                );
+            }
+
+            // Don't call fetchData until we know what the fields are
+            // TODO: this is a hack which polls to see if our eg-grid-field's
+            // have been processed.  There has to be a better way...
+            readycheck = 0;
+            this.checkReadyForDraw = function() {
+                if ($scope.dataList.allColumns.length) {
+                    self.fetchData();
+                } else {
+                    if (++readycheck > 10000) return; // failsafe, no fields defined
+                    // check again after the next $digest loop
+                    $scope.$evalAsync(function() {self.checkReadyForDraw()});
+                }
+            }
+            this.checkReadyForDraw();
+        }
+    };
+})
+
+.directive('egGridField', function() {
+    return {
+        require : '^egGrid',
+        restrict : 'A',
+        transclude : true,
+        scope : {
+            name : '@',
+            path : '@',
+            label : '@'
+        },
+        templateUrl : '/eg/staff/parts/t_autogrid_field',
+
+        // pass field info from the UI into the grid.  The grid stores
+        // the field data within its dataList and returns a link to the
+        // dataList.  From there, our visibility, etc. is based on the 
+        // dispostion of the dataList.
+        link : function(scope, element, attrs, egGridCtrl) {
+            var ctx = egGridCtrl.addField(scope);
+            scope.dataList = ctx.dataList;
+            scope.column = ctx.column;
+        }
+    };
+});
+
+
diff --git a/Open-ILS/web/js/ui/default/staff/test/app.js b/Open-ILS/web/js/ui/default/staff/test/app.js
new file mode 100644 (file)
index 0000000..28ca01c
--- /dev/null
@@ -0,0 +1,38 @@
+angular.module('egTestApp', ['ngRoute', 'ui.bootstrap', 
+    'egCoreMod', 'egUiMod', 'egListMod', 'egGridMod'])
+
+.config(function($routeProvider, $locationProvider) {
+    $locationProvider.html5Mode(true);
+
+    var resolver = {delay : function(egStartup) { return egStartup.go() }};
+
+    $routeProvider.when('/test/autogrid', {
+        templateUrl: './test/t_autogrid',
+        controller: 'TestGridCtrl',
+        resolve : resolver
+    });
+
+    //$routeProvider.otherwise({redirectTo : '/circ/patron/search'});
+})
+
+.controller('egTestCtrl', ['$scope', function($scope) {
+    /*
+    $scope.$on('$viewContentLoaded', function() {
+        console.debug("viewContentLoaded");
+    });
+    */
+}])
+
+.controller('TestGridCtrl', 
+       ['$scope', 
+function($scope) {
+
+    console.log('TestGridCtrl');
+    $scope.testGridQuery = {id : {'<>' : null}};
+    $scope.testGridSort = ['depth', 'parent_ou_id', 'name']
+
+    //$scope.fmClass="aou";
+}]);
+
+