diff --git a/src/app/components/add-task/add-task.component.ts b/src/app/components/add-task/add-task.component.ts index 70632cd..cc6111d 100644 --- a/src/app/components/add-task/add-task.component.ts +++ b/src/app/components/add-task/add-task.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; import { AssignedComponent } from './assigned/assigned.component'; -import { Subtask, Task } from '../../interfaces/task.interface'; +import { Subtask, Task, TaskStatus } from '../../interfaces/task.interface'; import { OverlayService } from '../../services/overlay.service'; import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component'; import { ActivatedRoute, Router } from '@angular/router'; @@ -97,7 +97,13 @@ export class AddTaskComponent implements OnInit, OnDestroy { async loadExistingTaskData() { if (this.overlayType === 'newTaskOverlay') { // OverlayData = Status "todo" - this.taskData.status = this.overlayData; + const statusCandidate = this.overlayData as TaskStatus; + + if (Object.values(TaskStatus).includes(statusCandidate)) { + this.taskData.status = statusCandidate; + } else { + console.warn('Invalid status in overlayData:', this.overlayData); + } } else if (this.overlayData) { // OverlayData = Current TaskId const taskData = await firstValueFrom(this.getTaskData(this.overlayData)); diff --git a/src/app/components/board/board.component.html b/src/app/components/board/board.component.html index 544c735..463701f 100644 --- a/src/app/components/board/board.component.html +++ b/src/app/components/board/board.component.html @@ -12,7 +12,6 @@ (input)="searchTask(searchValue)" /> - @if (this.searchInput) { clear - - } @else { search} + /> + } + - +
+ @for (status of STATUSES; track status) {
- {{ "board.todo" | translate }}{{ STATUS_LABELS[status] | translate }} + add
-
- - @for (task of filteredTasks[TODO]; track task.id) { - - - - } @empty { - - @if (isLoading) { - - } @else { - {{ "board.noTasks" | translate }} - } } - - - @if (taskMovedTo === TODO && taskMovedFrom !== TODO) { - - } -
-
-
-
- {{ "board.inProgress" | translate }}add -
-
- - @for (task of filteredTasks[IN_PROGRESS]; track task.id) { - - - - } @empty { - - @if (isLoading) { - - } @else { - {{ "board.noTasks" | translate }} - } } - - - @if (taskMovedTo === IN_PROGRESS && taskMovedFrom !== IN_PROGRESS) { - - } -
-
-
-
- {{ "board.awaitFeedback" | translate }}add -
-
- - @for (task of filteredTasks[AWAIT_FEEDBACK]; track task.id) { - - - - } @empty { - - @if (isLoading) { - - } @else { - {{ "board.noTasks" | translate }} - } } - - - @if (taskMovedTo === AWAIT_FEEDBACK && taskMovedFrom !== - AWAIT_FEEDBACK) { - - } -
-
-
-
- {{ "board.done" | translate }}add -
-
+
- @for (task of filteredTasks[DONE]; track task.id) { + @for (task of filteredTasks[status]; track task.id) { } @empty { + @if (isLoading) { @@ -181,11 +77,12 @@ } } - @if (taskMovedTo === DONE && taskMovedFrom !== DONE) { + @if (taskMovedTo === status && taskMovedFrom !== status) { }
+ }
diff --git a/src/app/components/board/board.component.ts b/src/app/components/board/board.component.ts index 54c854e..04d2810 100644 --- a/src/app/components/board/board.component.ts +++ b/src/app/components/board/board.component.ts @@ -9,7 +9,11 @@ import { Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { TaskHighlightedComponent } from './task/task-highlighted/task-highlighted.component'; import { ApiService } from '../../services/api.service'; -import { Task, TaskMoveEvent } from '../../interfaces/task.interface'; +import { + Task, + TaskMoveEvent, + TaskStatus, +} from '../../interfaces/task.interface'; import { TaskService } from '../../services/task.service'; import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component'; import { debounceTime, finalize, Subject, take, takeUntil } from 'rxjs'; @@ -17,6 +21,7 @@ import { UpdateNotifierService } from '../../services/update-notifier.service'; import { ToastNotificationService } from '../../services/toast-notification.service'; import { ResizeService } from '../../services/resize.service'; import { HeadlineComponent } from '../../shared/components/headline/headline.component'; +import { STATUS_LABELS, STATUSES } from '../../constants/task-status.constants'; @Component({ selector: 'app-board', @@ -35,10 +40,8 @@ import { HeadlineComponent } from '../../shared/components/headline/headline.com styleUrl: './board.component.scss', }) export class BoardComponent implements OnInit, OnDestroy { - readonly TODO = 'todo'; - readonly IN_PROGRESS = 'inprogress'; - readonly AWAIT_FEEDBACK = 'awaitfeedback'; - readonly DONE = 'done'; + readonly STATUSES = STATUSES; + readonly STATUS_LABELS = STATUS_LABELS; allTasks: Array = []; filteredTasks: { [key: string]: Task[] } = {}; @@ -166,7 +169,7 @@ export class BoardComponent implements OnInit, OnDestroy { * @param taskId The id of the task being moved. * @param status The status of the column where the task was dropped. */ - handleItemDropped(task: Task, status: string): void { + handleItemDropped(task: Task, status: TaskStatus): void { if (!task || task.status === status) return; this.apiService @@ -186,7 +189,7 @@ export class BoardComponent implements OnInit, OnDestroy { * @param taskId The ID of the task to be updated. * @param status The new status to assign to the task. */ - updateTaskStatus(taskId: string, status: string) { + updateTaskStatus(taskId: string, status: TaskStatus) { const updatedTask = this.allTasks.find((task) => task.id === taskId); if (updatedTask) { const oldStatus = updatedTask.status; diff --git a/src/app/components/board/task/task-menu/task-menu.component.html b/src/app/components/board/task/task-menu/task-menu.component.html index 68e4f62..2ebccbc 100644 --- a/src/app/components/board/task/task-menu/task-menu.component.html +++ b/src/app/components/board/task/task-menu/task-menu.component.html @@ -1,11 +1,11 @@
- @if (boardTaskStatus !== TODO) { - - } @if (boardTaskStatus !== IN_PROGRESS) { - - } @if (boardTaskStatus !== AWAIT_FEEDBACK) { - - } @if (boardTaskStatus !== DONE) { - - } + + @for (status of STATUSES; track status) { + + + @if (status !== currentTaskStatus) { + + } }
diff --git a/src/app/components/board/task/task-menu/task-menu.component.ts b/src/app/components/board/task/task-menu/task-menu.component.ts index 36519a3..f121c4a 100644 --- a/src/app/components/board/task/task-menu/task-menu.component.ts +++ b/src/app/components/board/task/task-menu/task-menu.component.ts @@ -1,27 +1,31 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Task, TaskMoveEvent } from '../../../../interfaces/task.interface'; +import { + Task, + TaskMoveEvent, + TaskStatus, +} from '../../../../interfaces/task.interface'; +import { + STATUS_LABELS, + STATUSES, +} from '../../../../constants/task-status.constants'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'app-task-menu', standalone: true, - imports: [], + imports: [TranslateModule], templateUrl: './task-menu.component.html', styleUrl: './task-menu.component.scss', }) export class TaskMenuComponent { @Input() task!: Task; - @Input() boardTaskStatus!: string; + @Input() currentTaskStatus!: TaskStatus; @Output() updateStatusEmitter = new EventEmitter(); - readonly TODO = 'todo'; - readonly IN_PROGRESS = 'inprogress'; - readonly AWAIT_FEEDBACK = 'awaitfeedback'; - readonly DONE = 'done'; + readonly STATUSES = STATUSES; + readonly STATUS_LABELS = STATUS_LABELS; - moveTask(moveTo: string) { - this.updateStatusEmitter.emit({ - task: this.task, - moveTo: moveTo, - }); + moveTask(moveTo: TaskStatus) { + this.updateStatusEmitter.emit({ task: this.task, moveTo }); } } diff --git a/src/app/components/board/task/task.component.html b/src/app/components/board/task/task.component.html index a1fc126..77b3300 100644 --- a/src/app/components/board/task/task.component.html +++ b/src/app/components/board/task/task.component.html @@ -91,7 +91,7 @@ @if (mobileMenuOpen){ } diff --git a/src/app/constants/task-status.constants.ts b/src/app/constants/task-status.constants.ts new file mode 100644 index 0000000..1448176 --- /dev/null +++ b/src/app/constants/task-status.constants.ts @@ -0,0 +1,15 @@ +import { TaskStatus } from '../interfaces/task.interface'; + +export const STATUSES: TaskStatus[] = [ + TaskStatus.TODO, + TaskStatus.IN_PROGRESS, + TaskStatus.AWAIT_FEEDBACK, + TaskStatus.DONE, +]; + +export const STATUS_LABELS: Record = { + [TaskStatus.TODO]: 'taskStatus.todo', + [TaskStatus.IN_PROGRESS]: 'taskStatus.inProgress', + [TaskStatus.AWAIT_FEEDBACK]: 'taskStatus.awaitFeedback', + [TaskStatus.DONE]: 'taskStatus.done', +}; diff --git a/src/app/interfaces/task.interface.ts b/src/app/interfaces/task.interface.ts index 93997db..e0a8bae 100644 --- a/src/app/interfaces/task.interface.ts +++ b/src/app/interfaces/task.interface.ts @@ -1,11 +1,22 @@ import { UserSummary } from './user.interface'; +// Enums + +export enum TaskStatus { + TODO = 'todo', + IN_PROGRESS = 'inProgress', + AWAIT_FEEDBACK = 'awaitFeedback', + DONE = 'done', +} + +// Interfaces + export interface Task { id?: string; title: string; description: string; category: string; - status: string; + status: TaskStatus; priority: string; subtasks: Subtask[]; assignees: Assignee[]; @@ -27,5 +38,5 @@ export interface Assignee { export interface TaskMoveEvent { task: Task; - moveTo: string; + moveTo: TaskStatus; } diff --git a/src/app/services/drag-drop.service.ts b/src/app/services/drag-drop.service.ts index 7fa67e0..01dc91c 100644 --- a/src/app/services/drag-drop.service.ts +++ b/src/app/services/drag-drop.service.ts @@ -1,11 +1,11 @@ import { Injectable, EventEmitter } from '@angular/core'; -import { Task } from '../interfaces/task.interface'; +import { Task, TaskStatus } from '../interfaces/task.interface'; @Injectable({ providedIn: 'root', }) export class DragDropService { - itemDropped = new EventEmitter<{ task: Task; status: string }>(); + itemDropped = new EventEmitter<{ task: Task; status: TaskStatus }>(); itemMovedFrom = new EventEmitter<{ status: string }>(); itemMovedTo = new EventEmitter<{ status: string }>(); @@ -37,7 +37,7 @@ export class DragDropService { * - Prevents the default browser behavior. * - Emits the `itemMovedTo` event with the status of the column. */ - allowDrop(event: DragEvent, status: string) { + allowDrop(event: DragEvent, status: TaskStatus) { event.preventDefault(); this.itemMovedTo.emit({ status }); } @@ -53,7 +53,7 @@ export class DragDropService { * - Sets the dragged task to null. * - Emits the `itemMovedTo` event with an empty status, to reset the highlight. */ - drop(event: DragEvent, newStatus: string) { + drop(event: DragEvent, newStatus: TaskStatus) { event.preventDefault(); if (this.draggedTask) { diff --git a/src/app/services/overlay.service.ts b/src/app/services/overlay.service.ts index dff8d6b..9fe936f 100644 --- a/src/app/services/overlay.service.ts +++ b/src/app/services/overlay.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; +import { TaskState } from 'zone.js/lib/zone-impl'; @Injectable({ providedIn: 'root', @@ -16,7 +17,7 @@ export class OverlayService { * @param overlay The overlay type. * @param data The overlay data. */ - setOverlayData(overlay: string, data: any) { + setOverlayData(overlay: string | TaskState, data: any) { this.overlayDataSubject.next({ overlay, data }); } diff --git a/src/app/services/task.service.ts b/src/app/services/task.service.ts index 41b3eeb..961a599 100644 --- a/src/app/services/task.service.ts +++ b/src/app/services/task.service.ts @@ -1,18 +1,23 @@ import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { ApiService } from './api.service'; -import { Task } from '../interfaces/task.interface'; +import { Task, TaskStatus } from '../interfaces/task.interface'; import { catchError, map, switchMap } from 'rxjs/operators'; import { UserSummary } from '../interfaces/user.interface'; @Injectable({ providedIn: 'root' }) export class TaskService { - private readonly statuses = ['todo', 'inprogress', 'awaitfeedback', 'done']; + private readonly statuses: TaskStatus[] = [ + TaskStatus.TODO, + TaskStatus.IN_PROGRESS, + TaskStatus.AWAIT_FEEDBACK, + TaskStatus.DONE, + ]; private readonly priorities = ['low', 'medium', 'high']; constructor(private apiService: ApiService) {} - getStatuses(): string[] { + getStatuses(): TaskStatus[] { return this.statuses; } diff --git a/src/app/shared/components/overlay/overlay.component.ts b/src/app/shared/components/overlay/overlay.component.ts index 6d03ae3..db7359c 100644 --- a/src/app/shared/components/overlay/overlay.component.ts +++ b/src/app/shared/components/overlay/overlay.component.ts @@ -103,6 +103,9 @@ export class OverlayComponent implements OnInit, OnDestroy { * @param {boolean} close Indicates the close event. */ onCloseOverlay() { + this.overlayType = null; + this.overlayData = null; + this.shouldShowOverlay = false; this.overlayService.clearOverlayData(); } diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 0f6a5f6..a2bed9d 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -117,16 +117,18 @@ }, "board": { "headline": "Tafel", - "todo": "Zu Erledigen", - "inProgress": "In Bearbeitung", - "awaitFeedback": "Warte auf Rückmeldung", - "done": "Erledigt", "addTask": "Neue Aufgabe", "findTask": "Aufgabe suchen", "noTasks": "Keine Aufgaben", "dropHere": "Hier ablegen", "emptyTask": "Keine Aufgaben" }, + "taskStatus": { + "todo": "Zu Erledigen", + "inProgress": "In Bearbeitung", + "awaitFeedback": "Warte auf Rückmeldung", + "done": "Erledigt" + }, "addTask": { "headline": "Aufgabe", "title": "Titel", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 565b450..7f52719 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -117,16 +117,18 @@ }, "board": { "headline": "Board", - "todo": "To-do", - "inProgress": "In progress", - "awaitFeedback": "Await feedback", - "done": "Done", "addTask": "Add Task", "findTask": "Find Task", "noTasks": "No tasks", "dropHere": "Drop here", "emptyTask": "No tasks" }, + "taskStatus": { + "todo": "To-do", + "inProgress": "In progress", + "awaitFeedback": "Await feedback", + "done": "Done" + }, "addTask": { "headline": "Add Task", "title": "Title",