Merge "Refactor Project Group editing to match other objects"
This commit is contained in:
commit
565e42efac
|
@ -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