diff options
author | jmisinco | 2014-07-17 16:17:16 +0000 |
---|---|---|
committer | Roberto E. Escobar | 2014-10-01 21:03:58 +0000 |
commit | e82e4f925c9e21503fa5435e50a163fe3240924e (patch) | |
tree | 3ff1cdf10b701a5d5e6dd288083ed867d27100c8 /plugins/org.eclipse.osee.ats.rest | |
parent | f0c3f9ce5dcfb5c15b0c4e17f71b1da20e2f01a8 (diff) | |
download | org.eclipse.osee-e82e4f925c9e21503fa5435e50a163fe3240924e.tar.gz org.eclipse.osee-e82e4f925c9e21503fa5435e50a163fe3240924e.tar.xz org.eclipse.osee-e82e4f925c9e21503fa5435e50a163fe3240924e.zip |
feature[ats_ATS77015]: Add web UI for CPA
Change-Id: I0099e8ccf2c70b5d08335b6e1b7cbdaa93019faf
Diffstat (limited to 'plugins/org.eclipse.osee.ats.rest')
-rw-r--r-- | plugins/org.eclipse.osee.ats.rest/META-INF/MANIFEST.MF | 1 | ||||
-rw-r--r-- | plugins/org.eclipse.osee.ats.rest/build.properties | 3 | ||||
-rw-r--r-- | plugins/org.eclipse.osee.ats.rest/web/analyze.html | 90 | ||||
-rw-r--r-- | plugins/org.eclipse.osee.ats.rest/web/js/analyzeCtrl.js | 197 | ||||
-rw-r--r-- | plugins/org.eclipse.osee.ats.rest/web/js/app.js | 25 | ||||
-rw-r--r-- | plugins/org.eclipse.osee.ats.rest/web/js/cpaFactory.js | 108 | ||||
-rw-r--r-- | plugins/org.eclipse.osee.ats.rest/web/main.html | 51 |
7 files changed, 474 insertions, 1 deletions
diff --git a/plugins/org.eclipse.osee.ats.rest/META-INF/MANIFEST.MF b/plugins/org.eclipse.osee.ats.rest/META-INF/MANIFEST.MF index 02fd1587540..db93fa28f86 100644 --- a/plugins/org.eclipse.osee.ats.rest/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.osee.ats.rest/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Version: 0.19.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-Vendor: Eclipse Open System Engineering Environment Service-Component: OSGI-INF/*.xml +Osee-JaxRs-Resource: /web/*;path=/ats/cpa/ui Import-Package: com.google.gson;version="2.1.0", com.lowagie.text, com.lowagie.text.html, diff --git a/plugins/org.eclipse.osee.ats.rest/build.properties b/plugins/org.eclipse.osee.ats.rest/build.properties index d9e9369c5db..49b1233e59d 100644 --- a/plugins/org.eclipse.osee.ats.rest/build.properties +++ b/plugins/org.eclipse.osee.ats.rest/build.properties @@ -3,4 +3,5 @@ output.. = bin/ bin.includes = META-INF/,\ .,\ OSGI-INF/,\ - REST-INF/ + REST-INF/,\ + web/ diff --git a/plugins/org.eclipse.osee.ats.rest/web/analyze.html b/plugins/org.eclipse.osee.ats.rest/web/analyze.html new file mode 100644 index 00000000000..94d591fce59 --- /dev/null +++ b/plugins/org.eclipse.osee.ats.rest/web/analyze.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html> +<body> + <div class="container-fluid"> + <div class="row col-lg-12 h4" ng-show="!selectedProject"> + Select a project below to load: + </div> + <div class="row"> + <div class="col-md-3 h4"> + Project: <select ng-change="updateProject()" ng-model="selectedProject" + ng-options="item.name for item in projects"></select> + </div> + <div ng-show="selectedProject" class="col-md-2 h4">Analyzed: <strong>{{numAnalyzed()}}</strong></strong></div> + <div ng-show="selectedProject" class="col-md-2 h4">UnAnalyzed: <strong>{{items.length - numAnalyzed()}}</strong></strong></div> + <div ng-show="selectedProject" class="col-md-2 h4">Total: <strong>{{items.length}}</strong></div> + </div> + <div class="row col-lg-12" ng-show="selectedProject"> + <div class="panel panel-primary"> + <div class="panel-heading"> + <span> + <strong>Edit:</strong> + <button type="button" class="btn btn-default btn-xs" ng-click="updateApplicabilities()" ng-disabled="!itemsSelected()"" > + Applicability</button> + <button type="button" class="btn btn-default btn-xs" ng-click="updateAssignees()" ng-disabled="!itemsSelected()" > + Assignees</button> + <button type="button" class="btn btn-default btn-xs" ng-click="updateRationale()" ng-disabled="!itemsSelected()" > + Rationale</button> + </span> + <span class="pull-right"><strong>{{itemsSelected()}}</strong> of {{items.length}} Selected + <button type="button" class="btn btn-default btn-xs" ng-click="updateProject()" > + <span class="glyphicon glyphicon-refresh"></span></button> + </span> + </div> + <div ng-show="items" style="height: 600px" class="gridStyle" ng-grid="gridOptions"></div> + </div> + </div> + + <!-- definitions for modal forms --> + <script type="text/ng-template" id="editAssignees.html"> + <div class="modal-header"> + <h3 class="modal-title">Edit Assignees</h3> + <input class="form-control" type="search" ng-model="filter" placeholder="Search" focus-me> + </div> + <div class="modal-body" style="height: 400px; overflow: auto;"> + <table class="table table-bordered"> + <tr ng-repeat="item in items | filter:{name: filter}"> + <td width="30" style="text-align: left"> + <input type="checkbox" ng-model="item.$selected"/> + </td> + <td> + {{item.name}} + </td> + </tr> + </table> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" ng-click="ok()">OK</button> + <button class="btn btn-warning" ng-click="cancel()">Cancel</button> + </div> + </script> + + <script type="text/ng-template" id="editRationale.html"> + <div class="modal-header"> + <h3 class="modal-title">Edit Rationale</h3> + </div> + <div class="modal-body"> + <textarea class="form-control" rows="3" ng-model="newRationale.rationale" focus-me></textarea> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" ng-click="ok()">OK</button> + <button class="btn btn-warning" ng-click="cancel()">Cancel</button> + </div> + </script> + + <script type="text/ng-template" id="editApplicability.html"> + <div class="modal-header"> + <h3 class="modal-title">Edit Applicability</h3> + </div> + <div class="modal-body"> + <select ng-model="selected.applicability" ng-options="o as o for o in applicabilityEnum" focus-me></select> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" ng-click="ok()">OK</button> + <button class="btn btn-warning" ng-click="cancel()">Cancel</button> + </div> + </script> + </div> + +</body> +</html>
\ No newline at end of file diff --git a/plugins/org.eclipse.osee.ats.rest/web/js/analyzeCtrl.js b/plugins/org.eclipse.osee.ats.rest/web/js/analyzeCtrl.js new file mode 100644 index 00000000000..f0f66cf7e56 --- /dev/null +++ b/plugins/org.eclipse.osee.ats.rest/web/js/analyzeCtrl.js @@ -0,0 +1,197 @@ +/** + * Cpa Analyze Controller + */ +angular.module('CpaApp').controller('AnalyzeCtrl', + [ '$scope', 'CpaFactory', '$resource', '$window', '$modal', '$filter', function($scope, CpaFactory, $resource, $window, $modal, $filter) { + + CpaFactory.getConfig().$promise.then(function(data) { + $scope.applicabilityEnum = [''].concat(data.applicabilityOptions); + }); + + $scope.projects = CpaFactory.getProjects(); + + var idCellTmpl = '<button class="btn btn-default btn-sm" ng-disabled="!row.entity.decisionLocation" ng-click="openLink(row.entity.decisionLocation)">{{row.getProperty(col.field)}}</button>'; + + var origCellTmpl = '<button class="btn btn-default btn-sm" ng-disabled="!row.entity.origPcrLocation" ng-click="openLink(row.entity.origPcrLocation)">{{row.getProperty(col.field)}}</button>'; + + var dupCellTmpl = '<button class="btn btn-default btn-sm" ng-show="row.entity.duplicatedPcrLocation" ng-disabled="!row.entity.duplicatedPcrLocation" ng-click="openLink(row.entity.duplicatedPcrLocation)">{{row.getProperty(col.field)}}</button>'; + + var applCellTmpl = '<div class="ngCellText" ng-class="col.colIndex()"><select ng-change="updateApplicability(row.entity)" ng-model="COL_FIELD" ng-options="o as o for o in applicabilityEnum"></select></div>'; + + $scope.selectedItems = []; + + $scope.gridOptions = {data: 'items', + enableHighlighting: true, + selectedItems: $scope.selectedItems, + showSelectionCheckbox: true, + selectWithCheckboxOnly: true, + enableColumnResize: true, + showFilter: true, + sortInfo: {fields: ['uuid'], directions: ['asc']}, + columnDefs: [{field: 'uuid', displayName: 'Id', width: 85, cellTemplate: idCellTmpl}, + {field: 'pcrSystem', displayName: 'Type', width: 60}, + {field: 'originatingPcr.programName', displayName: 'Original Project', width: 100, cellTemplate: origCellTmpl}, + {field: 'originatingPcr.priority', displayName: 'Priority', width: 40}, + {field: 'originatingPcr.title', displayName: 'Title', width: 300}, + {field: 'applicability', displayName: 'Applicability', width: 70, cellTemplate: applCellTmpl}, + {field: 'assignees', displayName: 'Assignees', width: 150}, + {field: 'rationale', displayName: 'Rationale'}, + {field: 'duplicatedPcrId', displayName: 'Duplicate Pcr', width: 70, enableCellEdit: true, cellTemplate: dupCellTmpl}, + {field: 'completedDate', displayName: 'Completed Date', width: 90}, + {field: 'completedBy', displayName: 'Completed By', width: 100}] + }; + + $scope.$on('ngGridEventEndCellEdit', function(evt){ + // place holder for updating duplicated pcr id + }); + + $scope.updateProject = function() { + if($scope.selectedProject) { + $scope.items = null; + CpaFactory.getProjectCpas($scope.selectedProject).$promise.then(function(data){ + $scope.items = data; + }); + } + } + + $scope.openLink = function(url) { + $window.open(url); + } + + $scope.itemsSelected = function() { + return $scope.selectedItems.length; + } + + $scope.numAnalyzed = function() { + var count = 0; + angular.forEach($scope.items, function(item) { + count += item.applicability ? 1 : 0; + }); + return count; + } + + $scope.getSelected = function(includeCompleted) { + var toUpdate = []; + for(var i = 0; i < $scope.selectedItems.length; i++) { + if(!$scope.selectedItems[i].applicability || (includeCompleted && $scope.selectedItems[i].applicability)) { + toUpdate.push($scope.selectedItems[i]); + } + } + return toUpdate; + } + + $scope.updateApplicability = function(item) { + item.$selected = item.$selected && !item.applicability; + CpaFactory.updateApplicability(item, item.applicability); + } + + $scope.updateAssignees = function() { + var modalInstance = $modal.open({ + templateUrl: 'editAssignees.html', + controller: AssigneeModalCtrl, + size: 'sm', + resolve: { + items: function () { + return CpaFactory.getUsers(); + } + } + }); + + modalInstance.result.then(function (selected) { + var toUpdate = $scope.getSelected(); + CpaFactory.updateAssignees(toUpdate, selected); + }); + } + + var AssigneeModalCtrl = function ($scope, $modalInstance, items) { + $scope.items = items; + + $scope.ok = function () { + var selected = []; + for(var i = 0; i < $scope.items.length; i++) { + if($scope.items[i].$selected) { + selected.push($scope.items[i]); + } + } + $modalInstance.close(selected); + }; + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + }; + + $scope.updateRationale = function() { + var toUpdate = $scope.getSelected(); + var existing = ""; + if(toUpdate.length === 1) { + existing = toUpdate[0].rationale; + } + var modalInstance = $modal.open({ + templateUrl: 'editRationale.html', + controller: RationaleModalCtrl, + resolve: { + existing: function () { + return existing; + } + } + }); + + modalInstance.result.then(function (rationale) { + CpaFactory.updateRationale(toUpdate, rationale); + }); + } + + var RationaleModalCtrl = function ($scope, $modalInstance, existing) { + + $scope.newRationale = {rationale: existing}; + + $scope.ok = function () { + $modalInstance.close($scope.newRationale.rationale); + }; + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + }; + + $scope.updateApplicabilities = function() { + var toUpdate = $scope.getSelected(true); + var existing = ""; + if(toUpdate.length === 1) { + existing = toUpdate[0].applicability; + } + var modalInstance = $modal.open({ + templateUrl: 'editApplicability.html', + controller: ApplicabilityModalCtrl, + size: 'sm', + resolve: { + existing: function () { + return existing; + }, + applicabilityEnum: function() { + return $scope.applicabilityEnum; + } + } + }); + + modalInstance.result.then(function (applicability) { + CpaFactory.updateApplicability(toUpdate, applicability); + }); + }; + + var ApplicabilityModalCtrl = function ($scope, $modalInstance, existing, applicabilityEnum) { + + $scope.applicabilityEnum = applicabilityEnum; + $scope.selected = {applicability: existing}; + + $scope.ok = function () { + $modalInstance.close($scope.selected.applicability); + }; + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + }; + + } ]);
\ No newline at end of file diff --git a/plugins/org.eclipse.osee.ats.rest/web/js/app.js b/plugins/org.eclipse.osee.ats.rest/web/js/app.js new file mode 100644 index 00000000000..19403c88dd1 --- /dev/null +++ b/plugins/org.eclipse.osee.ats.rest/web/js/app.js @@ -0,0 +1,25 @@ +/** + * Cpa app definition + */ +var app = angular.module('CpaApp', [ 'ngRoute', 'ngResource', 'ui.bootstrap', 'ngGrid' ]); + +app.config([ '$routeProvider', function($routeProvider) { + $routeProvider.when('/', { + redirectTo : "/analyze", + }).when('/analyze', { + templateUrl : 'analyze.html', + controller : 'AnalyzeCtrl' + }).otherwise({ + redirectTo : "/analyze" + }); +} ]); + +app.directive('focusMe', function($timeout) { + return function(scope, element, attrs) { + scope.$watch(attrs.focusMe, function() { + $timeout(function() { + element[0].focus(); + }, 20); + }); + }; + });
\ No newline at end of file diff --git a/plugins/org.eclipse.osee.ats.rest/web/js/cpaFactory.js b/plugins/org.eclipse.osee.ats.rest/web/js/cpaFactory.js new file mode 100644 index 00000000000..54793d52cee --- /dev/null +++ b/plugins/org.eclipse.osee.ats.rest/web/js/cpaFactory.js @@ -0,0 +1,108 @@ +/** + * Cpa Factory + */ + +angular.module('CpaApp').factory('CpaFactory', ['$resource', function($resource) { + + var factory = {}; + + var projectResource = $resource('/ats/cpa/program'); + var projectCpaResource = $resource('/ats/cpa/program/:uuid'); + var userResource = $resource('/ats/user', {active: 'Active'}); + var configResource = $resource('/ats/cpa/config'); + var decisionResource = $resource('/ats/cpa/decision'); + + var users = userResource.query(); + var config = configResource.get(); + + factory.getConfig = function() { + return config; + } + + factory.getProjects = function() { + return projectResource.query(); + } + + factory.getProjectCpas = function(project) { + return projectCpaResource.query(project) + } + + factory.getUsers = function() { + return users; + } + + factory.updateAssignees = function(cpas, newAssignees) { + var cpaArr = cpas instanceof Array ? cpas : [cpas]; + var assigneeArr = newAssignees instanceof Array ? newAssignees : [newAssignees]; + + var toPost = {}; + toPost.uuids = []; + toPost.assignees = []; + + var assigneeStr = ""; + for(var i = 0; i < newAssignees.length; i++) { + assigneeStr += newAssignees[i].name + ";"; + toPost.assignees.push(newAssignees[i].uuid); + } + assigneeStr = assigneeStr.slice(0, -1); + + for(var i = 0; i < cpas.length; i++) { + toPost.uuids.push(cpas[i].uuid); + } + + decisionResource.save(toPost, function() { + for(var i = 0; i < cpas.length; i++) { + cpas[i].assignees = assigneeStr; + } + }); + + } + + factory.updateRationale = function(cpas, newRationale) { + var cpaArr = cpas instanceof Array ? cpas : [cpas]; + + var toPost = {}; + toPost.uuids = []; + toPost.rationale = newRationale; + + for(var i = 0; i < cpas.length; i++) { + toPost.uuids.push(cpas[i].uuid); + } + + decisionResource.save(toPost, function() { + for(var i = 0; i < cpas.length; i++) { + cpas[i].rationale = newRationale; + } + }, function(err){ + for(var i = 0; i < cpas.length; i++) { + cpas[i].rationale = "Err - Refresh Table"; + } + }); + + } + + factory.updateApplicability = function(item, applicability) { + var items = item instanceof Array ? item : [item]; + var toPost = {}; + toPost.uuids = []; + toPost.applicability = applicability; + + for(var i = 0; i < items.length; i++) { + toPost.uuids.push(items[i].uuid); + } + + decisionResource.save(toPost, function() { + for(var i = 0; i < items.length; i++) { + items[i].applicability = applicability; + } + }, function(err){ + for(var i = 0; i < items.length; i++) { + items[i].applicability = "Err - Refresh Table"; + } + }); + + } + + return factory; + +}]);
\ No newline at end of file diff --git a/plugins/org.eclipse.osee.ats.rest/web/main.html b/plugins/org.eclipse.osee.ats.rest/web/main.html new file mode 100644 index 00000000000..39ca17c0370 --- /dev/null +++ b/plugins/org.eclipse.osee.ats.rest/web/main.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8"> +<title>Cross Project Applicability</title> +<link rel="stylesheet" href="/lib/css/bootstrap.min.css"> +<link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/ng-grid/2.0.11/ng-grid.min.css" /> +</head> +<body ng-app="CpaApp"> + + <nav class="navbar navbar-default" role="navigation"> + <div class="container-fluid"> + <div class="navbar-header"> + <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="#/analyze">Cross Project Applicability</a> + </div> + <div class="collapse navbar-collapse" ng-controller="HeaderController"> + <ul class="nav navbar-nav navbar-right"> + <li ng-class="{ active: isActive('/analyze')}"><a href="#/analyze">Analyze</a></li> + </ul> + </div> + </div> + </nav> + + <div ng-view></div> + + <script> + function HeaderController($scope, $location) { + $scope.isActive = function(viewLocation) { + return viewLocation === $location.path(); + }; + } + </script> + + <script src="/lib/js/jquery-2.0.3.min.js"></script> + <script src="/lib/js/angular.min.js"></script> + <script src="/lib/js/angular-route.min.js"></script> + <script src="/lib/js/angular-resource.min.js"></script> + <script src="http://cdnjs.cloudflare.com/ajax/libs/ng-grid/2.0.11/ng-grid.min.js"></script> + <script src="/lib/js/bootstrap.min.js"></script> + <script src="/lib/js/ui-bootstrap-tpls-0.11.0.min.js"></script> + <script src="js/app.js"></script> + <script src="js/cpaFactory.js"></script> + <script src="js/analyzeCtrl.js"></script> +</body> +</html>
\ No newline at end of file |