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:
@@ -414,14 +414,26 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
||||
|
||||
const handleSelectTask = useCallback(
|
||||
(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) {
|
||||
// Add task to bulk selection
|
||||
const task = tasks.find(t => t.id === taskId);
|
||||
if (task) {
|
||||
// Convert Task to IProjectTask format for bulk actions
|
||||
const projectTask: IProjectTask = {
|
||||
currentSelectedIds.add(taskId);
|
||||
} else {
|
||||
currentSelectedIds.delete(taskId);
|
||||
}
|
||||
|
||||
// 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,
|
||||
name: task.title, // Always use title as the name
|
||||
name: task.title,
|
||||
task_key: task.task_key,
|
||||
status: task.status,
|
||||
status_id: task.status,
|
||||
@@ -435,7 +447,7 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
||||
total_minutes: task.timeTracking.logged || 0,
|
||||
progress: task.progress,
|
||||
sub_tasks_count: task.sub_tasks_count || 0,
|
||||
assignees: task.assignees.map(assigneeId => ({
|
||||
assignees: task.assignees.map((assigneeId) => ({
|
||||
id: assigneeId,
|
||||
name: '',
|
||||
email: '',
|
||||
@@ -444,23 +456,18 @@ const TaskListBoard: React.FC<TaskListBoardProps> = ({ projectId, className = ''
|
||||
project_member_id: assigneeId,
|
||||
})),
|
||||
labels: task.labels,
|
||||
manual_progress: false, // Default value for Task type
|
||||
manual_progress: false,
|
||||
created_at: task.createdAt,
|
||||
updated_at: task.updatedAt,
|
||||
sort_order: task.order,
|
||||
};
|
||||
dispatch(selectTasks([...selectedTasks, projectTask]));
|
||||
dispatch(selectTaskIds([...selectedTaskIds, taskId]));
|
||||
}
|
||||
} else {
|
||||
// Remove task from bulk selection
|
||||
const updatedTasks = selectedTasks.filter(t => t.id !== taskId);
|
||||
const updatedTaskIds = selectedTaskIds.filter(id => id !== taskId);
|
||||
dispatch(selectTasks(updatedTasks));
|
||||
dispatch(selectTaskIds(updatedTaskIds));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Dispatch both actions to update the Redux state
|
||||
dispatch(selectTasks(newSelectedTasks));
|
||||
dispatch(selectTaskIds(newSelectedIds));
|
||||
},
|
||||
[dispatch, selectedTasks, selectedTaskIds, tasks]
|
||||
[dispatch, selectedTaskIds, tasks]
|
||||
);
|
||||
|
||||
const handleToggleSubtasks = useCallback(
|
||||
|
||||
@@ -55,7 +55,6 @@ import {
|
||||
fetchTask,
|
||||
} from '@/features/task-drawer/task-drawer.slice';
|
||||
import useDragCursor from '@/hooks/useDragCursor';
|
||||
import TaskContextMenu from './task-context-menu/task-context-menu';
|
||||
|
||||
interface TaskRowProps {
|
||||
task: Task;
|
||||
@@ -430,9 +429,7 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
|
||||
const addSubtaskInputRef = useRef<InputRef>(null);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Context menu state
|
||||
const [showContextMenu, setShowContextMenu] = useState(false);
|
||||
const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
|
||||
// Subtask expansion state (managed by Redux)
|
||||
|
||||
// PERFORMANCE OPTIMIZATION: Intersection Observer for lazy loading
|
||||
useEffect(() => {
|
||||
@@ -575,11 +572,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
|
||||
onToggleSubtasks?.(task.id);
|
||||
}, [task.id, onToggleSubtasks]);
|
||||
|
||||
const handleContextMenu = useCallback((e: React.MouseEvent) => {
|
||||
setShowContextMenu(true);
|
||||
setContextMenuPosition({ x: e.clientX, y: e.clientY });
|
||||
}, []);
|
||||
|
||||
// Handle successful subtask creation
|
||||
const handleSubtaskCreated = useCallback(
|
||||
(newTask: any) => {
|
||||
@@ -1015,54 +1007,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
|
||||
</span>
|
||||
</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>
|
||||
@@ -1112,8 +1056,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 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-task-id={task.id}
|
||||
data-group-id={groupId}
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
<div className="task-row-container flex h-10 max-h-10 relative">
|
||||
{/* All Columns - No Fixed Positioning */}
|
||||
@@ -1565,14 +1506,6 @@ const TaskRow: React.FC<TaskRowProps> = React.memo(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showContextMenu && (
|
||||
<TaskContextMenu
|
||||
task={task}
|
||||
projectId={projectId}
|
||||
position={contextMenuPosition}
|
||||
onClose={() => setShowContextMenu(false)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user