refactor: improve task database communication and enhance data flow in components
This commit is contained in:
parent
6e0429255b
commit
72ca03086d
12 changed files with 189 additions and 172 deletions
|
|
@ -24,7 +24,7 @@
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"inlineStyleLanguage": "scss",
|
"inlineStyleLanguage": "scss",
|
||||||
"assets": ["src/favicon.ico", "src/assets"],
|
"assets": ["src/favicon.ico", "src/assets"],
|
||||||
"styles": ["src/styles.css", "node_modules/ngx-toastr/toastr.css"],
|
"styles": ["src/styles.scss", "node_modules/ngx-toastr/toastr.css"],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
"tsConfig": "tsconfig.spec.json",
|
"tsConfig": "tsconfig.spec.json",
|
||||||
"inlineStyleLanguage": "scss",
|
"inlineStyleLanguage": "scss",
|
||||||
"assets": ["src/favicon.ico", "src/assets"],
|
"assets": ["src/favicon.ico", "src/assets"],
|
||||||
"styles": ["src/styles.css", "node_modules/ngx-toastr/toastr.css"],
|
"styles": ["src/styles.scss", "node_modules/ngx-toastr/toastr.css"],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -324,7 +324,7 @@
|
||||||
? 'block'
|
? 'block'
|
||||||
: 'none'
|
: 'none'
|
||||||
}"
|
}"
|
||||||
(click)="deleteTaskData(overlayData)"
|
(click)="deleteTask(overlayData)"
|
||||||
></app-form-btn>
|
></app-form-btn>
|
||||||
<app-form-btn
|
<app-form-btn
|
||||||
[class]="'btn-submit'"
|
[class]="'btn-submit'"
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { SharedService } from '../../services/shared.service';
|
import { SharedService } from '../../services/shared.service';
|
||||||
|
import { AuthService } from '../../services/auth.service';
|
||||||
|
import { firstValueFrom, map } from 'rxjs';
|
||||||
|
import { TaskService } from '../../services/task.service';
|
||||||
|
import { ApiService } from '../../services/api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-add-task',
|
selector: 'app-add-task',
|
||||||
|
|
@ -44,6 +48,9 @@ export class AddTaskComponent implements OnInit {
|
||||||
public firebaseService: FirebaseService,
|
public firebaseService: FirebaseService,
|
||||||
private overlayService: OverlayService,
|
private overlayService: OverlayService,
|
||||||
private sharedService: SharedService,
|
private sharedService: SharedService,
|
||||||
|
private taskService: TaskService,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private authService: AuthService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -52,15 +59,15 @@ export class AddTaskComponent implements OnInit {
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
category: '',
|
category: '',
|
||||||
status: 'todo',
|
status: this.taskService.getStatuses()[0],
|
||||||
priority: 'medium',
|
priority: this.taskService.getPriorities()[0],
|
||||||
subtasks: [],
|
subtasks: [],
|
||||||
subtasksTitle: [],
|
subtasksTitle: [],
|
||||||
subtasksDone: [],
|
subtasksDone: [],
|
||||||
assigned: [],
|
assigned: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
userData: [],
|
userData: [],
|
||||||
creator: this.firebaseService.getCurrentUserId(),
|
creator: '',
|
||||||
date: this.currentDate,
|
date: this.currentDate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -72,21 +79,30 @@ export class AddTaskComponent implements OnInit {
|
||||||
* - Loads any existing task data from local storage.
|
* - Loads any existing task data from local storage.
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.setCurrentUserId();
|
||||||
this.loadEditTaskData();
|
this.loadEditTaskData();
|
||||||
this.routeParams();
|
this.routeParams();
|
||||||
this.loadLocalStorageData();
|
this.loadLocalStorageData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCurrentUserId() {
|
||||||
|
this.authService
|
||||||
|
.getCurrentUserId()
|
||||||
|
.pipe(map((userId) => userId ?? ''))
|
||||||
|
.subscribe((userId) => {
|
||||||
|
this.taskData.creator = userId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads task data for editing if applicable, or sets the task status if this is a new task overlay.
|
* Loads task data for editing if applicable, or sets the task status if this is a new task overlay.
|
||||||
* @param {string} overlayData The task id or status of the task to be loaded.
|
* @param {string} overlayData The task id or status of the task to be loaded.
|
||||||
* @param {string} overlayType The type of overlay to be opened.
|
* @param {string} overlayType The type of overlay to be opened.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
loadEditTaskData() {
|
async loadEditTaskData() {
|
||||||
const excludedValues = ['', 'todo', 'inprogress', 'awaitfeedback', 'done'];
|
if (this.overlayData) {
|
||||||
if (!excludedValues.includes(this.overlayData)) {
|
const taskData = await firstValueFrom(this.getTaskData(this.overlayData));
|
||||||
const taskData = this.getTaskData(this.overlayData)[0];
|
|
||||||
Object.assign(this.taskData, taskData);
|
Object.assign(this.taskData, taskData);
|
||||||
} else if (this.overlayType === 'newTaskOverlay') {
|
} else if (this.overlayType === 'newTaskOverlay') {
|
||||||
this.taskData.status = this.overlayData;
|
this.taskData.status = this.overlayData;
|
||||||
|
|
@ -156,9 +172,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
* @returns {Task[]} an array of tasks with the given id
|
* @returns {Task[]} an array of tasks with the given id
|
||||||
*/
|
*/
|
||||||
getTaskData(taskId: string) {
|
getTaskData(taskId: string) {
|
||||||
return this.firebaseService
|
return this.apiService.getTaskById(taskId);
|
||||||
.getAllTasks()
|
|
||||||
.filter((task) => task.id === taskId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -357,43 +371,20 @@ export class AddTaskComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Submits the task form and saves the task data to the Firebase Realtime Database.
|
|
||||||
*
|
|
||||||
* If the form is valid and the overlay data is one of the allowed values, this
|
|
||||||
* function adds a new task to the 'tasks' node in the Firebase Realtime Database.
|
|
||||||
* If the overlay data is not one of the allowed values, this function updates the
|
|
||||||
* task with the given overlay data in the 'tasks' node.
|
|
||||||
*
|
|
||||||
* After submitting the form, this function resets the form and closes the overlay.
|
|
||||||
* It then navigates to the '/board' route.
|
|
||||||
* @param ngForm the form to submit
|
|
||||||
* @param overlayData the overlay data corresponding to the task to be submitted
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onSubmit(ngForm: NgForm, overlayData: string) {
|
onSubmit(ngForm: NgForm, overlayData: string) {
|
||||||
const allowedValues = [
|
|
||||||
'',
|
|
||||||
'none',
|
|
||||||
'todo',
|
|
||||||
'inprogress',
|
|
||||||
'awaitfeedback',
|
|
||||||
'done',
|
|
||||||
];
|
|
||||||
if (ngForm.submitted && ngForm.form.valid) {
|
if (ngForm.submitted && ngForm.form.valid) {
|
||||||
if (allowedValues.includes(overlayData)) {
|
const { id, ...taskWithoutId } = this.taskData;
|
||||||
const { id, ...taskWithoutId } = this.taskData;
|
|
||||||
this.firebaseService.addNewTask(taskWithoutId);
|
this.apiService.saveNewTask(taskWithoutId).subscribe({
|
||||||
this.removeTaskData(ngForm);
|
next: (response) => {
|
||||||
this.closeOverlay();
|
this.removeTaskData(ngForm);
|
||||||
} else {
|
|
||||||
if (this.getTaskData(overlayData).length > 0) {
|
|
||||||
const { id, ...taskWithoutId } = this.taskData;
|
|
||||||
this.firebaseService.replaceTaskData(overlayData, taskWithoutId);
|
|
||||||
this.closeOverlay();
|
this.closeOverlay();
|
||||||
}
|
this.router.navigate(['/board']);
|
||||||
}
|
},
|
||||||
this.router.navigate(['/board']);
|
error: (error) => {
|
||||||
|
console.error('Fehler beim Speichern:', error);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -419,9 +410,9 @@ export class AddTaskComponent implements OnInit {
|
||||||
* @param overlayData the overlay data of the task to be deleted
|
* @param overlayData the overlay data of the task to be deleted
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
deleteTaskData(overlayData: string) {
|
deleteTask(overlayData: string) {
|
||||||
this.firebaseService.deleteTask(overlayData);
|
this.apiService.deleteTaskById(overlayData);
|
||||||
this.closeOverlay();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:click', ['$event'])
|
@HostListener('document:click', ['$event'])
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { ApiService } from '../../services/api.service';
|
||||||
import { Task } from '../../interfaces/task.interface';
|
import { Task } 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 { finalize, Subject, takeUntil } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-board',
|
selector: 'app-board',
|
||||||
|
|
@ -30,6 +31,8 @@ import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner
|
||||||
styleUrl: './board.component.scss',
|
styleUrl: './board.component.scss',
|
||||||
})
|
})
|
||||||
export class BoardComponent {
|
export class BoardComponent {
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
readonly TODO = 'todo';
|
readonly TODO = 'todo';
|
||||||
readonly IN_PROGRESS = 'inprogress';
|
readonly IN_PROGRESS = 'inprogress';
|
||||||
readonly AWAIT_FEEDBACK = 'awaitfeedback';
|
readonly AWAIT_FEEDBACK = 'awaitfeedback';
|
||||||
|
|
@ -53,33 +56,37 @@ export class BoardComponent {
|
||||||
taskMovedFrom: string = '';
|
taskMovedFrom: string = '';
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* Is called when the component is initialized.
|
|
||||||
* Calls the `loadTasks` method to load tasks and subscribes to drag-and-drop events via `subscribeToDragDropEvents`.
|
|
||||||
*/
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.loadTasks();
|
this.loadAllTasks();
|
||||||
this.subscribeToDragDropEvents();
|
this.subscribeToDragDropEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
loadAllTasks(): void {
|
||||||
* Retrieves all tasks from the API and initializes the `allTasks` and `filteredTasks` properties.
|
|
||||||
*/
|
|
||||||
|
|
||||||
loadTasks(): void {
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
this.taskService.loadAllTasks().subscribe({
|
this.taskService
|
||||||
next: (result) => {
|
.getTasksWithUsers()
|
||||||
this.allTasks = result.allTasks;
|
.pipe(finalize(() => (this.isLoading = false)))
|
||||||
this.filteredTasks = result.filteredTasks;
|
.subscribe({
|
||||||
this.isLoading = false;
|
next: (response) => {
|
||||||
},
|
this.allTasks = response.allTasks;
|
||||||
error: (err) => {
|
this.filteredTasks = this.groupTasksByStatus(response.allTasks);
|
||||||
console.error('Error loading the tasks:', err);
|
},
|
||||||
this.isLoading = false;
|
error: (err) => {
|
||||||
},
|
console.error('Error loading the tasks:', err);
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
groupTasksByStatus(tasks: Task[]): { [key: string]: Task[] } {
|
||||||
|
return tasks.reduce((acc, task) => {
|
||||||
|
const status = task.status;
|
||||||
|
if (!acc[status]) {
|
||||||
|
acc[status] = [];
|
||||||
|
}
|
||||||
|
acc[status].push(task);
|
||||||
|
return acc;
|
||||||
|
}, {} as { [key: string]: Task[] });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -178,4 +185,9 @@ export class BoardComponent {
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { FirebaseService } from '../../services/firebase.service';
|
||||||
import { Task } from '../../interfaces/task.interface';
|
import { Task } 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 { finalize } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-summary',
|
selector: 'app-summary',
|
||||||
|
|
@ -29,25 +30,26 @@ export class SummaryComponent {
|
||||||
* This method loads all tasks from the TaskService.
|
* This method loads all tasks from the TaskService.
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.loadTasks();
|
this.loadAllTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all tasks from the TaskService.
|
* Loads all tasks from the TaskService.
|
||||||
*/
|
*/
|
||||||
loadTasks(): void {
|
loadAllTasks(): void {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
this.taskService.loadAllTasks().subscribe({
|
this.taskService
|
||||||
next: (result) => {
|
.getTasks()
|
||||||
this.allTasks = result.allTasks;
|
.pipe(finalize(() => (this.isLoading = false)))
|
||||||
this.isLoading = false;
|
.subscribe({
|
||||||
},
|
next: (response) => {
|
||||||
error: (err) => {
|
this.allTasks = response;
|
||||||
console.error('Error loading the tasks:', err);
|
},
|
||||||
this.isLoading = false;
|
error: (err) => {
|
||||||
},
|
console.error('Error loading the tasks:', err);
|
||||||
});
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -36,20 +36,28 @@ export class ApiService {
|
||||||
|
|
||||||
// ------------- TASKS ------------- //
|
// ------------- TASKS ------------- //
|
||||||
|
|
||||||
|
getTasks(): Observable<Task[]> {
|
||||||
|
return this.request<Task[]>('GET', `/api/tasks/`);
|
||||||
|
}
|
||||||
|
|
||||||
getTaskById(taskId: string): Observable<Task> {
|
getTaskById(taskId: string): Observable<Task> {
|
||||||
return this.request<Task>('GET', `/api/tasks/${taskId}/`);
|
return this.request<Task>('GET', `/api/tasks/${taskId}/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTasksByStatus(status: string): Observable<Task[]> {
|
|
||||||
return this.request<Task[]>('GET', '/api/tasks/', undefined, { status });
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTaskStatus(taskId: string, status: string): Observable<Task> {
|
updateTaskStatus(taskId: string, status: string): Observable<Task> {
|
||||||
return this.request<Task>('PUT', `/api/tasks/${taskId}/update_status/`, {
|
return this.request<Task>('PATCH', `/api/tasks/${taskId}/update_status/`, {
|
||||||
status,
|
status,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveNewTask(task: Task): Observable<Task> {
|
||||||
|
return this.request<Task>('POST', '/api/tasks/', task);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTaskById(taskId: string): Observable<Task> {
|
||||||
|
return this.request<Task>('DELETE', `/api/tasks/${taskId}/`);
|
||||||
|
}
|
||||||
|
|
||||||
// ------------- USERS ------------- //
|
// ------------- USERS ------------- //
|
||||||
|
|
||||||
getUserById(userId: string): Observable<User> {
|
getUserById(userId: string): Observable<User> {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import {
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { TokenService } from './token.service';
|
import { TokenService } from './token.service';
|
||||||
import { ErrorHandlingService } from './error-handling.service';
|
import { ErrorHandlingService } from './error-handling.service';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable, forkJoin, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { Task } from '../interfaces/task.interface';
|
import { Task } from '../interfaces/task.interface';
|
||||||
import { catchError, map, switchMap } from 'rxjs/operators';
|
import { catchError, map, switchMap } from 'rxjs/operators';
|
||||||
|
|
@ -8,55 +8,67 @@ import { UserSummary } from '../interfaces/user.interface';
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class TaskService {
|
export class TaskService {
|
||||||
private readonly statuses = ['todo', 'inprogress', 'awaitfeedback', 'done'];
|
private readonly statuses = ['todo', 'inprogress', 'awaitfeedback', 'done'];
|
||||||
|
private readonly priorities = ['low', 'medium', 'high'];
|
||||||
|
|
||||||
constructor(private apiService: ApiService) {}
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
loadAllTasks(): Observable<{
|
getStatuses(): string[] {
|
||||||
allTasks: Task[];
|
return this.statuses;
|
||||||
filteredTasks: Record<string, Task[]>;
|
}
|
||||||
}> {
|
|
||||||
return forkJoin(
|
|
||||||
this.statuses.map((status) => this.fetchTasksByStatus(status))
|
|
||||||
).pipe(
|
|
||||||
switchMap((results) => {
|
|
||||||
const allTasks = results.flat();
|
|
||||||
const filteredTasks = this.statuses.reduce((acc, status, index) => {
|
|
||||||
acc[status] = results[index] || [];
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, Task[]>);
|
|
||||||
|
|
||||||
return this.loadUsersForTasks(allTasks).pipe(
|
getPriorities(): string[] {
|
||||||
map((tasksWithUsers) => {
|
return this.priorities;
|
||||||
const updatedFilteredTasks = this.statuses.reduce((acc, status) => {
|
}
|
||||||
acc[status] = tasksWithUsers.filter(
|
|
||||||
(task) => task.status === status
|
|
||||||
);
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, Task[]>);
|
|
||||||
|
|
||||||
return {
|
getTasks(): Observable<Task[]> {
|
||||||
allTasks: tasksWithUsers,
|
return this.fetchTasks();
|
||||||
filteredTasks: updatedFilteredTasks,
|
}
|
||||||
};
|
|
||||||
})
|
getTasksWithUsers(): Observable<{ allTasks: Task[] }> {
|
||||||
);
|
return this.fetchTasks().pipe(
|
||||||
})
|
switchMap((tasks) =>
|
||||||
|
this.attachUsersToTasks(tasks).pipe(
|
||||||
|
map((tasksWithUsers) => ({ allTasks: tasksWithUsers }))
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchTasksByStatus(status: string): Observable<Task[]> {
|
getTaskById(taskId: string): Observable<Task | null> {
|
||||||
return this.apiService.getTasksByStatus(status).pipe(
|
return this.apiService.getTaskById(taskId).pipe(
|
||||||
catchError((error) => {
|
catchError((error) => {
|
||||||
console.error(
|
console.error('Error loading the task:', error);
|
||||||
`Fehler beim Abrufen von Tasks für Status ${status}:`,
|
return of(null);
|
||||||
error
|
}),
|
||||||
);
|
switchMap((task) => (task ? this.attachUsersToTask(task) : of(null)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchTasks(): Observable<Task[]> {
|
||||||
|
return this.apiService.getTasks().pipe(
|
||||||
|
catchError((error) => {
|
||||||
|
console.error('Error when retrieving the tasks:', error);
|
||||||
return of([]);
|
return of([]);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadUsersForTasks(tasks: Task[]): Observable<Task[]> {
|
private attachUsersToTask(task: Task): Observable<Task> {
|
||||||
|
const userIds = [task.creator, ...task.assignees.map((a) => a.userId)];
|
||||||
|
|
||||||
|
return this.apiService.getUsersByIds(userIds).pipe(
|
||||||
|
catchError((error) => {
|
||||||
|
console.error('Error when retrieving user data:', error);
|
||||||
|
return of([]);
|
||||||
|
}),
|
||||||
|
map((users) => ({
|
||||||
|
...task,
|
||||||
|
userData: this.mapTaskUsers(task, this.createUserMap(users)),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private attachUsersToTasks(tasks: Task[]): Observable<Task[]> {
|
||||||
const userIds = new Set(
|
const userIds = new Set(
|
||||||
tasks.flatMap((task) => [
|
tasks.flatMap((task) => [
|
||||||
task.creator,
|
task.creator,
|
||||||
|
|
@ -66,62 +78,32 @@ export class TaskService {
|
||||||
|
|
||||||
return this.apiService.getUsersByIds([...userIds]).pipe(
|
return this.apiService.getUsersByIds([...userIds]).pipe(
|
||||||
catchError((error) => {
|
catchError((error) => {
|
||||||
console.error('Fehler beim Abrufen der Benutzer:', error);
|
console.error('Error when retrieving users:', error);
|
||||||
return of([]);
|
return of([]);
|
||||||
}),
|
}),
|
||||||
map((users) => {
|
map((users) => {
|
||||||
const userMap = this.createUserMap(users);
|
const userMap = this.createUserMap(users);
|
||||||
return tasks.map((task) => ({
|
return tasks.map((task) => ({
|
||||||
...task,
|
...task,
|
||||||
userData: [
|
userData: this.mapTaskUsers(task, userMap),
|
||||||
...task.assignees.map((a) => userMap[a.userId] || null),
|
|
||||||
userMap[task.creator] || null,
|
|
||||||
].filter(Boolean),
|
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSingleTask(taskId: string): Observable<Task | null> {
|
|
||||||
return this.apiService.getTaskById(taskId).pipe(
|
|
||||||
switchMap((task) => {
|
|
||||||
if (!task) return of(null);
|
|
||||||
const userIds = [task.creator, ...task.assignees.map((a) => a.userId)];
|
|
||||||
|
|
||||||
return this.apiService.getUsersByIds(userIds).pipe(
|
|
||||||
map((users) => ({
|
|
||||||
...task,
|
|
||||||
userData: this.mapTaskUsers(task, users),
|
|
||||||
})),
|
|
||||||
catchError((error) => {
|
|
||||||
console.error('Fehler beim Abrufen der Benutzerdaten:', error);
|
|
||||||
return of(task);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
catchError((error) => {
|
|
||||||
console.error('Fehler beim Laden des Tasks:', error);
|
|
||||||
return of(null);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createUserMap(users: UserSummary[]): Record<string, UserSummary> {
|
private createUserMap(users: UserSummary[]): Record<string, UserSummary> {
|
||||||
return users.reduce((acc, user) => {
|
return users.reduce((acc, user) => {
|
||||||
if (user?.id)
|
if (user?.id) {
|
||||||
acc[user.id] = {
|
acc[user.id] = user;
|
||||||
id: user.id,
|
}
|
||||||
firstName: user.firstName,
|
|
||||||
lastName: user.lastName,
|
|
||||||
initials: user.initials,
|
|
||||||
color: user.color,
|
|
||||||
};
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, UserSummary>);
|
}, {} as Record<string, UserSummary>);
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapTaskUsers(task: Task, users: UserSummary[]): UserSummary[] {
|
private mapTaskUsers(
|
||||||
const userMap = this.createUserMap(users);
|
task: Task,
|
||||||
|
userMap: Record<string, UserSummary>
|
||||||
|
): UserSummary[] {
|
||||||
return [
|
return [
|
||||||
...task.assignees.map((a) => userMap[a.userId] || null),
|
...task.assignees.map((a) => userMap[a.userId] || null),
|
||||||
userMap[task.creator] || null,
|
userMap[task.creator] || null,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@if (overlayType === 'newTaskOverlay' || (overlayData !== '' &&
|
@if (overlayType === 'newTaskOverlay' || (overlayData !== '' &&
|
||||||
getTaskData(overlayData).length > 0)) {
|
getTaskData(overlayData) !== undefined)) {
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<app-btn-close (click)="closeDialog()"></app-btn-close>
|
<app-btn-close (click)="closeDialog()"></app-btn-close>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { AssignedComponent } from '../../../../components/add-task/assigned/assi
|
||||||
import { AddTaskComponent } from '../../../../components/add-task/add-task.component';
|
import { AddTaskComponent } from '../../../../components/add-task/add-task.component';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { BtnBackComponent } from '../../buttons/btn-back/btn-back.component';
|
import { BtnBackComponent } from '../../buttons/btn-back/btn-back.component';
|
||||||
|
import { ApiService } from '../../../../services/api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-task-edit-overlay',
|
selector: 'app-task-edit-overlay',
|
||||||
|
|
@ -24,6 +25,7 @@ export class TaskEditOverlayComponent {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public firebaseService: FirebaseService,
|
public firebaseService: FirebaseService,
|
||||||
|
private apiService: ApiService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router
|
private router: Router
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -53,9 +55,7 @@ export class TaskEditOverlayComponent {
|
||||||
* @returns {Task[]} an array of tasks with the given id
|
* @returns {Task[]} an array of tasks with the given id
|
||||||
*/
|
*/
|
||||||
getTaskData(taskId: string) {
|
getTaskData(taskId: string) {
|
||||||
return this.firebaseService
|
return this.apiService.getTaskById(taskId);
|
||||||
.getAllTasks()
|
|
||||||
.filter((task) => task.id === taskId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@if(task.creator === firebaseService.getCurrentUserId()) {
|
@if(task.creator === currentUserId) {
|
||||||
<div class="btns">
|
<div class="btns">
|
||||||
<div class="btn btn-delete" (click)="deleteTask(overlayData)">
|
<div class="btn btn-delete" (click)="deleteTask(overlayData)">
|
||||||
<img src="./../../../../../assets/img/contact/delete.svg" alt="" />
|
<img src="./../../../../../assets/img/contact/delete.svg" alt="" />
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ import { BtnBackComponent } from '../../buttons/btn-back/btn-back.component';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { Task } from '../../../../interfaces/task.interface';
|
import { Task } from '../../../../interfaces/task.interface';
|
||||||
import { TaskService } from '../../../../services/task.service';
|
import { TaskService } from '../../../../services/task.service';
|
||||||
|
import { AuthService } from '../../../../services/auth.service';
|
||||||
|
import { map } from 'rxjs';
|
||||||
|
import { ApiService } from '../../../../services/api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-task-overlay',
|
selector: 'app-task-overlay',
|
||||||
|
|
@ -22,11 +25,14 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
|
|
||||||
task: Task | null = null;
|
task: Task | null = null;
|
||||||
overlayMobile: boolean = false;
|
overlayMobile: boolean = false;
|
||||||
|
currentUserId: string = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public firebaseService: FirebaseService,
|
public firebaseService: FirebaseService,
|
||||||
private overlayService: OverlayService,
|
private overlayService: OverlayService,
|
||||||
private taskService: TaskService,
|
private taskService: TaskService,
|
||||||
|
private authService: AuthService,
|
||||||
|
private apiService: ApiService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -44,10 +50,20 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
* Also sets `overlayMobile` to `true` if parameters are successfully retrieved.
|
* Also sets `overlayMobile` to `true` if parameters are successfully retrieved.
|
||||||
*/
|
*/
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.setCurrentUserId();
|
||||||
this.setOverlayDataFromRoute();
|
this.setOverlayDataFromRoute();
|
||||||
this.loadTask(this.overlayData);
|
this.loadTask(this.overlayData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCurrentUserId() {
|
||||||
|
this.authService
|
||||||
|
.getCurrentUserId()
|
||||||
|
.pipe(map((userId) => userId ?? ''))
|
||||||
|
.subscribe((userId) => {
|
||||||
|
this.currentUserId = userId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setOverlayDataFromRoute() {
|
setOverlayDataFromRoute() {
|
||||||
if (this.overlayData === '') {
|
if (this.overlayData === '') {
|
||||||
this.route.params.subscribe((params) => {
|
this.route.params.subscribe((params) => {
|
||||||
|
|
@ -58,7 +74,7 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTask(taskId: string) {
|
loadTask(taskId: string) {
|
||||||
this.taskService.loadSingleTask(taskId).subscribe({
|
this.taskService.getTaskById(taskId).subscribe({
|
||||||
next: (task) => {
|
next: (task) => {
|
||||||
this.task = task;
|
this.task = task;
|
||||||
},
|
},
|
||||||
|
|
@ -102,8 +118,15 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
* @param overlayData the overlay data of the task to be deleted
|
* @param overlayData the overlay data of the task to be deleted
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
deleteTask(overlayData: string) {
|
deleteTask(taskId: string) {
|
||||||
this.firebaseService.deleteTask(overlayData);
|
this.apiService.deleteTaskById(taskId).subscribe({
|
||||||
|
next: (task) => {
|
||||||
|
console.log('Task deleted successfully:', task);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
console.error('Error deleting task', err);
|
||||||
|
},
|
||||||
|
});
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue