Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions worklenz-backend/src/controllers/tasks-controller-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,11 @@ export default class TasksControllerV2 extends TasksControllerBase {
end: l.end,
names: l.names
})) || [],
all_labels: task.all_labels?.map((l: any) => ({
id: l.id || l.label_id,
name: l.name,
color_code: l.color_code || "#1890ff"
})) || [],
dueDate: task.end_date || task.END_DATE,
startDate: task.start_date,
timeTracking: {
Expand Down
19 changes: 12 additions & 7 deletions worklenz-frontend/src/components/LabelsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,16 +223,21 @@ const LabelsSelector: React.FC<LabelsSelectorProps> = ({ task, isDarkMode = fals
<div
key={label.id}
className={`
flex items-center gap-2 p-2 cursor-pointer transition-colors
flex items-center gap-2 px-2 py-1 cursor-pointer transition-colors
${isDarkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-50'}
`}
onClick={() => handleLabelToggle(label)}
onClick={(e) => {
e.stopPropagation();
handleLabelToggle(label);
}}
>
<Checkbox
checked={checkLabelSelected(label.id || '')}
onChange={() => handleLabelToggle(label)}
isDarkMode={isDarkMode}
/>
<div style={{ pointerEvents: 'none' }}>
<Checkbox
checked={checkLabelSelected(label.id || '')}
onChange={() => {}} // Empty handler since we handle click on the div
isDarkMode={isDarkMode}
/>
</div>

<div
className="w-3 h-3 rounded-full shrink-0"
Expand Down
8 changes: 4 additions & 4 deletions worklenz-frontend/src/components/task-list-v2/TaskRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,17 @@ const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumn
name: task.title || task.name,
parent_task_id: task.parent_task_id,
manual_progress: false,
all_labels: task.labels?.map(label => ({
all_labels: task.all_labels?.map(label => ({
id: label.id,
name: label.name,
color_code: label.color,
color_code: label.color_code,
})) || [],
labels: task.labels?.map(label => ({
id: label.id,
name: label.name,
color_code: label.color,
})) || [],
}), [task.id, task.title, task.name, task.parent_task_id, task.labels, task.labels?.length]);
}), [task.id, task.title, task.name, task.parent_task_id, task.all_labels, task.labels, task.all_labels?.length, task.labels?.length]);

// Handle checkbox change
const handleCheckboxChange = useCallback((e: any) => {
Expand Down Expand Up @@ -556,7 +556,7 @@ const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumn

case 'labels':
return (
<div className="flex items-center gap-0.5 flex-wrap min-w-0" style={{ ...baseStyle, minWidth: '150px' }}>
<div className="flex items-center gap-0.5 flex-wrap min-w-0" style={{ ...baseStyle, minWidth: '150px', width: 'auto', flexGrow: 1 }}>
<TaskLabelsCell labels={task.labels} isDarkMode={isDarkMode} />
<LabelsSelector task={labelsAdapter} isDarkMode={isDarkMode} />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ export const fetchTasksV3 = createAsyncThunk(
end: l.end,
names: l.names,
})) || [],
all_labels: task.all_labels?.map((l: { id: string; label_id: string; name: string; color_code: string }) => ({
id: l.id || l.label_id,
name: l.name,
color_code: l.color_code || '#1890ff',
})) || [],
dueDate: task.dueDate,
startDate: task.startDate,
timeTracking: {
Expand Down
27 changes: 26 additions & 1 deletion worklenz-frontend/src/hooks/useTaskSocketHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
selectCurrentGroupingV3,
fetchTasksV3,
addSubtaskToParent,
removeTemporarySubtask,
} from '@/features/task-management/task-management.slice';
import {
updateEnhancedKanbanSubtask,
Expand Down Expand Up @@ -153,13 +154,19 @@ export const useTaskSocketHandlers = () => {
const updatedTask: Task = {
...currentTask,
labels:
labels.all_labels?.map(l => ({
labels.labels?.map(l => ({
id: l.id || '',
name: l.name || '',
color: l.color_code || '#1890ff',
end: l.end,
names: l.names,
})) || [],
all_labels:
labels.all_labels?.map(l => ({
id: l.id || '',
name: l.name || '',
color_code: l.color_code || '#1890ff',
})) || [],
updatedAt: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
Expand Down Expand Up @@ -675,6 +682,24 @@ export const useTaskSocketHandlers = () => {
parent_task_id: data.parent_task_id,
is_sub_task: true,
};

// Before adding the real subtask, remove any temporary subtasks with the same name
// This prevents duplication from optimistic updates
const parentTask = store.getState().taskManagement.entities[data.parent_task_id];
if (parentTask && parentTask.sub_tasks) {
const temporarySubtasks = parentTask.sub_tasks.filter(
(st: Task) => st.isTemporary && st.name === subtask.title
);

// Remove each temporary subtask
temporarySubtasks.forEach((tempSubtask: Task) => {
dispatch(removeTemporarySubtask({
parentTaskId: data.parent_task_id,
tempId: tempSubtask.id
}));
});
}

dispatch(addSubtaskToParent({ parentId: data.parent_task_id, subtask }));

// Also update enhanced kanban slice for subtask creation
Expand Down
1 change: 1 addition & 0 deletions worklenz-frontend/src/types/task-management.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface Task {
statusColor?: string;
priorityColor?: string;
labels?: { id: string; name: string; color: string; end?: boolean; names?: string[] }[];
all_labels?: { id: string; name: string; color_code: string }[]; // Complete list of labels for selection logic
comments_count?: number;
attachments_count?: number;
has_dependencies?: boolean;
Expand Down