Refactor Project Group editing to match other objects
Currently project groups have a separate "edit" view, which makes it hard to route to them by name. This commit changes this so that editing is done in the detail view, as it is for other objects like Projects or Stories, in order to facilitate routing by name. Depends-On: https://review.openstack.org/#/c/590047/ Change-Id: I7f26838ce255268653cd2f912a4f7bb2a13d2816
This commit is contained in:
parent
e6bf69db1e
commit
a4614afd95
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
});
|
|
@ -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)
|
||||
|
|
|
@ -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()">
|
||||
+
|
||||
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)">
|
||||
×
|
||||
</button>
|
||||
</th>
|
||||
</script>
|
||||
|
|
|
@ -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()">
|
||||
+
|
||||
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)">
|
||||
×
|
||||
</button>
|
||||
</th>
|
||||
</script>
|
Loading…
Reference in New Issue