From 3c82bc3443d476f65db15d8a68c884d7eda13ba8 Mon Sep 17 00:00:00 2001 From: Paul Van Eck Date: Thu, 9 Jun 2016 15:45:22 -0700 Subject: [PATCH] Add vendor UI This adds the initial UI functionality for the UI in relations to vendors. Change-Id: I58a3b00a421d2d65d59014e586bacc974aa8637b Co-Authored-By: Andrey Pavlov --- refstack-ui/app/app.js | 15 ++ .../app/components/vendors/vendor.html | 85 +++++++ .../components/vendors/vendorController.js | 185 ++++++++++++++++ .../app/components/vendors/vendors.html | 69 ++++++ .../components/vendors/vendorsController.js | 162 ++++++++++++++ refstack-ui/app/index.html | 3 + .../app/shared/alerts/confirmModal.html | 13 ++ .../app/shared/alerts/confirmModalFactory.js | 67 ++++++ refstack-ui/app/shared/header/header.html | 16 ++ .../app/shared/header/headerController.js | 9 + refstack-ui/tests/unit/ControllerSpec.js | 207 ++++++++++++++++++ 11 files changed, 831 insertions(+) create mode 100644 refstack-ui/app/components/vendors/vendor.html create mode 100644 refstack-ui/app/components/vendors/vendorController.js create mode 100644 refstack-ui/app/components/vendors/vendors.html create mode 100644 refstack-ui/app/components/vendors/vendorsController.js create mode 100644 refstack-ui/app/shared/alerts/confirmModal.html create mode 100644 refstack-ui/app/shared/alerts/confirmModalFactory.js diff --git a/refstack-ui/app/app.js b/refstack-ui/app/app.js index fc60145a..44ad016b 100644 --- a/refstack-ui/app/app.js +++ b/refstack-ui/app/app.js @@ -78,6 +78,21 @@ url: '/logout', templateUrl: '/components/logout/logout.html', controller: 'LogoutController as ctrl' + }). + state('userVendors', { + url: '/user_vendors', + templateUrl: '/components/vendors/vendors.html', + controller: 'VendorsController as ctrl' + }). + state('publicVendors', { + url: '/public_vendors', + templateUrl: '/components/vendors/vendors.html', + controller: 'VendorsController as ctrl' + }). + state('vendor', { + url: '/vendor/:vendorID', + templateUrl: '/components/vendors/vendor.html', + controller: 'VendorController as ctrl' }); } diff --git a/refstack-ui/app/components/vendors/vendor.html b/refstack-ui/app/components/vendors/vendor.html new file mode 100644 index 00000000..4938e2c3 --- /dev/null +++ b/refstack-ui/app/components/vendors/vendor.html @@ -0,0 +1,85 @@ +
+
+
+
+ Vendor ID: {{ctrl.vendorId}}
+ Type: + OpenStack + Private + Pending Approval + Official +
+ Name: {{ctrl.vendor.name}}
+ Description: {{ctrl.vendor.description}}
+
+
+ + +
+ +
+
+
+ Decline reason: {{ctrl.vendorProperties.reason}}
+
+
+ +
+
+

Vendor Users

