Merge "Add product versions UI" into feature/vendor
This commit is contained in:
commit
34997b7c37
@ -12,5 +12,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div ng-include src="'components/products/partials/management.html'"></div>
|
||||
<div class="clearfix"></div>
|
||||
<div ng-include src="'components/products/partials/versions.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="ctrl.showError" class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
||||
<span class="sr-only">Error:</span>
|
||||
{{ctrl.error}}
|
||||
</div>
|
||||
|
@ -12,5 +12,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div ng-include src="'components/products/partials/management.html'"></div>
|
||||
<div class="clearfix"></div>
|
||||
<div ng-include src="'components/products/partials/versions.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="ctrl.showError" class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
||||
<span class="sr-only">Error:</span>
|
||||
{{ctrl.error}}
|
||||
</div>
|
||||
|
29
refstack-ui/app/components/products/partials/versions.html
Normal file
29
refstack-ui/app/components/products/partials/versions.html
Normal file
@ -0,0 +1,29 @@
|
||||
<strong>Version(s) Available:</strong>
|
||||
<span ng-repeat="item in ctrl.productVersions | orderBy:'version'">
|
||||
<a ng-show="item.version && ctrl.product.can_manage" class="label label-info" ng-click="ctrl.openVersionModal(item)">
|
||||
{{item.version}}
|
||||
</a>
|
||||
<span ng-hide="ctrl.product.can_manage" class="label label-info">{{item.version}}</span>
|
||||
</span>
|
||||
|
||||
<a ng-if="ctrl.product.can_manage"
|
||||
title="Add a new product version."
|
||||
ng-click="ctrl.showNewVersionInput = true">
|
||||
<small><span class="glyphicon glyphicon-plus"></span></small>
|
||||
</a>
|
||||
<div ng-if="ctrl.showNewVersionInput" class="row" style="margin-top: 5px;">
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input ng-model="ctrl.newProductVersion"
|
||||
type="text" class="form-control" placeholder="New Version">
|
||||
<span class="input-group-btn">
|
||||
<button
|
||||
class="btn btn-default"
|
||||
type="button"
|
||||
ng-click="ctrl.addProductVersion()">
|
||||
Add
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,51 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4>Manage Version</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="pull-left">
|
||||
<strong>Version:</strong> {{modal.version.version}}<br />
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a class="glyphicon glyphicon-trash"
|
||||
ng-click="modal.deleteProductVersion()"
|
||||
confirm="Are you sure you want to delete product version {{modal.version.version}}?">
|
||||
</a>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<br />
|
||||
(Optional) Associate cloud provider ID (CPID) with product version for easier
|
||||
test run associating.
|
||||
<br />
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<strong>CPID:</strong><br />
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" ng-model="modal.version.cpid" />
|
||||
<span class="input-group-btn">
|
||||
<button
|
||||
class="btn btn-default"
|
||||
type="button"
|
||||
ng-click="modal.saveChanges()">
|
||||
Save
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="modal.showError" class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
||||
<span class="sr-only">Error:</span>
|
||||
{{modal.error}}
|
||||
</div>
|
||||
<div ng-show="modal.showSuccess" class="alert alert-success" role="success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
|
||||
<span class="sr-only">Success:</span>
|
||||
Updated Successfully.
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" type="button" ng-click="modal.close()">Close</button>
|
||||
</div>
|
||||
</div>
|
@ -20,7 +20,7 @@
|
||||
.controller('ProductController', ProductController);
|
||||
|
||||
ProductController.$inject = [
|
||||
'$scope', '$http', '$state', '$stateParams', '$window',
|
||||
'$scope', '$http', '$state', '$stateParams', '$window', '$uibModal',
|
||||
'refstackApiUrl', 'raiseAlert'
|
||||
];
|
||||
|
||||
@ -30,21 +30,27 @@
|
||||
* view details of the product.
|
||||
*/
|
||||
function ProductController($scope, $http, $state, $stateParams,
|
||||
$window, refstackApiUrl, raiseAlert) {
|
||||
$window, $uibModal, refstackApiUrl, raiseAlert) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.getProduct = getProduct;
|
||||
ctrl.getProductVersions = getProductVersions;
|
||||
ctrl.deleteProduct = deleteProduct;
|
||||
ctrl.deleteProductVersion = deleteProductVersion;
|
||||
ctrl.switchProductPublicity = switchProductPublicity;
|
||||
ctrl.addProductVersion = addProductVersion;
|
||||
ctrl.openVersionModal = openVersionModal;
|
||||
|
||||
/** The product id extracted from the URL route. */
|
||||
ctrl.id = $stateParams.id;
|
||||
ctrl.productVersions = [];
|
||||
|
||||
if (!$scope.auth.isAuthenticated) {
|
||||
$state.go('home');
|
||||
}
|
||||
|
||||
ctrl.getProduct();
|
||||
ctrl.getProductVersions();
|
||||
|
||||
/**
|
||||
* This will contact the Refstack API to get a product information.
|
||||
@ -52,32 +58,49 @@
|
||||
function getProduct() {
|
||||
ctrl.showError = false;
|
||||
ctrl.product = null;
|
||||
// Construct the API URL based on user-specified filters.
|
||||
var content_url = refstackApiUrl + '/products/' + ctrl.id;
|
||||
ctrl.productRequest =
|
||||
ctrl.productRequest = $http.get(content_url).success(
|
||||
function(data) {
|
||||
ctrl.product = data;
|
||||
ctrl.product_properties =
|
||||
angular.fromJson(data.properties);
|
||||
}
|
||||
).error(function(error) {
|
||||
ctrl.productRequest = $http.get(content_url).success(
|
||||
function(data) {
|
||||
ctrl.product = data;
|
||||
ctrl.product_properties =
|
||||
angular.fromJson(data.properties);
|
||||
}
|
||||
).error(function(error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.error =
|
||||
'Error retrieving from server: ' +
|
||||
angular.toJson(error);
|
||||
}).then(function() {
|
||||
var url = refstackApiUrl + '/vendors/' +
|
||||
ctrl.product.organization_id;
|
||||
$http.get(url).success(function(data) {
|
||||
ctrl.vendor = data;
|
||||
}).error(function(error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.error =
|
||||
'Error retrieving from server: ' +
|
||||
angular.toJson(error);
|
||||
}).then(function() {
|
||||
var url = refstackApiUrl + '/vendors/' +
|
||||
ctrl.product.organization_id;
|
||||
$http.get(url).success(function(data) {
|
||||
ctrl.vendor = data;
|
||||
}).error(function(error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.error =
|
||||
'Error retrieving from server: ' +
|
||||
angular.toJson(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will contact the Refstack API to get product versions.
|
||||
*/
|
||||
function getProductVersions() {
|
||||
ctrl.showError = false;
|
||||
var content_url = refstackApiUrl + '/products/' + ctrl.id +
|
||||
'/versions';
|
||||
ctrl.productVersionsRequest = $http.get(content_url).success(
|
||||
function(data) {
|
||||
ctrl.productVersions = data;
|
||||
}
|
||||
).error(function(error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.error =
|
||||
'Error retrieving versions from server: ' +
|
||||
angular.toJson(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,6 +115,38 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will delete the given product versions.
|
||||
*/
|
||||
function deleteProductVersion(versionId) {
|
||||
var url = [
|
||||
refstackApiUrl, '/products/', ctrl.id,
|
||||
'/versions/', versionId ].join('');
|
||||
$http.delete(url).success(function () {
|
||||
ctrl.getProductVersions();
|
||||
}).error(function (error) {
|
||||
raiseAlert('danger', 'Error: ', error.detail);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a POST request to the API server to add a new version for
|
||||
* the product.
|
||||
*/
|
||||
function addProductVersion() {
|
||||
var url = [refstackApiUrl, '/products/', ctrl.id,
|
||||
'/versions'].join('');
|
||||
ctrl.addVersionRequest = $http.post(url,
|
||||
{'version': ctrl.newProductVersion})
|
||||
.success(function (data) {
|
||||
ctrl.productVersions.push(data);
|
||||
ctrl.newProductVersion = '';
|
||||
ctrl.showNewVersionInput = false;
|
||||
}).error(function (error) {
|
||||
raiseAlert('danger', error.title, error.detail);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will switch public/private property of the product.
|
||||
*/
|
||||
@ -105,5 +160,90 @@
|
||||
raiseAlert('danger', 'Error: ', error.detail);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will open the modal that will allow a product version
|
||||
* to be managed.
|
||||
*/
|
||||
function openVersionModal(version) {
|
||||
$uibModal.open({
|
||||
templateUrl: '/components/products/partials' +
|
||||
'/versionsModal.html',
|
||||
backdrop: true,
|
||||
windowClass: 'modal',
|
||||
animation: true,
|
||||
controller: 'ProductVersionModalController as modal',
|
||||
size: 'lg',
|
||||
resolve: {
|
||||
version: function () {
|
||||
return version;
|
||||
},
|
||||
parent: function () {
|
||||
return ctrl;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('refstackApp')
|
||||
.controller('ProductVersionModalController',
|
||||
ProductVersionModalController);
|
||||
|
||||
ProductVersionModalController.$inject = [
|
||||
'$uibModalInstance', '$http', 'refstackApiUrl', 'version', 'parent'
|
||||
];
|
||||
|
||||
/**
|
||||
* Product Version Modal Controller
|
||||
* This controller is for the modal that appears if a user wants to
|
||||
* manage a product version.
|
||||
*/
|
||||
function ProductVersionModalController($uibModalInstance, $http,
|
||||
refstackApiUrl, version, parent) {
|
||||
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.version = version;
|
||||
ctrl.parent = parent;
|
||||
|
||||
ctrl.close = close;
|
||||
ctrl.deleteProductVersion = deleteProductVersion;
|
||||
ctrl.saveChanges = saveChanges;
|
||||
|
||||
/**
|
||||
* This function will close/dismiss the modal.
|
||||
*/
|
||||
function close() {
|
||||
$uibModalInstance.dismiss('exit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the parent function to delete a version, then close the modal.
|
||||
*/
|
||||
function deleteProductVersion() {
|
||||
ctrl.parent.deleteProductVersion(ctrl.version.id);
|
||||
ctrl.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* This will update the current version, saving changes.
|
||||
*/
|
||||
function saveChanges() {
|
||||
ctrl.showSuccess = false;
|
||||
ctrl.showError = false;
|
||||
var url = [
|
||||
refstackApiUrl, '/products/', ctrl.version.product_id,
|
||||
'/versions/', ctrl.version.id ].join('');
|
||||
var content = {'cpid': ctrl.version.cpid};
|
||||
$http.put(url, content).success(function() {
|
||||
ctrl.showSuccess = true;
|
||||
}).error(function(error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.error = error.detail;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
})();
|
||||
|
@ -19,7 +19,6 @@ RefStack
|
||||
<li ng-class="{ active: header.isActive('/about')}"><a ui-sref="about">About</a></li>
|
||||
<li ng-class="{ active: header.isActive('/guidelines')}"><a ui-sref="guidelines">DefCore Guidelines</a></li>
|
||||
<li ng-class="{ active: header.isActive('/community_results')}"><a ui-sref="communityResults">Community Results</a></li>
|
||||
<!---
|
||||
<li ng-class="{ active: header.isCatalogActive('public')}" class="dropdown" uib-dropdown>
|
||||
<a role="button" class="dropdown-toggle" uib-dropdown-toggle>
|
||||
Catalog <strong class="caret"></strong>
|
||||
@ -29,11 +28,9 @@ RefStack
|
||||
<li><a ui-sref="publicProducts">Products</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
--->
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ng-class="{ active: header.isActive('/user_results')}" ng-if="auth.isAuthenticated"><a ui-sref="userResults">My Results</a></li>
|
||||
<!---
|
||||
<li ng-if="auth.isAuthenticated" ng-class="{ active: header.isCatalogActive('user')}" class="dropdown" uib-dropdown>
|
||||
<a role="button" class="dropdown-toggle" uib-dropdown-toggle>
|
||||
My Catalog <strong class="caret"></strong>
|
||||
@ -43,7 +40,6 @@ RefStack
|
||||
<li><a ui-sref="userProducts">My Products</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
--->
|
||||
<li ng-class="{ active: header.isActive('/profile')}" ng-if="auth.isAuthenticated"><a ui-sref="profile">Profile</a></li>
|
||||
<li ng-if="auth.isAuthenticated"><a href="" ng-click="auth.doSignOut()">Sign Out</a></li>
|
||||
<li ng-if="!auth.isAuthenticated"><a href="" ng-click="auth.doSignIn()">Sign In / Sign Up</a></li>
|
||||
|
@ -1103,6 +1103,10 @@ describe('Refstack controllers', function () {
|
||||
'type': 0,
|
||||
'id': '1234',
|
||||
'description': 'some description'};
|
||||
var fakeVersionResp = [{'id': 'asdf',
|
||||
'cpid': null,
|
||||
'version': '1.0',
|
||||
'product_id': '1234'}];
|
||||
var fakeVendorResp = {'id': 'fake-org-id',
|
||||
'type': 3,
|
||||
'can_manage': true,
|
||||
@ -1128,6 +1132,8 @@ describe('Refstack controllers', function () {
|
||||
);
|
||||
$httpBackend.when('GET', fakeApiUrl +
|
||||
'/products/1234').respond(fakeProdResp);
|
||||
$httpBackend.when('GET', fakeApiUrl +
|
||||
'/products/1234/versions').respond(fakeVersionResp);
|
||||
$httpBackend.when('GET', fakeApiUrl +
|
||||
'/vendors/fake-org-id').respond(fakeVendorResp);
|
||||
}));
|
||||
@ -1144,6 +1150,16 @@ describe('Refstack controllers', function () {
|
||||
expect(ctrl.vendor).toEqual(fakeVendorResp);
|
||||
});
|
||||
|
||||
it('should have a function to get a list of product versions',
|
||||
function () {
|
||||
$httpBackend
|
||||
.expectGET(fakeApiUrl + '/products/1234/versions')
|
||||
.respond(200, fakeVersionResp);
|
||||
ctrl.getProductVersions();
|
||||
$httpBackend.flush();
|
||||
expect(ctrl.productVersions).toEqual(fakeVersionResp);
|
||||
});
|
||||
|
||||
it('should have a function to delete a product',
|
||||
function () {
|
||||
$httpBackend.expectDELETE(fakeApiUrl + '/products/1234')
|
||||
@ -1153,6 +1169,26 @@ describe('Refstack controllers', function () {
|
||||
expect(fakeWindow.location.href).toEqual('/');
|
||||
});
|
||||
|
||||
it('should have a function to delete a product version',
|
||||
function () {
|
||||
$httpBackend
|
||||
.expectDELETE(fakeApiUrl + '/products/1234/versions/abc')
|
||||
.respond(204, '');
|
||||
ctrl.deleteProductVersion('abc');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should have a function to add a product version',
|
||||
function () {
|
||||
ctrl.newProductVersion = 'abc';
|
||||
$httpBackend.expectPOST(
|
||||
fakeApiUrl + '/products/1234/versions',
|
||||
{version: 'abc'})
|
||||
.respond(200, {'id': 'foo'});
|
||||
ctrl.addProductVersion();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should have a function to switch the publicity of a project',
|
||||
function () {
|
||||
ctrl.product = {'public': true};
|
||||
@ -1162,5 +1198,55 @@ describe('Refstack controllers', function () {
|
||||
ctrl.switchProductPublicity();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should have a method to open a modal for version management',
|
||||
function () {
|
||||
var modal;
|
||||
inject(function ($uibModal) {
|
||||
modal = $uibModal;
|
||||
});
|
||||
spyOn(modal, 'open');
|
||||
ctrl.openVersionModal();
|
||||
expect(modal.open).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProductVersionModalController', function() {
|
||||
|
||||
var ctrl, modalInstance, state, parent;
|
||||
var fakeVersion = {'id': 'asdf', 'cpid': null,
|
||||
'version': '1.0','product_id': '1234'};
|
||||
|
||||
beforeEach(inject(function ($controller) {
|
||||
modalInstance = {
|
||||
dismiss: jasmine.createSpy('modalInstance.dismiss')
|
||||
};
|
||||
parent = {
|
||||
deleteProductVersion: jasmine.createSpy('deleteProductVersion')
|
||||
};
|
||||
ctrl = $controller('ProductVersionModalController',
|
||||
{$uibModalInstance: modalInstance, $state: state,
|
||||
version: fakeVersion, parent: parent}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should have a function to prompt a version deletion',
|
||||
function () {
|
||||
ctrl.deleteProductVersion();
|
||||
expect(parent.deleteProductVersion)
|
||||
.toHaveBeenCalledWith('asdf');
|
||||
expect(modalInstance.dismiss).toHaveBeenCalledWith('exit');
|
||||
});
|
||||
|
||||
it('should have a function to save changes',
|
||||
function () {
|
||||
ctrl.version.cpid = 'some-cpid';
|
||||
var expectedContent = { 'cpid': 'some-cpid'};
|
||||
$httpBackend.expectPUT(
|
||||
fakeApiUrl + '/products/1234/versions/asdf',
|
||||
expectedContent).respond(200, '');
|
||||
ctrl.saveChanges();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user