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