+ + + + + + + + + + + + + + + + + +
Open IDFull NameE-Mail
{{user.openid}}{{user.fullname}}{{user.email}} + Remove +
+ +
+
+ + +
+
+ +
+
+
+
+
+ + +
+
+ + diff --git a/refstack-ui/app/components/vendors/vendorController.js b/refstack-ui/app/components/vendors/vendorController.js new file mode 100644 index 00000000..4ae22c3e --- /dev/null +++ b/refstack-ui/app/components/vendors/vendorController.js @@ -0,0 +1,185 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('refstackApp') + .controller('VendorController', VendorController); + + VendorController.$inject = [ + '$rootScope', '$scope', '$http', '$state', '$stateParams', '$window', + 'refstackApiUrl', 'raiseAlert', 'confirmModal' + ]; + + /** + * RefStack Vendor Controller + * This controller is for the '/vendor/' details page where owner can + * view details of the Vendor and manage users. + */ + function VendorController($rootScope, $scope, $http, $state, $stateParams, + $window, refstackApiUrl, raiseAlert, confirmModal) { + var ctrl = this; + + ctrl.getVendor = getVendor; + ctrl.getVendorUsers = getVendorUsers; + ctrl.registerVendor = registerVendor; + ctrl.approveVendor = approveVendor; + ctrl.declineVendor = declineVendor; + ctrl.deleteVendor = deleteVendor; + ctrl.removeUserFromVendor = removeUserFromVendor; + ctrl.addUserToVendor = addUserToVendor; + + /** The vendor id extracted from the URL route. */ + ctrl.vendorId = $stateParams.vendorID; + + // Should only be on user-vendors-page if authenticated. + if (!$scope.auth.isAuthenticated) { + $state.go('home'); + } + + /** + * This will contact the Refstack API to get a vendor information. + */ + function getVendor() { + ctrl.showError = false; + ctrl.vendor = null; + // Construct the API URL based on user-specified filters. + var contentUrl = refstackApiUrl + '/vendors/' + ctrl.vendorId; + ctrl.vendorRequest = + $http.get(contentUrl).success(function(data) { + ctrl.vendor = data; + var isAdmin = $rootScope.auth.currentUser.is_admin; + ctrl.vendor.canDelete = ctrl.vendor.type != 0 + && (ctrl.vendor.can_manage || isAdmin); + ctrl.vendor.canRegister = + ctrl.vendor.type == 1; + ctrl.vendor.canApprove = isAdmin; + ctrl.vendorProperties = angular.fromJson(data.properties); + }).error(function(error) { + ctrl.showError = true; + ctrl.error = + 'Error retrieving from server: ' + + angular.toJson(error); + }); + } + ctrl.getVendor(); + + /** + * This will 'send' application for registration. + */ + function registerVendor() { + var url = [refstackApiUrl, '/vendors/', ctrl.vendorId, + '/action'].join(''); + $http.post(url, {register: null}).success(function() { + ctrl.getVendor(); + }).error(function(error) { + raiseAlert('danger', 'Error: ', error.detail); + }); + } + + /** + * This will approve application for registration. + */ + function approveVendor() { + var url = [refstackApiUrl, '/vendors/', ctrl.vendorId, + '/action'].join(''); + $http.post(url, {approve: null}).success(function() { + ctrl.getVendor(); + }).error(function(error) { + raiseAlert('danger', 'Error: ', error.detail); + }); + } + + /** + * This will decline a vendor's application for registration. + */ + function declineVendor() { + confirmModal('Please input decline reason', function(reason) { + var url = [refstackApiUrl, '/vendors/', ctrl.vendorId, + '/action'].join(''); + $http.post(url, {deny: null, reason: reason}).success( + function() { + ctrl.getVendor(); + }).error(function(error) { + raiseAlert('danger', 'Error: ', error.detail); + }); + }); + } + + /** + * Delete the current vendor. + */ + function deleteVendor() { + var url = [refstackApiUrl, '/vendors/', ctrl.vendorId].join(''); + $http.delete(url).success(function () { + $window.location.href = '/'; + }).error(function (error) { + raiseAlert('danger', 'Error: ', error.detail); + }); + } + + /** + * Updates list of users in the vendor's group + */ + function getVendorUsers() { + ctrl.showError = false; + var contentUrl = refstackApiUrl + '/vendors/' + ctrl.vendorId + + '/users'; + ctrl.usersRequest = + $http.get(contentUrl).success(function(data) { + ctrl.vendorUsers = data; + ctrl.currentUser = $rootScope.auth.currentUser.openid; + }).error(function(error) { + ctrl.showError = true; + ctrl.error = + 'Error retrieving from server: ' + + angular.toJson(error); + }); + } + ctrl.getVendorUsers(); + + /** + * Removes user with specific openid from vendor's group + * @param {Object} openid + */ + function removeUserFromVendor(openid) { + var url = [refstackApiUrl, '/vendors/', ctrl.vendorId, + '/users/', btoa(openid)].join(''); + $http.delete(url).success(function () { + ctrl.getVendorUsers(); + }).error(function (error) { + raiseAlert('danger', 'Error: ', error.detail); + }); + } + + /** + * Adds a user to a vendor group given an Open ID. + * @param {Object} openid + */ + function addUserToVendor(openid) { + var url = [refstackApiUrl, '/vendors/', ctrl.vendorId, + '/users/', btoa(openid)].join(''); + $http.put(url).success(function() { + ctrl.userToAdd = ''; + ctrl.getVendorUsers(); + }).error(function(error) { + raiseAlert('danger', 'Problem adding user. ' + + 'Is the Open ID correct? Error: ', + error.detail); + }); + } + } +})(); diff --git a/refstack-ui/app/components/vendors/vendors.html b/refstack-ui/app/components/vendors/vendors.html new file mode 100644 index 00000000..26acc3c0 --- /dev/null +++ b/refstack-ui/app/components/vendors/vendors.html @@ -0,0 +1,69 @@ +

{{ctrl.pageHeader}}

+

{{ctrl.pageParagraph}}

+ +
+
+ +
+ +
+ + + + + + + + + + + + + + + + +
NameDescriptionType
{{vendor.name}}{{vendor.name}}{{vendor.description || '-'}} +  OpenStack +  Private +  Pending Approval +  Official +
+
+ +
+
+

Add new vendor

+

Creating a vendor allows you to associate test results to specific vendors/products. + Created vendors are private, but vendors can be registered with the Foundation to become public and official. + This will require approval by a Foundation administrator.

+
+
+ +

+ +

+
+
+ +

+ +

+
+
+ +
+
+
+ +
+ + diff --git a/refstack-ui/app/components/vendors/vendorsController.js b/refstack-ui/app/components/vendors/vendorsController.js new file mode 100644 index 00000000..6386d328 --- /dev/null +++ b/refstack-ui/app/components/vendors/vendorsController.js @@ -0,0 +1,162 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('refstackApp') + .controller('VendorsController', VendorsController); + + VendorsController.$inject = [ + '$rootScope', '$scope', '$http', '$state', + 'refstackApiUrl','raiseAlert' + ]; + + /** + * RefStack Vendors Controller + * This controller is for the '/user_vendors' or '/public_vendors' page + * where a user can browse a listing of his/her vendors or public vendors. + */ + function VendorsController($rootScope, $scope, $http, $state, + refstackApiUrl, raiseAlert) { + var ctrl = this; + + ctrl.update = update; + ctrl.updateData = updateData; + ctrl._filterVendor = _filterVendor; + ctrl.addVendor = addVendor; + + /** Check to see if this page should display user-specific vendors. */ + ctrl.isUserVendors = $state.current.name === 'userVendors'; + + /** Show private vendors in list for foundation admin */ + ctrl.withPrivate = false; + + /** Properties for adding new vendor */ + ctrl.name = ''; + ctrl.description = ''; + + // Should only be on user-vendors-page if authenticated. + if (ctrl.isUserVendors && !$scope.auth.isAuthenticated) { + $state.go('home'); + } + + ctrl.pageHeader = ctrl.isUserVendors ? + 'My Vendors' : 'Public Vendors'; + + ctrl.pageParagraph = ctrl.isUserVendors ? + 'Your added vendors are listed here.' : + 'Public Vendors approved by the OpenStack Foundation are ' + + 'listed here.'; + + if (ctrl.isUserVendors) { + ctrl.authRequest = $scope.auth.doSignCheck() + .then(ctrl.update); + } else { + ctrl.update(); + } + + ctrl.rawData = null; + ctrl.isAdminView = $rootScope.auth + && $rootScope.auth.currentUser + && $rootScope.auth.currentUser.is_admin; + + /** + * This will contact the Refstack API to get a listing of test run + * results. + */ + function update() { + ctrl.showError = false; + ctrl.data = null; + // Construct the API URL based on user-specified filters. + var contentUrl = refstackApiUrl + '/vendors'; + if (typeof ctrl.rawData == 'undefined' + || ctrl.rawData === null) { + ctrl.vendorsRequest = + $http.get(contentUrl).success(function (data) { + ctrl.rawData = data; + ctrl.updateData(); + }).error(function (error) { + ctrl.rawData = null; + ctrl.showError = true; + ctrl.error = + 'Error retrieving vendors listing from server: ' + + angular.toJson(error); + }); + } else { + ctrl.updateData(); + } + } + + /** + * This will update data for view with current settings on page. + */ + function updateData() { + ctrl.data = {}; + ctrl.data.vendors = ctrl.rawData.vendors.filter(function(vendor) { + return ctrl._filterVendor(vendor); + }); + ctrl.data.vendors.sort(function(a, b) { + if (a.type > b.type) { + return 1; + } + if (a.type < b.type) { + return -1; + } + return a.name.localeCompare(b.name); + }); + } + + /** + * Returns true if vendor can be displayed on this page. + */ + function _filterVendor(vendor) { + if (!ctrl.isUserVendors) { + return (vendor.type == 0 || vendor.type == 3); + } + + if (!$rootScope.auth || !$rootScope.auth.currentUser) { + return false; + } + + if ($rootScope.auth.currentUser.is_admin) { + return vendor.type != 1 || ctrl.withPrivate; + } + + return vendor.can_manage; + } + + /** + * This will add a new vendor record. + */ + function addVendor() { + var url = refstackApiUrl + '/vendors'; + var data = { + name: ctrl.name, + description: ctrl.description + }; + ctrl.name = ''; + ctrl.description = ''; + $http.post(url, data).success(function (data) { + ctrl.rawData = null; + ctrl.update(); + }).error(function (error) { + ctrl.showError = true; + ctrl.error = + 'Error adding new vendor: ' + angular.toJson(error); + }); + } + } +})(); diff --git a/refstack-ui/app/index.html b/refstack-ui/app/index.html index 24f403d5..f3111645 100644 --- a/refstack-ui/app/index.html +++ b/refstack-ui/app/index.html @@ -38,12 +38,15 @@ + + + diff --git a/refstack-ui/app/shared/alerts/confirmModal.html b/refstack-ui/app/shared/alerts/confirmModal.html new file mode 100644 index 00000000..82478a51 --- /dev/null +++ b/refstack-ui/app/shared/alerts/confirmModal.html @@ -0,0 +1,13 @@ + + + diff --git a/refstack-ui/app/shared/alerts/confirmModalFactory.js b/refstack-ui/app/shared/alerts/confirmModalFactory.js new file mode 100644 index 00000000..91e568ec --- /dev/null +++ b/refstack-ui/app/shared/alerts/confirmModalFactory.js @@ -0,0 +1,67 @@ +(function () { + 'use strict'; + + angular + .module('refstackApp') + .factory('confirmModal', confirmModal); + + confirmModal.$inject = ['$uibModal']; + + /** + * Opens confirm modal dialog with input textbox + */ + function confirmModal($uibModal) { + return function(text, successHandler) { + $uibModal.open({ + templateUrl: '/shared/alerts/confirmModal.html', + controller: 'CustomConfirmModalController as confirmModal', + size: 'md', + resolve: { + data: function () { + return { + text: text, + successHandler: successHandler + }; + } + } + }); + }; + } + + angular + .module('refstackApp') + .controller('CustomConfirmModalController', + CustomConfirmModalController); + + CustomConfirmModalController.$inject = ['$uibModalInstance', 'data']; + + /** + * This is the controller for the alert pop-up. + */ + function CustomConfirmModalController($uibModalInstance, data) { + var ctrl = this; + + ctrl.confirm = confirm; + ctrl.cancel = cancel; + + ctrl.data = angular.copy(data); + + /** + * Initiate confirmation and call the success handler with the + * input text. + */ + function confirm() { + $uibModalInstance.close(); + if (angular.isDefined(ctrl.data.successHandler)) { + ctrl.data.successHandler(ctrl.inputText); + } + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } +})(); diff --git a/refstack-ui/app/shared/header/header.html b/refstack-ui/app/shared/header/header.html index 89cbc690..c50a4399 100644 --- a/refstack-ui/app/shared/header/header.html +++ b/refstack-ui/app/shared/header/header.html @@ -19,9 +19,25 @@ RefStack
  • About
  • DefCore Guidelines
  • Community Results
  • +