diff --git a/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx b/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx
index 997a8256..99fb5bb1 100644
--- a/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx
+++ b/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx
@@ -62,10 +62,7 @@ import ImprovedTaskFilters from '@/components/task-management/improved-task-filt
import OptimizedBulkActionBar from '@/components/task-management/optimized-bulk-action-bar';
import CustomColumnModal from '@/pages/projects/projectView/taskList/task-list-table/custom-columns/custom-column-modal/custom-column-modal';
import AddTaskRow from './components/AddTaskRow';
-import {
- AddCustomColumnButton,
- CustomColumnHeader,
-} from './components/CustomColumnComponents';
+import { AddCustomColumnButton, CustomColumnHeader } from './components/CustomColumnComponents';
// Hooks and utilities
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
@@ -126,7 +123,10 @@ const TaskListV2Section: React.FC = () => {
);
// Custom hooks
- const { activeId, handleDragStart, handleDragOver, handleDragEnd } = useDragAndDrop(allTasks, groups);
+ const { activeId, handleDragStart, handleDragOver, handleDragEnd } = useDragAndDrop(
+ allTasks,
+ groups
+ );
const bulkActions = useBulkActions();
// Enable real-time updates via socket handlers
@@ -156,49 +156,51 @@ const TaskListV2Section: React.FC = () => {
});
// Add visible custom columns
- const visibleCustomColumns = customColumns
- ?.filter(column => column.pinned)
- ?.map(column => {
- // Give selection columns more width for dropdown content
- const fieldType = column.custom_column_obj?.fieldType;
- let defaultWidth = 160;
- if (fieldType === 'selection') {
- defaultWidth = 150; // Reduced width for selection dropdowns
- } else if (fieldType === 'people') {
- defaultWidth = 170; // Extra width for people with avatars
- }
+ const visibleCustomColumns =
+ customColumns
+ ?.filter(column => column.pinned)
+ ?.map(column => {
+ // Give selection columns more width for dropdown content
+ const fieldType = column.custom_column_obj?.fieldType;
+ let defaultWidth = 160;
+ if (fieldType === 'selection') {
+ defaultWidth = 150; // Reduced width for selection dropdowns
+ } else if (fieldType === 'people') {
+ defaultWidth = 170; // Extra width for people with avatars
+ }
- // Map the configuration data structure to the expected format
- const customColumnObj = column.custom_column_obj || (column as any).configuration;
+ // Map the configuration data structure to the expected format
+ const customColumnObj = column.custom_column_obj || (column as any).configuration;
- // Transform configuration format to custom_column_obj format if needed
- let transformedColumnObj = customColumnObj;
- if (customColumnObj && !customColumnObj.fieldType && customColumnObj.field_type) {
- transformedColumnObj = {
- ...customColumnObj,
- fieldType: customColumnObj.field_type,
- numberType: customColumnObj.number_type,
- labelPosition: customColumnObj.label_position,
- previewValue: customColumnObj.preview_value,
- firstNumericColumn: customColumnObj.first_numeric_column_key,
- secondNumericColumn: customColumnObj.second_numeric_column_key,
- selectionsList: customColumnObj.selections_list || customColumnObj.selectionsList || [],
- labelsList: customColumnObj.labels_list || customColumnObj.labelsList || [],
+ // Transform configuration format to custom_column_obj format if needed
+ let transformedColumnObj = customColumnObj;
+ if (customColumnObj && !customColumnObj.fieldType && customColumnObj.field_type) {
+ transformedColumnObj = {
+ ...customColumnObj,
+ fieldType: customColumnObj.field_type,
+ numberType: customColumnObj.number_type,
+ labelPosition: customColumnObj.label_position,
+ previewValue: customColumnObj.preview_value,
+ firstNumericColumn: customColumnObj.first_numeric_column_key,
+ secondNumericColumn: customColumnObj.second_numeric_column_key,
+ selectionsList:
+ customColumnObj.selections_list || customColumnObj.selectionsList || [],
+ labelsList: customColumnObj.labels_list || customColumnObj.labelsList || [],
+ };
+ }
+
+ return {
+ id: column.key || column.id || 'unknown',
+ label: column.name || t('customColumns.customColumnHeader'),
+ width: `${(column as any).width || defaultWidth}px`,
+ key: column.key || column.id || 'unknown',
+ custom_column: true,
+ custom_column_obj: transformedColumnObj,
+ isCustom: true,
+ name: column.name,
+ uuid: column.id,
};
- }
-
- return {
- id: column.key || column.id || 'unknown',
- label: column.name || t('customColumns.customColumnHeader'),
- width: `${(column as any).width || defaultWidth}px`,
- key: column.key || column.id || 'unknown',
- custom_column: true,
- custom_column_obj: transformedColumnObj,
- isCustom: true,
- name: column.name,
- uuid: column.id,
- };
- }) || [];
+ }) || [];
return [...baseVisibleColumns, ...visibleCustomColumns];
}, [fields, columns, customColumns, t]);
@@ -222,15 +224,15 @@ const TaskListV2Section: React.FC = () => {
if (backendColumn) {
return {
...field,
- visible: backendColumn.pinned ?? field.visible
+ visible: backendColumn.pinned ?? field.visible,
};
}
return field;
});
// Only update if there are actual changes
- const hasChanges = updatedFields.some((field, index) =>
- field.visible !== fields[index].visible
+ const hasChanges = updatedFields.some(
+ (field, index) => field.visible !== fields[index].visible
);
if (hasChanges) {
@@ -269,65 +271,73 @@ const TaskListV2Section: React.FC = () => {
);
// Function to update custom column values
- const updateTaskCustomColumnValue = useCallback((taskId: string, columnKey: string, value: string) => {
- try {
- if (!urlProjectId) {
- console.error('Project ID is missing');
- return;
- }
+ const updateTaskCustomColumnValue = useCallback(
+ (taskId: string, columnKey: string, value: string) => {
+ try {
+ if (!urlProjectId) {
+ console.error('Project ID is missing');
+ return;
+ }
- const body = {
- task_id: taskId,
- column_key: columnKey,
- value: value,
- project_id: urlProjectId,
- };
-
- // Update the Redux store immediately for optimistic updates
- const currentTask = allTasks.find(task => task.id === taskId);
- if (currentTask) {
- const updatedTask = {
- ...currentTask,
- custom_column_values: {
- ...currentTask.custom_column_values,
- [columnKey]: value,
- },
- updated_at: new Date().toISOString(),
+ const body = {
+ task_id: taskId,
+ column_key: columnKey,
+ value: value,
+ project_id: urlProjectId,
};
- // Import and dispatch the updateTask action
- import('@/features/task-management/task-management.slice').then(({ updateTask }) => {
- dispatch(updateTask(updatedTask));
- });
- }
+ // Update the Redux store immediately for optimistic updates
+ const currentTask = allTasks.find(task => task.id === taskId);
+ if (currentTask) {
+ const updatedTask = {
+ ...currentTask,
+ custom_column_values: {
+ ...currentTask.custom_column_values,
+ [columnKey]: value,
+ },
+ updated_at: new Date().toISOString(),
+ };
- if (socket && connected) {
- socket.emit(SocketEvents.TASK_CUSTOM_COLUMN_UPDATE.toString(), JSON.stringify(body));
- } else {
- console.warn('Socket not connected, unable to emit TASK_CUSTOM_COLUMN_UPDATE event');
+ // Import and dispatch the updateTask action
+ import('@/features/task-management/task-management.slice').then(({ updateTask }) => {
+ dispatch(updateTask(updatedTask));
+ });
+ }
+
+ if (socket && connected) {
+ socket.emit(SocketEvents.TASK_CUSTOM_COLUMN_UPDATE.toString(), JSON.stringify(body));
+ } else {
+ console.warn('Socket not connected, unable to emit TASK_CUSTOM_COLUMN_UPDATE event');
+ }
+ } catch (error) {
+ console.error('Error updating custom column value:', error);
}
- } catch (error) {
- console.error('Error updating custom column value:', error);
- }
- }, [urlProjectId, socket, connected, allTasks, dispatch]);
+ },
+ [urlProjectId, socket, connected, allTasks, dispatch]
+ );
// Custom column settings handler
- const handleCustomColumnSettings = useCallback((columnKey: string) => {
- if (!columnKey) return;
+ const handleCustomColumnSettings = useCallback(
+ (columnKey: string) => {
+ if (!columnKey) return;
- const columnData = visibleColumns.find(col => col.key === columnKey || col.id === columnKey);
+ const columnData = visibleColumns.find(col => col.key === columnKey || col.id === columnKey);
- // Use the UUID for API calls, not the key (nanoid)
- // For custom columns, prioritize the uuid field over id field
- const columnId = (columnData as any)?.uuid || columnData?.id || columnKey;
+ // Use the UUID for API calls, not the key (nanoid)
+ // For custom columns, prioritize the uuid field over id field
+ const columnId = (columnData as any)?.uuid || columnData?.id || columnKey;
- dispatch(setCustomColumnModalAttributes({
- modalType: 'edit',
- columnId: columnId,
- columnData: columnData
- }));
- dispatch(toggleCustomColumnModalOpen(true));
- }, [dispatch, visibleColumns]);
+ dispatch(
+ setCustomColumnModalAttributes({
+ modalType: 'edit',
+ columnId: columnId,
+ columnData: columnData,
+ })
+ );
+ dispatch(toggleCustomColumnModalOpen(true));
+ },
+ [dispatch, visibleColumns]
+ );
// Add callback for task added
const handleTaskAdded = useCallback(() => {
@@ -350,25 +360,27 @@ const TaskListV2Section: React.FC = () => {
const visibleTasksInGroup = isCurrentGroupCollapsed
? []
: group.taskIds
- .map(taskId => allTasks.find(task => task.id === taskId))
- .filter((task): task is Task => task !== undefined);
+ .map(taskId => allTasks.find(task => task.id === taskId))
+ .filter((task): task is Task => task !== undefined);
const tasksForVirtuoso = visibleTasksInGroup.map(task => ({
...task,
originalIndex: allTasks.indexOf(task),
}));
- const itemsWithAddTask = !isCurrentGroupCollapsed ? [
- ...tasksForVirtuoso,
- {
- id: `add-task-${group.id}`,
- isAddTaskRow: true,
- groupId: group.id,
- groupType: currentGrouping || 'status',
- groupValue: group.id, // Use the actual database ID from backend
- projectId: urlProjectId,
- }
- ] : tasksForVirtuoso;
+ const itemsWithAddTask = !isCurrentGroupCollapsed
+ ? [
+ ...tasksForVirtuoso,
+ {
+ id: `add-task-${group.id}`,
+ isAddTaskRow: true,
+ groupId: group.id,
+ groupType: currentGrouping || 'status',
+ groupValue: group.id, // Use the actual database ID from backend
+ projectId: urlProjectId,
+ },
+ ]
+ : tasksForVirtuoso;
const groupData = {
...group,
@@ -398,8 +410,6 @@ const TaskListV2Section: React.FC = () => {
const isGroupCollapsed = collapsedGroups.has(group.id);
const isGroupEmpty = group.actualCount === 0;
-
-
return (
0 ? 'mt-2' : ''}>
{
{isGroupEmpty && !isGroupCollapsed && (
- {visibleColumns.map((column, index) => (
-
- ))}
+ {visibleColumns.map((column, index) => {
+ const emptyColumnStyle = {
+ width: column.width,
+ flexShrink: 0,
+ ...(column.id === 'labels' && column.width === 'auto'
+ ? { minWidth: '200px', flexGrow: 1 }
+ : {}),
+ };
+ return (
+
+ );
+ })}
@@ -440,7 +460,6 @@ const TaskListV2Section: React.FC = () => {
(taskIndex: number) => {
const item = virtuosoItems[taskIndex];
-
if (!item || !urlProjectId) return null;
if ('isAddTaskRow' in item && item.isAddTaskRow) {
@@ -469,70 +488,86 @@ const TaskListV2Section: React.FC = () => {
);
// Render column headers
- const renderColumnHeaders = useCallback(() => (
-
-
- {visibleColumns.map((column, index) => {
- const columnStyle: ColumnStyle = {
- width: column.width,
- flexShrink: 0,
- ...(column.id === 'labels' && column.width === 'auto'
- ? {
- minWidth: '200px',
- flexGrow: 1,
- }
- : {}),
- ...((column as any).minWidth && { minWidth: (column as any).minWidth }),
- ...((column as any).maxWidth && { maxWidth: (column as any).maxWidth }),
- };
+ const renderColumnHeaders = useCallback(
+ () => (
+
+
+ {visibleColumns.map((column, index) => {
+ const columnStyle: ColumnStyle = {
+ width: column.width,
+ flexShrink: 0,
+ ...(column.id === 'labels' && column.width === 'auto'
+ ? {
+ minWidth: '200px',
+ flexGrow: 1,
+ }
+ : {}),
+ ...((column as any).minWidth && { minWidth: (column as any).minWidth }),
+ ...((column as any).maxWidth && { maxWidth: (column as any).maxWidth }),
+ };
- return (
-
- {column.id === 'dragHandle' || column.id === 'checkbox' ? (
-
- ) : (column as any).isCustom ? (
-
- ) : (
- t(column.label || '')
- )}
-
- );
- })}
- {/* Add Custom Column Button - positioned at the end and scrolls with content */}
-
-
+ style={columnStyle}
+ >
+ {column.id === 'dragHandle' || column.id === 'checkbox' ? (
+
+ ) : (column as any).isCustom ? (
+
+ ) : (
+ t(column.label || '')
+ )}
+
+ );
+ })}
+ {/* Add Custom Column Button - positioned at the end and scrolls with content */}
+
-
- ), [visibleColumns, t, handleCustomColumnSettings]);
-
-
+ ),
+ [visibleColumns, t, handleCustomColumnSettings]
+ );
// Loading and error states
if (loading || loadingColumns) return
;
- if (error) return
{t('emptyStates.errorPrefix')} {error}
;
+ if (error)
+ return (
+
+ {t('emptyStates.errorPrefix')} {error}
+
+ );
// Show message when no data
if (groups.length === 0 && !loading) {
@@ -556,58 +591,61 @@ const TaskListV2Section: React.FC = () => {
}
return (
-
-
-
- {/* Table Container */}
+
+
+ {/* Table Container */}
+
+ {/* Task List Content with Sticky Header */}
- {/* Task List Content with Sticky Header */}
+ {/* Sticky Column Headers */}
- {/* Sticky Column Headers */}
-
- {renderColumnHeaders()}
-
-
!('isAddTaskRow' in item) && !item.parent_task_id)
- .map(item => item.id)
- .filter((id): id is string => id !== undefined)}
- strategy={verticalListSortingStrategy}
- >
-
- {/* Render groups manually for debugging */}
- {virtuosoGroups.map((group, groupIndex) => (
-
- {/* Group Header */}
- {renderGroup(groupIndex)}
+ {renderColumnHeaders()}
+
+
!('isAddTaskRow' in item) && !item.parent_task_id)
+ .map(item => item.id)
+ .filter((id): id is string => id !== undefined)}
+ strategy={verticalListSortingStrategy}
+ >
+
+ {/* Render groups manually for debugging */}
+ {virtuosoGroups.map((group, groupIndex) => (
+
+ {/* Group Header */}
+ {renderGroup(groupIndex)}
- {/* Group Tasks */}
- {!collapsedGroups.has(group.id) && group.tasks.map((task, taskIndex) => {
- const globalTaskIndex = virtuosoGroups
- .slice(0, groupIndex)
- .reduce((sum, g) => sum + g.count, 0) + taskIndex;
+ {/* Group Tasks */}
+ {!collapsedGroups.has(group.id) &&
+ group.tasks.map((task, taskIndex) => {
+ const globalTaskIndex =
+ virtuosoGroups.slice(0, groupIndex).reduce((sum, g) => sum + g.count, 0) +
+ taskIndex;
return (
@@ -615,63 +653,73 @@ const TaskListV2Section: React.FC = () => {
);
})}
-
- ))}
-
-
-
+
+ ))}
+
+
+
- {/* Drag Overlay */}
-
- {activeId ? (
-
-
-
-
-
-
- {allTasks.find(task => task.id === activeId)?.name ||
- allTasks.find(task => task.id === activeId)?.title ||
- t('emptyStates.dragTaskFallback')}
-
-
- {allTasks.find(task => task.id === activeId)?.task_key}
-
+ {/* Drag Overlay */}
+
+ {activeId ? (
+
+
+
+
+
+
+ {allTasks.find(task => task.id === activeId)?.name ||
+ allTasks.find(task => task.id === activeId)?.title ||
+ t('emptyStates.dragTaskFallback')}
+
+
+ {allTasks.find(task => task.id === activeId)?.task_key}
- ) : null}
-
-
- {/* Bulk Action Bar */}
- {selectedTaskIds.length > 0 && urlProjectId && (
-
- bulkActions.handleBulkStatusChange(statusId, selectedTaskIds)}
- onBulkPriorityChange={(priorityId) => bulkActions.handleBulkPriorityChange(priorityId, selectedTaskIds)}
- onBulkPhaseChange={(phaseId) => bulkActions.handleBulkPhaseChange(phaseId, selectedTaskIds)}
- onBulkAssignToMe={() => bulkActions.handleBulkAssignToMe(selectedTaskIds)}
- onBulkAssignMembers={(memberIds) => bulkActions.handleBulkAssignMembers(memberIds, selectedTaskIds)}
- onBulkAddLabels={(labelIds) => bulkActions.handleBulkAddLabels(labelIds, selectedTaskIds)}
- onBulkArchive={() => bulkActions.handleBulkArchive(selectedTaskIds)}
- onBulkDelete={() => bulkActions.handleBulkDelete(selectedTaskIds)}
- onBulkDuplicate={() => bulkActions.handleBulkDuplicate(selectedTaskIds)}
- onBulkExport={() => bulkActions.handleBulkExport(selectedTaskIds)}
- onBulkSetDueDate={(date) => bulkActions.handleBulkSetDueDate(date, selectedTaskIds)}
- />
- )}
+ ) : null}
+
- {/* Custom Column Modal */}
- {createPortal(
, document.body, 'custom-column-modal')}
-
-
+ {/* Bulk Action Bar */}
+ {selectedTaskIds.length > 0 && urlProjectId && (
+
+
+ bulkActions.handleBulkStatusChange(statusId, selectedTaskIds)
+ }
+ onBulkPriorityChange={priorityId =>
+ bulkActions.handleBulkPriorityChange(priorityId, selectedTaskIds)
+ }
+ onBulkPhaseChange={phaseId =>
+ bulkActions.handleBulkPhaseChange(phaseId, selectedTaskIds)
+ }
+ onBulkAssignToMe={() => bulkActions.handleBulkAssignToMe(selectedTaskIds)}
+ onBulkAssignMembers={memberIds =>
+ bulkActions.handleBulkAssignMembers(memberIds, selectedTaskIds)
+ }
+ onBulkAddLabels={labelIds =>
+ bulkActions.handleBulkAddLabels(labelIds, selectedTaskIds)
+ }
+ onBulkArchive={() => bulkActions.handleBulkArchive(selectedTaskIds)}
+ onBulkDelete={() => bulkActions.handleBulkDelete(selectedTaskIds)}
+ onBulkDuplicate={() => bulkActions.handleBulkDuplicate(selectedTaskIds)}
+ onBulkExport={() => bulkActions.handleBulkExport(selectedTaskIds)}
+ onBulkSetDueDate={date => bulkActions.handleBulkSetDueDate(date, selectedTaskIds)}
+ />
+
+ )}
+
+ {/* Custom Column Modal */}
+ {createPortal(, document.body, 'custom-column-modal')}
+
+
);
};
diff --git a/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx b/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx
index bf2a663c..93510cd0 100644
--- a/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx
+++ b/worklenz-frontend/src/components/task-list-v2/TaskRow.tsx
@@ -271,7 +271,7 @@ const TaskRow: React.FC
= memo(({ taskId, projectId, visibleColumn
case 'checkbox':
return (
-
+
= memo(({ taskId, projectId, visibleColumn
case 'taskKey':
return (
-
+
{task.task_key || 'N/A'}
@@ -291,7 +291,7 @@ const TaskRow: React.FC
= memo(({ taskId, projectId, visibleColumn
case 'title':
return (
-
+
{/* Indentation for subtasks - tighter spacing */}
{isSubtask &&
}
@@ -417,7 +417,7 @@ const TaskRow: React.FC
= memo(({ taskId, projectId, visibleColumn
case 'description':
return (
-
+
= memo(({ taskId, projectId, visibleColumn
case 'status':
return (
-
+
= memo(({ taskId, projectId, visibleColumn
case 'assignees':
return (
-
+
= memo(({ taskId, projectId, visibleColumn
case 'priority':
return (
-
+
= memo(({ taskId, projectId, visibleColumn
case 'dueDate':
return (
-
+
{activeDatePicker === 'dueDate' ? (
= memo(({ taskId, projectId, visibleColumn
case 'progress':
return (
-
+
{task.progress !== undefined &&
task.progress >= 0 &&
(task.progress === 100 ? (
@@ -555,8 +555,13 @@ const TaskRow: React.FC
= memo(({ taskId, projectId, visibleColumn
);
case 'labels':
+ const labelsColumn = visibleColumns.find(col => col.id === 'labels');
+ const labelsStyle = {
+ ...baseStyle,
+ ...(labelsColumn?.width === 'auto' ? { minWidth: '200px', flexGrow: 1 } : {})
+ };
return (
-
+
@@ -564,7 +569,7 @@ const TaskRow: React.FC
= memo(({ taskId, projectId, visibleColumn
case 'phase':
return (
-
+
= memo(({ taskId, projectId, visibleColumn
case 'timeTracking':
return (
-
+
);
case 'estimation':
+ // Use timeTracking.estimated which is the converted value from backend's total_minutes
+ const estimationDisplay = (() => {
+ const estimatedHours = task.timeTracking?.estimated;
+
+ if (estimatedHours && estimatedHours > 0) {
+ // Convert decimal hours to hours and minutes for display
+ const hours = Math.floor(estimatedHours);
+ const minutes = Math.round((estimatedHours - hours) * 60);
+
+ if (hours > 0 && minutes > 0) {
+ return `${hours}h ${minutes}m`;
+ } else if (hours > 0) {
+ return `${hours}h`;
+ } else if (minutes > 0) {
+ return `${minutes}m`;
+ }
+ }
+
+ return null;
+ })();
+
return (
-
- {task.timeTracking?.estimated ? (
+
+ {estimationDisplay ? (
- {task.timeTracking.estimated}h
+ {estimationDisplay}
) : (
- 0
+ -
)}
@@ -597,7 +623,7 @@ const TaskRow: React.FC
= memo(({ taskId, projectId, visibleColumn
case 'startDate':
return (
-
+
{activeDatePicker === 'startDate' ? (
= memo(({ taskId, projectId, visibleColumn
case 'completedDate':
return (
-
+
{formattedDates.completed ? (
{formattedDates.completed}
@@ -668,7 +694,7 @@ const TaskRow: React.FC = memo(({ taskId, projectId, visibleColumn
case 'createdDate':
return (
-
+
{formattedDates.created ? (
{formattedDates.created}
@@ -680,9 +706,8 @@ const TaskRow: React.FC = memo(({ taskId, projectId, visibleColumn
);
case 'lastUpdated':
- console.log('formattedDates.updated', formattedDates.updated);
return (
-
+
{formattedDates.updated ? (
{formattedDates.updated}
@@ -695,7 +720,7 @@ const TaskRow: React.FC = memo(({ taskId, projectId, visibleColumn
case 'reporter':
return (
-
+
{task.reporter ? (
{task.reporter}
) : (
@@ -709,7 +734,7 @@ const TaskRow: React.FC
= memo(({ taskId, projectId, visibleColumn
const column = visibleColumns.find(col => col.id === columnId);
if (column && (column.custom_column || column.isCustom) && updateTaskCustomColumnValue) {
return (
-
+
= memo(({
case 'checkbox':
case 'taskKey':
case 'description':
- return ;
+ return ;
+ case 'labels':
+ const labelsStyle = {
+ ...baseStyle,
+ ...(width === 'auto' ? { minWidth: '200px', flexGrow: 1 } : {})
+ };
+ return ;
case 'title':
return (
-
+
@@ -129,7 +135,7 @@ const AddTaskRow: React.FC
= memo(({
);
default:
- return
;
+ return
;
}
}, [isAdding, taskName, handleAddTask, handleCancel, t]);
diff --git a/worklenz-frontend/src/features/task-management/task-management.slice.ts b/worklenz-frontend/src/features/task-management/task-management.slice.ts
index c555f6e3..c10805e1 100644
--- a/worklenz-frontend/src/features/task-management/task-management.slice.ts
+++ b/worklenz-frontend/src/features/task-management/task-management.slice.ts
@@ -281,8 +281,8 @@ export const fetchTasksV3 = createAsyncThunk(
dueDate: task.dueDate,
startDate: task.startDate,
timeTracking: {
- estimated: convertTimeValue(task.total_time),
- logged: convertTimeValue(task.time_spent),
+ estimated: task.timeTracking?.estimated || 0,
+ logged: task.timeTracking?.logged || 0,
},
customFields: {},
custom_column_values: task.custom_column_values || {},
diff --git a/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts b/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts
index cf857653..cc55829b 100644
--- a/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts
+++ b/worklenz-frontend/src/hooks/useTaskSocketHandlers.ts
@@ -670,15 +670,32 @@ export const useTaskSocketHandlers = () => {
const handleEstimationChange = useCallback(
- (task: { id: string; parent_task: string | null; estimation: number }) => {
- if (!task) return;
+ (data: { id: string; parent_task: string | null; total_hours: number; total_minutes: number }) => {
+ if (!data) return;
+ // Update the old task slice (for backward compatibility)
const taskWithProgress = {
- ...task,
+ ...data,
manual_progress: false,
} as IProjectTask;
dispatch(updateTaskEstimation({ task: taskWithProgress }));
+
+ // Update task-management slice for task-list-v2 components
+ const currentTask = store.getState().taskManagement.entities[data.id];
+ if (currentTask) {
+ const estimatedHours = (data.total_hours || 0) + (data.total_minutes || 0) / 60;
+ const updatedTask: Task = {
+ ...currentTask,
+ timeTracking: {
+ ...currentTask.timeTracking,
+ estimated: estimatedHours,
+ },
+ updatedAt: new Date().toISOString(),
+ updated_at: new Date().toISOString(),
+ };
+ dispatch(updateTask(updatedTask));
+ }
},
[dispatch]
);