Task drag-and-drop.

This commit is contained in:
kiswa 2017-08-04 23:41:14 +00:00
parent ad051c1220
commit 92132b3f59
9 changed files with 162 additions and 11 deletions

View File

@ -17,12 +17,19 @@
<div class="right" *ngIf="activeBoard">
<label>
{{ strings['boards_hideFiltered'] }}:
<input type="checkbox">
<input type="checkbox" [(ngModel)]="hideFiltered"
(change)="toggleFiltered()">
</label>
<label>
{{ strings['boards_userFilter'] }}:
<select [(ngModel)]="userFilter">
<option [ngValue]="null">Any</option>
<select [(ngModel)]="userFilter"
(change)="filterTasks('user')">
<option [ngValue]="null">
{{ strings['boards_filterByAny'] }}
</option>
<option [ngValue]="-1">
{{ strings['boards_filterByUnassigned'] }}
</option>
<option *ngFor="let user of activeBoard.users"
[ngValue]="user.id">
{{ user.username }}
@ -31,8 +38,14 @@
</label>
<label>
{{ strings['boards_categoryFilter'] }}:
<select [(ngModel)]="categoryFilter">
<option [ngValue]="null">Any</option>
<select [(ngModel)]="categoryFilter"
(change)="filterTasks('cat')">
<option [ngValue]="null">
{{ strings['boards_filterByAny'] }}
</option>
<option [ngValue]="-1">
{{ strings['boards_filterByUncategorized'] }}
</option>
<option *ngFor="let category of activeBoard.categories"
[ngValue]="category.id">
{{ category.name }}
@ -61,7 +74,7 @@
</div>
<div class="board" *ngIf="activeBoard">
<tb-column class="column"
<tb-column class="column" [id]="column.id"
*ngFor="let column of activeBoard.columns"
[column]="column"
[boards]="boards"

View File

