Merge "Refactor Project Group editing to match other objects"

This commit is contained in:
Zuul 2018-09-09 17:25:45 +00:00 committed by Gerrit Code Review
commit 565e42efac
5 changed files with 356 additions and 418 deletions

View File

@ -20,7 +20,8 @@
*/
angular.module('sb.project_group').controller('ProjectGroupDetailController',
function ($scope, $stateParams, projectGroup, Story, Project,
Preference, SubscriptionList, CurrentUser, Subscription) {
Preference, SubscriptionList, CurrentUser, Subscription,
$q, ProjectGroupItem, ArrayUtil, $log) {
'use strict';
var projectPageSize = Preference.get(
@ -43,6 +44,12 @@ angular.module('sb.project_group').controller('ProjectGroupDetailController',
$scope.projects = [];
$scope.isSearchingProjects = false;
$scope.editMode = false;
$scope.toggleEdit = function() {
$scope.editMode = !$scope.editMode;
};
/**
* List the projects in this Project Group
*/
@ -205,6 +212,194 @@ angular.module('sb.project_group').controller('ProjectGroupDetailController',
};
/**
* UI flag, are we saving?
*
* @type {boolean}
*/
$scope.isSaving = false;
/**
* Project typeahead search method.
*/
$scope.searchProjects = function (value) {
var deferred = $q.defer();
Project.browse({name: value, limit: 10},
function (results) {
// Dedupe the results.
var idxList = [];
for (var i = 0; i < $scope.projects.length; i++) {
var project = $scope.projects[i];
if (!!project) {
idxList.push(project.id);
}
}
for (var j = results.length - 1; j >= 0; j--) {
var resultId = results[j].id;
if (idxList.indexOf(resultId) > -1) {
results.splice(j, 1);
}
}
deferred.resolve(results);
},
function (error) {
$log.error(error);
deferred.resolve([]);
});
return deferred.promise;
};
/**
* Formats the project name.
*/
$scope.formatProjectName = function (model) {
if (!!model) {
return model.name;
}
return '';
};
/**
* Remove a project from the list
*/
$scope.removeProject = function (index) {
$scope.projects.splice(index, 1);
};
/**
* Save the project and the associated groups
*/
$scope.save = function () {
$scope.isSaving = true;
ProjectGroupItem.browse({projectGroupId: $scope.projectGroup.id},
function(results) {
var loadedIds = [];
results.forEach(function (project) {
loadedIds.push(project.id);
});
var promises = [];
// Get the desired ID's.
var desiredIds = [];
$scope.projects.forEach(function (project) {
desiredIds.push(project.id);
});
// Intersect loaded vs. current to get a list of project
// reference to delete.
var idsToDelete = ArrayUtil.difference(
loadedIds, desiredIds);
idsToDelete.forEach(function (id) {
// Get a deferred promise...
var removeProjectDeferred = $q.defer();
// Construct the item.
var item = new ProjectGroupItem({
id: id,
projectGroupId: projectGroup.id
});
// Delete the item.
item.$delete(function (result) {
removeProjectDeferred.resolve(result);
},
function (error) {
removeProjectDeferred.reject(error);
}
);
promises.push(removeProjectDeferred.promise);
});
// Intersect current vs. loaded to get a list of project
// reference to add.
var idsToAdd = ArrayUtil.difference(desiredIds, loadedIds);
idsToAdd.forEach(function (id) {
// Get a deferred promise...
var addProjectDeferred = $q.defer();
// Construct the item.
var item = new ProjectGroupItem({
id: id,
projectGroupId: projectGroup.id
});
// Delete the item.
item.$create(function (result) {
addProjectDeferred.resolve(result);
},
function (error) {
addProjectDeferred.reject(error);
}
);
promises.push(addProjectDeferred.promise);
});
// Save the project group itself.
var deferred = $q.defer();
promises.push(deferred.promise);
$scope.projectGroup.$update(function (success) {
deferred.resolve(success);
}, function (error) {
$log.error(error);
deferred.reject(error);
});
// Roll all the promises into one big happy promise.
$q.all(promises).then(
function () {
$scope.editMode = false;
$scope.isSaving = false;
},
function (error) {
$log.error(error);
}
);
}
);
};
/**
* Add project.
*/
$scope.addProject = function () {
$scope.projects.push({});
};
/**
* Insert item into the project list.
*/
$scope.selectNewProject = function (index, model) {
// Put our model into the array
$scope.projects[index] = model;
};
/**
* Check that we have valid projects on the list
*/
$scope.checkValidProjects = function () {
if ($scope.projects.length === 0) {
return false;
}
// check if projects contain a valid project_id
for (var i = 0; i < $scope.projects.length; i++) {
var project = $scope.projects[i];
if (!project.id) {
return false;
}
}
return true;
};
$scope.listProjects();
$scope.filterStories();

View File

@ -1,222 +0,0 @@
/*
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
*
* 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.
*/
/**
* New Project Group edit controller.
*/
angular.module('sb.project_group').controller('ProjectGroupEditController',
function ($q, $log, $scope, $state, projectGroup, projects, Project,
ProjectGroupItem, ArrayUtil) {
'use strict';
/**
* The project group we're editing. Resolved by the route.
*/
$scope.projectGroup = projectGroup;
/**
* The list of projects in this group (Resolved by the route).
*/
$scope.projects = projects;
/**
* A collection of all project ID's that have been loaded on
* initialization. This list is used to determine the project member
* diff.
*/
var loadedIds = [];
$scope.projects.forEach(function (project) {
loadedIds.push(project.id);
});
/**
* UI flag, are we saving?
*
* @type {boolean}
*/
$scope.isSaving = false;
/**
* Project typeahead search method.
*/
$scope.searchProjects = function (value) {
var deferred = $q.defer();
Project.browse({name: value, limit: 10},
function (results) {
// Dedupe the results.
var idxList = [];
for (var i = 0; i < $scope.projects.length; i++) {
var project = $scope.projects[i];
if (!!project) {
idxList.push(project.id);
}
}
for (var j = results.length - 1; j >= 0; j--) {
var resultId = results[j].id;
if (idxList.indexOf(resultId) > -1) {
results.splice(j, 1);
}
}
deferred.resolve(results);
},
function (error) {
$log.error(error);
deferred.resolve([]);
});
return deferred.promise;
};
/**
* Formats the project name.
*/
$scope.formatProjectName = function (model) {
if (!!model) {
return model.name;
}
return '';
};
/**
* Remove a project from the list
*/
$scope.removeProject = function (index) {
$scope.projects.splice(index, 1);
};
/**
* Save the project and the associated groups
*/
$scope.save = function () {
$scope.isSaving = true;
var promises = [];
// Get the desired ID's.
var desiredIds = [];
$scope.projects.forEach(function (project) {
desiredIds.push(project.id);
});
// Intersect loaded vs. current to get a list of project
// reference to delete.
var idsToDelete = ArrayUtil.difference(loadedIds, desiredIds);
idsToDelete.forEach(function (id) {
// Get a deferred promise...
var removeProjectDeferred = $q.defer();
// Construct the item.
var item = new ProjectGroupItem({
id: id,
projectGroupId: projectGroup.id
});
// Delete the item.
item.$delete(function (result) {
removeProjectDeferred.resolve(result);
},
function (error) {
removeProjectDeferred.reject(error);
}
);
promises.push(removeProjectDeferred.promise);
});
// Intersect current vs. loaded to get a list of project
// reference to add.
var idsToAdd = ArrayUtil.difference(desiredIds, loadedIds);
idsToAdd.forEach(function (id) {
// Get a deferred promise...
var addProjectDeferred = $q.defer();
// Construct the item.
var item = new ProjectGroupItem({
id: id,
projectGroupId: projectGroup.id
});
// Delete the item.
item.$create(function (result) {
addProjectDeferred.resolve(result);
},
function (error) {
addProjectDeferred.reject(error);
}
);
promises.push(addProjectDeferred.promise);
});
// Save the project group itself.
var deferred = $q.defer();
promises.push(deferred.promise);
$scope.projectGroup.$update(function (success) {
deferred.resolve(success);
}, function (error) {
$log.error(error);
deferred.reject(error);
});
// Roll all the promises into one big happy promise.
$q.all(promises).then(
function () {
$state.go('sb.project_group.list', {});
},
function (error) {
$log.error(error);
}
);
};
/**
* Add project.
*/
$scope.addProject = function () {
$scope.projects.push({});
};
/**
* Insert item into the project list.
*/
$scope.selectNewProject = function (index, model) {
// Put our model into the array
$scope.projects[index] = model;
};
/**
* Check that we have valid projects on the list
*/
$scope.checkValidProjects = function () {
if ($scope.projects.length === 0) {
return false;
}
// check if projects contain a valid project_id
for (var i = 0; i < $scope.projects.length; i++) {
var project = $scope.projects[i];
if (!project.id) {
return false;
}
}
return true;
};
});

View File

@ -58,21 +58,6 @@ angular.module('sb.project_group',
deferred.reject(error);
});
return deferred.promise;
}
}
})
.state('sb.project_group.edit', {
url: '/{id:[0-9]+}/edit',
templateUrl: 'app/project_group/template/edit.html',
controller: 'ProjectGroupEditController',
resolve: {
projectGroup: function ($stateParams, ProjectGroup) {
return ProjectGroup.get({id: $stateParams.id}).$promise;
},
projects: function ($stateParams, ProjectGroupItem) {
return ProjectGroupItem.browse(
{projectGroupId: $stateParams.id}
).$promise;
},
isSuperuser: PermissionResolver
.requirePermission('is_superuser', true)

View File

@ -6,7 +6,7 @@
<i class="fa fa-sb-project-group"></i>
{{projectGroup.title}}
<small>
<a href="#!/project_group/{{projectGroup.id}}/edit" permission="is_superuser">
<a href="" ng-click="toggleEdit()" permission="is_superuser">
<i class="fa fa-pencil-alt"></i>
</a>
<subscribe resource="project_group"
@ -18,7 +18,8 @@
</h1>
</div>
</div>
<div class="row">
<div ng-include src="'/inline/project_group_edit.html'" ng-if="editMode"></div>
<div class="row" ng-if="!editMode">
<div class="col-xs-2 text-center text-muted">
<span class="hidden-xs">
<i class="fa fa-3x fa-sb-project"></i>
@ -57,7 +58,7 @@
</table>
</div>
</div>
<div class="row">
<div class="row" ng-if="!editMode">
<div class="col-xs-12">
<hr/>
</div>
@ -129,3 +130,159 @@
</div>
</div>
</div>
<script type="text/ng-template" id="/inline/project_group_edit.html">
<div class="row">
<div class="col-xs-12">
<form class="form-horizontal"
role="form"
name="projectGroupForm"
ng-class="{'has-error': projectGroupForm.title.$invalid}">
<div class="form-group">
<label for="title" class="col-sm-2 control-label">
Title:
</label>
<div class="col-sm-10">
<input id="title"
name="title"
type="text"
class="form-control"
ng-model="projectGroup.title"
required
ng-disabled="isSaving"
maxlength="255"
ng-minlength="3"
ng-pattern="PROJECT_NAME_REGEX"
placeholder="Project Group Title">
</input>
<div class="help-block text-danger"
ng-show="projectGroupForm.title.$invalid">
<span ng-show="projectGroupForm.title.$error.required">
A project group title is required.
</span>
<span ng-show="projectGroupForm.title.$error.pattern">
A project group title must begin with a letter, and may only
contain letters, numbers, forward slashes, periods, and
dashes. It should not start or end with a separator and
must not contain two or more sequential separators.
</span>
<span ng-show="projectGroupForm.title.$error.minlength">
A project group title must have at least 3 characters.
</span>
</div>
</div>
</div>
<div class="form-group">
<label for="name" class="col-sm-2 control-label">
URL:
</label>
<div class="col-sm-10">
<input id="name"
name="name"
type="text"
class="form-control"
ng-model="projectGroup.name"
required
ng-disabled="isSaving"
maxlength="100"
ng-minlength="3"
placeholder="URL Stub for the project group">
</input>
<div class="help-block text-danger"
ng-show="projectGroupForm.name.$invalid">
<span ng-show="projectGroupForm.name.$error.required">
A project group URL is required.
</span>
<span ng-show="projectGroupForm.name.$error.minlength">
A project group URL must have at least 3 characters.
</span>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-2 text-right">
<label class="control-label">
Projects:
</label>
</div>
<div class="col-sm-10">
<form role="form" name="projectsForm">
<table class="table table-striped table-outlined">
<tbody>
<tr ng-repeat="(index, project) in projects"
ng-include
src="'/inline/project_row.html'">
</tr>
</tbody>
</table>
</form>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-xs-offset-2">
<button type="button"
class="btn btn-default"
ng-disabled="isSaving"
ng-click="addProject()">
&plus;
Add another project
</button>
</div>
<div class="col-xs-6 text-right">
<button type="button"
class="btn btn-primary"
ng-click="save()"
ng-disabled="!projectGroupForm.$valid || !projectsForm.$valid || !checkValidProjects() || isSaving">
Save
</button>
<a href=""
ng-click="toggleEdit()"
ng-disabled="isSaving"
class="btn btn-default">
Cancel
</a>
</div>
</div>
</script>
<!-- Template for story metadata -->
<script type="text/ng-template" id="/inline/project_row.html">
<td class="col-xs-11">
<div class="has-feedback has-feedback-no-label">
<input id="project"
type="text"
placeholder="Select a Project"
autocomplete="off"
required
ng-model="project"
typeahead-wait-ms="200"
typeahead-editable="false"
typeahead="project as project.name for project
in searchProjects($viewValue)"
typeahead-loading="loadingProjects"
typeahead-input-formatter="formatProjectName($model)"
typeahead-on-select="selectNewProject(index, $model)"
class="form-control input-sm"
ng-disabled="isSaving"
/>
<span class="form-control-feedback text-muted
form-control-feedback-sm">
<i class="fa fa-sync fa-spin" ng-show="loadingProjects"></i>
<i class="fa fa-search" ng-hide="loadingProjects"></i>
</span>
</div>
</td>
<th class="col-xs-1"
ng-show="projects.length > 1">
<button type="button" class="close"
ng-click="removeProject(index)">
&times;
</button>
</th>
</script>

View File

@ -1,177 +0,0 @@
<!--
~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
~
~ 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.
-->
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<h1><i class="fa fa-sb-project-group"></i> {{projectGroup.title}}
</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<form class="form-horizontal"
role="form"
name="projectGroupForm"
ng-class="{'has-error': projectGroupForm.title.$invalid}">
<div class="form-group">
<label for="title" class="col-sm-2 control-label">
Title:
</label>
<div class="col-sm-10">
<input id="title"
name="title"
type="text"
class="form-control"
ng-model="projectGroup.title"
required
ng-disabled="isSaving"
maxlength="255"
ng-minlength="3"
ng-pattern="PROJECT_NAME_REGEX"
placeholder="Project Group Title">
</input>
<div class="help-block text-danger"
ng-show="projectGroupForm.title.$invalid">
<span ng-show="projectGroupForm.title.$error.required">
A project group title is required.
</span>
<span ng-show="projectGroupForm.title.$error.pattern">
A project group title must begin with a letter, and may only
contain letters, numbers, forward slashes, periods, and
dashes. It should not start or end with a separator and
must not contain two or more sequential separators.
</span>
<span ng-show="projectGroupForm.title.$error.minlength">
A project group title must have at least 3 characters.
</span>
</div>
</div>
</div>
<div class="form-group">
<label for="name" class="col-sm-2 control-label">
URL:
</label>
<div class="col-sm-10">
<input id="name"
name="name"
type="text"
class="form-control"
ng-model="projectGroup.name"
required
ng-disabled="isSaving"
maxlength="100"
ng-minlength="3"
placeholder="URL Stub for the project group">
</input>
<div class="help-block text-danger"
ng-show="projectGroupForm.name.$invalid">
<span ng-show="projectGroupForm.name.$error.required">
A project group URL is required.
</span>
<span ng-show="projectGroupForm.name.$error.minlength">
A project group URL must have at least 3 characters.
</span>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-2 text-right">
<label class="control-label">
Projects:
</label>
</div>
<div class="col-sm-10">
<form role="form" name="projectsForm">
<table class="table table-striped table-outlined">
<tbody>
<tr ng-repeat="(index, project) in projects"
ng-include
src="'/inline/project_row.html'">
</tr>
</tbody>
</table>
</form>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-xs-offset-2">
<button type="button"
class="btn btn-default"
ng-disabled="isSaving"
ng-click="addProject()">
&plus;
Add another project
</button>
</div>
<div class="col-xs-6 text-right">
<button type="button"
class="btn btn-primary"
ng-click="save()"
ng-disabled="!projectGroupForm.$valid || !projectsForm.$valid || !checkValidProjects() || isSaving">
Save
</button>
<a href="#!/project_group"
ng-disabled="isSaving"
class="btn btn-default">
Cancel
</a>
</div>
</div>
</div>
<!-- Template for story metadata -->
<script type="text/ng-template" id="/inline/project_row.html">
<td class="col-xs-11">
<div class="has-feedback has-feedback-no-label">
<input id="project"
type="text"
placeholder="Select a Project"
autocomplete="off"
required
ng-model="project"
typeahead-wait-ms="200"
typeahead-editable="false"
typeahead="project as project.name for project
in searchProjects($viewValue)"
typeahead-loading="loadingProjects"
typeahead-input-formatter="formatProjectName($model)"
typeahead-on-select="selectNewProject(index, $model)"
class="form-control input-sm"
ng-disabled="isSaving"
/>
<span class="form-control-feedback text-muted
form-control-feedback-sm">
<i class="fa fa-sync fa-spin" ng-show="loadingProjects"></i>
<i class="fa fa-search" ng-hide="loadingProjects"></i>
</span>
</div>
</td>
<th class="col-xs-1"
ng-show="projects.length > 1">
<button type="button" class="close"
ng-click="removeProject(index)">
&times;
</button>
</th>
</script>