Merge pull request #209 from shancds/fix/task-manual-progress-update
Fix/task manual progress update
This commit is contained in:
@@ -58,10 +58,10 @@ export async function on_task_status_change(_io: Server, socket: Socket, data?:
|
|||||||
FROM tasks
|
FROM tasks
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`, [body.task_id]);
|
`, [body.task_id]);
|
||||||
|
|
||||||
const currentProgress = progressResult.rows[0]?.progress_value;
|
const currentProgress = progressResult.rows[0]?.progress_value;
|
||||||
const isManualProgress = progressResult.rows[0]?.manual_progress;
|
const isManualProgress = progressResult.rows[0]?.manual_progress;
|
||||||
|
|
||||||
// Only update if not already 100%
|
// Only update if not already 100%
|
||||||
if (currentProgress !== 100) {
|
if (currentProgress !== 100) {
|
||||||
// Update progress to 100%
|
// Update progress to 100%
|
||||||
@@ -70,9 +70,9 @@ export async function on_task_status_change(_io: Server, socket: Socket, data?:
|
|||||||
SET progress_value = 100, manual_progress = TRUE
|
SET progress_value = 100, manual_progress = TRUE
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`, [body.task_id]);
|
`, [body.task_id]);
|
||||||
|
|
||||||
log(`Task ${body.task_id} moved to done status - progress automatically set to 100%`, null);
|
log(`Task ${body.task_id} moved to done status - progress automatically set to 100%`, null);
|
||||||
|
|
||||||
// Log the progress change to activity logs
|
// Log the progress change to activity logs
|
||||||
await logProgressChange({
|
await logProgressChange({
|
||||||
task_id: body.task_id,
|
task_id: body.task_id,
|
||||||
@@ -80,7 +80,7 @@ export async function on_task_status_change(_io: Server, socket: Socket, data?:
|
|||||||
new_value: "100",
|
new_value: "100",
|
||||||
socket
|
socket
|
||||||
});
|
});
|
||||||
|
|
||||||
// If this is a subtask, update parent task progress
|
// If this is a subtask, update parent task progress
|
||||||
if (body.parent_task) {
|
if (body.parent_task) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -88,6 +88,23 @@ export async function on_task_status_change(_io: Server, socket: Socket, data?:
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Task is moving from "done" to "todo" or "doing" - reset manual_progress to FALSE
|
||||||
|
// so progress can be recalculated based on subtasks
|
||||||
|
await db.query(`
|
||||||
|
UPDATE tasks
|
||||||
|
SET manual_progress = FALSE
|
||||||
|
WHERE id = $1
|
||||||
|
`, [body.task_id]);
|
||||||
|
|
||||||
|
log(`Task ${body.task_id} moved from done status - manual_progress reset to FALSE`, null);
|
||||||
|
|
||||||
|
// If this is a subtask, update parent task progress
|
||||||
|
if (body.parent_task) {
|
||||||
|
setTimeout(() => {
|
||||||
|
socket.emit(SocketEvents.GET_TASK_PROGRESS.toString(), body.parent_task);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const info = await TasksControllerV2.getTaskCompleteRatio(body.parent_task || body.task_id);
|
const info = await TasksControllerV2.getTaskCompleteRatio(body.parent_task || body.task_id);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { setTaskAssignee } from '@/features/task-drawer/task-drawer.slice';
|
|||||||
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||||
import { updateTaskAssignees as updateBoardTaskAssignees } from '@/features/board/board-slice';
|
import { updateTaskAssignees as updateBoardTaskAssignees } from '@/features/board/board-slice';
|
||||||
import { updateTaskAssignees as updateTasksListTaskAssignees } from '@/features/tasks/tasks.slice';
|
import { updateTaskAssignees as updateTasksListTaskAssignees } from '@/features/tasks/tasks.slice';
|
||||||
|
import { updateEnhancedKanbanTaskAssignees } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
interface TaskDrawerAssigneeSelectorProps {
|
interface TaskDrawerAssigneeSelectorProps {
|
||||||
task: ITaskViewModel;
|
task: ITaskViewModel;
|
||||||
}
|
}
|
||||||
@@ -88,12 +89,12 @@ const TaskDrawerAssigneeSelector = ({ task }: TaskDrawerAssigneeSelectorProps) =
|
|||||||
SocketEvents.QUICK_ASSIGNEES_UPDATE.toString(),
|
SocketEvents.QUICK_ASSIGNEES_UPDATE.toString(),
|
||||||
(data: ITaskAssigneesUpdateResponse) => {
|
(data: ITaskAssigneesUpdateResponse) => {
|
||||||
dispatch(setTaskAssignee(data));
|
dispatch(setTaskAssignee(data));
|
||||||
// if (tab === 'tasks-list') {
|
if (tab === 'tasks-list') {
|
||||||
// dispatch(updateTasksListTaskAssignees(data));
|
dispatch(updateTasksListTaskAssignees(data));
|
||||||
// }
|
}
|
||||||
// if (tab === 'board') {
|
if (tab === 'board') {
|
||||||
// dispatch(updateBoardTaskAssignees(data));
|
dispatch(updateEnhancedKanbanTaskAssignees(data));
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { useAppDispatch } from '@/hooks/useAppDispatch';
|
|||||||
import { setTaskLabels } from '@/features/task-drawer/task-drawer.slice';
|
import { setTaskLabels } from '@/features/task-drawer/task-drawer.slice';
|
||||||
import { setLabels, updateTaskLabel } from '@/features/tasks/tasks.slice';
|
import { setLabels, updateTaskLabel } from '@/features/tasks/tasks.slice';
|
||||||
import { setBoardLabels, updateBoardTaskLabel } from '@/features/board/board-slice';
|
import { setBoardLabels, updateBoardTaskLabel } from '@/features/board/board-slice';
|
||||||
|
import { updateEnhancedKanbanTaskLabels } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
import { ILabelsChangeResponse } from '@/types/tasks/taskList.types';
|
import { ILabelsChangeResponse } from '@/types/tasks/taskList.types';
|
||||||
import { ITaskLabelFilter } from '@/types/tasks/taskLabel.types';
|
import { ITaskLabelFilter } from '@/types/tasks/taskLabel.types';
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ const TaskDrawerLabels = ({ task, t }: TaskDrawerLabelsProps) => {
|
|||||||
dispatch(updateTaskLabel(data));
|
dispatch(updateTaskLabel(data));
|
||||||
}
|
}
|
||||||
if (tab === 'board') {
|
if (tab === 'board') {
|
||||||
dispatch(updateBoardTaskLabel(data));
|
dispatch(updateEnhancedKanbanTaskLabels(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -90,9 +91,9 @@ const TaskDrawerLabels = ({ task, t }: TaskDrawerLabelsProps) => {
|
|||||||
if (tab === 'tasks-list') {
|
if (tab === 'tasks-list') {
|
||||||
dispatch(updateTaskLabel(data));
|
dispatch(updateTaskLabel(data));
|
||||||
}
|
}
|
||||||
if (tab === 'board') {
|
if (tab === 'board') {
|
||||||
dispatch(updateBoardTaskLabel(data));
|
dispatch(updateEnhancedKanbanTaskLabels(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { ITaskListPriorityChangeResponse } from '@/types/tasks/task-list-priorit
|
|||||||
import { setTaskPriority } from '@/features/task-drawer/task-drawer.slice';
|
import { setTaskPriority } from '@/features/task-drawer/task-drawer.slice';
|
||||||
import { updateTaskPriority as updateBoardTaskPriority } from '@/features/board/board-slice';
|
import { updateTaskPriority as updateBoardTaskPriority } from '@/features/board/board-slice';
|
||||||
import { updateTaskPriority as updateTasksListTaskPriority } from '@/features/tasks/tasks.slice';
|
import { updateTaskPriority as updateTasksListTaskPriority } from '@/features/tasks/tasks.slice';
|
||||||
|
import { updateEnhancedKanbanTaskPriority } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
|
|
||||||
type PriorityDropdownProps = {
|
type PriorityDropdownProps = {
|
||||||
task: ITaskViewModel;
|
task: ITaskViewModel;
|
||||||
@@ -48,7 +49,7 @@ const PriorityDropdown = ({ task }: PriorityDropdownProps) => {
|
|||||||
dispatch(updateTasksListTaskPriority(data));
|
dispatch(updateTasksListTaskPriority(data));
|
||||||
}
|
}
|
||||||
if (tab === 'board') {
|
if (tab === 'board') {
|
||||||
dispatch(updateBoardTaskPriority(data));
|
dispatch(updateEnhancedKanbanTaskPriority(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { setTaskStatus } from '@/features/task-drawer/task-drawer.slice';
|
|||||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||||
import { updateBoardTaskStatus } from '@/features/board/board-slice';
|
import { updateBoardTaskStatus } from '@/features/board/board-slice';
|
||||||
import { updateTaskProgress, updateTaskStatus } from '@/features/tasks/tasks.slice';
|
import { updateTaskProgress, updateTaskStatus } from '@/features/tasks/tasks.slice';
|
||||||
|
import { updateEnhancedKanbanTaskStatus, updateEnhancedKanbanTaskProgress } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||||
|
|
||||||
interface TaskDrawerProgressProps {
|
interface TaskDrawerProgressProps {
|
||||||
@@ -102,14 +103,27 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
socket?.once(SocketEvents.GET_TASK_PROGRESS.toString(), (data: any) => {
|
socket?.once(SocketEvents.GET_TASK_PROGRESS.toString(), (data: any) => {
|
||||||
dispatch(
|
if (tab === 'tasks-list') {
|
||||||
updateTaskProgress({
|
dispatch(
|
||||||
taskId: task.id,
|
updateTaskProgress({
|
||||||
progress: data.complete_ratio,
|
taskId: task.id,
|
||||||
totalTasksCount: data.total_tasks_count,
|
progress: data.complete_ratio,
|
||||||
completedCount: data.completed_count,
|
totalTasksCount: data.total_tasks_count,
|
||||||
})
|
completedCount: data.completed_count,
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (tab === 'board') {
|
||||||
|
dispatch(
|
||||||
|
updateEnhancedKanbanTaskProgress({
|
||||||
|
id: task.id,
|
||||||
|
complete_ratio: data.complete_ratio,
|
||||||
|
completed_count: data.completed_count,
|
||||||
|
total_tasks_count: data.total_tasks_count,
|
||||||
|
parent_task: task.parent_task_id || null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (task.id) {
|
if (task.id) {
|
||||||
@@ -185,7 +199,7 @@ const TaskDrawerProgress = ({ task, form }: TaskDrawerProgressProps) => {
|
|||||||
dispatch(updateTaskStatus(data));
|
dispatch(updateTaskStatus(data));
|
||||||
}
|
}
|
||||||
if (tab === 'board') {
|
if (tab === 'board') {
|
||||||
dispatch(updateBoardTaskStatus(data));
|
dispatch(updateEnhancedKanbanTaskStatus(data));
|
||||||
}
|
}
|
||||||
if (data.parent_task)
|
if (data.parent_task)
|
||||||
socket?.emit(SocketEvents.GET_TASK_PROGRESS.toString(), data.parent_task);
|
socket?.emit(SocketEvents.GET_TASK_PROGRESS.toString(), data.parent_task);
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import { SocketEvents } from '@/shared/socket-events';
|
|||||||
import useTaskDrawerUrlSync from '@/hooks/useTaskDrawerUrlSync';
|
import useTaskDrawerUrlSync from '@/hooks/useTaskDrawerUrlSync';
|
||||||
import { deleteTask } from '@/features/tasks/tasks.slice';
|
import { deleteTask } from '@/features/tasks/tasks.slice';
|
||||||
import { deleteBoardTask, updateTaskName } from '@/features/board/board-slice';
|
import { deleteBoardTask, updateTaskName } from '@/features/board/board-slice';
|
||||||
|
import { updateEnhancedKanbanTaskName } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
|
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||||
|
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
||||||
|
|
||||||
type TaskDrawerHeaderProps = {
|
type TaskDrawerHeaderProps = {
|
||||||
inputRef: React.RefObject<InputRef | null>;
|
inputRef: React.RefObject<InputRef | null>;
|
||||||
@@ -26,6 +29,7 @@ const TaskDrawerHeader = ({ inputRef, t }: TaskDrawerHeaderProps) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { socket, connected } = useSocket();
|
const { socket, connected } = useSocket();
|
||||||
const { clearTaskFromUrl } = useTaskDrawerUrlSync();
|
const { clearTaskFromUrl } = useTaskDrawerUrlSync();
|
||||||
|
const { tab } = useTabSearchParam();
|
||||||
const isDeleting = useRef(false);
|
const isDeleting = useRef(false);
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
@@ -84,7 +88,13 @@ const TaskDrawerHeader = ({ inputRef, t }: TaskDrawerHeaderProps) => {
|
|||||||
|
|
||||||
const handleReceivedTaskNameChange = (data: { id: string; parent_task: string; name: string }) => {
|
const handleReceivedTaskNameChange = (data: { id: string; parent_task: string; name: string }) => {
|
||||||
if (data.id === selectedTaskId) {
|
if (data.id === selectedTaskId) {
|
||||||
dispatch(updateTaskName({ task: data }));
|
const taskData = { ...data, manual_progress: false } as IProjectTask;
|
||||||
|
dispatch(updateTaskName({ task: taskData }));
|
||||||
|
|
||||||
|
// Also update enhanced kanban if on board tab
|
||||||
|
if (tab === 'board') {
|
||||||
|
dispatch(updateEnhancedKanbanTaskName({ task: taskData }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -152,7 +162,7 @@ const TaskDrawerHeader = ({ inputRef, t }: TaskDrawerHeaderProps) => {
|
|||||||
|
|
||||||
<TaskDrawerStatusDropdown
|
<TaskDrawerStatusDropdown
|
||||||
statuses={taskFormViewModel?.statuses ?? []}
|
statuses={taskFormViewModel?.statuses ?? []}
|
||||||
task={taskFormViewModel?.task ?? {}}
|
task={taskFormViewModel?.task ?? {} as ITaskViewModel}
|
||||||
teamId={currentSession?.team_id ?? ''}
|
teamId={currentSession?.team_id ?? ''}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import { ITaskListStatusChangeResponse } from '@/types/tasks/task-list-status.ty
|
|||||||
import { ITaskListPriorityChangeResponse } from '@/types/tasks/task-list-priority.types';
|
import { ITaskListPriorityChangeResponse } from '@/types/tasks/task-list-priority.types';
|
||||||
import { ITaskLabelFilter } from '@/types/tasks/taskLabel.types';
|
import { ITaskLabelFilter } from '@/types/tasks/taskLabel.types';
|
||||||
import { labelsApiService } from '@/api/taskAttributes/labels/labels.api.service';
|
import { labelsApiService } from '@/api/taskAttributes/labels/labels.api.service';
|
||||||
|
import { ITaskAssigneesUpdateResponse } from '@/types/tasks/task-assignee-update-response';
|
||||||
|
import { ITaskAssignee } from '@/types/project/projectTasksViewModel.types';
|
||||||
|
import { InlineMember } from '@/types/teamMembers/inlineMember.types';
|
||||||
|
import { ILabelsChangeResponse } from '@/types/tasks/taskList.types';
|
||||||
|
|
||||||
export enum IGroupBy {
|
export enum IGroupBy {
|
||||||
STATUS = 'status',
|
STATUS = 'status',
|
||||||
@@ -357,6 +361,53 @@ export const fetchEnhancedKanbanLabels = createAsyncThunk(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Helper functions for common operations (similar to board-slice.ts)
|
||||||
|
const findTaskInAllGroups = (
|
||||||
|
taskGroups: ITaskListGroup[],
|
||||||
|
taskId: string
|
||||||
|
): { task: IProjectTask; group: ITaskListGroup; groupId: string } | null => {
|
||||||
|
for (const group of taskGroups) {
|
||||||
|
const task = group.tasks.find(t => t.id === taskId);
|
||||||
|
if (task) return { task, group, groupId: group.id };
|
||||||
|
|
||||||
|
// Check in subtasks
|
||||||
|
for (const parentTask of group.tasks) {
|
||||||
|
if (!parentTask.sub_tasks) continue;
|
||||||
|
const subtask = parentTask.sub_tasks.find(st => st.id === taskId);
|
||||||
|
if (subtask) return { task: subtask, group, groupId: group.id };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteTaskFromGroup = (
|
||||||
|
taskGroups: ITaskListGroup[],
|
||||||
|
task: IProjectTask,
|
||||||
|
groupId: string,
|
||||||
|
index: number | null = null
|
||||||
|
): void => {
|
||||||
|
const group = taskGroups.find(g => g.id === groupId);
|
||||||
|
if (!group || !task.id) return;
|
||||||
|
|
||||||
|
if (task.is_sub_task) {
|
||||||
|
const parentTask = group.tasks.find(t => t.id === task.parent_task_id);
|
||||||
|
if (parentTask) {
|
||||||
|
const subTaskIndex = parentTask.sub_tasks?.findIndex(t => t.id === task.id);
|
||||||
|
if (typeof subTaskIndex !== 'undefined' && subTaskIndex !== -1) {
|
||||||
|
parentTask.sub_tasks_count = Math.max((parentTask.sub_tasks_count || 0) - 1, 0);
|
||||||
|
parentTask.sub_tasks?.splice(subTaskIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const taskIndex = index ?? group.tasks.findIndex(t => t.id === task.id);
|
||||||
|
if (taskIndex !== -1) {
|
||||||
|
group.tasks.splice(taskIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const enhancedKanbanSlice = createSlice({
|
const enhancedKanbanSlice = createSlice({
|
||||||
name: 'enhancedKanbanReducer',
|
name: 'enhancedKanbanReducer',
|
||||||
initialState,
|
initialState,
|
||||||
@@ -497,7 +548,7 @@ const enhancedKanbanSlice = createSlice({
|
|||||||
|
|
||||||
// Enhanced Kanban external status update (for use in task drawer dropdown)
|
// Enhanced Kanban external status update (for use in task drawer dropdown)
|
||||||
updateEnhancedKanbanTaskStatus: (state, action: PayloadAction<ITaskListStatusChangeResponse>) => {
|
updateEnhancedKanbanTaskStatus: (state, action: PayloadAction<ITaskListStatusChangeResponse>) => {
|
||||||
const { id: task_id, status_id } = action.payload;
|
const { id: task_id, status_id, color_code, color_code_dark, complete_ratio, statusCategory } = action.payload;
|
||||||
let oldGroupId: string | null = null;
|
let oldGroupId: string | null = null;
|
||||||
let foundTask: IProjectTask | null = null;
|
let foundTask: IProjectTask | null = null;
|
||||||
// Find the task and its group
|
// Find the task and its group
|
||||||
@@ -510,6 +561,14 @@ const enhancedKanbanSlice = createSlice({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!foundTask) return;
|
if (!foundTask) return;
|
||||||
|
|
||||||
|
// Update the task properties
|
||||||
|
foundTask.status_color = color_code;
|
||||||
|
foundTask.status_color_dark = color_code_dark;
|
||||||
|
foundTask.complete_ratio = +complete_ratio;
|
||||||
|
foundTask.status = status_id;
|
||||||
|
foundTask.status_category = statusCategory;
|
||||||
|
|
||||||
// If grouped by status and the group changes, move the task
|
// If grouped by status and the group changes, move the task
|
||||||
if (state.groupBy === IGroupBy.STATUS && oldGroupId && oldGroupId !== status_id) {
|
if (state.groupBy === IGroupBy.STATUS && oldGroupId && oldGroupId !== status_id) {
|
||||||
// Remove from old group
|
// Remove from old group
|
||||||
@@ -531,6 +590,128 @@ const enhancedKanbanSlice = createSlice({
|
|||||||
state.taskCache[task_id] = foundTask;
|
state.taskCache[task_id] = foundTask;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Enhanced Kanban priority update (for use in task drawer dropdown)
|
||||||
|
updateEnhancedKanbanTaskPriority: (state, action: PayloadAction<ITaskListPriorityChangeResponse>) => {
|
||||||
|
const { id, priority_id, color_code, color_code_dark } = action.payload;
|
||||||
|
|
||||||
|
// Find the task in any group
|
||||||
|
const taskInfo = findTaskInAllGroups(state.taskGroups, id);
|
||||||
|
if (!taskInfo || !priority_id) return;
|
||||||
|
|
||||||
|
const { task, groupId } = taskInfo;
|
||||||
|
|
||||||
|
// Update the task properties
|
||||||
|
task.priority = priority_id;
|
||||||
|
task.priority_color = color_code;
|
||||||
|
task.priority_color_dark = color_code_dark;
|
||||||
|
|
||||||
|
// If grouped by priority and not a subtask, move the task to the new priority group
|
||||||
|
if (
|
||||||
|
state.groupBy === IGroupBy.PRIORITY &&
|
||||||
|
!task.is_sub_task &&
|
||||||
|
groupId !== priority_id
|
||||||
|
) {
|
||||||
|
// Remove from current group
|
||||||
|
deleteTaskFromGroup(state.taskGroups, task, groupId);
|
||||||
|
|
||||||
|
// Add to new priority group
|
||||||
|
const newGroup = state.taskGroups.find(g => g.id === priority_id);
|
||||||
|
if (newGroup) {
|
||||||
|
newGroup.tasks.unshift(task);
|
||||||
|
state.groupCache[priority_id] = newGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
state.taskCache[id] = task;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Enhanced Kanban assignee update (for use in task drawer dropdown)
|
||||||
|
updateEnhancedKanbanTaskAssignees: (state, action: PayloadAction<ITaskAssigneesUpdateResponse>) => {
|
||||||
|
const { id, assignees, names } = action.payload;
|
||||||
|
|
||||||
|
// Find the task in any group
|
||||||
|
const taskInfo = findTaskInAllGroups(state.taskGroups, id);
|
||||||
|
if (!taskInfo) return;
|
||||||
|
|
||||||
|
const { task } = taskInfo;
|
||||||
|
|
||||||
|
// Update the task properties
|
||||||
|
task.assignees = assignees as ITaskAssignee[];
|
||||||
|
task.names = names as InlineMember[];
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
state.taskCache[id] = task;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Enhanced Kanban label update (for use in task drawer dropdown)
|
||||||
|
updateEnhancedKanbanTaskLabels: (state, action: PayloadAction<ILabelsChangeResponse>) => {
|
||||||
|
const label = action.payload;
|
||||||
|
for (const group of state.taskGroups) {
|
||||||
|
// Find the task or its subtask
|
||||||
|
const task =
|
||||||
|
group.tasks.find(task => task.id === label.id) ||
|
||||||
|
group.tasks
|
||||||
|
.flatMap(task => task.sub_tasks || [])
|
||||||
|
.find(subtask => subtask.id === label.id);
|
||||||
|
if (task) {
|
||||||
|
task.labels = label.labels || [];
|
||||||
|
task.all_labels = label.all_labels || [];
|
||||||
|
// Update cache
|
||||||
|
state.taskCache[label.id] = task;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Enhanced Kanban progress update (for use in task drawer and socket events)
|
||||||
|
updateEnhancedKanbanTaskProgress: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
complete_ratio: number;
|
||||||
|
completed_count: number;
|
||||||
|
total_tasks_count: number;
|
||||||
|
parent_task: string;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { id, complete_ratio, completed_count, total_tasks_count, parent_task } = action.payload;
|
||||||
|
|
||||||
|
// Find the task in any group
|
||||||
|
const taskInfo = findTaskInAllGroups(state.taskGroups, parent_task || id);
|
||||||
|
|
||||||
|
// Check if taskInfo exists before destructuring
|
||||||
|
if (!taskInfo) return;
|
||||||
|
|
||||||
|
const { task } = taskInfo;
|
||||||
|
|
||||||
|
// Update the task properties
|
||||||
|
task.complete_ratio = +complete_ratio;
|
||||||
|
task.completed_count = completed_count;
|
||||||
|
task.total_tasks_count = total_tasks_count;
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
state.taskCache[parent_task || id] = task;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Enhanced Kanban task name update (for use in task drawer header)
|
||||||
|
updateEnhancedKanbanTaskName: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
task: IProjectTask;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
|
const { task } = action.payload;
|
||||||
|
|
||||||
|
// Find the task and update it
|
||||||
|
const result = findTaskInAllGroups(state.taskGroups, task.id || '');
|
||||||
|
if (result) {
|
||||||
|
result.task.name = task.name;
|
||||||
|
// Update cache
|
||||||
|
state.taskCache[task.id!] = result.task;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
updateTaskPriority: (state, action: PayloadAction<ITaskListPriorityChangeResponse>) => {
|
updateTaskPriority: (state, action: PayloadAction<ITaskListPriorityChangeResponse>) => {
|
||||||
const { id: task_id, priority_id } = action.payload;
|
const { id: task_id, priority_id } = action.payload;
|
||||||
|
|
||||||
@@ -608,6 +789,9 @@ const enhancedKanbanSlice = createSlice({
|
|||||||
const group = state.taskGroups.find(g => g.id === sectionId);
|
const group = state.taskGroups.find(g => g.id === sectionId);
|
||||||
if (group) {
|
if (group) {
|
||||||
group.tasks.push(task);
|
group.tasks.push(task);
|
||||||
|
// Update cache
|
||||||
|
state.taskCache[task.id!] = task;
|
||||||
|
state.groupCache[sectionId] = group;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -737,6 +921,11 @@ export const {
|
|||||||
reorderGroups,
|
reorderGroups,
|
||||||
addTaskToGroup,
|
addTaskToGroup,
|
||||||
updateEnhancedKanbanTaskStatus,
|
updateEnhancedKanbanTaskStatus,
|
||||||
|
updateEnhancedKanbanTaskPriority,
|
||||||
|
updateEnhancedKanbanTaskAssignees,
|
||||||
|
updateEnhancedKanbanTaskLabels,
|
||||||
|
updateEnhancedKanbanTaskProgress,
|
||||||
|
updateEnhancedKanbanTaskName,
|
||||||
} = enhancedKanbanSlice.actions;
|
} = enhancedKanbanSlice.actions;
|
||||||
|
|
||||||
export default enhancedKanbanSlice.reducer;
|
export default enhancedKanbanSlice.reducer;
|
||||||
Reference in New Issue
Block a user