@ -9,6 +9,7 @@ import {
ApiResponse,
Board,
Column,
Task,
User,
Notification,
AuthService,
@ -31,9 +32,12 @@ export class BoardDisplay implements OnInit {
private boardNavId: number;
private userFilter: number;
private categoryFilter: number;
private noBoardsMessage: string;
private pageName: string;
private loading: boolean;
private hideFiltered: boolean;
constructor(private title: Title,
private router: Router,
@ -71,6 +75,8 @@ export class BoardDisplay implements OnInit {
this.activeBoard = board;
title.setTitle('TaskBoard - ' + board.name);
this.userFilter = null;
this.categoryFilter = null;
});
auth.userChanged.subscribe((user: User) => {
@ -96,6 +102,34 @@ export class BoardDisplay implements OnInit {
}
}
ngAfterContentInit() {
let bag = this.dragula.find('tasks-bag');
if (bag) {
this.dragula.destroy('tasks-bag');
}
this.dragula.setOptions('tasks-bag', {
moves: (el: any, container: any, handle: any) => {
return handle.classList.contains('drag-handle');
}
});
this.dragula.dropModel.subscribe((value: any) => {
this.activeBoard.columns.forEach(column => {
let position = 0;
column.tasks.forEach(task => {
task.column_id = column.id;
task.position = position;
position++;
});
});
this.boardService.updateBoard(this.activeBoard).subscribe();
});
}
goToBoard(): void {
if (this.boardNavId === null) {
return;
@ -112,6 +146,70 @@ export class BoardDisplay implements OnInit {
});
}
toggleFiltered() {
this.activeBoard.columns.forEach(column => {
column.tasks.forEach(task => {
task.hideFiltered = this.hideFiltered;
});
});
}
filterTasks(type: string) {
this.activeBoard.columns.forEach(column => {
column.tasks.forEach(task => {
task.filtered = false;
if (this.userFilter) {
let found = false;
if (this.userFilter === -1 &&
task.assignees.length === 0) {
found = true;
}
task.assignees.forEach(user => {
if (user.id === this.userFilter) {
found = true;
}
});
if (!found) {
task.filtered = true;
}
}
if (this.categoryFilter) {
let found = false;
if (this.categoryFilter === -1 &&
task.categories.length === 0) {
found = true;
}
task.categories.forEach(cat => {
if (cat.id === this.categoryFilter) {
found = true;
}
});
if (!found) {
task.filtered = true;
}
}
});
});
}
private runFilter(testFunc: Function) {
this.activeBoard.columns.forEach(column => {
column.tasks.forEach(task => {
let found = testFunc(task);
task.filtered = !found;
});
});
}
private updateBoardsList(boards: Array<any>): void {
let activeBoards: Array<Board> = [];

View File

@ -42,6 +42,12 @@ export class BoardService {
.catch(this.errorHandler);
}
updateBoard(board: Board): Observable<ApiResponse> {
return this.http.post('api/boards/' + board.id, board)
.map(this.toApiResponse)
.catch(this.errorHandler);
}
updateColumn(column: Column): Observable<ApiResponse> {
return this.http.post('api/columns/' + column.id, column)
.map(this.toApiResponse)

View File

@ -71,8 +71,10 @@
</button>
</div>
<div class="tasks">
<tb-task class="task-container" *ngFor="let task of columnData.tasks"
<div class="tasks"
[dragula]="'tasks-bag'" [dragulaModel]="columnData.tasks">
<tb-task class="task-container" [id]="task.id"
*ngFor="let task of columnData.tasks"
[task]="task" [boards]="boards"
[add-task]="getShowModalFunction()"
[edit-task]="getShowModalFunction(task.id)"

View File

@ -1,7 +1,9 @@
<div class="task"
[ngClass]="{ 'filtered': taskData.filtered,
'hide': taskData.hideFiltered }"
[style.backgroundColor]="taskData.color"
[style.color]="getTextColor(taskData.color)">
<h4>
<h4 class="drag-handle">
<span class="icon"
[style.color]="getTextColor(taskData.color)"
[ngClass]="{ 'icon-minus-squared-alt': !isCollapsed,

View File

@ -169,8 +169,16 @@ export class TaskDisplay implements OnInit {
menuText += '</select>';
let action = (event: any) => {
if (event.target.tagName !== 'SELECT') {
return;
}
this.changeTaskColumn();
};
return new ContextMenuItem(menuText,
() => { this.changeTaskColumn(); },
action,
false, false);
}
@ -240,7 +248,11 @@ export class TaskDisplay implements OnInit {
menuText += '</select>';
let action = () => {
let action = (event: any) => {
if (event.target.tagName !== 'SELECT') {
return;
}
if (text === this.strings.boards_copyTaskTo) {
this.copyTaskToBoard();
return;

View File

@ -190,6 +190,10 @@
"boards_taskCopied": "copied to board",
"boards_taskMoved": "moved to board",
"boards_filterByAny": "Any",
"boards_filterByUnassigned": "Unassigned",
"boards_filterByUncategorized": "Uncategorized",
"boards_sortByPosition": "Position",
"boards_sortByDueDate": "Due Date",
"boards_sortByLastModified": "Last Modifed",

View File

@ -190,6 +190,10 @@
"boards_taskCopied": "copiado a tablero",
"boards_taskMoved": "movido a tablero",
"boards_filterByAny": "Alguna",
"boards_filterByUnassigned": "Sin Asignar",
"boards_filterByUncategorized": "Sin Categoría",
"boards_sortByPosition": "Posición",
"boards_sortByDueDate": "Fecha de Vencimiento",
"boards_sortByLastModified": "Última Modificación",

View File

@ -190,6 +190,8 @@
}
.tasks {
flex: 1 0;
flex-direction: column;
overflow-y: auto;
padding: 7px;
padding-top: 0;
@ -283,6 +285,14 @@
display: none;
}
}
&.filtered {
opacity: .4;
&.hide {
display: none;
}
}
}
}