- Changed `name` property in `ITaskAssignee` interface from optional to required for better type safety. - Updated `Dropdown` component's prop from `dropdownRender` to `popupRender` for consistency. - Renamed `destroyPopupOnHide` to `destroyOnHidden` in relevant components for clarity. - Split combined selectors in `ProjectViewTaskList` to prevent unnecessary re-renders, enhancing performance.
146 lines
4.2 KiB
TypeScript
146 lines
4.2 KiB
TypeScript
import { useMemo, useCallback } from 'react';
|
|
import {
|
|
DndContext,
|
|
DragEndEvent,
|
|
DragOverEvent,
|
|
DragStartEvent,
|
|
PointerSensor,
|
|
useSensor,
|
|
useSensors,
|
|
KeyboardSensor,
|
|
TouchSensor,
|
|
} from '@dnd-kit/core';
|
|
import { sortableKeyboardCoordinates } from '@dnd-kit/sortable';
|
|
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
|
import { useAppSelector } from '@/hooks/useAppSelector';
|
|
import { updateTaskStatus } from '@/features/tasks/tasks.slice';
|
|
import { ITaskListGroup } from '@/types/tasks/taskList.types';
|
|
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
|
|
|
export const useTaskDragAndDrop = () => {
|
|
const dispatch = useAppDispatch();
|
|
|
|
// Memoize the selector to prevent unnecessary rerenders
|
|
const taskGroups = useAppSelector(state => state.taskReducer.taskGroups);
|
|
const groupBy = useAppSelector(state => state.taskReducer.groupBy);
|
|
|
|
// Memoize sensors configuration for better performance
|
|
const sensors = useSensors(
|
|
useSensor(PointerSensor, {
|
|
activationConstraint: {
|
|
distance: 8,
|
|
},
|
|
}),
|
|
useSensor(KeyboardSensor, {
|
|
coordinateGetter: sortableKeyboardCoordinates,
|
|
}),
|
|
useSensor(TouchSensor, {
|
|
activationConstraint: {
|
|
delay: 250,
|
|
tolerance: 5,
|
|
},
|
|
})
|
|
);
|
|
|
|
const handleDragStart = useCallback((event: DragStartEvent) => {
|
|
// Add visual feedback for drag start
|
|
const { active } = event;
|
|
if (active) {
|
|
document.body.style.cursor = 'grabbing';
|
|
}
|
|
}, []);
|
|
|
|
const handleDragOver = useCallback((event: DragOverEvent) => {
|
|
// Handle drag over logic if needed
|
|
// This can be used for visual feedback during drag
|
|
}, []);
|
|
|
|
const handleDragEnd = useCallback(
|
|
(event: DragEndEvent) => {
|
|
// Reset cursor
|
|
document.body.style.cursor = '';
|
|
|
|
const { active, over } = event;
|
|
|
|
if (!active || !over || !taskGroups) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const activeId = active.id as string;
|
|
const overId = over.id as string;
|
|
|
|
// Find the task being dragged
|
|
let draggedTask: IProjectTask | null = null;
|
|
let sourceGroupId: string | null = null;
|
|
|
|
for (const group of taskGroups) {
|
|
const task = group.tasks?.find((t: IProjectTask) => t.id === activeId);
|
|
if (task) {
|
|
draggedTask = task;
|
|
sourceGroupId = group.id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!draggedTask || !sourceGroupId) {
|
|
console.warn('Could not find dragged task');
|
|
return;
|
|
}
|
|
|
|
// Determine target group
|
|
let targetGroupId: string | null = null;
|
|
|
|
// Check if dropped on a group container
|
|
const targetGroup = taskGroups.find((group: ITaskListGroup) => group.id === overId);
|
|
if (targetGroup) {
|
|
targetGroupId = targetGroup.id;
|
|
} else {
|
|
// Check if dropped on another task
|
|
for (const group of taskGroups) {
|
|
const targetTask = group.tasks?.find((t: IProjectTask) => t.id === overId);
|
|
if (targetTask) {
|
|
targetGroupId = group.id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!targetGroupId || targetGroupId === sourceGroupId) {
|
|
return; // No change needed
|
|
}
|
|
|
|
// Update task status based on group change
|
|
const targetGroupData = taskGroups.find((group: ITaskListGroup) => group.id === targetGroupId);
|
|
if (targetGroupData && groupBy === 'status') {
|
|
const updatePayload: any = {
|
|
task_id: draggedTask.id,
|
|
status_id: targetGroupData.id,
|
|
};
|
|
|
|
if (draggedTask.parent_task_id) {
|
|
updatePayload.parent_task = draggedTask.parent_task_id;
|
|
}
|
|
|
|
dispatch(updateTaskStatus(updatePayload));
|
|
}
|
|
} catch (error) {
|
|
console.error('Error handling drag end:', error);
|
|
}
|
|
},
|
|
[taskGroups, groupBy, dispatch]
|
|
);
|
|
|
|
// Memoize the drag and drop configuration
|
|
const dragAndDropConfig = useMemo(
|
|
() => ({
|
|
sensors,
|
|
onDragStart: handleDragStart,
|
|
onDragOver: handleDragOver,
|
|
onDragEnd: handleDragEnd,
|
|
}),
|
|
[sensors, handleDragStart, handleDragOver, handleDragEnd]
|
|
);
|
|
|
|
return dragAndDropConfig;
|
|
};
|