Add and remove tasks from context menus
This commit is contained in:
parent
783b868220
commit
2243eebda9
@ -126,7 +126,7 @@ class Tasks extends BaseController {
|
||||
$id = (int)$args['id'];
|
||||
$task = R::load('task', $id);
|
||||
|
||||
if ((int)$task->id !== $id) {
|
||||
if ((int)$task->id !== $id || (int)$task->id === 0) {
|
||||
$this->logger->addError('Remove Task: ', [$task]);
|
||||
$this->apiJson->addAlert('error', 'Error removing task. ' .
|
||||
'No task found for ID ' . $id . '.');
|
||||
@ -134,8 +134,9 @@ class Tasks extends BaseController {
|
||||
return $this->jsonResponse($response);
|
||||
}
|
||||
|
||||
if (!$this->checkBoardAccess(
|
||||
$this->getBoardId($task->column_id), $request)) {
|
||||
$boardId = $this->getBoardId($task->column_id);
|
||||
|
||||
if (!$this->checkBoardAccess($boardId, $request)) {
|
||||
return $this->jsonResponse($response, 403);
|
||||
}
|
||||
|
||||
@ -151,6 +152,9 @@ class Tasks extends BaseController {
|
||||
$this->apiJson->addAlert('success',
|
||||
'Task ' . $before->title . ' removed.');
|
||||
|
||||
$board = R::load('board', $boardId);
|
||||
$this->apiJson->addData(R::exportAll($board));
|
||||
|
||||
return $this->jsonResponse($response);
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,18 @@ export class BoardService {
|
||||
});
|
||||
}
|
||||
|
||||
removeTask(taskId: number): Observable<ApiResponse> {
|
||||
return this.http.delete('api/tasks/' + taskId)
|
||||
.map(res => {
|
||||
let response: ApiResponse = res.json();
|
||||
return response;
|
||||
})
|
||||
.catch((res, caught) => {
|
||||
let response: ApiResponse = res.json();
|
||||
return Observable.of(response);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Determine when to use this
|
||||
refreshToken(): void {
|
||||
this.http.post('api/refresh', {}).subscribe();
|
||||
|
@ -29,27 +29,29 @@
|
||||
|
||||
<div class="tasks">
|
||||
<tb-task class="task-container" *ngFor="let task of columnData.ownTask"
|
||||
[task]="task" [add-task]="getShowModalFunction()"></tb-task>
|
||||
[task]="task" [add-task]="getShowModalFunction(task.id)"
|
||||
[remove-task]="getRemoveTaskFunction(task.id)"></tb-task>
|
||||
</div>
|
||||
|
||||
<tb-context-menu [menu-items]="contextMenuItems"></tb-context-menu>
|
||||
|
||||
<!--<tb-modal modal-title="Confirm Task Removal" blocking="true">-->
|
||||
<!-- <div class="center">Removing a task cannot be undone.<br>Continue?</div>-->
|
||||
<!-- <div class="buttons">-->
|
||||
<!-- <button class="flat"-->
|
||||
<!-- (click)="removeUser()">Yes</button>-->
|
||||
<!-- <button #defaultAction-->
|
||||
<!-- (click)="modal.close(MODAL_CONFIRM_ID)">No</button>-->
|
||||
<!-- </div>-->
|
||||
<!--</tb-modal>-->
|
||||
<tb-modal modal-title="Confirm Task Removal" blocking="true"
|
||||
modal-id="{{ MODAL_CONFIRM_ID + columnData.id }}">
|
||||
<div class="center">Removing a task cannot be undone.<br>Continue?</div>
|
||||
<div class="buttons">
|
||||
<button class="flat"
|
||||
(click)="removeTask()">Yes</button>
|
||||
<button #defaultAction
|
||||
(click)="modal.close(MODAL_CONFIRM_ID + columnData.id)">No</button>
|
||||
</div>
|
||||
</tb-modal>
|
||||
|
||||
<tb-modal *ngIf="activeBoard" modal-title="Add Task"
|
||||
modal-id="{{ MODAL_ID + columnData.id }}">
|
||||
<label>
|
||||
Title
|
||||
<input #focusMe type="text" name="title" placeholder="Task Title"
|
||||
[(ngModel)]="modalProps.title">
|
||||
[(ngModel)]="modalProps.title">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
|
@ -40,7 +40,10 @@ export class ColumnDisplay implements OnInit {
|
||||
private contextMenuItems: Array<ContextMenuItem>;
|
||||
|
||||
private MODAL_ID: string;
|
||||
private MODAL_CONFIRM_ID: string;
|
||||
|
||||
private modalProps: Task;
|
||||
private taskToRemove: number;
|
||||
|
||||
@Input('column') columnData: Column;
|
||||
|
||||
@ -55,11 +58,13 @@ export class ColumnDisplay implements OnInit {
|
||||
this.collapseTasks = false;
|
||||
|
||||
this.contextMenuItems = [
|
||||
new ContextMenuItem('Add New Task',
|
||||
new ContextMenuItem('Add Task',
|
||||
this.getShowModalFunction())
|
||||
];
|
||||
|
||||
this.MODAL_ID = 'add-task-form-';
|
||||
this.MODAL_CONFIRM_ID = 'task-remove-confirm';
|
||||
|
||||
this.modalProps = new Task();
|
||||
|
||||
boardService.activeBoardChanged.subscribe((board: Board) => {
|
||||
@ -145,6 +150,36 @@ export class ColumnDisplay implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
removeTask() {
|
||||
this.boardService.removeTask(this.taskToRemove)
|
||||
.subscribe((response: ApiResponse) => {
|
||||
response.alerts.forEach(note => this.notes.add(note));
|
||||
|
||||
if (response.status !== 'success') {
|
||||
return;
|
||||
}
|
||||
|
||||
let boardData = response.data[1][0];
|
||||
|
||||
let newBoard = new Board(+boardData.id, boardData.name,
|
||||
boardData.is_active === '1',
|
||||
boardData.ownColumn,
|
||||
boardData.ownCategory,
|
||||
boardData.ownAutoAction,
|
||||
boardData.ownIssuetracker,
|
||||
boardData.sharedUser);
|
||||
|
||||
this.boardService.updateActiveBoard(newBoard);
|
||||
});
|
||||
}
|
||||
|
||||
private getRemoveTaskFunction(taskId: number): Function {
|
||||
return () => {
|
||||
this.taskToRemove = taskId;
|
||||
this.modal.open(this.MODAL_CONFIRM_ID + this.columnData.id);
|
||||
};
|
||||
}
|
||||
|
||||
private getShowModalFunction(): Function {
|
||||
return () => { this.showModal(); };
|
||||
}
|
||||
|
@ -11,10 +11,9 @@
|
||||
<span *ngIf="taskData.points > 0" class="badge right" title="Points">
|
||||
{{ taskData.points }}</span>
|
||||
</h4>
|
||||
|
||||
<div class="description" [innerHTML]="getTaskDescription()"></div>
|
||||
<div class="hidden">
|
||||
<pre>{{ taskData | json }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<span *ngIf="userOptions.show_assignee">
|
||||
Assigned To:
|
||||
@ -25,6 +24,7 @@
|
||||
Unassigned
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="right">
|
||||
<span *ngIf="taskData.due_date">
|
||||
Due: {{ taskData.due_date }}
|
||||
@ -39,6 +39,7 @@
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<tb-context-menu [menu-items]="contextMenuItems"></tb-context-menu>
|
||||
</div>
|
||||
|
||||
|
@ -5,16 +5,23 @@ import {
|
||||
} from '@angular/core';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
|
||||
import * as Marked from 'marked';
|
||||
import * as marked from 'marked';
|
||||
import * as hljs from 'highlight.js';
|
||||
|
||||
import {
|
||||
ApiResponse,
|
||||
Board,
|
||||
Column,
|
||||
ContextMenu,
|
||||
ContextMenuItem,
|
||||
Notification,
|
||||
Task,
|
||||
UserOptions,
|
||||
AuthService
|
||||
AuthService,
|
||||
ModalService,
|
||||
NotificationsService
|
||||
} from '../../shared/index';
|
||||
import { BoardService } from '../board.service';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-task',
|
||||
@ -23,16 +30,35 @@ import {
|
||||
export class TaskDisplay implements OnInit {
|
||||
private userOptions: UserOptions;
|
||||
private contextMenuItems: Array<ContextMenuItem>;
|
||||
private selectMenuItem: ContextMenuItem;
|
||||
|
||||
private activeBoard: Board;
|
||||
|
||||
@Input('task') taskData: Task;
|
||||
@Input('add-task') addTask: Function;
|
||||
@Input('remove-task') removeTask: Function;
|
||||
|
||||
constructor(private auth: AuthService,
|
||||
private sanitizer: DomSanitizer) {
|
||||
private sanitizer: DomSanitizer,
|
||||
private boardService: BoardService,
|
||||
private modal: ModalService,
|
||||
private notes: NotificationsService) {
|
||||
auth.userChanged.subscribe(() => {
|
||||
this.userOptions = auth.userOptions;
|
||||
});
|
||||
|
||||
boardService.activeBoardChanged.subscribe((board: Board) => {
|
||||
let menuText = 'Move to Column: <select>';
|
||||
|
||||
board.columns.forEach((column: Column) => {
|
||||
menuText += '<option>' + column.name + '</option>';
|
||||
});
|
||||
|
||||
menuText += '</select>';
|
||||
|
||||
this.selectMenuItem = new ContextMenuItem(menuText, null, false, false);
|
||||
});
|
||||
|
||||
this.initMarked();
|
||||
}
|
||||
|
||||
@ -40,17 +66,20 @@ export class TaskDisplay implements OnInit {
|
||||
this.contextMenuItems = [
|
||||
new ContextMenuItem('View Task'),
|
||||
new ContextMenuItem('Edit Task'),
|
||||
new ContextMenuItem('Delete Task'),
|
||||
new ContextMenuItem('Remove Task', this.removeTask),
|
||||
new ContextMenuItem('', null, true),
|
||||
new ContextMenuItem('Move to Column:', null, false, false),
|
||||
new ContextMenuItem('Copy To Board'),
|
||||
new ContextMenuItem('Move To Board'),
|
||||
new ContextMenuItem('', null, true),
|
||||
new ContextMenuItem('Add New Task', this.addTask)
|
||||
this.selectMenuItem,
|
||||
new ContextMenuItem('', null, true),
|
||||
new ContextMenuItem('Add Task', this.addTask)
|
||||
];
|
||||
}
|
||||
|
||||
getTaskDescription(): SafeHtml {
|
||||
return this.sanitizer.bypassSecurityTrustHtml(
|
||||
Marked(this.taskData.description));
|
||||
marked(this.taskData.description));
|
||||
}
|
||||
|
||||
// Expects a color in full HEX with leading #, e.g. #ffffe0
|
||||
@ -64,7 +93,7 @@ export class TaskDisplay implements OnInit {
|
||||
}
|
||||
|
||||
private initMarked() {
|
||||
let renderer = new Marked.Renderer();
|
||||
let renderer = new marked.Renderer();
|
||||
|
||||
renderer.listitem = text => {
|
||||
if (/^\s*\[[x ]\]\s*/.test(text)) {
|
||||
@ -91,7 +120,7 @@ export class TaskDisplay implements OnInit {
|
||||
return out;
|
||||
};
|
||||
|
||||
Marked.setOptions({
|
||||
marked.setOptions({
|
||||
renderer,
|
||||
smartypants: true,
|
||||
highlight: code => {
|
||||
|
@ -6,9 +6,7 @@
|
||||
(click)="callAction(item.action)">
|
||||
<hr *ngIf="item.isSeparator">
|
||||
|
||||
<div *ngIf="!item.isSeparator">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
<div *ngIf="!item.isSeparator"[innerHTML]="getText(item)"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
Input,
|
||||
ElementRef
|
||||
} from '@angular/core';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
|
||||
import { ContextMenuItem } from './context-menu-item.model';
|
||||
import { ContextMenuService } from './context-menu.service';
|
||||
@ -18,7 +19,8 @@ export class ContextMenu {
|
||||
animate = true;
|
||||
|
||||
constructor(private el: ElementRef,
|
||||
private menuService: ContextMenuService) {
|
||||
private menuService: ContextMenuService,
|
||||
private sanitizer: DomSanitizer) {
|
||||
menuService.registerMenu(this);
|
||||
|
||||
let parentElement = el.nativeElement.parentElement;
|
||||
@ -31,6 +33,10 @@ export class ContextMenu {
|
||||
};
|
||||
}
|
||||
|
||||
getText(item: ContextMenuItem): SafeHtml {
|
||||
return this.sanitizer.bypassSecurityTrustHtml(item.text);
|
||||
}
|
||||
|
||||
callAction(action: Function) {
|
||||
if (action) {
|
||||
action();
|
||||
@ -49,7 +55,7 @@ export class ContextMenu {
|
||||
target.style.top = event.pageY + 'px';
|
||||
|
||||
// Adjust position if near an edge
|
||||
setTimeout(() => {
|
||||
let adjustPosition = () => {
|
||||
let rect = target.getBoundingClientRect();
|
||||
|
||||
let offsetX = (event.pageX + rect.width + edgeBuffer) > window.innerWidth;
|
||||
@ -57,8 +63,9 @@ export class ContextMenu {
|
||||
|
||||
target.style.left = event.pageX - (offsetX ? rect.width : 0) + 'px';
|
||||
target.style.top = event.pageY - (offsetY ? rect.height : 0) + 'px';
|
||||
},
|
||||
0);
|
||||
};
|
||||
|
||||
setTimeout(adjustPosition, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user