From 6f5e5f5c30d959431d20515cb86c32cb9f251f2d Mon Sep 17 00:00:00 2001 From: shancds Date: Wed, 25 Jun 2025 09:08:06 +0530 Subject: [PATCH 1/3] style(enhanced-kanban): comment out unused styles in EnhancedKanbanGroup for cleaner code --- .../src/components/enhanced-kanban/EnhancedKanbanGroup.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx index 4dcca867..8b4dbf05 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx @@ -413,7 +413,7 @@ const EnhancedKanbanGroup: React.FC = React.memo(({ type="text" size="small" shape="circle" - style={{ color: themeMode === 'dark' ? '#383838' : '' }} + // style={{ color: themeMode === 'dark' ? '#383838' : '' }} onClick={() => { setShowNewCardTop(true); setShowNewCardBottom(false); @@ -433,8 +433,8 @@ const EnhancedKanbanGroup: React.FC = React.memo(({ From 9ce6cd63d10f76cd5835503217038f5160365a4e Mon Sep 17 00:00:00 2001 From: shancds Date: Wed, 25 Jun 2025 09:53:41 +0530 Subject: [PATCH 2/3] refactor(enhanced-kanban): remove inline style from VirtualizedTaskList for cleaner markup --- .../src/components/enhanced-kanban/VirtualizedTaskList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worklenz-frontend/src/components/enhanced-kanban/VirtualizedTaskList.tsx b/worklenz-frontend/src/components/enhanced-kanban/VirtualizedTaskList.tsx index c271da35..e3ca9f7e 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/VirtualizedTaskList.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/VirtualizedTaskList.tsx @@ -38,7 +38,7 @@ const VirtualizedTaskList: React.FC = ({ onTaskRender?.(task, index); return ( -
+
Date: Wed, 25 Jun 2025 15:24:44 +0530 Subject: [PATCH 3/3] feat(enhanced-kanban): integrate ImprovedTaskFilters and fetchBoardSubTasks for enhanced task management - Replaced the existing TaskListFilters with ImprovedTaskFilters in EnhancedKanbanBoard for better filtering capabilities. - Updated EnhancedKanbanTaskCard to handle subtask expansion and fetching using the new fetchBoardSubTasks action. - Added sectionId prop to EnhancedKanbanTaskCard and EnhancedKanbanGroup for improved task organization. - Refactored project-view-header to utilize fetchEnhancedKanbanGroups for loading task groups. --- .../enhanced-kanban/EnhancedKanbanBoard.tsx | 17 +- .../enhanced-kanban/EnhancedKanbanGroup.tsx | 2 + .../EnhancedKanbanTaskCard.tsx | 158 +++++++++++++----- .../enhanced-kanban/enhanced-kanban.slice.ts | 54 ++++++ .../projectView/project-view-header.tsx | 4 +- 5 files changed, 187 insertions(+), 48 deletions(-) diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoard.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoard.tsx index 1837be05..6189ca14 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoard.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoard.tsx @@ -44,6 +44,7 @@ import { ITaskStatusCreateRequest } from '@/types/tasks/task-status-create-reque import alertService from '@/services/alerts/alertService'; import { IGroupBy } from '@/features/enhanced-kanban/enhanced-kanban.slice'; import EnhancedKanbanCreateSection from './EnhancedKanbanCreateSection'; +import ImprovedTaskFilters from '../task-management/improved-task-filters'; // Import the TaskListFilters component const TaskListFilters = React.lazy(() => import('@/pages/projects/projectView/taskList/task-list-filters/task-list-filters')); @@ -382,15 +383,12 @@ const EnhancedKanbanBoard: React.FC = ({ projectId, cl return ( <> - - Loading filters...
}> - - - + {/* Task Filters */} +
+ Loading filters...
}> + + +
{/* Performance Monitor - only show for large datasets */} {/* {performanceMetrics.totalTasks > 100 && } */} @@ -431,6 +429,7 @@ const EnhancedKanbanBoard: React.FC = ({ projectId, cl {activeTask && ( )} diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx index 8b4dbf05..5d33a77a 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanGroup.tsx @@ -121,6 +121,7 @@ const EnhancedKanbanGroup: React.FC = React.memo(({ const renderTask = useMemo(() => (task: any, index: number) => ( = React.memo(({ diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanTaskCard.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanTaskCard.tsx index 1ba62614..d2959707 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanTaskCard.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanTaskCard.tsx @@ -20,26 +20,46 @@ import { ForkOutlined } from '@ant-design/icons'; import { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; import { CaretDownFilled, CaretRightFilled } from '@ant-design/icons'; +import { fetchBoardSubTasks } from '@/features/enhanced-kanban/enhanced-kanban.slice'; +import { Divider } from 'antd'; +import { List } from 'antd'; +import { Skeleton } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; +import BoardSubTaskCard from '@/pages/projects/projectView/board/board-section/board-sub-task-card/board-sub-task-card'; +import BoardCreateSubtaskCard from '@/pages/projects/projectView/board/board-section/board-sub-task-card/board-create-sub-task-card'; +import { useTranslation } from 'react-i18next'; interface EnhancedKanbanTaskCardProps { task: IProjectTask; + sectionId: string; isActive?: boolean; isDragOverlay?: boolean; isDropTarget?: boolean; } +// Priority and status colors - moved outside component to avoid recreation +const PRIORITY_COLORS = { + critical: '#ff4d4f', + high: '#ff7a45', + medium: '#faad14', + low: '#52c41a', +} as const; const EnhancedKanbanTaskCard: React.FC = React.memo(({ task, + sectionId, isActive = false, isDragOverlay = false, isDropTarget = false }) => { const dispatch = useAppDispatch(); + const { t } = useTranslation('kanban-board'); const themeMode = useAppSelector(state => state.themeReducer.mode); + const [showNewSubtaskCard, setShowNewSubtaskCard] = useState(false); const [dueDate, setDueDate] = useState( task?.end_date ? dayjs(task?.end_date) : null ); const [isSubTaskShow, setIsSubTaskShow] = useState(false); + const projectId = useAppSelector(state => state.projectReducer.projectId); const { attributes, listeners, @@ -70,14 +90,8 @@ const EnhancedKanbanTaskCard: React.FC = React.memo // Don't handle click if we're dragging if (isDragging) return; - - // Add a small delay to ensure it's a click and not the start of a drag - const clickTimeout = setTimeout(() => { - dispatch(setSelectedTaskId(id)); - dispatch(setShowTaskDrawer(true)); - }, 50); - - return () => clearTimeout(clickTimeout); + dispatch(setSelectedTaskId(id)); + dispatch(setShowTaskDrawer(true)); }, [dispatch, isDragging]); const renderLabels = useMemo(() => { @@ -97,6 +111,32 @@ const EnhancedKanbanTaskCard: React.FC = React.memo ); }, [task.labels, themeMode]); + + + const handleSubTaskExpand = useCallback(() => { + console.log('handleSubTaskExpand', task, projectId); + if (task && task.id && projectId) { + if (task.show_sub_tasks) { + // If subtasks are already loaded, just toggle visibility + setIsSubTaskShow(prev => !prev); + } else { + // If subtasks need to be fetched, show the section first with loading state + setIsSubTaskShow(true); + dispatch(fetchBoardSubTasks({ taskId: task.id, projectId })); + } + } + }, [task, projectId, dispatch]); + + const handleSubtaskButtonClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + handleSubTaskExpand(); + }, [handleSubTaskExpand]); + + const handleAddSubtaskClick = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + setShowNewSubtaskCard(true); + }, []); + return (
= React.memo {/* Action Icons */} - +
= React.memo - {task && } + align="center" + justify="space-between" + style={{ + marginBlock: 8, + }} + > + {task && } - - + + - {/* Subtask Section */} - + + + + {isSubTaskShow && ( + + + + {task.sub_tasks_loading && ( + + + + )} + + {!task.sub_tasks_loading && task?.sub_tasks && + task?.sub_tasks.map((subtask: any) => ( + + ))} + + {showNewSubtaskCard && ( + + )} + + - + )} +
); diff --git a/worklenz-frontend/src/features/enhanced-kanban/enhanced-kanban.slice.ts b/worklenz-frontend/src/features/enhanced-kanban/enhanced-kanban.slice.ts index fe9b1479..eca3f60e 100644 --- a/worklenz-frontend/src/features/enhanced-kanban/enhanced-kanban.slice.ts +++ b/worklenz-frontend/src/features/enhanced-kanban/enhanced-kanban.slice.ts @@ -263,6 +263,60 @@ export const reorderEnhancedKanbanGroups = createAsyncThunk( } ); +export const fetchBoardSubTasks = createAsyncThunk( + 'enhancedKanban/fetchBoardSubTasks', + async ( + { taskId, projectId }: { taskId: string; projectId: string }, + { rejectWithValue, getState } + ) => { + try { + const state = getState() as { enhancedKanbanReducer: EnhancedKanbanState }; + const { enhancedKanbanReducer } = state; + + // Check if the task is already expanded (optional, can be enhanced later) + // const task = enhancedKanbanReducer.taskGroups.flatMap(group => group.tasks).find(t => t.id === taskId); + // if (task?.show_sub_tasks) { + // return []; + // } + + const selectedMembers = enhancedKanbanReducer.taskAssignees + .filter(member => member.selected) + .map(member => member.id) + .join(' '); + + const selectedLabels = enhancedKanbanReducer.labels + .filter(label => label.selected) + .map(label => label.id) + .join(' '); + + const config: ITaskListConfigV2 = { + id: projectId, + archived: enhancedKanbanReducer.archived, + group: enhancedKanbanReducer.groupBy, + field: enhancedKanbanReducer.fields.map(field => `${field.key} ${field.sort_order}`).join(','), + order: '', + search: enhancedKanbanReducer.search || '', + statuses: '', + members: selectedMembers, + projects: '', + isSubtasksInclude: false, + labels: selectedLabels, + priorities: enhancedKanbanReducer.priorities.join(' '), + parent_task: taskId, + }; + + const response = await tasksApiService.getTaskList(config); + return response.body; + } catch (error) { + logger.error('Fetch Enhanced Board Sub Tasks', error); + if (error instanceof Error) { + return rejectWithValue(error.message); + } + return rejectWithValue('Failed to fetch sub tasks'); + } + } +); + const enhancedKanbanSlice = createSlice({ name: 'enhancedKanbanReducer', initialState, diff --git a/worklenz-frontend/src/pages/projects/projectView/project-view-header.tsx b/worklenz-frontend/src/pages/projects/projectView/project-view-header.tsx index b2a17504..4545824b 100644 --- a/worklenz-frontend/src/pages/projects/projectView/project-view-header.tsx +++ b/worklenz-frontend/src/pages/projects/projectView/project-view-header.tsx @@ -48,6 +48,7 @@ import useIsProjectManager from '@/hooks/useIsProjectManager'; import useTabSearchParam from '@/hooks/useTabSearchParam'; import { addTaskCardToTheTop, fetchBoardTaskGroups } from '@/features/board/board-slice'; import { fetchPhasesByProjectId } from '@/features/projects/singleProject/phase/phases.slice'; +import { fetchEnhancedKanbanGroups } from '@/features/enhanced-kanban/enhanced-kanban.slice'; const ProjectViewHeader = () => { const navigate = useNavigate(); @@ -79,7 +80,8 @@ const ProjectViewHeader = () => { dispatch(fetchTaskGroups(projectId)); break; case 'board': - dispatch(fetchBoardTaskGroups(projectId)); + // dispatch(fetchBoardTaskGroups(projectId)); + dispatch(fetchEnhancedKanbanGroups(projectId)); break; case 'project-insights-member-overview': dispatch(setRefreshTimestamp());