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 4dcca867..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(({ type="text" size="small" shape="circle" - style={{ color: themeMode === 'dark' ? '#383838' : '' }} + // style={{ color: themeMode === 'dark' ? '#383838' : '' }} onClick={() => { setShowNewCardTop(true); setShowNewCardBottom(false); @@ -433,8 +434,8 @@ const EnhancedKanbanGroup: React.FC = React.memo(({ @@ -488,6 +489,7 @@ const EnhancedKanbanGroup: React.FC = 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/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 ( -
+
{ + 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 bb518e86..bb6c35b6 100644 --- a/worklenz-frontend/src/pages/projects/projectView/project-view-header.tsx +++ b/worklenz-frontend/src/pages/projects/projectView/project-view-header.tsx @@ -53,6 +53,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(); @@ -84,7 +85,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());