Enhance task progress calculation and UI handling

- Updated task progress calculation logic to incorporate weights and time-based estimations for subtasks.
- Improved SQL migrations to support new progress calculation methods and ensure accurate parent task updates.
- Enhanced frontend components to conditionally display progress inputs based on task type and project settings.
- Implemented socket events for real-time updates on subtask counts and progress changes, ensuring consistent UI behavior.
- Added logging for progress updates and task state changes to improve debugging and user experience.
This commit is contained in:
chamikaJ
2025-05-02 13:21:32 +05:30
parent 8f913b0f4e
commit a5b881c609
16 changed files with 870 additions and 112 deletions

View File

@@ -5,7 +5,7 @@ import { useAppSelector } from '@/hooks/useAppSelector';
import { ITaskViewModel } from '@/types/tasks/task.types';
import Flex from 'antd/lib/flex';
import { SocketEvents } from '@/shared/socket-events';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { useSocket } from '@/socket/socketContext';
interface TaskDrawerProgressProps {
@@ -17,16 +17,48 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => {
const { t } = useTranslation('task-drawer/task-drawer');
const { project } = useAppSelector(state => state.projectReducer);
const { socket, connected } = useSocket();
const [confirmedHasSubtasks, setConfirmedHasSubtasks] = useState<boolean | null>(null);
const isSubTask = !!task?.parent_task_id;
const hasSubTasks = task?.sub_tasks_count > 0;
const hasSubTasks = task?.sub_tasks_count > 0 || confirmedHasSubtasks === true;
// Show manual progress input only for tasks without subtasks (not parent tasks)
// Parent tasks get their progress calculated from subtasks
// Additional debug logging
console.log(`TaskDrawerProgress for task ${task.id} (${task.name}): hasSubTasks=${hasSubTasks}, count=${task.sub_tasks_count}, confirmedHasSubtasks=${confirmedHasSubtasks}`);
// HIGHEST PRIORITY CHECK: Never show progress inputs for parent tasks with subtasks
// This check happens before any other logic to ensure consistency
if (hasSubTasks) {
console.error(`REJECTED: Progress input for parent task ${task.id} with ${task.sub_tasks_count} subtasks. confirmedHasSubtasks=${confirmedHasSubtasks}`);
return null;
}
// Double-check by directly querying for subtasks from the server
useEffect(() => {
if (connected && task.id) {
socket?.emit(SocketEvents.GET_TASK_SUBTASKS_COUNT.toString(), task.id);
}
// Listen for the subtask count response
const handleSubtasksCount = (data: any) => {
if (data.task_id === task.id) {
console.log(`Received subtask count for task ${task.id}: ${data.subtask_count}, has_subtasks=${data.has_subtasks}`);
setConfirmedHasSubtasks(data.has_subtasks);
}
};
socket?.on(SocketEvents.TASK_SUBTASKS_COUNT.toString(), handleSubtasksCount);
return () => {
socket?.off(SocketEvents.TASK_SUBTASKS_COUNT.toString(), handleSubtasksCount);
};
}, [socket, connected, task.id]);
// Never show manual progress input for parent tasks (tasks with subtasks)
// Only show progress input for tasks without subtasks
const showManualProgressInput = !hasSubTasks;
// Only show weight input for subtasks in weighted progress mode
const showTaskWeightInput = project?.use_weighted_progress && isSubTask;
const showTaskWeightInput = project?.use_weighted_progress && isSubTask && !hasSubTasks;
useEffect(() => {
// Listen for progress updates from the server
@@ -53,8 +85,13 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => {
};
}, [socket, connected, task.id, form]);
// One last check before rendering
if (hasSubTasks) {
return null;
}
const handleProgressChange = (value: number | null) => {
if (connected && task.id && value !== null) {
if (connected && task.id && value !== null && !hasSubTasks) {
// Ensure parent_task_id is not undefined
const parent_task_id = task.parent_task_id || null;
@@ -67,13 +104,6 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => {
})
);
// If this task has subtasks, request recalculation of its progress
if (hasSubTasks) {
setTimeout(() => {
socket?.emit(SocketEvents.GET_TASK_PROGRESS.toString(), task.id);
}, 100);
}
// If this is a subtask, request the parent's progress to be updated in UI
if (parent_task_id) {
setTimeout(() => {
@@ -84,7 +114,7 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => {
};
const handleWeightChange = (value: number | null) => {
if (connected && task.id && value !== null) {
if (connected && task.id && value !== null && !hasSubTasks) {
// Ensure parent_task_id is not undefined
const parent_task_id = task.parent_task_id || null;
@@ -116,6 +146,11 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => {
return null; // Don't show any progress inputs if not applicable
}
// Final safety check
if (hasSubTasks) {
return null;
}
return (
<>
{showTaskWeightInput && (

View File

@@ -33,6 +33,42 @@ interface TaskDetailsFormProps {
taskFormViewModel?: ITaskFormViewModel | null;
}
// Custom wrapper that enforces stricter rules for displaying progress input
interface ConditionalProgressInputProps {
task: ITaskViewModel;
form: any; // Using any for the form as the exact type may be complex
}
const ConditionalProgressInput = ({ task, form }: ConditionalProgressInputProps) => {
const { project } = useAppSelector(state => state.projectReducer);
const hasSubTasks = task?.sub_tasks_count > 0;
const isSubTask = !!task?.parent_task_id;
// Add more aggressive logging and checks
console.log(`Task ${task.id} status: hasSubTasks=${hasSubTasks}, isSubTask=${isSubTask}, modes: time=${project?.use_time_progress}, manual=${project?.use_manual_progress}, weighted=${project?.use_weighted_progress}`);
// STRICT RULE: Never show progress input for parent tasks with subtasks
// This is the most important check and must be done first
if (hasSubTasks) {
console.log(`Task ${task.id} has ${task.sub_tasks_count} subtasks. Hiding progress input.`);
return null;
}
// Only for tasks without subtasks, determine which input to show based on project mode
if (project?.use_time_progress) {
// In time-based mode, show progress input ONLY for tasks without subtasks
return <TaskDrawerProgress task={{...task, sub_tasks_count: hasSubTasks ? 1 : 0}} form={form} />;
} else if (project?.use_manual_progress) {
// In manual mode, show progress input ONLY for tasks without subtasks
return <TaskDrawerProgress task={{...task, sub_tasks_count: hasSubTasks ? 1 : 0}} form={form} />;
} else if (project?.use_weighted_progress && isSubTask) {
// In weighted mode, show weight input for subtasks
return <TaskDrawerProgress task={{...task, sub_tasks_count: hasSubTasks ? 1 : 0}} form={form} />;
}
return null;
};
const TaskDetailsForm = ({ taskFormViewModel = null }: TaskDetailsFormProps) => {
const { t } = useTranslation('task-drawer/task-drawer');
const [form] = Form.useForm();
@@ -121,8 +157,11 @@ const TaskDetailsForm = ({ taskFormViewModel = null }: TaskDetailsFormProps) =>
<TaskDrawerEstimation t={t} task={taskFormViewModel?.task as ITaskViewModel} form={form} />
{(project?.use_manual_progress || project?.use_weighted_progress) && (taskFormViewModel?.task) && (
<TaskDrawerProgress task={taskFormViewModel?.task as ITaskViewModel} form={form} />
{taskFormViewModel?.task && (
<ConditionalProgressInput
task={taskFormViewModel?.task as ITaskViewModel}
form={form}
/>
)}
<Form.Item name="priority" label={t('taskInfoTab.details.priority')}>

View File

@@ -54,7 +54,7 @@ const ProjectViewTaskList = () => {
<Flex vertical gap={16} style={{ overflowX: 'hidden' }}>
<TaskListFilters position="list" />
{(taskGroups.length === 0 && !loadingGroups) ? (
{(taskGroups && taskGroups.length === 0 && !loadingGroups) ? (
<Empty description="No tasks group found" />
) : (
<Skeleton active loading={loadingGroups} className='mt-4 p-4'>

View File

@@ -63,4 +63,8 @@ export enum SocketEvents {
UPDATE_TASK_PROGRESS,
UPDATE_TASK_WEIGHT,
TASK_PROGRESS_UPDATED,
// Task subtasks count events
GET_TASK_SUBTASKS_COUNT,
TASK_SUBTASKS_COUNT,
}