Replace "New Story" button in the top bar with a dropdown menu
Now there are more things than just stories for normal users to create (boards and worklists too), a dropdown menu is sensible here rather than adding a number of buttons. This also adds a way for superusers to easily create projects and project groups from any page. Change-Id: Icb192e64d4bae8af22b01960ecac3c00d092da85
This commit is contained in:
125
src/app/boards/controller/add_board_controller.js
Normal file
125
src/app/boards/controller/add_board_controller.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Codethink Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller for the "new board" modal popup.
|
||||
*/
|
||||
angular.module('sb.board').controller('AddBoardController',
|
||||
function ($scope, $modalInstance, $state, params, Board, Project,
|
||||
Worklist, $q, BoardHelper) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Create the new board.
|
||||
*/
|
||||
function saveBoard() {
|
||||
$scope.board.$create(
|
||||
function (result) {
|
||||
$modalInstance.dismiss('success');
|
||||
$state.go('sb.board.detail', {boardID: result.id});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a function which adds a "lane" to the board in the given
|
||||
* position. This lane is just a reference to the worklist used to
|
||||
* represent it.
|
||||
*/
|
||||
function addLaneDetails(position) {
|
||||
return function(lane) {
|
||||
$scope.board.lanes.push({
|
||||
board_id: $scope.board.id,
|
||||
list_id: lane.id,
|
||||
position: position
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create worklists to represent the lanes of the board, then save
|
||||
* the board.
|
||||
*/
|
||||
function saveBoardWithLanes() {
|
||||
$scope.board.lanes = [];
|
||||
var lanePromises = [];
|
||||
for (var i = 0; i < $scope.lanes.length; i++) {
|
||||
var lane = $scope.lanes[i];
|
||||
var addLane = addLaneDetails(i);
|
||||
lane.project_id = $scope.board.project_id;
|
||||
lane.private = $scope.board.private;
|
||||
lanePromises.push(lane.$create(addLane));
|
||||
}
|
||||
$q.all(lanePromises).then(saveBoard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the board, and any worklists created to serve as lanes.
|
||||
*/
|
||||
$scope.save = function() {
|
||||
if ($scope.lanes.length > 0) {
|
||||
saveBoardWithLanes();
|
||||
} else {
|
||||
saveBoard();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Close this modal without saving.
|
||||
*/
|
||||
$scope.close = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a lane.
|
||||
*/
|
||||
$scope.addLane = function() {
|
||||
$scope.lanes.push(new Worklist({
|
||||
title: '',
|
||||
editing: true
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a lane.
|
||||
*/
|
||||
$scope.removeLane = function(lane) {
|
||||
var idx = $scope.lanes.indexOf(lane);
|
||||
$scope.lanes.splice(idx, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle editing of a lane title.
|
||||
*/
|
||||
$scope.toggleEdit = function(lane) {
|
||||
lane.editing = !lane.editing;
|
||||
};
|
||||
|
||||
/**
|
||||
* Config for the lanes sortable.
|
||||
*/
|
||||
$scope.lanesSortable = {
|
||||
dragMove: BoardHelper.maybeScrollContainer('new-board')
|
||||
};
|
||||
|
||||
// Create a blank Board to begin with.
|
||||
$scope.lanes = [];
|
||||
$scope.board = new Board({
|
||||
title: '',
|
||||
lanes: []
|
||||
});
|
||||
});
|
||||
48
src/app/boards/service/new_board_service.js
Normal file
48
src/app/boards/service/new_board_service.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Codethink Limited.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
angular.module('sb.board').factory('NewBoardService',
|
||||
function ($modal, $log, Session, SessionModalService) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
showNewBoardModal: function (userId) {
|
||||
if (!Session.isLoggedIn()) {
|
||||
return SessionModalService.showLoginRequiredModal();
|
||||
} else {
|
||||
var modalInstance = $modal.open(
|
||||
{
|
||||
size: 'lg',
|
||||
templateUrl: 'app/boards/template/new.html',
|
||||
controller: 'AddBoardController',
|
||||
resolve: {
|
||||
params: function () {
|
||||
return {
|
||||
userId: userId || null
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Return the modal's promise.
|
||||
return modalInstance.result;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
118
src/app/boards/template/new.html
Normal file
118
src/app/boards/template/new.html
Normal file
@@ -0,0 +1,118 @@
|
||||
<!--
|
||||
~ Copyright (c) 2015 Codethink Limited
|
||||
~
|
||||
~ 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="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<button type="button" class="close" aria-hidden="true"
|
||||
ng-click="close()">×</button>
|
||||
<h3 class="panel-title">New Board</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<form class="form-horizontal" role="form" name="worklistForm">
|
||||
<div class="form-group">
|
||||
<label for="title" class="col-sm-2 control-label">
|
||||
Title
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="title"
|
||||
focus
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="board.title"
|
||||
required
|
||||
maxlength="100"
|
||||
placeholder="Board title">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description"
|
||||
class="col-sm-2 control-label">
|
||||
Description
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea id="description"
|
||||
class="form-control"
|
||||
ng-model="board.description"
|
||||
msd-elastic
|
||||
placeholder="A short description of the board's purpose.">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="private" class="col-sm-2 control-label">
|
||||
Private
|
||||
</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<input id="private"
|
||||
type="checkbox"
|
||||
class="modal-checkbox"
|
||||
ng-model="board.private"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kanban-board"
|
||||
id="new-board"
|
||||
ng-model="lanes"
|
||||
as-sortable="lanesSortable">
|
||||
<div class="kanban-lane"
|
||||
ng-repeat="worklist in lanes"
|
||||
as-sortable-item>
|
||||
<span ng-if="!worklist.editing">
|
||||
<i class="fa fa-bars" as-sortable-item-handle></i>
|
||||
|
||||
<a ng-click="toggleEdit(worklist)">
|
||||
{{worklist.title}}
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<button type="button" class="close" title="Remove"
|
||||
ng-click="removeLane(worklist)">
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
ng-model="worklist.title"
|
||||
placeholder="Lane Title"
|
||||
focus
|
||||
ng-if="worklist.editing"
|
||||
ng-blur="toggleEdit(worklist)" />
|
||||
</div><div class="kanban-lane-clickable"
|
||||
ng-click="addLane()">
|
||||
Add lane
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-right">
|
||||
<button type="button"
|
||||
class="btn btn-primary"
|
||||
ng-click="save()">
|
||||
Save Changes
|
||||
</button>
|
||||
<button type="button"
|
||||
ng-click="close()"
|
||||
class="btn btn-default">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -19,9 +19,10 @@
|
||||
* and search box.
|
||||
*/
|
||||
angular.module('storyboard').controller('HeaderController',
|
||||
function ($q, $scope, $rootScope, $state, NewStoryService, Session,
|
||||
SessionState, CurrentUser, Criteria, Notification, Priority,
|
||||
Project, Story, ProjectGroup) {
|
||||
function ($q, $scope, $rootScope, $state, $modal, NewStoryService,
|
||||
Session, SessionState, CurrentUser, Criteria, Notification,
|
||||
Priority, Project, Story, ProjectGroup, NewWorklistService,
|
||||
NewBoardService, SessionModalService) {
|
||||
'use strict';
|
||||
|
||||
function resolveCurrentUser() {
|
||||
@@ -49,11 +50,66 @@ angular.module('storyboard').controller('HeaderController',
|
||||
NewStoryService.showNewStoryModal()
|
||||
.then(function (story) {
|
||||
// On success, go to the story detail.
|
||||
$scope.showMobileNewMenu = false;
|
||||
$state.go('sb.story.detail', {storyId: story.id});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new worklist.
|
||||
*/
|
||||
$scope.newWorklist = function () {
|
||||
NewWorklistService.showNewWorklistModal()
|
||||
.then(function (worklist) {
|
||||
// On success, go to the worklist detail.
|
||||
$scope.showMobileNewMenu = false;
|
||||
$state.go('sb.worklist.detail',
|
||||
{worklistID: worklist.id});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new board.
|
||||
*/
|
||||
$scope.newBoard = function () {
|
||||
NewBoardService.showNewBoardModal()
|
||||
.then(function (board) {
|
||||
// On success, go to the board detail.
|
||||
$scope.showMobileNewMenu = false;
|
||||
$state.go('sb.board.detail', {boardID: board.id});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new project-group.
|
||||
*/
|
||||
$scope.newProjectGroup = function () {
|
||||
$scope.modalInstance = $modal.open(
|
||||
{
|
||||
templateUrl: 'app/project_group/template/new.html',
|
||||
controller: 'ProjectGroupNewController'
|
||||
});
|
||||
|
||||
$scope.modalInstance.result.then(function (projectGroup) {
|
||||
// On success, go to the project group detail.
|
||||
$scope.showMobileNewMenu = false;
|
||||
$state.go(
|
||||
'sb.project_group_detail',
|
||||
{id: projectGroup.id}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show modal informing the user login is required.
|
||||
*/
|
||||
$scope.showLoginRequiredModal = function() {
|
||||
SessionModalService.showLoginRequiredModal();
|
||||
};
|
||||
|
||||
/**
|
||||
* Log out the user.
|
||||
*/
|
||||
|
||||
@@ -39,12 +39,49 @@
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
<li> </li>
|
||||
<li>
|
||||
<li class="dropdown" dropdown ng-show="isLoggedIn">
|
||||
<button type="button"
|
||||
class="btn btn-primary navbar-btn"
|
||||
ng-click="newStory()">
|
||||
class="btn btn-primary navbar-btn dropdown-toggle"
|
||||
dropdown-toggle>
|
||||
<i class="fa fa-plus-circle"></i>
|
||||
New Story
|
||||
Create New...
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href ng-click="newStory()">
|
||||
Story
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="newWorklist()">
|
||||
Worklist
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="newBoard()">
|
||||
Board
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="currentUser.is_superuser">
|
||||
<a href="#!/project/new">
|
||||
Project
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="currentUser.is_superuser">
|
||||
<a href ng-click="newProjectGroup()">
|
||||
Project Group
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li ng-show="!isLoggedIn">
|
||||
<button type="button"
|
||||
class="btn btn-primary navbar-btn dropdown-toggle"
|
||||
ng-click="showLoginRequiredModal()">
|
||||
<i class="fa fa-plus-circle"></i>
|
||||
Create New...
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -118,14 +155,55 @@
|
||||
ng-hide="isLoggedIn">
|
||||
<i class="fa fa-sign-in"></i>
|
||||
</a>
|
||||
<a href="#" ng-click="newStory()"
|
||||
class="btn btn-sm btn-primary navbar-btn">
|
||||
<button type="button"
|
||||
ng-click="showMobileNewMenu = !showMobileNewMenu"
|
||||
class="btn btn-sm btn-primary navbar-btn"
|
||||
ng-show="isLoggedIn">
|
||||
<i class="fa fa-plus-circle"></i>
|
||||
New Story
|
||||
</a>
|
||||
Create New...
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-primary navbar-btn dropdown-toggle"
|
||||
ng-click="showLoginRequiredModal()"
|
||||
ng-show="!isLoggedIn">
|
||||
<i class="fa fa-plus-circle"></i>
|
||||
Create New...
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#!/">StoryBoard</a>
|
||||
</div>
|
||||
|
||||
<div collapse="!showMobileNewMenu" id="mobile-new-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<a href ng-click="newStory()">
|
||||
Story
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="newWorklist()">
|
||||
Worklist
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href ng-click="newBoard()">
|
||||
Board
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="currentUser.is_superuser">
|
||||
<a href="#!/project/new"
|
||||
ng-click="showMobileNewMenu = false">
|
||||
Project
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="currentUser.is_superuser">
|
||||
<a href ng-click="newProjectGroup()">
|
||||
Project Group
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div collapse="!showMobileMenu" id="mobile-dropdown-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li active-path="^\/profile\/preferences*">
|
||||
|
||||
47
src/app/worklists/service/new_worklist_service.js
Normal file
47
src/app/worklists/service/new_worklist_service.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Codethink Limited.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
angular.module('sb.worklist').factory('NewWorklistService',
|
||||
function ($modal, $log, Session, SessionModalService) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
showNewWorklistModal: function (userId) {
|
||||
if (!Session.isLoggedIn()) {
|
||||
return SessionModalService.showLoginRequiredModal();
|
||||
} else {
|
||||
var modalInstance = $modal.open(
|
||||
{
|
||||
templateUrl: 'app/worklists/template/new.html',
|
||||
controller: 'AddWorklistController',
|
||||
resolve: {
|
||||
params: function () {
|
||||
return {
|
||||
userId: userId || null
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Return the modal's promise.
|
||||
return modalInstance.result;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
71
src/app/worklists/template/new.html
Normal file
71
src/app/worklists/template/new.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<!--
|
||||
~ Copyright (c) 2015 Codethink Limited
|
||||
~
|
||||
~ 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="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<button type="button" class="close" aria-hidden="true"
|
||||
ng-click="close()">×</button>
|
||||
<h3 class="panel-title">New Worklist</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<form class="form-horizontal" role="form" name="worklistForm">
|
||||
<div class="form-group">
|
||||
<label for="name" class="col-sm-2 control-label">
|
||||
Title
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input id="name"
|
||||
focus
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="worklist.title"
|
||||
required
|
||||
maxlength="100"
|
||||
placeholder="Worklist Title">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="private" class="col-sm-2 control-label">
|
||||
Private
|
||||
</label>
|
||||
<div class="col-sm-10 checkbox">
|
||||
<input id="private"
|
||||
type="checkbox"
|
||||
class="modal-checkbox"
|
||||
ng-model="worklist.private"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-right">
|
||||
<button type="button"
|
||||
class="btn btn-primary"
|
||||
ng-click="save()">
|
||||
Save Changes
|
||||
</button>
|
||||
<button type="button"
|
||||
ng-click="close()"
|
||||
class="btn btn-default">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user