import React, { useCallback, useMemo, useState } from 'react'; import { useSortable, defaultAnimateLayoutChanges } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { IProjectTask } from '@/types/project/projectTasksViewModel.types'; import { useAppSelector } from '@/hooks/useAppSelector'; import './EnhancedKanbanTaskCard.css'; import Flex from 'antd/es/flex'; import Tag from 'antd/es/tag'; import Tooltip from 'antd/es/tooltip'; import Progress from 'antd/es/progress'; import Button from 'antd/es/button'; import { useAppDispatch } from '@/hooks/useAppDispatch'; import { setShowTaskDrawer, setSelectedTaskId } from '@/features/task-drawer/task-drawer.slice'; import PrioritySection from '../board/taskCard/priority-section/priority-section'; import Typography from 'antd/es/typography'; import CustomDueDatePicker from '../board/custom-due-date-picker'; import { themeWiseColor } from '@/utils/themeWiseColor'; import { ForkOutlined } from '@ant-design/icons'; import { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; import { CaretDownFilled, CaretRightFilled } from '@ant-design/icons'; import { fetchBoardSubTasks, toggleTaskExpansion, } 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'; import EnhancedKanbanCreateSubtaskCard from './EnhancedKanbanCreateSubtaskCard'; import LazyAssigneeSelectorWrapper from '@/components/task-management/lazy-assignee-selector'; import AvatarGroup from '@/components/AvatarGroup'; 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 projectId = useAppSelector(state => state.projectReducer.projectId); const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: task.id!, data: { type: 'task', task, }, disabled: isDragOverlay, animateLayoutChanges: defaultAnimateLayoutChanges, }); const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.5 : 1, backgroundColor: themeMode === 'dark' ? '#292929' : '#fafafa', }; const handleCardClick = useCallback( (e: React.MouseEvent, id: string) => { // Prevent the event from propagating to parent elements e.stopPropagation(); // Don't handle click if we're dragging if (isDragging) return; dispatch(setSelectedTaskId(id)); dispatch(setShowTaskDrawer(true)); }, [dispatch, isDragging] ); const renderLabels = useMemo(() => { if (!task?.labels?.length) return null; return ( <> {task.labels.slice(0, 2).map((label: any) => ( {label.name} ))} {task.labels.length > 2 && + {task.labels.length - 2}} ); }, [task.labels, themeMode]); const handleSubTaskExpand = useCallback(() => { if (task && task.id && projectId) { // Check if subtasks are already loaded and we have subtask data if (task.sub_tasks && task.sub_tasks.length > 0 && task.sub_tasks_count > 0) { // If subtasks are already loaded, just toggle visibility dispatch(toggleTaskExpansion(task.id)); } else if (task.sub_tasks_count > 0) { // If we have a subtask count but no loaded subtasks, fetch them dispatch(toggleTaskExpansion(task.id)); dispatch(fetchBoardSubTasks({ taskId: task.id, projectId })); } else { // If no subtasks exist, just toggle visibility (will show empty state) dispatch(toggleTaskExpansion(task.id)); } } }, [task, projectId, dispatch]); const handleSubtaskButtonClick = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); handleSubTaskExpand(); }, [handleSubTaskExpand] ); const handleAddSubtaskClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); setShowNewSubtaskCard(true); }, []); return (
handleCardClick(e, task.id || '')}> {renderLabels} = 100 ? 9 : 7} /> {/* Action Icons */}
{task.name} {/* Subtask Section - only show if count > 1 */} {task.sub_tasks_count != null && Number(task.sub_tasks_count) > 1 && ( )} {task.show_sub_tasks && ( {task.sub_tasks_loading && ( )} {!task.sub_tasks_loading && task?.sub_tasks && task.sub_tasks.length > 0 && task.sub_tasks.map((subtask: any) => ( ))} {!task.sub_tasks_loading && (!task?.sub_tasks || task.sub_tasks.length === 0) && task.sub_tasks_count === 0 && (
{t('noSubtasks', 'No subtasks')}
)} {showNewSubtaskCard && ( )}
)}
); } ); export default EnhancedKanbanTaskCard;