refactor(task-list): streamline task addition and socket handling
- Removed local socket listener from AddTaskRow, delegating task addition to the global socket handler for real-time updates. - Simplified the handleTaskAdded function in TaskListV2, eliminating the need for refetching tasks upon addition. - Updated useTaskSocketHandlers to handle task data more efficiently, ensuring proper integration with the Redux store.
This commit is contained in:
@@ -271,10 +271,9 @@ const TaskListV2: React.FC = () => {
|
|||||||
|
|
||||||
// Add callback for task added
|
// Add callback for task added
|
||||||
const handleTaskAdded = useCallback(() => {
|
const handleTaskAdded = useCallback(() => {
|
||||||
if (urlProjectId) {
|
// Task is now added in real-time via socket, no need to refetch
|
||||||
dispatch(fetchTasksV3(urlProjectId));
|
// The global socket handler will handle the real-time update
|
||||||
}
|
}, []);
|
||||||
}, [dispatch, urlProjectId]);
|
|
||||||
|
|
||||||
// Memoized values for GroupedVirtuoso
|
// Memoized values for GroupedVirtuoso
|
||||||
const virtuosoGroups = useMemo(() => {
|
const virtuosoGroups = useMemo(() => {
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import React, { useState, useCallback, memo, useEffect } from 'react';
|
import React, { useState, useCallback, memo } from 'react';
|
||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSocket } from '@/socket/socketContext';
|
import { useSocket } from '@/socket/socketContext';
|
||||||
import { SocketEvents } from '@/shared/socket-events';
|
import { SocketEvents } from '@/shared/socket-events';
|
||||||
import { useAuthService } from '@/hooks/useAuth';
|
import { useAuthService } from '@/hooks/useAuth';
|
||||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
|
||||||
import { addTaskToGroup } from '@/features/task-management/task-management.slice';
|
|
||||||
import { Task } from '@/types/task-management.types';
|
|
||||||
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
|
||||||
|
|
||||||
interface AddTaskRowProps {
|
interface AddTaskRowProps {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
@@ -35,77 +31,12 @@ const AddTaskRow: React.FC<AddTaskRowProps> = memo(({
|
|||||||
const [taskName, setTaskName] = useState('');
|
const [taskName, setTaskName] = useState('');
|
||||||
const { socket, connected } = useSocket();
|
const { socket, connected } = useSocket();
|
||||||
const { t } = useTranslation('task-list-table');
|
const { t } = useTranslation('task-list-table');
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
// Get session data for reporter_id and team_id
|
// Get session data for reporter_id and team_id
|
||||||
const currentSession = useAuthService().getCurrentSession();
|
const currentSession = useAuthService().getCurrentSession();
|
||||||
|
|
||||||
// Listen for task creation completion and add to Redux store immediately
|
// The global socket handler (useTaskSocketHandlers) will handle task addition
|
||||||
useEffect(() => {
|
// No need for local socket listener to avoid duplicate additions
|
||||||
if (!socket) return;
|
|
||||||
|
|
||||||
const handleTaskCreated = (data: IProjectTask) => {
|
|
||||||
if (data) {
|
|
||||||
// Transform backend response to Task format for real-time addition
|
|
||||||
const task: Task = {
|
|
||||||
id: data.id || '',
|
|
||||||
task_key: data.task_key || '',
|
|
||||||
title: data.name || '',
|
|
||||||
description: data.description || '',
|
|
||||||
status: (data.status_category?.is_todo
|
|
||||||
? 'todo'
|
|
||||||
: data.status_category?.is_doing
|
|
||||||
? 'doing'
|
|
||||||
: data.status_category?.is_done
|
|
||||||
? 'done'
|
|
||||||
: 'todo') as 'todo' | 'doing' | 'done',
|
|
||||||
priority: (data.priority_value === 3
|
|
||||||
? 'critical'
|
|
||||||
: data.priority_value === 2
|
|
||||||
? 'high'
|
|
||||||
: data.priority_value === 1
|
|
||||||
? 'medium'
|
|
||||||
: 'low') as 'critical' | 'high' | 'medium' | 'low',
|
|
||||||
phase: data.phase_name || 'Development',
|
|
||||||
progress: data.complete_ratio || 0,
|
|
||||||
assignees: data.assignees?.map(a => a.team_member_id) || [],
|
|
||||||
assignee_names: data.names || [],
|
|
||||||
labels:
|
|
||||||
data.labels?.map(l => ({
|
|
||||||
id: l.id || '',
|
|
||||||
name: l.name || '',
|
|
||||||
color: l.color_code || '#1890ff',
|
|
||||||
end: l.end,
|
|
||||||
names: l.names,
|
|
||||||
})) || [],
|
|
||||||
dueDate: data.end_date,
|
|
||||||
startDate: data.start_date,
|
|
||||||
timeTracking: {
|
|
||||||
estimated: (data.total_hours || 0) + (data.total_minutes || 0) / 60,
|
|
||||||
logged: (data.time_spent?.hours || 0) + (data.time_spent?.minutes || 0) / 60,
|
|
||||||
},
|
|
||||||
created_at: data.created_at || new Date().toISOString(),
|
|
||||||
updated_at: data.updated_at || new Date().toISOString(),
|
|
||||||
order: data.sort_order || 0,
|
|
||||||
sub_tasks: [],
|
|
||||||
sub_tasks_count: 0,
|
|
||||||
show_sub_tasks: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add task to the correct group in Redux store for immediate UI update
|
|
||||||
dispatch(addTaskToGroup({ task, groupId }));
|
|
||||||
|
|
||||||
// Optional: Call onTaskAdded for any additional UI updates
|
|
||||||
onTaskAdded();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.on(SocketEvents.QUICK_TASK.toString(), handleTaskCreated);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
socket.off(SocketEvents.QUICK_TASK.toString(), handleTaskCreated);
|
|
||||||
};
|
|
||||||
}, [socket, onTaskAdded, dispatch, groupId]);
|
|
||||||
|
|
||||||
const handleAddTask = useCallback(() => {
|
const handleAddTask = useCallback(() => {
|
||||||
if (!taskName.trim() || !currentSession) return;
|
if (!taskName.trim() || !currentSession) return;
|
||||||
@@ -147,7 +78,7 @@ const AddTaskRow: React.FC<AddTaskRowProps> = memo(({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating task:', error);
|
console.error('Error creating task:', error);
|
||||||
}
|
}
|
||||||
}, [taskName, projectId, groupType, groupValue, socket, connected, currentSession, onTaskAdded]);
|
}, [taskName, projectId, groupType, groupValue, socket, connected, currentSession]);
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
setTaskName('');
|
setTaskName('');
|
||||||
|
|||||||
@@ -575,7 +575,9 @@ export const useTaskSocketHandlers = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleNewTaskReceived = useCallback(
|
const handleNewTaskReceived = useCallback(
|
||||||
(data: IProjectTask) => {
|
(response: any) => {
|
||||||
|
// Handle array format response [index, taskData]
|
||||||
|
const data = Array.isArray(response) ? response[1] : response;
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (data.parent_task_id) {
|
if (data.parent_task_id) {
|
||||||
// Handle subtask creation
|
// Handle subtask creation
|
||||||
@@ -600,10 +602,10 @@ export const useTaskSocketHandlers = () => {
|
|||||||
: 'low') as 'critical' | 'high' | 'medium' | 'low',
|
: 'low') as 'critical' | 'high' | 'medium' | 'low',
|
||||||
phase: data.phase_name || 'Development',
|
phase: data.phase_name || 'Development',
|
||||||
progress: data.complete_ratio || 0,
|
progress: data.complete_ratio || 0,
|
||||||
assignees: data.assignees?.map(a => a.team_member_id) || [],
|
assignees: data.assignees?.map((a: any) => a.team_member_id) || [],
|
||||||
assignee_names: data.names || [],
|
assignee_names: data.names || [],
|
||||||
labels:
|
labels:
|
||||||
data.labels?.map(l => ({
|
data.labels?.map((l: any) => ({
|
||||||
id: l.id || '',
|
id: l.id || '',
|
||||||
name: l.name || '',
|
name: l.name || '',
|
||||||
color: l.color_code || '#1890ff',
|
color: l.color_code || '#1890ff',
|
||||||
@@ -654,10 +656,10 @@ export const useTaskSocketHandlers = () => {
|
|||||||
: 'low') as 'critical' | 'high' | 'medium' | 'low',
|
: 'low') as 'critical' | 'high' | 'medium' | 'low',
|
||||||
phase: data.phase_name || 'Development',
|
phase: data.phase_name || 'Development',
|
||||||
progress: data.complete_ratio || 0,
|
progress: data.complete_ratio || 0,
|
||||||
assignees: data.assignees?.map(a => a.team_member_id) || [],
|
assignees: data.assignees?.map((a: any) => a.team_member_id) || [],
|
||||||
assignee_names: data.names || [],
|
assignee_names: data.names || [],
|
||||||
labels:
|
labels:
|
||||||
data.labels?.map(l => ({
|
data.labels?.map((l: any) => ({
|
||||||
id: l.id || '',
|
id: l.id || '',
|
||||||
name: l.name || '',
|
name: l.name || '',
|
||||||
color: l.color_code || '#1890ff',
|
color: l.color_code || '#1890ff',
|
||||||
@@ -697,7 +699,7 @@ export const useTaskSocketHandlers = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use addTaskToGroup with the actual group UUID
|
// Use addTaskToGroup with the actual group UUID
|
||||||
dispatch(addTaskToGroup({ task, groupId }));
|
dispatch(addTaskToGroup({ task, groupId: groupId || '' }));
|
||||||
|
|
||||||
// Also update enhanced kanban slice for regular task creation
|
// Also update enhanced kanban slice for regular task creation
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|||||||
Reference in New Issue
Block a user