feat: integrate moveTask functionality into TaskMenuComponent
This commit is contained in:
parent
2632f859fe
commit
00b811afbd
7 changed files with 129 additions and 51 deletions
|
|
@ -57,7 +57,10 @@
|
||||||
<div id="todo" class="details">
|
<div id="todo" class="details">
|
||||||
<!-- Loop through 'todo' tasks and display them -->
|
<!-- Loop through 'todo' tasks and display them -->
|
||||||
@for (task of filteredTasks[TODO]; track task.id) {
|
@for (task of filteredTasks[TODO]; track task.id) {
|
||||||
<app-task [task]="task"></app-task>
|
<app-task
|
||||||
|
[task]="task"
|
||||||
|
(updateStatusEmitter)="onStatusUpdate($event)"
|
||||||
|
></app-task>
|
||||||
|
|
||||||
<!-- Show empty state if no 'todo' tasks exist -->
|
<!-- Show empty state if no 'todo' tasks exist -->
|
||||||
} @empty {
|
} @empty {
|
||||||
|
|
@ -90,7 +93,10 @@
|
||||||
<div id="inprogress" class="details">
|
<div id="inprogress" class="details">
|
||||||
<!-- Loop through 'in progress' tasks and display them -->
|
<!-- Loop through 'in progress' tasks and display them -->
|
||||||
@for (task of filteredTasks[IN_PROGRESS]; track task.id) {
|
@for (task of filteredTasks[IN_PROGRESS]; track task.id) {
|
||||||
<app-task [task]="task"></app-task>
|
<app-task
|
||||||
|
[task]="task"
|
||||||
|
(updateStatusEmitter)="onStatusUpdate($event)"
|
||||||
|
></app-task>
|
||||||
|
|
||||||
<!-- Show empty state if no 'in progress' tasks exist -->
|
<!-- Show empty state if no 'in progress' tasks exist -->
|
||||||
} @empty {
|
} @empty {
|
||||||
|
|
@ -123,7 +129,10 @@
|
||||||
<div id="awaitfeedback" class="details">
|
<div id="awaitfeedback" class="details">
|
||||||
<!-- Loop through 'await feedback' tasks and display them -->
|
<!-- Loop through 'await feedback' tasks and display them -->
|
||||||
@for (task of filteredTasks[AWAIT_FEEDBACK]; track task.id) {
|
@for (task of filteredTasks[AWAIT_FEEDBACK]; track task.id) {
|
||||||
<app-task [task]="task"></app-task>
|
<app-task
|
||||||
|
[task]="task"
|
||||||
|
(updateStatusEmitter)="onStatusUpdate($event)"
|
||||||
|
></app-task>
|
||||||
|
|
||||||
<!-- Show empty state if no 'await feedback' tasks exist -->
|
<!-- Show empty state if no 'await feedback' tasks exist -->
|
||||||
} @empty {
|
} @empty {
|
||||||
|
|
@ -157,7 +166,10 @@
|
||||||
<div id="done" class="details">
|
<div id="done" class="details">
|
||||||
<!-- Loop through 'done' tasks and display them -->
|
<!-- Loop through 'done' tasks and display them -->
|
||||||
@for (task of filteredTasks[DONE]; track task.id) {
|
@for (task of filteredTasks[DONE]; track task.id) {
|
||||||
<app-task [task]="task"></app-task>
|
<app-task
|
||||||
|
[task]="task"
|
||||||
|
(updateStatusEmitter)="onStatusUpdate($event)"
|
||||||
|
></app-task>
|
||||||
|
|
||||||
<!-- Show empty state if no 'done' tasks exist -->
|
<!-- Show empty state if no 'done' tasks exist -->
|
||||||
} @empty {
|
} @empty {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { Router } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { TaskHighlightedComponent } from './task/task-highlighted/task-highlighted.component';
|
import { TaskHighlightedComponent } from './task/task-highlighted/task-highlighted.component';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
import { Task } from '../../interfaces/task.interface';
|
import { Task, TaskMoveEvent } from '../../interfaces/task.interface';
|
||||||
import { TaskService } from '../../services/task.service';
|
import { TaskService } from '../../services/task.service';
|
||||||
import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component';
|
import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component';
|
||||||
import { debounceTime, finalize, Subject, take, takeUntil } from 'rxjs';
|
import { debounceTime, finalize, Subject, take, takeUntil } from 'rxjs';
|
||||||
|
|
@ -38,13 +38,13 @@ export class BoardComponent implements OnInit, OnDestroy {
|
||||||
readonly AWAIT_FEEDBACK = 'awaitfeedback';
|
readonly AWAIT_FEEDBACK = 'awaitfeedback';
|
||||||
readonly DONE = 'done';
|
readonly DONE = 'done';
|
||||||
|
|
||||||
allTasks: Task[] = [];
|
allTasks: Array<Task> = [];
|
||||||
filteredTasks: { [key: string]: Task[] } = {};
|
filteredTasks: { [key: string]: Task[] } = {};
|
||||||
|
|
||||||
searchValue: string = '';
|
taskMovedTo = '';
|
||||||
searchInput: boolean = false;
|
taskMovedFrom = '';
|
||||||
taskMovedTo: string = '';
|
searchValue = '';
|
||||||
taskMovedFrom: string = '';
|
searchInput = false;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
@ -151,6 +151,14 @@ export class BoardComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the status of a task when its status is changed.
|
||||||
|
* @param event The event containing the task and the new status to move to.
|
||||||
|
*/
|
||||||
|
onStatusUpdate(event: TaskMoveEvent) {
|
||||||
|
this.handleItemDropped(event.task, event.moveTo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the event when an item is dropped on a column.
|
* Handles the event when an item is dropped on a column.
|
||||||
* @param taskId The id of the task being moved.
|
* @param taskId The id of the task being moved.
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<section>
|
<section>
|
||||||
@if (boardTaskStatus !== 'todo') {
|
@if (boardTaskStatus !== TODO) {
|
||||||
<div class="link" (click)="moveTask('todo')">To do</div>
|
<div class="link" (click)="moveTask(TODO)">To do</div>
|
||||||
} @if (boardTaskStatus !== 'inprogress') {
|
} @if (boardTaskStatus !== IN_PROGRESS) {
|
||||||
<div class="link" (click)="moveTask('inprogress')">In progress</div>
|
<div class="link" (click)="moveTask(IN_PROGRESS)">In progress</div>
|
||||||
} @if (boardTaskStatus !== 'awaitfeedback') {
|
} @if (boardTaskStatus !== AWAIT_FEEDBACK) {
|
||||||
<div class="link" (click)="moveTask('awaitfeedback')">Await feedback</div>
|
<div class="link" (click)="moveTask(AWAIT_FEEDBACK)">Await feedback</div>
|
||||||
} @if (boardTaskStatus !== 'done') {
|
} @if (boardTaskStatus !== DONE) {
|
||||||
<div class="link" (click)="moveTask('done')">Done</div>
|
<div class="link" (click)="moveTask(DONE)">Done</div>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { Task, TaskMoveEvent } from '../../../../interfaces/task.interface';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-task-menu',
|
selector: 'app-task-menu',
|
||||||
|
|
@ -8,22 +9,19 @@ import { Component, Input } from '@angular/core';
|
||||||
styleUrl: './task-menu.component.scss',
|
styleUrl: './task-menu.component.scss',
|
||||||
})
|
})
|
||||||
export class TaskMenuComponent {
|
export class TaskMenuComponent {
|
||||||
@Input() taskId: string = '';
|
@Input() task!: Task;
|
||||||
@Input() boardTaskStatus: string = '';
|
@Input() boardTaskStatus!: string;
|
||||||
|
@Output() updateStatusEmitter = new EventEmitter<TaskMoveEvent>();
|
||||||
|
|
||||||
constructor() {}
|
readonly TODO = 'todo';
|
||||||
|
readonly IN_PROGRESS = 'inprogress';
|
||||||
|
readonly AWAIT_FEEDBACK = 'awaitfeedback';
|
||||||
|
readonly DONE = 'done';
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the task to a specified status.
|
|
||||||
*
|
|
||||||
* This function updates the status of the task identified by `taskId`
|
|
||||||
* in both `allTasks` and `filteredTasks` lists to the new status
|
|
||||||
* specified by `moveTo`. It also updates the task status in the
|
|
||||||
* Firestore database.
|
|
||||||
*
|
|
||||||
* @param moveTo - The new status to move the task to.
|
|
||||||
*/
|
|
||||||
moveTask(moveTo: string) {
|
moveTask(moveTo: string) {
|
||||||
// TODO
|
this.updateStatusEmitter.emit({
|
||||||
|
task: this.task,
|
||||||
|
moveTo: moveTo,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
<section>
|
<section>
|
||||||
@if (task.id) {
|
@if (task.id) {
|
||||||
<div (click)="handleMenuButtonClick($event, task.id)">
|
<div (click)="handleMenuButtonClick($event, task.id)">
|
||||||
|
{{ disableDrag }}
|
||||||
<div
|
<div
|
||||||
class="content"
|
class="content"
|
||||||
draggable="true"
|
[draggable]="disableDrag"
|
||||||
(dragstart)="dragDropService.startDragging($event, task)"
|
(dragstart)="
|
||||||
|
disableDrag ? dragDropService.startDragging($event, task) : null
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div
|
<div
|
||||||
|
|
@ -65,10 +68,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (isMenuOpen){
|
@if (menuOpen){
|
||||||
<app-task-menu
|
<app-task-menu
|
||||||
[taskId]="task.id"
|
[task]="task"
|
||||||
[boardTaskStatus]="task.status"
|
[boardTaskStatus]="task.status"
|
||||||
|
(updateStatusEmitter)="onStatusUpdate($event)"
|
||||||
></app-task-menu>
|
></app-task-menu>
|
||||||
} @if (AssignedDialogId != '') {
|
} @if (AssignedDialogId != '') {
|
||||||
<div class="dialog" [style.left.px]="dialogX" [style.top.px]="dialogY">
|
<div class="dialog" [style.left.px]="dialogX" [style.top.px]="dialogY">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, HostListener, Input } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
} from '@angular/core';
|
||||||
import { DragDropService } from '../../../services/drag-drop.service';
|
import { DragDropService } from '../../../services/drag-drop.service';
|
||||||
import { Task } from '../../../interfaces/task.interface';
|
import {
|
||||||
|
Assignee,
|
||||||
|
Task,
|
||||||
|
TaskMoveEvent,
|
||||||
|
} from '../../../interfaces/task.interface';
|
||||||
import { OverlayService } from '../../../services/overlay.service';
|
import { OverlayService } from '../../../services/overlay.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { TaskMenuComponent } from './task-menu/task-menu.component';
|
import { TaskMenuComponent } from './task-menu/task-menu.component';
|
||||||
|
|
@ -16,15 +26,13 @@ import { take } from 'rxjs';
|
||||||
styleUrl: './task.component.scss',
|
styleUrl: './task.component.scss',
|
||||||
})
|
})
|
||||||
export class TaskComponent {
|
export class TaskComponent {
|
||||||
@Input() task: Task = {} as Task;
|
@Input() task!: Task;
|
||||||
|
@Output() updateStatusEmitter = new EventEmitter<TaskMoveEvent>();
|
||||||
|
|
||||||
isMenuOpen: boolean = false;
|
readonly DISABLE_DRAG_BREAKPOINT = 667;
|
||||||
AssignedDialogId: string = '';
|
readonly DIALOG_OFFSET_X = 25;
|
||||||
dialogX: number = 0;
|
readonly DIALOG_OFFSET_Y = 10;
|
||||||
dialogY: number = 0;
|
|
||||||
|
|
||||||
creator: any = '';
|
|
||||||
assignees: any[] = [];
|
|
||||||
categoryColors = new Map<string, string>([
|
categoryColors = new Map<string, string>([
|
||||||
['User Story', '#0038ff'],
|
['User Story', '#0038ff'],
|
||||||
['Technical Task', '#20d7c2'],
|
['Technical Task', '#20d7c2'],
|
||||||
|
|
@ -32,6 +40,15 @@ export class TaskComponent {
|
||||||
|
|
||||||
pageViewMedia$ = this.resizeService.pageViewMedia$;
|
pageViewMedia$ = this.resizeService.pageViewMedia$;
|
||||||
|
|
||||||
|
menuOpen = false;
|
||||||
|
disableDrag = false;
|
||||||
|
AssignedDialogId = '';
|
||||||
|
dialogX = 0;
|
||||||
|
dialogY = 0;
|
||||||
|
|
||||||
|
creator = '';
|
||||||
|
assignees: Assignee[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dragDropService: DragDropService,
|
public dragDropService: DragDropService,
|
||||||
public overlayService: OverlayService,
|
public overlayService: OverlayService,
|
||||||
|
|
@ -39,6 +56,20 @@ export class TaskComponent {
|
||||||
private router: Router
|
private router: Router
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the initial state of the `disableDrag` property based on the window width.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.updateDisableDragStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the `disableDrag` property based on the current window width.
|
||||||
|
*/
|
||||||
|
private updateDisableDragStatus(): void {
|
||||||
|
this.disableDrag = window.innerWidth > this.DISABLE_DRAG_BREAKPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the button click event on a task in the board
|
* Handles the button click event on a task in the board
|
||||||
* @param event the MouseEvent
|
* @param event the MouseEvent
|
||||||
|
|
@ -60,7 +91,7 @@ export class TaskComponent {
|
||||||
* Toggle the task menu for the current task
|
* Toggle the task menu for the current task
|
||||||
*/
|
*/
|
||||||
toggleTaskMenu() {
|
toggleTaskMenu() {
|
||||||
this.isMenuOpen = !this.isMenuOpen;
|
this.menuOpen = !this.menuOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,7 +103,7 @@ export class TaskComponent {
|
||||||
* If the page is not in the media view, open the task overlay
|
* If the page is not in the media view, open the task overlay
|
||||||
*/
|
*/
|
||||||
openTaskDetailsOverlay(taskId: string) {
|
openTaskDetailsOverlay(taskId: string) {
|
||||||
if (this.isMenuOpen) {
|
if (this.menuOpen) {
|
||||||
this.toggleTaskMenu();
|
this.toggleTaskMenu();
|
||||||
}
|
}
|
||||||
this.pageViewMedia$.pipe(take(1)).subscribe((isPageViewMedia) => {
|
this.pageViewMedia$.pipe(take(1)).subscribe((isPageViewMedia) => {
|
||||||
|
|
@ -96,7 +127,7 @@ export class TaskComponent {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isMenuClicked) {
|
if (!isMenuClicked) {
|
||||||
this.isMenuOpen = false;
|
this.menuOpen = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,8 +148,8 @@ export class TaskComponent {
|
||||||
* @param event the MouseEvent that triggered the dialog
|
* @param event the MouseEvent that triggered the dialog
|
||||||
*/
|
*/
|
||||||
updateDialogPosition(event: MouseEvent) {
|
updateDialogPosition(event: MouseEvent) {
|
||||||
this.dialogX = event.clientX + 25;
|
this.dialogX = event.clientX + this.DIALOG_OFFSET_X;
|
||||||
this.dialogY = event.clientY + 10;
|
this.dialogY = event.clientY + this.DIALOG_OFFSET_Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -149,4 +180,24 @@ export class TaskComponent {
|
||||||
|
|
||||||
return (completedSubtasksCount / this.task.subtasks.length) * 100;
|
return (completedSubtasksCount / this.task.subtasks.length) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits an event to update the status of a task.
|
||||||
|
* @param event The TaskMoveEvent containing the task and the new status to move to.
|
||||||
|
*/
|
||||||
|
onStatusUpdate(event: TaskMoveEvent) {
|
||||||
|
this.updateStatusEmitter.emit({
|
||||||
|
task: event.task,
|
||||||
|
moveTo: event.moveTo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize')
|
||||||
|
/**
|
||||||
|
* Called when the window is resized.
|
||||||
|
* Updates the disableDragStatus so that tasks are not draggable on mobile devices.
|
||||||
|
*/
|
||||||
|
onResize() {
|
||||||
|
this.updateDisableDragStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,3 +24,8 @@ export interface Assignee {
|
||||||
id?: string;
|
id?: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TaskMoveEvent {
|
||||||
|
task: Task;
|
||||||
|
moveTo: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue