feat(drag-and-drop): enhance task grouping updates and socket event handling

- Updated the `useDragAndDrop` hook to emit specific grouping field change events (phase, priority, status) when tasks are moved between groups.
- Refactored the task management slice to prevent direct updates to task grouping fields during drag-and-drop operations, ensuring these updates are handled via socket events after backend confirmation.
- Introduced a new socket handler for task sort order changes to update task properties based on backend responses, improving synchronization between frontend and backend task states.
This commit is contained in:
chamikaJ
2025-07-15 14:22:27 +05:30
parent a03d9ef6a4
commit 17371200ca
3 changed files with 101 additions and 42 deletions

View File

@@ -37,7 +37,7 @@ export const useDragAndDrop = (allTasks: Task[], groups: TaskGroup[]) => {
const teamId = currentSession?.team_id || '';
// Use new bulk update approach - recalculate ALL task orders to prevent duplicates
const taskUpdates = [];
const taskUpdates: any[] = [];
// Create a copy of all groups and perform the move operation
const updatedGroups = groups.map(group => ({
@@ -108,6 +108,32 @@ export const useDragAndDrop = (allTasks: Task[], groups: TaskGroup[]) => {
console.log('Emitting TASK_SORT_ORDER_CHANGE:', socketData);
socket.emit(SocketEvents.TASK_SORT_ORDER_CHANGE.toString(), socketData);
// Also emit the specific grouping field change event for the moved task
if (sourceGroup.id !== targetGroup.id) {
if (currentGrouping === 'phase') {
// Emit phase change event
socket.emit(SocketEvents.TASK_PHASE_CHANGE.toString(), {
task_id: taskId,
phase_id: targetGroup.id,
parent_task: task.parent_task_id || null,
});
} else if (currentGrouping === 'priority') {
// Emit priority change event
socket.emit(SocketEvents.TASK_PRIORITY_CHANGE.toString(), JSON.stringify({
task_id: taskId,
priority_id: targetGroup.id,
team_id: teamId,
}));
} else if (currentGrouping === 'status') {
// Emit status change event
socket.emit(SocketEvents.TASK_STATUS_CHANGE.toString(), JSON.stringify({
task_id: taskId,
status_id: targetGroup.id,
team_id: teamId,
}));
}
}
},
[socket, connected, projectId, allTasks, groups, currentGrouping, currentSession]
);

View File

@@ -686,42 +686,8 @@ const taskManagementSlice = createSlice({
destinationGroup.taskIds.push(sourceTaskId); // Add to end if destination task not found
}
// Update task's grouping field to reflect new group (e.g., status, priority, phase)
// This assumes the group ID directly corresponds to the task's field value
if (sourceTask) {
let updatedTask = { ...sourceTask };
switch (state.grouping?.id) {
case IGroupBy.STATUS:
updatedTask.status = destinationGroup.id;
break;
case IGroupBy.PRIORITY:
updatedTask.priority = destinationGroup.id;
break;
case IGroupBy.PHASE:
// Handle unmapped group specially
if (destinationGroup.id === 'Unmapped' || destinationGroup.title === 'Unmapped') {
updatedTask.phase = ''; // Clear phase for unmapped group
} else {
updatedTask.phase = destinationGroup.id;
}
break;
case IGroupBy.MEMBERS:
// If moving to a member group, ensure task is assigned to that member
// This assumes the group ID is the member ID
if (!updatedTask.assignees) {
updatedTask.assignees = [];
}
if (!updatedTask.assignees.includes(destinationGroup.id)) {
updatedTask.assignees.push(destinationGroup.id);
}
// If moving from a member group, and the task is no longer in any member group,
// consider removing the assignment (more complex logic might be needed here)
break;
default:
break;
}
newEntities[sourceTaskId] = updatedTask;
}
// Do NOT update the task's grouping field (priority, phase, status) here.
// This will be handled by the socket event handler after backend confirmation.
// Update order for affected tasks in both groups using the appropriate sort field
const sortField = getSortOrderField(state.grouping?.id);

View File

@@ -989,6 +989,71 @@ export const useTaskSocketHandlers = () => {
}
}, [dispatch]);
// Handler for task sort order change events
const handleTaskSortOrderChange = useCallback((data: any[]) => {
try {
if (!Array.isArray(data) || data.length === 0) return;
// DEBUG: Log the data received from the backend
console.log('[TASK_SORT_ORDER_CHANGE] Received data:', data);
// Get canonical lists from Redux
const state = store.getState();
const priorityList = state.priorityReducer?.priorities || [];
const phaseList = state.phaseReducer?.phaseList || [];
const statusList = state.taskStatusReducer?.status || [];
// The backend sends an array of tasks with updated sort orders and possibly grouping fields
data.forEach((taskData: any) => {
const currentTask = state.taskManagement.entities[taskData.id];
if (currentTask) {
let updatedTask: Task = {
...currentTask,
order: taskData.sort_order || taskData.current_sort_order || currentTask.order,
updatedAt: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
// Update grouping fields if present
if (typeof taskData.priority_id !== 'undefined') {
const found = priorityList.find(p => p.id === taskData.priority_id);
if (found) {
updatedTask.priority = found.name;
// updatedTask.priority_id = found.id; // Only if Task type has priority_id
} else {
updatedTask.priority = taskData.priority_id || '';
// updatedTask.priority_id = taskData.priority_id;
}
}
if (typeof taskData.phase_id !== 'undefined') {
const found = phaseList.find(p => p.id === taskData.phase_id);
if (found) {
updatedTask.phase = found.name;
// updatedTask.phase_id = found.id; // Only if Task type has phase_id
} else {
updatedTask.phase = taskData.phase_id || '';
// updatedTask.phase_id = taskData.phase_id;
}
}
if (typeof taskData.status_id !== 'undefined') {
const found = statusList.find(s => s.id === taskData.status_id);
if (found) {
updatedTask.status = found.name;
// updatedTask.status_id = found.id; // Only if Task type has status_id
} else {
updatedTask.status = taskData.status_id || '';
// updatedTask.status_id = taskData.status_id;
}
}
dispatch(updateTask(updatedTask));
}
});
} catch (error) {
logger.error('Error handling task sort order change event:', error);
}
}, [dispatch]);
// Register socket event listeners
useEffect(() => {
if (!socket) return;
@@ -1022,6 +1087,7 @@ export const useTaskSocketHandlers = () => {
{ event: SocketEvents.TASK_CUSTOM_COLUMN_UPDATE.toString(), handler: handleCustomColumnUpdate },
{ event: SocketEvents.TASK_TIMER_START.toString(), handler: handleTimerStart },
{ event: SocketEvents.TASK_TIMER_STOP.toString(), handler: handleTimerStop },
{ event: SocketEvents.TASK_SORT_ORDER_CHANGE.toString(), handler: handleTaskSortOrderChange },
];
@@ -1056,6 +1122,7 @@ export const useTaskSocketHandlers = () => {
handleCustomColumnUpdate,
handleTimerStart,
handleTimerStop,
handleTaskSortOrderChange,
]);
};