Some project ui/ux updates
- Collapsed edit/list/overview project into one view much like story detail. - Removed the "Project detail" prefix from the detail page. - Removed the "Project name" header from the list. - Removed extraneous views. - Added a permission resolver that only returns the value of a permission rather than blocking the route resolution. Change-Id: I026aad56d3be2882e5a9b99a8c738634e6c6a1dc
This commit is contained in:
parent
235444abb6
commit
16f4ee00c7
@ -49,5 +49,28 @@ angular.module('sb.auth').constant('PermissionResolver',
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves the value of the provided permission.
|
||||
*/
|
||||
resolvePermission: function (permName) {
|
||||
'use strict';
|
||||
|
||||
return function ($q, $log, PermissionManager) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
PermissionManager.resolve(permName).then(
|
||||
function (value) {
|
||||
deferred.resolve(value);
|
||||
},
|
||||
function () {
|
||||
deferred.resolve(false);
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
}
|
||||
});
|
||||
|
@ -28,7 +28,8 @@
|
||||
* seconds. 3 is preferable.
|
||||
*/
|
||||
angular.module('sb.projects').controller('ProjectDetailController',
|
||||
function ($scope, $state, $stateParams, Project, Story) {
|
||||
function ($scope, $state, $stateParams, Project, Story, Session,
|
||||
isSuperuser) {
|
||||
'use strict';
|
||||
|
||||
// Parse the ID
|
||||
@ -43,15 +44,6 @@ angular.module('sb.projects').controller('ProjectDetailController',
|
||||
*/
|
||||
$scope.project = {};
|
||||
|
||||
/**
|
||||
* The count of stories for this project.
|
||||
*
|
||||
* TODO(krotscheck): Once we have proper paging requests working,
|
||||
* this should become a count-only request, so we can delegate project
|
||||
* story searches to the ProjectStoryListController.
|
||||
*/
|
||||
$scope.projectStoryCount = 0;
|
||||
|
||||
/**
|
||||
* UI flag for when we're initially loading the view.
|
||||
*
|
||||
@ -84,40 +76,50 @@ angular.module('sb.projects').controller('ProjectDetailController',
|
||||
$scope.isUpdating = false;
|
||||
}
|
||||
|
||||
|
||||
// Sanity check, do we actually have an ID? (zero is falsy)
|
||||
if (!id && id !== 0) {
|
||||
// We should never reach this, however that logic lives outside
|
||||
// of this controller which could be unknowningly refactored.
|
||||
$scope.error = {
|
||||
error: true,
|
||||
error_code: 404,
|
||||
error_message: 'You did not provide a valid ID.'
|
||||
};
|
||||
/**
|
||||
* Resets our loading flags.
|
||||
*/
|
||||
function handleServiceSuccess() {
|
||||
$scope.isLoading = false;
|
||||
} else {
|
||||
// We've got an ID, so let's load it...
|
||||
$scope.isUpdating = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the project
|
||||
*/
|
||||
function loadProject() {
|
||||
Project.read(
|
||||
{'id': id},
|
||||
function (result) {
|
||||
// We've got a result, assign it to the view and unset our
|
||||
// loading flag.
|
||||
$scope.project = result;
|
||||
$scope.isLoading = false;
|
||||
},
|
||||
handleServiceError
|
||||
);
|
||||
// Load the count of stories while we're at it...
|
||||
Story.query({project_id: id},
|
||||
function (result, headers) {
|
||||
// Only extract the total header...
|
||||
$scope.projectStoryCount =
|
||||
headers('X-List-Total') || result.length;
|
||||
|
||||
handleServiceSuccess();
|
||||
},
|
||||
handleServiceError
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the form back.
|
||||
*/
|
||||
$scope.cancel = function () {
|
||||
loadProject();
|
||||
$scope.showEditForm = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle/display the edit form
|
||||
*/
|
||||
$scope.toggleEditMode = function () {
|
||||
if (isSuperuser) {
|
||||
$scope.showEditForm = !$scope.showEditForm;
|
||||
} else {
|
||||
$scope.showEditForm = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scope method, invoke this when you want to update the project.
|
||||
*/
|
||||
@ -131,25 +133,12 @@ angular.module('sb.projects').controller('ProjectDetailController',
|
||||
function () {
|
||||
// Unset our loading flag and navigate to the detail view.
|
||||
$scope.isUpdating = false;
|
||||
$state.go('project.detail', {id: $scope.project.id});
|
||||
$scope.showEditForm = false;
|
||||
handleServiceSuccess();
|
||||
},
|
||||
handleServiceError
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete method.
|
||||
*/
|
||||
$scope.remove = function () {
|
||||
// Set our progress flags and clear previous error conditions.
|
||||
$scope.isUpdating = true;
|
||||
$scope.error = {};
|
||||
|
||||
$scope.project.$delete(
|
||||
function () {
|
||||
$state.go('project.list');
|
||||
},
|
||||
handleServiceError
|
||||
);
|
||||
};
|
||||
loadProject();
|
||||
});
|
||||
|
@ -19,24 +19,24 @@
|
||||
* creation and management of projects.
|
||||
*/
|
||||
angular.module('sb.projects',
|
||||
['ui.router', 'sb.services', 'sb.util', 'sb.auth'])
|
||||
['ui.router', 'sb.services', 'sb.util', 'sb.auth'])
|
||||
.config(function ($stateProvider, $urlRouterProvider, SessionResolver,
|
||||
PermissionResolver) {
|
||||
'use strict';
|
||||
|
||||
// Routing Defaults.
|
||||
$urlRouterProvider.when('/project', '/project/list');
|
||||
$urlRouterProvider.when('/project/{id:[0-9]+}',
|
||||
function ($match) {
|
||||
return '/project/' + $match.id + '/overview';
|
||||
});
|
||||
|
||||
// Set our page routes.
|
||||
$stateProvider
|
||||
.state('project', {
|
||||
abstract: true,
|
||||
url: '/project',
|
||||
template: '<div ui-view></div>'
|
||||
template: '<div ui-view></div>',
|
||||
resolve: {
|
||||
isSuperuser: PermissionResolver
|
||||
.resolvePermission('is_superuser', true)
|
||||
}
|
||||
})
|
||||
.state('project.list', {
|
||||
url: '/list',
|
||||
@ -44,38 +44,10 @@ angular.module('sb.projects',
|
||||
controller: 'ProjectListController'
|
||||
})
|
||||
.state('project.detail', {
|
||||
abstract: true,
|
||||
url: '/{id:[0-9]+}',
|
||||
templateUrl: 'app/templates/project/detail.html',
|
||||
controller: 'ProjectDetailController'
|
||||
})
|
||||
.state('project.detail.overview', {
|
||||
url: '/overview',
|
||||
templateUrl: 'app/templates/project/overview.html'
|
||||
})
|
||||
.state('project.detail.edit', {
|
||||
url: '/edit',
|
||||
templateUrl: 'app/templates/project/edit.html',
|
||||
resolve: {
|
||||
isLoggedIn: SessionResolver.requireLoggedIn,
|
||||
isSuperuser: PermissionResolver
|
||||
.requirePermission('is_superuser', true)
|
||||
}
|
||||
}).
|
||||
state('project.detail.delete', {
|
||||
url: '/delete',
|
||||
templateUrl: 'app/templates/project/delete.html',
|
||||
resolve: {
|
||||
isLoggedIn: SessionResolver.requireLoggedIn,
|
||||
isSuperuser: PermissionResolver
|
||||
.requirePermission('is_superuser', true)
|
||||
}
|
||||
})
|
||||
.state('project.detail.stories', {
|
||||
url: '/stories',
|
||||
templateUrl: 'app/templates/project/stories.html',
|
||||
controller: 'ProjectStoryListController'
|
||||
})
|
||||
.state('project.new', {
|
||||
url: '/new',
|
||||
templateUrl: 'app/templates/project/new.html',
|
||||
|
@ -1,34 +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="row">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<h2 class="text-danger text-center">
|
||||
Are you certain that you want to delete this project?
|
||||
</h2>
|
||||
|
||||
<p class="text-center lead">
|
||||
This will set the project to a "deleted" state, and any stories and
|
||||
tasks will no longer be visible.
|
||||
</p>
|
||||
|
||||
<div class="text-center">
|
||||
<a href="" class="btn btn-danger" ng-click="remove()">
|
||||
Remove this project
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -13,47 +13,143 @@
|
||||
~ License for the specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
<div class="container" ng-show="isLoading">
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<p class="text-center">
|
||||
<i class="fa fa-refresh fa-spin fa-lg"></i>
|
||||
</p>
|
||||
<div ng-include
|
||||
class="col-xs-12"
|
||||
src="'/inline/project_detail.html'"
|
||||
ng-hide="showEditForm">
|
||||
</div>
|
||||
<div ng-include
|
||||
src="'/inline/project_detail_form.html'"
|
||||
ng-show="showEditForm">
|
||||
</div>
|
||||
<div ng-include src="'/inline/story_list.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" ng-hide="isLoading">
|
||||
<div class="row">
|
||||
<h1 class="no-border no-margin-bottom">Project detail:
|
||||
|
||||
<!-- Template for the header and description -->
|
||||
<script type="text/ng-template" id="/inline/project_detail.html">
|
||||
<h1>
|
||||
<span ng-show="project.name">
|
||||
{{project.name}}
|
||||
</h1>
|
||||
<ul class="nav nav-tabs nav-tabs-down nav-thick">
|
||||
<li active-path="^\/project\/[0-9]+\/overview.*">
|
||||
<a href="#!/project/{{project.id}}/overview">
|
||||
Overview
|
||||
</a>
|
||||
</li>
|
||||
<li active-path="^\/project\/[0-9]+\/stories.*">
|
||||
<a href="#!/project/{{project.id}}/stories">
|
||||
Stories
|
||||
<span ng-show="!!projectStoryCount">
|
||||
({{projectStoryCount}})
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li active-path="^\/project\/[0-9]+\/edit.*"
|
||||
permission="is_superuser">
|
||||
<a href="#!/project/{{project.id}}/edit">
|
||||
Edit
|
||||
</a>
|
||||
</li>
|
||||
<li active-path="^\/project\/[0-9]+\/delete.*"
|
||||
permission="is_superuser">
|
||||
<a href="#!/project/{{project.id}}/delete">
|
||||
Delete
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
</span>
|
||||
<em ng-hide="project.name" class="text-muted">
|
||||
No title
|
||||
</em>
|
||||
<small ng-show="isLoggedIn">
|
||||
<a href="" ng-click="toggleEditMode()" permission="is_superuser">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
</small>
|
||||
</h1>
|
||||
<p>
|
||||
<span ng-show="project.description"
|
||||
class="honor-carriage-return">{{project.description}}
|
||||
</span>
|
||||
<em ng-hide="project.description" class="text-muted">
|
||||
No description provided
|
||||
</em>
|
||||
</p>
|
||||
</script>
|
||||
|
||||
|
||||
<!-- Template for the header and description -->
|
||||
<script type="text/ng-template" id="/inline/project_detail_form.html">
|
||||
<br/>
|
||||
<form name="projectForm">
|
||||
<div class="form-group">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="project.name"
|
||||
required
|
||||
ng-disabled="isUpdating"
|
||||
placeholder="Project Name">
|
||||
</input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<textarea placeholder="Enter a project description here"
|
||||
class="form-control"
|
||||
rows="3"
|
||||
required
|
||||
ng-disabled="isUpdating"
|
||||
ng-model="project.description">
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
<div class="pull-right">
|
||||
<div class="btn" ng-show="isUpdating">
|
||||
<i class="fa fa-spinner fa-lg fa-spin"></i>
|
||||
</div>
|
||||
<button type="button"
|
||||
class="btn btn-primary"
|
||||
ng-click="update()"
|
||||
ng-disabled="!projectForm.$valid">
|
||||
Save
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-default"
|
||||
ng-click="cancel()">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr/>
|
||||
</script>
|
||||
|
||||
<!-- Template for the task list -->
|
||||
<script type="text/ng-template" id="/inline/story_list.html">
|
||||
<div ng-controller="ProjectStoryListController">
|
||||
<div class="col-xs-12">
|
||||
<a href=""
|
||||
class="btn btn-link pull-right"
|
||||
ng-click="newStory()"
|
||||
ng-show="isLoggedIn">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add story
|
||||
</a>
|
||||
<ul class="nav nav-tabs clearfix">
|
||||
<li ng-class="{'active': filter == 'active'}">
|
||||
<a href=""
|
||||
ng-click="setFilter('active')">Active</a>
|
||||
</li>
|
||||
<li ng-class="{'active': filter == 'merged'}">
|
||||
<a href=""
|
||||
ng-click="setFilter('merged')">Merged</a>
|
||||
</li>
|
||||
<li ng-class="{'active': filter == 'invalid'}">
|
||||
<a href=""
|
||||
ng-click="setFilter('invalid')">Invalid</a>
|
||||
</li>
|
||||
</ul>
|
||||
<table class="table table-striped"
|
||||
ng-hide="isSearching || stories.length == 0">
|
||||
<tbody>
|
||||
<tr ng-repeat="story in stories"
|
||||
ng-controller="StoryListItemController"
|
||||
ng-include="'app/templates/story/story_list_item.html'">
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div ng-show="isSearching">
|
||||
<hr/>
|
||||
<p class="text-center">
|
||||
<i class="fa fa-refresh fa-spin fa-lg"></i>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p ng-show="!isSearching && stories.length == 0"
|
||||
class="text-center text-warning">
|
||||
<br/>
|
||||
<em> We were unable to find any stories.
|
||||
Perhaps you would like to create one?</em>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ui-view></div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
@ -1,64 +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="row">
|
||||
<div class="col-xs-12">
|
||||
<form class="form-horizontal" role="form" name="projectForm">
|
||||
<div class="form-group">
|
||||
<label for="name" class="col-sm-2 control-label">
|
||||
Project Name:
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<input id="name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="project.name"
|
||||
required
|
||||
placeholder="Project Name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description"
|
||||
class="col-sm-2 control-label">
|
||||
Project Description
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea id="description"
|
||||
class="form-control"
|
||||
ng-model="project.description"
|
||||
required
|
||||
placeholder="A brief project description">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="button"
|
||||
ng-click="update()"
|
||||
class="btn btn-primary"
|
||||
ng-disabled="!projectForm.$valid">
|
||||
Save Changes
|
||||
</button>
|
||||
<a href="#!/project/list"
|
||||
class="btn btn-default">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@ -46,9 +46,7 @@
|
||||
ng-hide="isSearching || projects.length == 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-sm-10">
|
||||
<small>Project Name</small>
|
||||
</th>
|
||||
<th class="col-sm-10"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -1,28 +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="row">
|
||||
<div class="col-xs-12">
|
||||
<p ng-show="project.description"
|
||||
class="honor-carriage-return">{{project.description}}
|
||||
</p>
|
||||
|
||||
<p ng-hide="project.description"
|
||||
class="text-muted text-center">
|
||||
No description available.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
@ -1,83 +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="col-sm-3 col-xs-10">
|
||||
<div class="btn-group btn-group-justified">
|
||||
<div class="form-group has-feedback has-feedback-no-label">
|
||||
<input type="text"
|
||||
class="form-control input-sm"
|
||||
placeholder="Search Stories"
|
||||
ng-disabled="isSearching"
|
||||
ng-enter="search()"
|
||||
ng-model="searchQuery"/>
|
||||
<span class="fa fa-search form-control-feedback"
|
||||
ng-hide="isSearching"></span>
|
||||
<span class="fa fa-refresh fa-spin form-control-feedback"
|
||||
ng-show="isSearching"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-9 col-xs-2">
|
||||
<a href=""
|
||||
ng-click="newStory()"
|
||||
class="pull-right btn btn-default btn-sm">
|
||||
<i class="fa fa-plus"></i>
|
||||
<span class="hidden-xs">New Story</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12">
|
||||
<ul class="nav nav-tabs clearfix">
|
||||
<li ng-class="{'active': filter == 'active'}">
|
||||
<a href=""
|
||||
ng-click="setFilter('active')">Active</a>
|
||||
</li>
|
||||
<li ng-class="{'active': filter == 'merged'}">
|
||||
<a href=""
|
||||
ng-click="setFilter('merged')">Merged</a>
|
||||
</li>
|
||||
<li ng-class="{'active': filter == 'invalid'}">
|
||||
<a href=""
|
||||
ng-click="setFilter('invalid')">Invalid</a>
|
||||
</li>
|
||||
</ul>
|
||||
<table class="table table-striped"
|
||||
ng-hide="isSearching || stories.length == 0">
|
||||
<tbody>
|
||||
<tr ng-repeat="story in stories"
|
||||
ng-controller="StoryListItemController"
|
||||
ng-include="'app/templates/story/story_list_item.html'">
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div ng-show="!isSearching && stories.length == 0"
|
||||
class="col-sm-12 text-center text-warning">
|
||||
<br/>
|
||||
<p> We were unable to find any stories in this project.
|
||||
Perhaps you would like to create one?</p>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<div ng-show="isSearching">
|
||||
<br/>
|
||||
<p class="text-center">
|
||||
<i class="fa fa-refresh fa-spin fa-lg"></i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user