Merge "Complex priorities UI in stories"
This commit is contained in:
commit
661b1b961f
@ -30,7 +30,7 @@
|
|||||||
<i class="fa fa-caret-right"></i>
|
<i class="fa fa-caret-right"></i>
|
||||||
</a>
|
</a>
|
||||||
<br ng-if="!!minimalPager" />
|
<br ng-if="!!minimalPager" />
|
||||||
<span class="btn-group" dropdown>
|
<span ng-if="!!onPageSize" class="btn-group" dropdown>
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-xs btn-default"
|
<button class="btn btn-xs btn-default"
|
||||||
dropdown-toggle>
|
dropdown-toggle>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||||
|
* Copyright (c) 2016 Codethink Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
|
* 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
|
* not use this file except in compliance with the License. You may obtain
|
||||||
@ -23,7 +24,7 @@ angular.module('sb.story').controller('StoryDetailController',
|
|||||||
Story, Project, Branch, creator, tasks, Task, DSCacheFactory,
|
Story, Project, Branch, creator, tasks, Task, DSCacheFactory,
|
||||||
User, $q, storyboardApiBase, SessionModalService, moment,
|
User, $q, storyboardApiBase, SessionModalService, moment,
|
||||||
$document, $anchorScroll, $timeout, $location, currentUser,
|
$document, $anchorScroll, $timeout, $location, currentUser,
|
||||||
enableEditableComments, Tags) {
|
enableEditableComments, Tags, worklists) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var pageSize = Preference.get('story_detail_page_size');
|
var pageSize = Preference.get('story_detail_page_size');
|
||||||
@ -85,8 +86,7 @@ angular.module('sb.story').controller('StoryDetailController',
|
|||||||
story_id: $scope.story.id,
|
story_id: $scope.story.id,
|
||||||
branch_id: branch.id,
|
branch_id: branch.id,
|
||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
status: 'todo',
|
status: 'todo'
|
||||||
priority: 'medium'
|
|
||||||
});
|
});
|
||||||
$scope.projects[project.name]
|
$scope.projects[project.name]
|
||||||
.branchNames.push(branch.name);
|
.branchNames.push(branch.name);
|
||||||
@ -97,6 +97,61 @@ angular.module('sb.story').controller('StoryDetailController',
|
|||||||
|
|
||||||
angular.forEach(tasks, mapTaskToProject);
|
angular.forEach(tasks, mapTaskToProject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All worklists containing this story or tasks within it, with
|
||||||
|
* information about which task is relevant added.
|
||||||
|
*
|
||||||
|
* @type {[Worklist]}
|
||||||
|
*/
|
||||||
|
function setWorklists() {
|
||||||
|
function isNotArchived(card) {
|
||||||
|
return !card.archived;
|
||||||
|
}
|
||||||
|
|
||||||
|
var taskIds = $scope.tasks.map(function(task) {
|
||||||
|
return task.id;
|
||||||
|
});
|
||||||
|
for (var i = 0; i < worklists.length; i++) {
|
||||||
|
var worklist = worklists[i];
|
||||||
|
worklist.relatedItems = [];
|
||||||
|
worklist.items = worklist.items.filter(isNotArchived);
|
||||||
|
for (var j = 0; j < worklist.items.length; j++) {
|
||||||
|
var item = worklist.items[j];
|
||||||
|
if (item.item_type === 'story') {
|
||||||
|
if (item.item_id === story.id) {
|
||||||
|
worklist.relatedItems.push(item);
|
||||||
|
}
|
||||||
|
} else if (item.item_type === 'task') {
|
||||||
|
if (taskIds.indexOf(item.item_id) > -1) {
|
||||||
|
worklist.relatedItems.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.worklists = worklists.map(function(list) {
|
||||||
|
if (list.relatedItems.length > 0) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}).filter(function(list) { return list; });
|
||||||
|
}
|
||||||
|
|
||||||
|
setWorklists();
|
||||||
|
|
||||||
|
$scope.showWorklistsModal = function() {
|
||||||
|
var modalInstance = $modal.open({
|
||||||
|
templateUrl: 'app/stories/template/worklists.html',
|
||||||
|
controller: 'StoryWorklistsController',
|
||||||
|
resolve: {
|
||||||
|
worklists: function () {
|
||||||
|
return $scope.worklists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return the modal's promise.
|
||||||
|
return modalInstance.result;
|
||||||
|
};
|
||||||
|
|
||||||
// Load the preference for each display event.
|
// Load the preference for each display event.
|
||||||
function reloadPagePreferences() {
|
function reloadPagePreferences() {
|
||||||
TimelineEventTypes.forEach(function (type) {
|
TimelineEventTypes.forEach(function (type) {
|
||||||
@ -531,8 +586,7 @@ angular.module('sb.story').controller('StoryDetailController',
|
|||||||
*/
|
*/
|
||||||
$scope.newTask = new Task({
|
$scope.newTask = new Task({
|
||||||
story_id: $scope.story.id,
|
story_id: $scope.story.id,
|
||||||
status: 'todo',
|
status: 'todo'
|
||||||
priority: 'medium'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
33
src/app/stories/controller/story_worklists_controller.js
Normal file
33
src/app/stories/controller/story_worklists_controller.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Codethink Ltd.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modal to display worklists related to stories
|
||||||
|
*/
|
||||||
|
angular.module('sb.story').controller('StoryWorklistsController',
|
||||||
|
function($scope, $modalInstance, worklists) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close this modal.
|
||||||
|
*/
|
||||||
|
$scope.close = function () {
|
||||||
|
$modalInstance.dismiss('cancel');
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.worklists = worklists;
|
||||||
|
}
|
||||||
|
);
|
@ -75,6 +75,19 @@ angular.module('sb.story', ['ui.router', 'sb.services', 'sb.util',
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
return user;
|
return user;
|
||||||
|
},
|
||||||
|
worklists: function(Worklist, $stateParams, CurrentUser) {
|
||||||
|
var userPromise = CurrentUser.resolve();
|
||||||
|
|
||||||
|
return userPromise.then(function(user) {
|
||||||
|
return Worklist.browse({
|
||||||
|
story_id: $stateParams.storyId,
|
||||||
|
subscriber_id: user.id,
|
||||||
|
hide_lanes: ''
|
||||||
|
}).$promise;
|
||||||
|
}, function() {
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,6 +23,14 @@
|
|||||||
<strong>This story is private</strong>.
|
<strong>This story is private</strong>.
|
||||||
Edit this story to change the privacy settings.
|
Edit this story to change the privacy settings.
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="hidden-xs hidden-sm col-sm-3 pull-right"
|
||||||
|
ng-show="worklists.length > 0 && !showEditForm">
|
||||||
|
<div ng-include src="'/inline/worklists.html'"></div>
|
||||||
|
</div>
|
||||||
<div ng-include
|
<div ng-include
|
||||||
src="'/inline/story_detail.html'"
|
src="'/inline/story_detail.html'"
|
||||||
ng-hide="showEditForm">
|
ng-hide="showEditForm">
|
||||||
@ -31,6 +39,15 @@
|
|||||||
src="'/inline/story_detail_form.html'"
|
src="'/inline/story_detail_form.html'"
|
||||||
ng-show="showEditForm">
|
ng-show="showEditForm">
|
||||||
</div>
|
</div>
|
||||||
|
<hr class="visible-sm" />
|
||||||
|
<div class="col-xs-12 visible-sm"
|
||||||
|
ng-show="worklists.length > 0 && !showEditForm">
|
||||||
|
<div ng-include src="'/inline/worklists.html'"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
<hr/>
|
<hr/>
|
||||||
<div ng-include src="'/inline/task_list.html'"></div>
|
<div ng-include src="'/inline/task_list.html'"></div>
|
||||||
<hr/>
|
<hr/>
|
||||||
@ -257,6 +274,58 @@
|
|||||||
</form>
|
</form>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Template for the list of relevant worklists -->
|
||||||
|
<script type="text/ng-template" id="/inline/worklists.html">
|
||||||
|
<table class="table table-striped table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>In worklists you subscribed to</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="worklist in worklists.slice(0, 5)">
|
||||||
|
<td>
|
||||||
|
<a href="/#!/worklist/{{worklist.id}}">{{worklist.title}}</a>
|
||||||
|
<span class="text-muted pull-right" ng-show="worklist.automatic">
|
||||||
|
(automatic)
|
||||||
|
</span>
|
||||||
|
<div ng-click="showItems = !showItems">
|
||||||
|
<a href>
|
||||||
|
<i class="fa"
|
||||||
|
ng-class="{'fa-caret-right': !showItems,
|
||||||
|
'fa-caret-down': showItems}"></i>
|
||||||
|
<small>
|
||||||
|
{{showItems ? 'Hide' : 'Show'}} items...
|
||||||
|
</small>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-condensed subtable" ng-show="showItems">
|
||||||
|
<tr ng-repeat="item in worklist.relatedItems | orderBy: 'list_position'">
|
||||||
|
<td>
|
||||||
|
{{item.item_type | capitalize}} {{item.item_id}}
|
||||||
|
<span class="pull-right">
|
||||||
|
{{item.list_position + 1}} of {{worklist.items.length}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-if="worklists.length > 5">
|
||||||
|
<td class="text-center">
|
||||||
|
<a href ng-click="showWorklistsModal()">More...</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tbody ng-if="worklists.length < 1">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">No worklists related to this story</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Template for the tags list and controls -->
|
<!-- Template for the tags list and controls -->
|
||||||
<script type="text/ng-template" id="/inline/tags.html">
|
<script type="text/ng-template" id="/inline/tags.html">
|
||||||
<strong>Tags</strong>
|
<strong>Tags</strong>
|
||||||
@ -383,7 +452,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-3">
|
||||||
<user-typeahead
|
<user-typeahead
|
||||||
ng-model="task.assignee_id"
|
ng-model="task.assignee_id"
|
||||||
enabled="isLoggedIn"
|
enabled="isLoggedIn"
|
||||||
@ -400,13 +469,6 @@
|
|||||||
status="{{ task.status }}"
|
status="{{ task.status }}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-2">
|
|
||||||
<task-priority-dropdown
|
|
||||||
editable="{{isLoggedIn}}"
|
|
||||||
on-change="updateTask(task, 'priority', priority)"
|
|
||||||
priority="{{newTask.priority}}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- Review link should go here once implemented in the API -->
|
<!-- Review link should go here once implemented in the API -->
|
||||||
</div>
|
</div>
|
||||||
<div class="row button-row" ng-show="showDetail">
|
<div class="row button-row" ng-show="showDetail">
|
||||||
@ -523,7 +585,7 @@
|
|||||||
placeholder="Enter task name"
|
placeholder="Enter task name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-3">
|
||||||
<user-typeahead
|
<user-typeahead
|
||||||
auto-focus="false"
|
auto-focus="false"
|
||||||
ng-model="projects[name].branches[branchName].newTask.assignee_id"
|
ng-model="projects[name].branches[branchName].newTask.assignee_id"
|
||||||
@ -542,15 +604,8 @@
|
|||||||
status="{{projects[name].branches[branchName].newTask.status}}"
|
status="{{projects[name].branches[branchName].newTask.status}}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-2">
|
|
||||||
<task-priority-dropdown
|
|
||||||
editable="{{isLoggedIn}}"
|
|
||||||
on-change="updateTask(projects[name].branches[branchName].newTask, 'priority', priority)"
|
|
||||||
priority="{{projects[name].branches[branchName].newTask.priority}}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- Review link should go here once implemented in the API -->
|
<!-- Review link should go here once implemented in the API -->
|
||||||
<div class="col-xs-2 text-right">
|
<div class="col-xs-2 text-right col-xs-offset-1">
|
||||||
<button ng-click="createTask(projects[name].branches[branchName].newTask,
|
<button ng-click="createTask(projects[name].branches[branchName].newTask,
|
||||||
projects[name].branches[branchName])"
|
projects[name].branches[branchName])"
|
||||||
class="btn btn-primary">
|
class="btn btn-primary">
|
||||||
@ -619,7 +674,7 @@
|
|||||||
placeholder="Enter task name"
|
placeholder="Enter task name"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-3">
|
||||||
<user-typeahead
|
<user-typeahead
|
||||||
auto-focus="false"
|
auto-focus="false"
|
||||||
ng-model="newTask.assignee_id"
|
ng-model="newTask.assignee_id"
|
||||||
@ -639,14 +694,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- Review link should go here once implemented in the API -->
|
<!-- Review link should go here once implemented in the API -->
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-2 text-right col-xs-offset-1">
|
||||||
<task-priority-dropdown
|
|
||||||
editable="{{isLoggedIn}}"
|
|
||||||
on-change="updateTask(newTask, 'priority', priority)"
|
|
||||||
priority="{{newTask.priority}}"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-2 text-right">
|
|
||||||
<button ng-click="createTask(newTask)"
|
<button ng-click="createTask(newTask)"
|
||||||
class="btn btn-primary">
|
class="btn btn-primary">
|
||||||
Save
|
Save
|
||||||
|
41
src/app/stories/template/worklists.html
Normal file
41
src/app/stories/template/worklists.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<button type="button" class="close" aria-hidden="true"
|
||||||
|
ng-click="close()">×</button>
|
||||||
|
<h3 class="panel-title">In worklists you're subscribed to</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="worklist in worklists">
|
||||||
|
<td>
|
||||||
|
<a href="/#!/worklist/{{worklist.id}}">{{worklist.title}}</a>
|
||||||
|
<span class="text-muted pull-right" ng-show="worklist.automatic">
|
||||||
|
(automatic)
|
||||||
|
</span>
|
||||||
|
<div ng-click="showItems = !showItems">
|
||||||
|
<a href>
|
||||||
|
<i class="fa"
|
||||||
|
ng-class="{'fa-caret-right': !showItems,
|
||||||
|
'fa-caret-down': showItems}"></i>
|
||||||
|
<small>
|
||||||
|
{{showItems ? 'Hide' : 'Show'}} items...
|
||||||
|
</small>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-condensed subtable" ng-show="showItems">
|
||||||
|
<tr ng-repeat="item in worklist.relatedItems | orderBy: 'list_position'">
|
||||||
|
<td>
|
||||||
|
{{item.item_type | capitalize}} {{item.item_id}}
|
||||||
|
<span class="pull-right">
|
||||||
|
{{item.list_position + 1}} of {{worklist.items.length}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -76,3 +76,7 @@ tr.selected-row {
|
|||||||
background-color: @brand-primary !important;
|
background-color: @brand-primary !important;
|
||||||
color: @navbar-default-color;
|
color: @navbar-default-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subtable {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
25
src/theme/base/stories.less
Normal file
25
src/theme/base/stories.less
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Codethink Ltd.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Story view styles
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* "Related Worklists" table style */
|
||||||
|
.position-label {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
@ -50,3 +50,4 @@
|
|||||||
@import './base/edit_tasks.less';
|
@import './base/edit_tasks.less';
|
||||||
@import './base/boards_worklists.less';
|
@import './base/boards_worklists.less';
|
||||||
@import './base/calendar.less';
|
@import './base/calendar.less';
|
||||||
|
@import './base/stories.less';
|
||||||
|
Loading…
Reference in New Issue
Block a user