emi-challenge-fe/src/app/features/tasks/feature/task-list-page/task-list-page.ts

200 lines
5.2 KiB
TypeScript

import { afterNextRender, ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
import { TaskStore } from '../../data-access/store/task-store';
import { Task, TaskState } from '../../data-access/models/task.model';
import { TaskDetailSidebar } from '../../ui/task-detail-sidebar/task-detail-sidebar';
import { TaskCreateSidebar } from '../../ui/task-create-sidebar/task-create-sidebar';
import { TaskBoardColumn } from '../../ui/task-board-column/task-board-column';
import { Pagination } from '@shared/ui/pagination/pagination';
export type ViewMode = 'table' | 'board' | 'timeline';
@Component({
selector: 'emi-task-list-page',
imports: [TaskDetailSidebar, TaskCreateSidebar, TaskBoardColumn, Pagination],
templateUrl: './task-list-page.html',
styleUrl: './task-list-page.css',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TaskListPage {
private readonly store = inject(TaskStore);
readonly PAGE_SIZE = 5;
constructor() {
afterNextRender(() => {
this.store.loadTasks();
});
}
readonly tasks = this.store.filteredTasks;
readonly loading = this.store.loading;
readonly selectedTask = this.store.selectedTask;
readonly transitioningState = this.store.transitioningState;
readonly showCreateSidebar = signal(false);
readonly activeView = signal<ViewMode>('table');
readonly currentPage = signal(1);
private readonly stateProgress: Record<TaskState, number> = {
new: 0,
active: 50,
resolved: 100,
closed: 100,
};
private readonly stateLabels: Record<TaskState, string> = {
new: 'New',
active: 'Active',
resolved: 'Resolved',
closed: 'Closed',
};
readonly totalPages = computed(() => {
return Math.max(1, Math.ceil(this.tasks().length / this.PAGE_SIZE));
});
readonly paginatedTasks = computed(() => {
const start = (this.currentPage() - 1) * this.PAGE_SIZE;
return this.tasks().slice(start, start + this.PAGE_SIZE);
});
readonly completionPercent = computed(() => {
const all = this.tasks();
if (all.length === 0) return 0;
const totalProgress = all.reduce((sum, t) => sum + this.getStateProgress(t), 0);
return Math.round(totalProgress / all.length);
});
readonly completedCount = computed(() =>
this.tasks().filter(t => this.isFinalized(t)).length
);
readonly activeCount = computed(() =>
this.tasks().filter(t => this.getState(t) === 'active').length
);
readonly newCount = computed(() =>
this.tasks().filter(t => this.getState(t) === 'new').length
);
readonly tasksByState = computed(() => {
const grouped: Record<TaskState, Task[]> = {
new: [],
active: [],
resolved: [],
closed: [],
};
for (const task of this.tasks()) {
const state = this.getState(task);
grouped[state].push(task);
}
return grouped;
});
readonly tasksByDate = computed(() => {
const sorted = [...this.tasks()].sort((a, b) =>
new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime()
);
const grouped: Record<string, Task[]> = {};
for (const task of sorted) {
const date = task.dueDate;
if (!grouped[date]) {
grouped[date] = [];
}
grouped[date].push(task);
}
return grouped;
});
readonly dueDates = computed(() => Object.keys(this.tasksByDate()));
onPageChange(page: number): void {
this.currentPage.set(page);
}
onViewChange(view: ViewMode): void {
this.activeView.set(view);
}
getState(task: Task): TaskState {
const history = task.stateHistory;
return history[history.length - 1]?.state ?? 'new';
}
getStateLabel(task: Task): string {
return this.stateLabels[this.getState(task)];
}
getStateProgress(task: Task): number {
return this.stateProgress[this.getState(task)];
}
isFinalized(task: Task): boolean {
const state = this.getState(task);
return state === 'resolved' || state === 'closed';
}
isSelected(task: Task): boolean {
return this.selectedTask()?.id === task.id;
}
onViewTask(task: Task): void {
this.showCreateSidebar.set(false);
this.store.setSelectedTask(task);
}
onCloseDetailSidebar(): void {
this.store.setSelectedTask(null);
}
onChangeState(state: TaskState): void {
const task = this.selectedTask();
if (task) {
this.store.transitionTask(task.id, state);
}
}
onDeleteTask(task: Task): void {
this.store.deleteTask(task.id);
this.store.setSelectedTask(null);
}
onAddNote(content: string): void {
const task = this.selectedTask();
if (task) {
this.store.addNote(task.id, content);
}
}
onDeleteNote(index: number): void {
const task = this.selectedTask();
if (task) {
this.store.deleteNote(task.id, index);
}
}
onOpenCreateSidebar(): void {
this.store.setSelectedTask(null);
this.showCreateSidebar.set(true);
}
onCloseCreateSidebar(): void {
this.showCreateSidebar.set(false);
}
onTaskCreated(): void {
this.showCreateSidebar.set(false);
}
onBoardViewTask(task: Task): void {
this.onViewTask(task);
}
onBoardEditTask(task: Task): void {
this.onViewTask(task);
}
onBoardDeleteTask(task: Task): void {
this.onDeleteTask(task);
}
}