feat(tasks): add color_code_dark to task groups and enhance task list display
- Introduced color_code_dark property to task groups for improved theming support. - Updated task list components to utilize color_code_dark based on the current theme mode. - Enhanced empty state handling in task list to dynamically create an unmapped group for better user experience. - Refactored task management slice to support dynamic group creation for unmapped tasks.
This commit is contained in:
@@ -16,6 +16,7 @@ export interface ITaskGroup {
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
color_code: string;
|
||||
color_code_dark: string;
|
||||
category_id: string | null;
|
||||
old_category_id?: string;
|
||||
todo_progress?: number;
|
||||
|
||||
@@ -110,20 +110,20 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
|
||||
private static getQuery(userId: string, options: ParsedQs) {
|
||||
// Determine which sort column to use based on grouping
|
||||
const groupBy = options.group || 'status';
|
||||
let defaultSortColumn = 'sort_order';
|
||||
const groupBy = options.group || "status";
|
||||
let defaultSortColumn = "sort_order";
|
||||
switch (groupBy) {
|
||||
case 'status':
|
||||
defaultSortColumn = 'status_sort_order';
|
||||
case "status":
|
||||
defaultSortColumn = "status_sort_order";
|
||||
break;
|
||||
case 'priority':
|
||||
defaultSortColumn = 'priority_sort_order';
|
||||
case "priority":
|
||||
defaultSortColumn = "priority_sort_order";
|
||||
break;
|
||||
case 'phase':
|
||||
defaultSortColumn = 'phase_sort_order';
|
||||
case "phase":
|
||||
defaultSortColumn = "phase_sort_order";
|
||||
break;
|
||||
default:
|
||||
defaultSortColumn = 'sort_order';
|
||||
defaultSortColumn = "sort_order";
|
||||
}
|
||||
|
||||
const searchField = options.search ? ["t.name", "CONCAT((SELECT key FROM projects WHERE id = t.project_id), '-', task_no)"] : defaultSortColumn;
|
||||
@@ -436,6 +436,7 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
name: UNMAPPED,
|
||||
category_id: null,
|
||||
color_code: "#fbc84c69",
|
||||
color_code_dark: "#fbc84c69",
|
||||
tasks: unmapped
|
||||
};
|
||||
}
|
||||
@@ -1008,7 +1009,6 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
const startTime = performance.now();
|
||||
const isSubTasks = !!req.query.parent_task;
|
||||
const groupBy = (req.query.group || GroupBy.STATUS) as string;
|
||||
const archived = req.query.archived === "true";
|
||||
|
||||
// PERFORMANCE OPTIMIZATION: Skip expensive progress calculation by default
|
||||
// Progress values are already calculated and stored in the database
|
||||
@@ -1017,23 +1017,17 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
const shouldRefreshProgress = req.query.refresh_progress === "true";
|
||||
|
||||
if (shouldRefreshProgress && req.params.id) {
|
||||
const progressStartTime = performance.now();
|
||||
await this.refreshProjectTaskProgressValues(req.params.id);
|
||||
const progressEndTime = performance.now();
|
||||
}
|
||||
|
||||
const queryStartTime = performance.now();
|
||||
const q = TasksControllerV2.getQuery(req.user?.id as string, req.query);
|
||||
const params = isSubTasks ? [req.params.id || null, req.query.parent_task] : [req.params.id || null];
|
||||
|
||||
const result = await db.query(q, params);
|
||||
const tasks = [...result.rows];
|
||||
const queryEndTime = performance.now();
|
||||
|
||||
// Get groups metadata dynamically from database
|
||||
const groupsStartTime = performance.now();
|
||||
const groups = await this.getGroups(groupBy, req.params.id);
|
||||
const groupsEndTime = performance.now();
|
||||
|
||||
// Create priority value to name mapping
|
||||
const priorityMap: Record<string, string> = {
|
||||
@@ -1051,10 +1045,7 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transform tasks with all necessary data preprocessing
|
||||
const transformStartTime = performance.now();
|
||||
const transformedTasks = tasks.map((task, index) => {
|
||||
// Update task with calculated values (lightweight version)
|
||||
TasksControllerV2.updateTaskViewModel(task);
|
||||
@@ -1125,10 +1116,6 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
reporter: task.reporter || null,
|
||||
};
|
||||
});
|
||||
const transformEndTime = performance.now();
|
||||
|
||||
// Create groups based on dynamic data from database
|
||||
const groupingStartTime = performance.now();
|
||||
const groupedResponse: Record<string, any> = {};
|
||||
|
||||
// Initialize groups from database data
|
||||
@@ -1148,6 +1135,7 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
tasks: [],
|
||||
taskIds: [],
|
||||
color: group.color_code || this.getDefaultGroupColor(groupBy, groupKey),
|
||||
color_code_dark: group.color_code_dark || this.getDefaultGroupColor(groupBy, groupKey),
|
||||
// Include additional metadata from database
|
||||
category_id: group.category_id,
|
||||
start_date: group.start_date,
|
||||
@@ -1294,8 +1282,6 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
responseGroups.push(groupedResponse[UNMAPPED.toLowerCase()]);
|
||||
}
|
||||
|
||||
const groupingEndTime = performance.now();
|
||||
|
||||
const endTime = performance.now();
|
||||
const totalTime = endTime - startTime;
|
||||
|
||||
@@ -1333,16 +1319,11 @@ export default class TasksControllerV2 extends TasksControllerBase {
|
||||
done: "#52c41a",
|
||||
},
|
||||
[GroupBy.PRIORITY]: {
|
||||
critical: "#ff4d4f",
|
||||
high: "#ff7a45",
|
||||
medium: "#faad14",
|
||||
low: "#52c41a",
|
||||
},
|
||||
[GroupBy.PHASE]: {
|
||||
planning: "#722ed1",
|
||||
development: "#1890ff",
|
||||
testing: "#faad14",
|
||||
deployment: "#52c41a",
|
||||
unmapped: "#fbc84c69",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -89,24 +89,24 @@ export const NumbersColorMap: { [x: string]: string } = {
|
||||
};
|
||||
|
||||
export const PriorityColorCodes: { [x: number]: string; } = {
|
||||
0: "#75c997",
|
||||
1: "#fbc84c",
|
||||
2: "#f37070"
|
||||
0: "#2E8B57",
|
||||
1: "#DAA520",
|
||||
2: "#CD5C5C"
|
||||
};
|
||||
|
||||
export const PriorityColorCodesDark: { [x: number]: string; } = {
|
||||
0: "#46D980",
|
||||
1: "#FFC227",
|
||||
2: "#FF4141"
|
||||
0: "#3CB371",
|
||||
1: "#B8860B",
|
||||
2: "#F08080"
|
||||
};
|
||||
|
||||
export const TASK_STATUS_TODO_COLOR = "#a9a9a9";
|
||||
export const TASK_STATUS_DOING_COLOR = "#70a6f3";
|
||||
export const TASK_STATUS_DONE_COLOR = "#75c997";
|
||||
|
||||
export const TASK_PRIORITY_LOW_COLOR = "#75c997";
|
||||
export const TASK_PRIORITY_MEDIUM_COLOR = "#fbc84c";
|
||||
export const TASK_PRIORITY_HIGH_COLOR = "#f37070";
|
||||
export const TASK_PRIORITY_LOW_COLOR = "#2E8B57";
|
||||
export const TASK_PRIORITY_MEDIUM_COLOR = "#DAA520";
|
||||
export const TASK_PRIORITY_HIGH_COLOR = "#CD5C5C";
|
||||
|
||||
export const TASK_DUE_COMPLETED_COLOR = "#75c997";
|
||||
export const TASK_DUE_UPCOMING_COLOR = "#70a6f3";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tooltip } from 'antd';
|
||||
|
||||
interface GroupProgressBarProps {
|
||||
todoProgress: number;
|
||||
@@ -21,18 +22,27 @@ const GroupProgressBar: React.FC<GroupProgressBarProps> = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const total = todoProgress + doingProgress + doneProgress;
|
||||
const total = (todoProgress || 0) + (doingProgress || 0) + (doneProgress || 0);
|
||||
|
||||
// Don't show if no progress values exist
|
||||
if (total === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Tooltip content with all values in rows
|
||||
const tooltipContent = (
|
||||
<div>
|
||||
<div>{t('todo')}: {todoProgress || 0}%</div>
|
||||
<div>{t('inProgress')}: {doingProgress || 0}%</div>
|
||||
<div>{t('done')}: {doneProgress || 0}%</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Compact progress text */}
|
||||
<span className="text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap font-medium">
|
||||
{doneProgress}% {t('done')}
|
||||
{doneProgress || 0}% {t('done')}
|
||||
</span>
|
||||
|
||||
{/* Compact progress bar */}
|
||||
@@ -40,27 +50,30 @@ const GroupProgressBar: React.FC<GroupProgressBarProps> = ({
|
||||
<div className="h-full flex">
|
||||
{/* Todo section - light green */}
|
||||
{todoProgress > 0 && (
|
||||
<div
|
||||
className="bg-green-200 dark:bg-green-800 transition-all duration-300"
|
||||
style={{ width: `${(todoProgress / total) * 100}%` }}
|
||||
title={`${t('todo')}: ${todoProgress}%`}
|
||||
/>
|
||||
<Tooltip title={tooltipContent} placement="top">
|
||||
<div
|
||||
className="bg-green-200 dark:bg-green-800 transition-all duration-300"
|
||||
style={{ width: `${(todoProgress / total) * 100}%` }}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* Doing section - medium green */}
|
||||
{doingProgress > 0 && (
|
||||
<div
|
||||
className="bg-green-400 dark:bg-green-600 transition-all duration-300"
|
||||
style={{ width: `${(doingProgress / total) * 100}%` }}
|
||||
title={`${t('inProgress')}: ${doingProgress}%`}
|
||||
/>
|
||||
<Tooltip title={tooltipContent} placement="top">
|
||||
<div
|
||||
className="bg-green-400 dark:bg-green-600 transition-all duration-300"
|
||||
style={{ width: `${(doingProgress / total) * 100}%` }}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* Done section - dark green */}
|
||||
{doneProgress > 0 && (
|
||||
<div
|
||||
className="bg-green-600 dark:bg-green-400 transition-all duration-300"
|
||||
style={{ width: `${(doneProgress / total) * 100}%` }}
|
||||
title={`${t('done')}: ${doneProgress}%`}
|
||||
/>
|
||||
<Tooltip title={tooltipContent} placement="top">
|
||||
<div
|
||||
className="bg-green-600 dark:bg-green-400 transition-all duration-300"
|
||||
style={{ width: `${(doneProgress / total) * 100}%` }}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,22 +81,25 @@ const GroupProgressBar: React.FC<GroupProgressBarProps> = ({
|
||||
{/* Small legend dots with better spacing */}
|
||||
<div className="flex items-center gap-1">
|
||||
{todoProgress > 0 && (
|
||||
<div
|
||||
className="w-1.5 h-1.5 bg-green-200 dark:bg-green-800 rounded-full"
|
||||
title={`${t('todo')}: ${todoProgress}%`}
|
||||
/>
|
||||
<Tooltip title={tooltipContent} placement="top">
|
||||
<div
|
||||
className="w-1.5 h-1.5 bg-green-200 dark:bg-green-800 rounded-full"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{doingProgress > 0 && (
|
||||
<div
|
||||
className="w-1.5 h-1.5 bg-green-400 dark:bg-green-600 rounded-full"
|
||||
title={`${t('inProgress')}: ${doingProgress}%`}
|
||||
/>
|
||||
<Tooltip title={tooltipContent} placement="top">
|
||||
<div
|
||||
className="w-1.5 h-1.5 bg-green-400 dark:bg-green-600 rounded-full"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{doneProgress > 0 && (
|
||||
<div
|
||||
className="w-1.5 h-1.5 bg-green-600 dark:bg-green-400 rounded-full"
|
||||
title={`${t('done')}: ${doneProgress}%`}
|
||||
/>
|
||||
<Tooltip title={tooltipContent} placement="top">
|
||||
<div
|
||||
className="w-1.5 h-1.5 bg-green-600 dark:bg-green-400 rounded-full"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,6 @@ import { fetchPhasesByProjectId } from '@/features/projects/singleProject/phase/
|
||||
// Components
|
||||
import TaskRowWithSubtasks from './TaskRowWithSubtasks';
|
||||
import TaskGroupHeader from './TaskGroupHeader';
|
||||
import ImprovedTaskFilters from '@/components/task-management/improved-task-filters';
|
||||
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';
|
||||
@@ -177,6 +176,8 @@ const TaskListV2Section: React.FC = () => {
|
||||
const { projectId: urlProjectId } = useParams();
|
||||
const { t } = useTranslation('task-list-table');
|
||||
const { socket, connected } = useSocket();
|
||||
const themeMode = useAppSelector(state => state.themeReducer.mode);
|
||||
const isDarkMode = themeMode === 'dark';
|
||||
|
||||
// Redux state selectors
|
||||
const allTasks = useAppSelector(selectAllTasksArray);
|
||||
@@ -490,7 +491,7 @@ const TaskListV2Section: React.FC = () => {
|
||||
isAddTaskRow: true,
|
||||
groupId: group.id,
|
||||
groupType: currentGrouping || 'status',
|
||||
groupValue: group.id, // Use the actual database ID from backend
|
||||
groupValue: group.id, // Send the UUID that backend expects
|
||||
projectId: urlProjectId,
|
||||
rowId: `add-task-${group.id}-0`,
|
||||
autoFocus: false,
|
||||
@@ -501,7 +502,7 @@ const TaskListV2Section: React.FC = () => {
|
||||
isAddTaskRow: true,
|
||||
groupId: group.id,
|
||||
groupType: currentGrouping || 'status',
|
||||
groupValue: group.id,
|
||||
groupValue: group.id, // Send the UUID that backend expects
|
||||
projectId: urlProjectId,
|
||||
rowId: rowId,
|
||||
autoFocus: index === groupAddRows.length - 1, // Auto-focus the latest row
|
||||
@@ -534,6 +535,7 @@ const TaskListV2Section: React.FC = () => {
|
||||
return virtuosoGroups.flatMap(group => group.tasks);
|
||||
}, [virtuosoGroups]);
|
||||
|
||||
|
||||
// Render functions
|
||||
const renderGroup = useCallback(
|
||||
(groupIndex: number) => {
|
||||
@@ -548,7 +550,7 @@ const TaskListV2Section: React.FC = () => {
|
||||
id: group.id,
|
||||
name: group.title,
|
||||
count: group.actualCount,
|
||||
color: group.color,
|
||||
color: isDarkMode ? group.color_code_dark : group.color,
|
||||
}}
|
||||
isCollapsed={isGroupCollapsed}
|
||||
onToggle={() => handleGroupCollapse(group.id)}
|
||||
@@ -679,13 +681,97 @@ const TaskListV2Section: React.FC = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
// Show message when no data
|
||||
// Show message when no data - but for phase grouping, create an unmapped group
|
||||
if (groups.length === 0 && !loading) {
|
||||
// If grouped by phase, show an unmapped group to allow task creation
|
||||
if (currentGrouping === 'phase') {
|
||||
const unmappedGroup = {
|
||||
id: 'Unmapped',
|
||||
title: 'Unmapped',
|
||||
groupType: 'phase',
|
||||
groupValue: 'Unmapped', // Use same ID as groupValue for consistency
|
||||
collapsed: false,
|
||||
tasks: [],
|
||||
taskIds: [],
|
||||
color: '#fbc84c69',
|
||||
actualCount: 0,
|
||||
count: 1, // For the add task row
|
||||
startIndex: 0
|
||||
};
|
||||
|
||||
|
||||
const unmappedVirtuosoGroups = [unmappedGroup];
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragStart={handleDragStart}
|
||||
onDragOver={handleDragOver}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<div className="flex flex-col bg-white dark:bg-gray-900 h-full overflow-hidden">
|
||||
<div
|
||||
className="border border-gray-200 dark:border-gray-700 rounded-lg"
|
||||
style={{
|
||||
height: 'calc(100vh - 240px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={contentScrollRef}
|
||||
className="flex-1 bg-white dark:bg-gray-900 relative"
|
||||
style={{
|
||||
overflowX: 'auto',
|
||||
overflowY: 'auto',
|
||||
minHeight: 0,
|
||||
}}
|
||||
>
|
||||
{/* Sticky Column Headers */}
|
||||
<div
|
||||
className="sticky top-0 z-30 bg-gray-50 dark:bg-gray-800"
|
||||
style={{ width: '100%', minWidth: 'max-content' }}
|
||||
>
|
||||
{renderColumnHeaders()}
|
||||
</div>
|
||||
|
||||
<div style={{ minWidth: 'max-content' }}>
|
||||
<div className="mt-2">
|
||||
<TaskGroupHeader
|
||||
group={{
|
||||
id: 'Unmapped',
|
||||
name: 'Unmapped',
|
||||
count: 0,
|
||||
color: '#fbc84c69',
|
||||
}}
|
||||
isCollapsed={false}
|
||||
onToggle={() => {}}
|
||||
projectId={urlProjectId || ''}
|
||||
/>
|
||||
<AddTaskRow
|
||||
groupId="Unmapped"
|
||||
groupType="phase"
|
||||
groupValue="Unmapped"
|
||||
projectId={urlProjectId || ''}
|
||||
visibleColumns={visibleColumns}
|
||||
onTaskAdded={handleTaskAdded}
|
||||
rowId="add-task-Unmapped-0"
|
||||
autoFocus={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
|
||||
// For other groupings, show the empty state message
|
||||
return (
|
||||
<div className="flex flex-col bg-white dark:bg-gray-900 h-full">
|
||||
<div className="flex-none" style={{ height: '74px', flexShrink: 0 }}>
|
||||
<ImprovedTaskFilters position="list" />
|
||||
</div>
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-medium text-gray-900 dark:text-white mb-2">
|
||||
@@ -806,19 +892,17 @@ const TaskListV2Section: React.FC = () => {
|
||||
{/* Drag Overlay */}
|
||||
<DragOverlay dropAnimation={{ duration: 200, easing: 'ease-in-out' }}>
|
||||
{activeId ? (
|
||||
<div className="bg-white dark:bg-gray-800 shadow-2xl rounded-lg border-2 border-blue-500 dark:border-blue-400 scale-105">
|
||||
<div className="px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<HolderOutlined className="text-blue-500 dark:text-blue-400" />
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{allTasks.find(task => task.id === activeId)?.name ||
|
||||
allTasks.find(task => task.id === activeId)?.title ||
|
||||
t('emptyStates.dragTaskFallback')}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{allTasks.find(task => task.id === activeId)?.task_key}
|
||||
</div>
|
||||
<div
|
||||
className="bg-white dark:bg-gray-800 shadow-lg rounded-md border border-blue-400 dark:border-blue-500 opacity-90"
|
||||
style={{ width: visibleColumns.find(col => col.id === 'title')?.width || '300px' }}
|
||||
>
|
||||
<div className="px-3 py-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<HolderOutlined className="text-gray-400 dark:text-gray-500 text-xs" />
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-white truncate flex-1">
|
||||
{allTasks.find(task => task.id === activeId)?.name ||
|
||||
allTasks.find(task => task.id === activeId)?.title ||
|
||||
t('emptyStates.dragTaskFallback')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -526,9 +526,25 @@ const taskManagementSlice = createSlice({
|
||||
},
|
||||
addTaskToGroup: (state, action: PayloadAction<{ task: Task; groupId: string }>) => {
|
||||
const { task, groupId } = action.payload;
|
||||
|
||||
state.ids.push(task.id);
|
||||
state.entities[task.id] = task;
|
||||
const group = state.groups.find(g => g.id === groupId);
|
||||
let group = state.groups.find(g => g.id === groupId);
|
||||
|
||||
// If group doesn't exist and it's "Unmapped", create it dynamically
|
||||
if (!group && groupId === 'Unmapped') {
|
||||
const unmappedGroup = {
|
||||
id: 'Unmapped',
|
||||
title: 'Unmapped',
|
||||
taskIds: [],
|
||||
type: 'phase' as const,
|
||||
color: '#fbc84c69',
|
||||
groupValue: 'Unmapped'
|
||||
};
|
||||
state.groups.push(unmappedGroup);
|
||||
group = unmappedGroup;
|
||||
}
|
||||
|
||||
if (group) {
|
||||
group.taskIds.push(task.id);
|
||||
}
|
||||
@@ -1170,7 +1186,7 @@ export default taskManagementSlice.reducer;
|
||||
|
||||
// V3 API selectors - no processing needed, data is pre-processed by backend
|
||||
export const selectTaskGroupsV3 = (state: RootState) => state.taskManagement.groups;
|
||||
export const selectCurrentGroupingV3 = (state: RootState) => state.taskManagement.grouping;
|
||||
export const selectCurrentGroupingV3 = (state: RootState) => state.grouping.currentGrouping;
|
||||
|
||||
// Column-related selectors
|
||||
export const selectColumns = (state: RootState) => state.taskManagement.columns;
|
||||
|
||||
@@ -855,10 +855,11 @@ export const useTaskSocketHandlers = () => {
|
||||
// For priority grouping, use priority field (which contains the priority UUID)
|
||||
groupId = data.priority;
|
||||
} else if (grouping === 'phase') {
|
||||
// For phase grouping, use phase_id
|
||||
groupId = data.phase_id;
|
||||
// For phase grouping, use phase_id, or 'Unmapped' if no phase_id
|
||||
groupId = data.phase_id || 'Unmapped';
|
||||
}
|
||||
|
||||
|
||||
// Use addTaskToGroup with the actual group UUID
|
||||
dispatch(addTaskToGroup({ task, groupId: groupId || '' }));
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ export interface TaskGroup {
|
||||
taskIds: string[];
|
||||
type?: 'status' | 'priority' | 'phase' | 'members';
|
||||
color?: string;
|
||||
color_code_dark?: string;
|
||||
collapsed?: boolean;
|
||||
groupValue?: string;
|
||||
// Add any other group properties as needed
|
||||
|
||||
Reference in New Issue
Block a user