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:
@@ -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 && (
|
||||
|
||||
@@ -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')}>
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user