From 21ab2f8a8281d4d30a941b9b36301f4f856cb209 Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Mon, 5 May 2025 11:47:17 +0530 Subject: [PATCH] Enhance task status change handling and progress updates - Updated SQL queries to retrieve color codes for task statuses from the correct table, ensuring accurate data representation. - Added logic to automatically set task progress to 100% when a task is marked as done, improving task completion handling. - Enhanced frontend components to manage task status changes and reflect updates in real-time, including handling parent task progress. - Integrated logging for task status changes and progress updates to improve traceability and debugging. --- .../commands/on-get-done-statuses.ts | 2 +- .../commands/on-task-status-change.ts | 45 +++++++++- .../task-drawer-progress.tsx | 82 +++++++++++++------ 3 files changed, 99 insertions(+), 30 deletions(-) diff --git a/worklenz-backend/src/socket.io/commands/on-get-done-statuses.ts b/worklenz-backend/src/socket.io/commands/on-get-done-statuses.ts index aa22d4cf..4783f4f5 100644 --- a/worklenz-backend/src/socket.io/commands/on-get-done-statuses.ts +++ b/worklenz-backend/src/socket.io/commands/on-get-done-statuses.ts @@ -27,7 +27,7 @@ export async function on_get_done_statuses( // Query to get all statuses in the "done" category for the project const result = await db.query(` - SELECT ts.id, ts.name, ts.sort_order, ts.color_code + SELECT ts.id, ts.name, ts.sort_order, stsc.color_code FROM task_statuses ts INNER JOIN sys_task_status_categories stsc ON ts.category_id = stsc.id WHERE ts.project_id = $1 diff --git a/worklenz-backend/src/socket.io/commands/on-task-status-change.ts b/worklenz-backend/src/socket.io/commands/on-task-status-change.ts index cce0531c..0d003b59 100644 --- a/worklenz-backend/src/socket.io/commands/on-task-status-change.ts +++ b/worklenz-backend/src/socket.io/commands/on-task-status-change.ts @@ -4,10 +4,11 @@ import db from "../../config/db"; import {NotificationsService} from "../../services/notifications/notifications.service"; import {TASK_STATUS_COLOR_ALPHA} from "../../shared/constants"; import {SocketEvents} from "../events"; -import {getLoggedInUserIdFromSocket, log_error, notifyProjectUpdates} from "../util"; +import {getLoggedInUserIdFromSocket, log, log_error, notifyProjectUpdates} from "../util"; import TasksControllerV2 from "../../controllers/tasks-controller-v2"; -import {getTaskDetails, logStatusChange} from "../../services/activity-logs/activity-logs.service"; +import {getTaskDetails, logProgressChange, logStatusChange} from "../../services/activity-logs/activity-logs.service"; import { assignMemberIfNot } from "./on-quick-assign-or-remove"; +import logger from "../../utils/logger"; export async function on_task_status_change(_io: Server, socket: Socket, data?: string) { try { @@ -49,6 +50,46 @@ export async function on_task_status_change(_io: Server, socket: Socket, data?: }); } + // Check if the new status is in a "done" category + if (changeResponse.status_category?.is_done) { + // Get current progress value + const progressResult = await db.query(` + SELECT progress_value, manual_progress + FROM tasks + WHERE id = $1 + `, [body.task_id]); + + const currentProgress = progressResult.rows[0]?.progress_value; + const isManualProgress = progressResult.rows[0]?.manual_progress; + + // Only update if not already 100% + if (currentProgress !== 100) { + // Update progress to 100% + await db.query(` + UPDATE tasks + SET progress_value = 100, manual_progress = TRUE + WHERE id = $1 + `, [body.task_id]); + + log(`Task ${body.task_id} moved to done status - progress automatically set to 100%`, null); + + // Log the progress change to activity logs + await logProgressChange({ + task_id: body.task_id, + old_value: currentProgress !== null ? currentProgress.toString() : "0", + new_value: "100", + socket + }); + + // If this is a subtask, update parent task progress + if (body.parent_task) { + setTimeout(() => { + socket.emit(SocketEvents.GET_TASK_PROGRESS.toString(), body.parent_task); + }, 100); + } + } + } + const info = await TasksControllerV2.getTaskCompleteRatio(body.parent_task || body.task_id); socket.emit(SocketEvents.TASK_STATUS_CHANGE.toString(), { diff --git a/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-progress/task-drawer-progress.tsx b/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-progress/task-drawer-progress.tsx index 75faea5b..f260800e 100644 --- a/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-progress/task-drawer-progress.tsx +++ b/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-progress/task-drawer-progress.tsx @@ -7,6 +7,14 @@ import Flex from 'antd/lib/flex'; import { SocketEvents } from '@/shared/socket-events'; import { useState, useEffect } from 'react'; import { useSocket } from '@/socket/socketContext'; +import { useAuthService } from '@/hooks/useAuth'; +import logger from '@/utils/errorLogger'; +import { ITaskListStatusChangeResponse } from '@/types/tasks/task-list-status.types'; +import { setTaskStatus } from '@/features/task-drawer/task-drawer.slice'; +import { useAppDispatch } from '@/hooks/useAppDispatch'; +import { updateBoardTaskStatus } from '@/features/board/board-slice'; +import { updateTaskStatus } from '@/features/tasks/tasks.slice'; +import useTabSearchParam from '@/hooks/useTabSearchParam'; interface TaskDrawerProgressProps { task: ITaskViewModel; @@ -15,17 +23,18 @@ interface TaskDrawerProgressProps { const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => { const { t } = useTranslation('task-drawer/task-drawer'); + const dispatch = useAppDispatch(); + const { tab } = useTabSearchParam(); + const { project } = useAppSelector(state => state.projectReducer); const { socket, connected } = useSocket(); const [isCompletionModalVisible, setIsCompletionModalVisible] = useState(false); + const currentSession = useAuthService().getCurrentSession(); const isSubTask = !!task?.parent_task_id; // Safe handling of sub_tasks_count which might be undefined in some cases const hasSubTasks = (task?.sub_tasks_count || 0) > 0; - // Log task details for debugging - console.log(`TaskDrawerProgress: task=${task?.id}, sub_tasks_count=${task?.sub_tasks_count}, hasSubTasks=${hasSubTasks}`); - // HIGHEST PRIORITY CHECK: Never show progress inputs for parent tasks with subtasks if (hasSubTasks) { return null; @@ -48,7 +57,7 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => { if (data.weight !== undefined) { form.setFieldsValue({ weight: data.weight }); } - + // Check if we should prompt the user to mark the task as done if (data.should_prompt_for_done) { setIsCompletionModalVisible(true); @@ -79,7 +88,7 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => { if (value === 100) { setIsCompletionModalVisible(true); } - + // Ensure parent_task_id is not undefined const parent_task_id = task.parent_task_id || null; @@ -114,7 +123,7 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => { parent_task_id: parent_task_id, }) ); - + // If this is a subtask, request the parent's progress to be updated in UI if (parent_task_id) { setTimeout(() => { @@ -127,30 +136,49 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => { const handleMarkTaskAsComplete = () => { // Close the modal setIsCompletionModalVisible(false); - + // Find a "Done" status for this project if (connected && task.id) { // Emit socket event to get "done" category statuses - socket?.emit(SocketEvents.GET_DONE_STATUSES.toString(), task.project_id, (doneStatuses: any[]) => { - if (doneStatuses && doneStatuses.length > 0) { - // Use the first "done" status - const doneStatusId = doneStatuses[0].id; - - // Emit socket event to update the task status - socket?.emit( - SocketEvents.TASK_STATUS_CHANGE.toString(), - JSON.stringify({ - task_id: task.id, - status_id: doneStatusId, - project_id: task.project_id - }) - ); - - console.log(`Task ${task.id} marked as done with status ${doneStatusId}`); - } else { - console.error(`No "done" statuses found for project ${task.project_id}`); + socket?.emit( + SocketEvents.GET_DONE_STATUSES.toString(), + task.project_id, + (doneStatuses: any[]) => { + if (doneStatuses && doneStatuses.length > 0) { + // Use the first "done" status + const doneStatusId = doneStatuses[0].id; + + // Emit socket event to update the task status + socket?.emit( + SocketEvents.TASK_STATUS_CHANGE.toString(), + JSON.stringify({ + task_id: task.id, + status_id: doneStatusId, + project_id: task.project_id, + team_id: currentSession?.team_id, + parent_task: task.parent_task_id || null, + }) + ); + socket?.once( + SocketEvents.TASK_STATUS_CHANGE.toString(), + (data: ITaskListStatusChangeResponse) => { + dispatch(setTaskStatus(data)); + + if (tab === 'tasks-list') { + dispatch(updateTaskStatus(data)); + } + if (tab === 'board') { + dispatch(updateBoardTaskStatus(data)); + } + if (data.parent_task) + socket?.emit(SocketEvents.GET_TASK_PROGRESS.toString(), data.parent_task); + } + ); + } else { + logger.error(`No "done" statuses found for project ${task.project_id}`); + } } - }); + ); } }; @@ -235,7 +263,7 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => { /> )} - +