refactor(task-management): optimize task selection handling in TaskListBoard

- Improved task selection logic by utilizing a Set for efficient addition and removal of selected task IDs.
- Streamlined the dispatch of selected tasks and IDs to Redux state, enhancing performance and readability.
- Removed unused context menu logic from TaskRow component, simplifying the codebase and focusing on subtask expansion management.
This commit is contained in:
chamikaJ
2025-07-03 09:44:16 +05:30
parent bb4229a82d
commit c19c1c2f34
2 changed files with 29 additions and 89 deletions

View File

@@ -414,14 +414,26 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
const handleSelectTask = useCallback( const handleSelectTask = useCallback(
(taskId: string, selected: boolean) => { (taskId: string, selected: boolean) => {
// Create a new Set from existing selections for efficient lookup and modification
const currentSelectedIds = new Set(selectedTaskIds);
// Update the selection state based on the checkbox action
if (selected) { if (selected) {
// Add task to bulk selection currentSelectedIds.add(taskId);
const task = tasks.find(t => t.id === taskId); } else {
if (task) { currentSelectedIds.delete(taskId);
// Convert Task to IProjectTask format for bulk actions }
const projectTask: IProjectTask = {
// Convert Set back to array for Redux state
const newSelectedIds = Array.from(currentSelectedIds);
// Map selected tasks to the required format
const newSelectedTasks = tasks
.filter((t) => newSelectedIds.includes(t.id))
.map(
(task): IProjectTask => ({
id: task.id, id: task.id,
name: task.title, // Always use title as the name name: task.title,
task_key: task.task_key, task_key: task.task_key,
status: task.status, status: task.status,
status_id: task.status, status_id: task.status,
@@ -435,7 +447,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
total_minutes: task.timeTracking.logged || 0, total_minutes: task.timeTracking.logged || 0,
progress: task.progress, progress: task.progress,
sub_tasks_count: task.sub_tasks_count || 0, sub_tasks_count: task.sub_tasks_count || 0,
assignees: task.assignees.map(assigneeId => ({ assignees: task.assignees.map((assigneeId) => ({
id: assigneeId, id: assigneeId,
name: '', name: '',
email: '', email: '',
@@ -444,23 +456,18 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
project_member_id: assigneeId, project_member_id: assigneeId,
})), })),
labels: task.labels, labels: task.labels,
manual_progress: false, // Default value for Task type manual_progress: false,
created_at: task.createdAt, created_at: task.createdAt,
updated_at: task.updatedAt, updated_at: task.updatedAt,
sort_order: task.order, sort_order: task.order,
}; })
dispatch(selectTasks([...selectedTasks, projectTask])); );
dispatch(selectTaskIds([...selectedTaskIds, taskId]));
} // Dispatch both actions to update the Redux state
} else { dispatch(selectTasks(newSelectedTasks));
// Remove task from bulk selection dispatch(selectTaskIds(newSelectedIds));
const updatedTasks = selectedTasks.filter(t => t.id !== taskId);
const updatedTaskIds = selectedTaskIds.filter(id => id !== taskId);
dispatch(selectTasks(updatedTasks));
dispatch(selectTaskIds(updatedTaskIds));
}
}, },
[dispatch, selectedTasks, selectedTaskIds, tasks] [dispatch, selectedTaskIds, tasks]
); );
const handleToggleSubtasks = useCallback( const handleToggleSubtasks = useCallback(

View File

@@ -55,7 +55,6 @@ import {
fetchTask, fetchTask,
} from '@/features/task-drawer/task-drawer.slice'; } from '@/features/task-drawer/task-drawer.slice';
import useDragCursor from '@/hooks/useDragCursor'; import useDragCursor from '@/hooks/useDragCursor';
import TaskContextMenu from './task-context-menu/task-context-menu';
interface TaskRowProps { interface TaskRowProps {
task: Task; task: Task;
@@ -430,9 +429,7 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
const addSubtaskInputRef = useRef<InputRef>(null); const addSubtaskInputRef = useRef<InputRef>(null);
const wrapperRef = useRef<HTMLDivElement>(null); const wrapperRef = useRef<HTMLDivElement>(null);
// Context menu state // Subtask expansion state (managed by Redux)
const [showContextMenu, setShowContextMenu] = useState(false);
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
// PERFORMANCE OPTIMIZATION: Intersection Observer for lazy loading // PERFORMANCE OPTIMIZATION: Intersection Observer for lazy loading
useEffect(() => { useEffect(() => {
@@ -575,11 +572,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
onToggleSubtasks?.(task.id); onToggleSubtasks?.(task.id);
}, [task.id, onToggleSubtasks]); }, [task.id, onToggleSubtasks]);
const handleContextMenu = useCallback((e: React.MouseEvent) => {
setShowContextMenu(true);
setContextMenuPosition({ x: e.clientX, y: e.clientY });
}, []);
// Handle successful subtask creation // Handle successful subtask creation
const handleSubtaskCreated = useCallback( const handleSubtaskCreated = useCallback(
(newTask: any) => { (newTask: any) => {
@@ -1015,54 +1007,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
</span> </span>
</div> </div>
)} )}
{/* Indicators section */}
{!editTaskName && (
<div className="task-indicators flex items-center gap-2">
{/* Comments indicator */}
{(task as any).comments_count > 0 && (
<Tooltip title={t('taskManagement.comments', 'Comments')}>
<MessageOutlined
style={{ fontSize: 14, color: isDarkMode ? '#b0b3b8' : '#888' }}
/>
</Tooltip>
)}
{/* Attachments indicator */}
{(task as any).attachments_count > 0 && (
<Tooltip title={t('taskManagement.attachments', 'Attachments')}>
<PaperClipOutlined
style={{ fontSize: 14, color: isDarkMode ? '#b0b3b8' : '#888' }}
/>
</Tooltip>
)}
{/* Dependencies indicator */}
{(task as any).has_dependencies && (
<Tooltip title={t('taskManagement.dependencies', 'Dependencies')}>
<MinusCircleOutlined
style={{ fontSize: 14, color: isDarkMode ? '#b0b3b8' : '#888' }}
/>
</Tooltip>
)}
{/* Subscribers indicator */}
{(task as any).has_subscribers && (
<Tooltip title={t('taskManagement.subscribers', 'Subscribers')}>
<EyeOutlined
style={{ fontSize: 14, color: isDarkMode ? '#b0b3b8' : '#888' }}
/>
</Tooltip>
)}
{/* Recurring indicator */}
{(task as any).schedule_id && (
<Tooltip title={t('taskManagement.recurringTask', 'Recurring Task')}>
<RetweetOutlined
style={{ fontSize: 14, color: isDarkMode ? '#b0b3b8' : '#888' }}
/>
</Tooltip>
)}
</div>
)}
</>
)}
</div>
</> </>
)} )}
</div> </div>
@@ -1112,8 +1056,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
)} )}
</div> </div>
)} )}
</>
)}
</div> </div>
{/* Right section with open button - CSS hover only */} {/* Right section with open button - CSS hover only */}
@@ -1536,7 +1478,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
data-dnd-dragging={isDragging ? 'true' : 'false'} data-dnd-dragging={isDragging ? 'true' : 'false'}
data-task-id={task.id} data-task-id={task.id}
data-group-id={groupId} data-group-id={groupId}
onContextMenu={handleContextMenu}
> >
<div className="task-row-container flex h-10 max-h-10 relative"> <div className="task-row-container flex h-10 max-h-10 relative">
{/* All Columns - No Fixed Positioning */} {/* All Columns - No Fixed Positioning */}
@@ -1565,14 +1506,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
</div> </div>
</div> </div>
</div> </div>
{showContextMenu && (
<TaskContextMenu
task={task}
projectId={projectId}
position={contextMenuPosition}
onClose={() => setShowContextMenu(false)}
/>
)}
</> </>
); );
}, },
@@ -1660,4 +1593,4 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
TaskRow.displayName = 'TaskRow'; TaskRow.displayName = 'TaskRow';
export default TaskRow; export default TaskRow;