feat: migrate subtask creation and toggle (check/uncheck) functionality to Django Rest
This commit is contained in:
parent
ff5abfa41a
commit
45a40a3980
7 changed files with 65 additions and 79 deletions
|
|
@ -1,8 +1,7 @@
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component, HostListener, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { FormsModule, NgForm } from '@angular/forms';
|
import { FormsModule, NgForm } from '@angular/forms';
|
||||||
import { AssignedComponent } from './assigned/assigned.component';
|
import { AssignedComponent } from './assigned/assigned.component';
|
||||||
import { FirebaseService } from '../../services/firebase.service';
|
|
||||||
import { Task } from '../../interfaces/task.interface';
|
import { Task } from '../../interfaces/task.interface';
|
||||||
import { OverlayService } from '../../services/overlay.service';
|
import { OverlayService } from '../../services/overlay.service';
|
||||||
import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component';
|
import { FormBtnComponent } from '../../shared/components/buttons/form-btn/form-btn.component';
|
||||||
|
|
@ -38,7 +37,6 @@ export class AddTaskComponent implements OnInit {
|
||||||
subtaskValue: string = '';
|
subtaskValue: string = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public firebaseService: FirebaseService,
|
|
||||||
private overlayService: OverlayService,
|
private overlayService: OverlayService,
|
||||||
private taskService: TaskService,
|
private taskService: TaskService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
|
|
@ -57,7 +55,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
priority: this.taskService.getPriorities()[0],
|
priority: this.taskService.getPriorities()[0],
|
||||||
subtasks: [],
|
subtasks: [],
|
||||||
subtasksTitle: [],
|
subtasksTitle: [],
|
||||||
subtasksDone: [],
|
subtasksStatus: [],
|
||||||
assigned: [],
|
assigned: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
userData: [],
|
userData: [],
|
||||||
|
|
@ -97,7 +95,9 @@ export class AddTaskComponent implements OnInit {
|
||||||
async loadEditTaskData() {
|
async loadEditTaskData() {
|
||||||
if (this.overlayData) {
|
if (this.overlayData) {
|
||||||
const taskData = await firstValueFrom(this.getTaskData(this.overlayData));
|
const taskData = await firstValueFrom(this.getTaskData(this.overlayData));
|
||||||
Object.assign(this.taskData, taskData);
|
if (taskData) {
|
||||||
|
this.taskData = { ...this.taskData, ...taskData };
|
||||||
|
}
|
||||||
} else if (this.overlayType === 'newTaskOverlay') {
|
} else if (this.overlayType === 'newTaskOverlay') {
|
||||||
this.taskData.status = this.overlayData;
|
this.taskData.status = this.overlayData;
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +149,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
*/
|
*/
|
||||||
addSubtask(subtaskName: string) {
|
addSubtask(subtaskName: string) {
|
||||||
this.taskData.subtasksTitle.unshift(subtaskName);
|
this.taskData.subtasksTitle.unshift(subtaskName);
|
||||||
this.taskData.subtasksDone.push(false);
|
this.taskData.subtasksStatus.push(false);
|
||||||
this.saveTaskData();
|
this.saveTaskData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
const index = this.taskData.subtasksTitle.indexOf(subtaskName);
|
const index = this.taskData.subtasksTitle.indexOf(subtaskName);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.taskData.subtasksTitle.splice(index, 1);
|
this.taskData.subtasksTitle.splice(index, 1);
|
||||||
this.taskData.subtasksDone.splice(index, 1);
|
this.taskData.subtasksStatus.splice(index, 1);
|
||||||
this.saveTaskData();
|
this.saveTaskData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -270,7 +270,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
this.taskData.category = '';
|
this.taskData.category = '';
|
||||||
this.taskData.assigned = [];
|
this.taskData.assigned = [];
|
||||||
this.taskData.subtasksTitle = [];
|
this.taskData.subtasksTitle = [];
|
||||||
this.taskData.subtasksDone = [];
|
this.taskData.subtasksStatus = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -298,9 +298,7 @@ export class AddTaskComponent implements OnInit {
|
||||||
|
|
||||||
onSubmit(ngForm: NgForm) {
|
onSubmit(ngForm: NgForm) {
|
||||||
if (ngForm.submitted && ngForm.form.valid) {
|
if (ngForm.submitted && ngForm.form.valid) {
|
||||||
const { id, ...taskWithoutId } = this.taskData;
|
this.apiService.saveNewTask(this.taskData).subscribe({
|
||||||
|
|
||||||
this.apiService.saveNewTask(taskWithoutId).subscribe({
|
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.toastNotificationService.createTaskSuccessToast();
|
this.toastNotificationService.createTaskSuccessToast();
|
||||||
this.updateNotifierService.notifyUpdate('task');
|
this.updateNotifierService.notifyUpdate('task');
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ export class TaskComponent {
|
||||||
* @returns the number of completed subtasks
|
* @returns the number of completed subtasks
|
||||||
*/
|
*/
|
||||||
completedSubtasks(): number {
|
completedSubtasks(): number {
|
||||||
return this.task.subtasks.filter((subtask) => subtask.done).length;
|
return this.task.subtasks.filter((subtask) => subtask.status).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -141,7 +141,7 @@ export class TaskComponent {
|
||||||
*/
|
*/
|
||||||
completedSubtasksPercent(): number {
|
completedSubtasksPercent(): number {
|
||||||
const completedSubtasksCount = this.task.subtasks.filter(
|
const completedSubtasksCount = this.task.subtasks.filter(
|
||||||
(subtask) => subtask.done
|
(subtask) => subtask.status
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
return (completedSubtasksCount / this.task.subtasks.length) * 100;
|
return (completedSubtasksCount / this.task.subtasks.length) * 100;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export interface Task {
|
||||||
priority: string;
|
priority: string;
|
||||||
subtasks: Subtask[];
|
subtasks: Subtask[];
|
||||||
subtasksTitle: string[];
|
subtasksTitle: string[];
|
||||||
subtasksDone: boolean[];
|
subtasksStatus: boolean[];
|
||||||
assigned: string[];
|
assigned: string[];
|
||||||
assignees: Assignee[];
|
assignees: Assignee[];
|
||||||
userData: UserSummary[];
|
userData: UserSummary[];
|
||||||
|
|
@ -18,9 +18,9 @@ export interface Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Subtask {
|
export interface Subtask {
|
||||||
id: number;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
done: boolean;
|
status: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Assignee {
|
export interface Assignee {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,21 @@ export class ApiService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSubtaskStatus(
|
||||||
|
taskId: string,
|
||||||
|
body: {
|
||||||
|
subtask_id: string;
|
||||||
|
subtask_title: string;
|
||||||
|
subtask_status: boolean;
|
||||||
|
}
|
||||||
|
): Observable<any> {
|
||||||
|
return this.request<any>(
|
||||||
|
'PATCH',
|
||||||
|
`/api/tasks/${taskId}/update_subtask/`,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
saveNewTask(task: Task): Observable<Task> {
|
saveNewTask(task: Task): Observable<Task> {
|
||||||
return this.request<Task>('POST', '/api/tasks/', task);
|
return this.request<Task>('POST', '/api/tasks/', task);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,12 @@ export class ToastNotificationService {
|
||||||
this.createInfoToast('Task successfully moved!', 'Task Moved');
|
this.createInfoToast('Task successfully moved!', 'Task Moved');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subtask
|
||||||
|
|
||||||
|
updateSubtaskSuccessToast(): void {
|
||||||
|
this.createSuccessToast('Subtask updated successfully!', 'Subtask Updated');
|
||||||
|
}
|
||||||
|
|
||||||
// Contacts
|
// Contacts
|
||||||
|
|
||||||
createContactSuccessToast(): void {
|
createContactSuccessToast(): void {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="date">
|
<div class="date">
|
||||||
<p>{{ "addTask.date" | translate }}:</p>
|
<p>{{ "addTask.date" | translate }}:</p>
|
||||||
{{ timeConverter(task.date) }}
|
{{ task.date | date : "dd. MMM yyyy" }}
|
||||||
</div>
|
</div>
|
||||||
<div class="priority">
|
<div class="priority">
|
||||||
<p>{{ "addTask.priority" | translate }}:</p>
|
<p>{{ "addTask.priority" | translate }}:</p>
|
||||||
|
|
@ -104,12 +104,17 @@
|
||||||
<div
|
<div
|
||||||
class="single-subtask"
|
class="single-subtask"
|
||||||
(click)="
|
(click)="
|
||||||
toggleSubtaskStatus(overlayData, i, task.subtasksDone, subtask.done)
|
toggleSubtaskStatus(
|
||||||
|
overlayData,
|
||||||
|
subtask.id,
|
||||||
|
subtask.title,
|
||||||
|
subtask.status
|
||||||
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
[src]="
|
[src]="
|
||||||
subtask.done
|
subtask.status
|
||||||
? './../../../../../assets/img/board/check-btn-checked.svg'
|
? './../../../../../assets/img/board/check-btn-checked.svg'
|
||||||
: './../../../../../assets/img/board/check-btn-unchecked.svg'
|
: './../../../../../assets/img/board/check-btn-unchecked.svg'
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,6 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
['Technical Task', '#20d7c2'],
|
['Technical Task', '#20d7c2'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
|
||||||
* OnInit lifecycle hook.
|
|
||||||
*
|
|
||||||
* Initializes the overlay component by checking if the `overlayData` is empty.
|
|
||||||
* If it is, subscribes to route parameters to obtain and assign the `id` to `overlayData`.
|
|
||||||
* Also sets `overlayMobile` to `true` if parameters are successfully retrieved.
|
|
||||||
*/
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.setCurrentUserId();
|
this.setCurrentUserId();
|
||||||
this.setOverlayDataFromRoute();
|
this.setOverlayDataFromRoute();
|
||||||
|
|
@ -117,11 +110,6 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the task with the given overlay data from the Firebase Realtime Database and closes the overlay.
|
|
||||||
* @param overlayData the overlay data of the task to be deleted
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
deleteTask(taskId: string) {
|
deleteTask(taskId: string) {
|
||||||
this.apiService.deleteTaskById(taskId).subscribe({
|
this.apiService.deleteTaskById(taskId).subscribe({
|
||||||
next: (task) => {
|
next: (task) => {
|
||||||
|
|
@ -135,28 +123,31 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the status of a single subtask of a task.
|
|
||||||
*
|
|
||||||
* This function takes the task id, the index of the subtask,
|
|
||||||
* the array of subtask statuses, and the status of the subtask.
|
|
||||||
* It toggles the status of the subtask at the given index
|
|
||||||
* and updates the subtasksDone property of the task in the
|
|
||||||
* Firebase Realtime Database.
|
|
||||||
*
|
|
||||||
* @param taskId The task id of the task containing the subtask.
|
|
||||||
* @param index The index of the subtask.
|
|
||||||
* @param array The array of subtask statuses.
|
|
||||||
* @param status The status of the subtask at the given index.
|
|
||||||
*/
|
|
||||||
toggleSubtaskStatus(
|
toggleSubtaskStatus(
|
||||||
taskId: string,
|
taskId: string,
|
||||||
index: number,
|
subtaskId: string,
|
||||||
array: boolean[],
|
subtaskTitle: string,
|
||||||
status: boolean
|
currentStatus: boolean
|
||||||
) {
|
) {
|
||||||
status ? (array[index] = false) : (array[index] = true);
|
const body = {
|
||||||
this.firebaseService.updateSubTask(taskId, array);
|
subtask_id: subtaskId,
|
||||||
|
subtask_title: subtaskTitle,
|
||||||
|
subtask_status: !currentStatus,
|
||||||
|
};
|
||||||
|
this.apiService.updateSubtaskStatus(taskId, body).subscribe(
|
||||||
|
(response) => {
|
||||||
|
this.task?.subtasks.forEach((subtask) => {
|
||||||
|
if (subtask.id === subtaskId) {
|
||||||
|
subtask.status = !currentStatus;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.toastNotificationService.updateSubtaskSuccessToast();
|
||||||
|
this.updateNotifierService.notifyUpdate('task');
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error('Error updating subtask:', error);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -170,33 +161,4 @@ export class TaskOverlayComponent implements OnInit {
|
||||||
capitalizeFirstLetter(data: string) {
|
capitalizeFirstLetter(data: string) {
|
||||||
return data.charAt(0).toUpperCase() + data.slice(1);
|
return data.charAt(0).toUpperCase() + data.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a date string to a human-readable format.
|
|
||||||
*
|
|
||||||
* @param dateString - The date string to convert.
|
|
||||||
* @returns A string representing the date in the format "MMM. DD, YYYY".
|
|
||||||
*/
|
|
||||||
timeConverter(dateString: string) {
|
|
||||||
var a = new Date(dateString);
|
|
||||||
var months = [
|
|
||||||
'Jan.',
|
|
||||||
'Feb.',
|
|
||||||
'Mar.',
|
|
||||||
'Apr.',
|
|
||||||
'May.',
|
|
||||||
'Jun.',
|
|
||||||
'Jul.',
|
|
||||||
'Aug.',
|
|
||||||
'Sep.',
|
|
||||||
'Oct.',
|
|
||||||
'Nov.',
|
|
||||||
'Dec.',
|
|
||||||
];
|
|
||||||
var year = a.getFullYear();
|
|
||||||
var month = months[a.getMonth()];
|
|
||||||
var date = a.getDate();
|
|
||||||
var time = month + ' ' + date + ', ' + year;
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue