From 8134c6af357c97e3b8ebd0fc9666698c729a3575 Mon Sep 17 00:00:00 2001 From: shancds Date: Thu, 3 Jul 2025 12:31:51 +0530 Subject: [PATCH] feat(enhanced-kanban): add task creation functionality to Kanban groups - Introduced EnhancedKanbanCreateTaskCard component for adding tasks directly within Kanban groups. - Implemented conditional rendering of task creation buttons based on user roles (owner/admin or project manager). - Enhanced the KanbanGroup component to support task creation at both the top and bottom of the task list, improving user experience. --- .../EnhancedKanbanBoardNativeDnD.tsx | 2 + .../KanbanGroup.tsx | 231 ++++++++++++------ 2 files changed, 159 insertions(+), 74 deletions(-) diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/EnhancedKanbanBoardNativeDnD.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/EnhancedKanbanBoardNativeDnD.tsx index 01600dfb..ab7254b1 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/EnhancedKanbanBoardNativeDnD.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/EnhancedKanbanBoardNativeDnD.tsx @@ -12,6 +12,7 @@ import { reorderGroups, reorderEnhancedKanbanGroups, reorderTasks, reorderEnhanc import { fetchStatusesCategories } from '@/features/taskAttributes/taskStatusSlice'; import { useAppSelector } from '@/hooks/useAppSelector'; import KanbanGroup from './KanbanGroup'; +import EnhancedKanbanCreateSection from '../EnhancedKanbanCreateSection'; @@ -222,6 +223,7 @@ const EnhancedKanbanBoardNativeDnD: React.FC<{ projectId: string }> = ({ project hoveredGroupId={hoveredGroupId} /> ))} + )} diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/KanbanGroup.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/KanbanGroup.tsx index f69ad544..5d73437e 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/KanbanGroup.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/KanbanGroup.tsx @@ -1,86 +1,169 @@ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, useState } from 'react'; import { useAppSelector } from '@/hooks/useAppSelector'; import { ITaskListGroup } from '@/types/tasks/taskList.types'; import TaskCard from './TaskCard'; +import { themeWiseColor } from '@/utils/themeWiseColor'; +import EnhancedKanbanCreateTaskCard from '../EnhancedKanbanCreateTaskCard'; +import { PlusOutlined } from '@ant-design/icons'; +import Button from 'antd/es/button'; +import { useTranslation } from 'react-i18next'; +import { useAuthService } from '@/hooks/useAuth'; +import useIsProjectManager from '@/hooks/useIsProjectManager'; interface KanbanGroupProps { - group: ITaskListGroup; - onGroupDragStart: (e: React.DragEvent, groupId: string) => void; - onGroupDragOver: (e: React.DragEvent) => void; - onGroupDrop: (e: React.DragEvent, groupId: string) => void; - onTaskDragStart: (e: React.DragEvent, taskId: string, groupId: string) => void; - onTaskDragOver: (e: React.DragEvent, groupId: string, taskIdx: number) => void; - onTaskDrop: (e: React.DragEvent, groupId: string, taskIdx: number) => void; - hoveredTaskIdx: number | null; - hoveredGroupId: string | null; + group: ITaskListGroup; + onGroupDragStart: (e: React.DragEvent, groupId: string) => void; + onGroupDragOver: (e: React.DragEvent) => void; + onGroupDrop: (e: React.DragEvent, groupId: string) => void; + onTaskDragStart: (e: React.DragEvent, taskId: string, groupId: string) => void; + onTaskDragOver: (e: React.DragEvent, groupId: string, taskIdx: number) => void; + onTaskDrop: (e: React.DragEvent, groupId: string, taskIdx: number) => void; + hoveredTaskIdx: number | null; + hoveredGroupId: string | null; } -const KanbanGroup: React.FC = memo(({ - group, - onGroupDragStart, - onGroupDragOver, - onGroupDrop, - onTaskDragStart, - onTaskDragOver, - onTaskDrop, - hoveredTaskIdx, - hoveredGroupId +const KanbanGroup: React.FC = memo(({ + group, + onGroupDragStart, + onGroupDragOver, + onGroupDrop, + onTaskDragStart, + onTaskDragOver, + onTaskDrop, + hoveredTaskIdx, + hoveredGroupId }) => { - const themeMode = useAppSelector(state => state.themeReducer.mode); - - const headerBackgroundColor = useMemo(() => { - if (themeMode === 'dark') { - return group.color_code_dark || group.color_code || '#1e1e1e'; - } - return group.color_code || '#f5f5f5'; - }, [themeMode, group.color_code, group.color_code_dark]); + const themeMode = useAppSelector(state => state.themeReducer.mode); + const { t } = useTranslation('kanban-board'); + const isOwnerOrAdmin = useAuthService().isOwnerOrAdmin(); + const isProjectManager = useIsProjectManager(); + const [showNewCardTop, setShowNewCardTop] = useState(false); + const [showNewCardBottom, setShowNewCardBottom] = useState(false); + const headerBackgroundColor = useMemo(() => { + if (themeMode === 'dark') { + return group.color_code_dark || group.color_code || '#1e1e1e'; + } + return group.color_code || '#f5f5f5'; + }, [themeMode, group.color_code, group.color_code_dark]); - return ( -
-
onGroupDragStart(e, group.id)} - onDragOver={onGroupDragOver} - onDrop={e => onGroupDrop(e, group.id)} - > -

{group.name}

- {group.tasks.length} -
-
- {/* Drop indicator at the top of the group */} - {hoveredGroupId === group.id && hoveredTaskIdx === 0 && ( -
-
-
- )} - - {group.tasks.map((task, idx) => ( - - - - ))} - - {/* Drop indicator at the end of the group */} - {hoveredGroupId === group.id && hoveredTaskIdx === group.tasks.length && ( -
-
-
- )} -
-
- ); + return ( +
+
onGroupDragStart(e, group.id)} + onDragOver={onGroupDragOver} + onDrop={e => onGroupDrop(e, group.id)} + > +

{group.name}

+ {group.tasks.length} +
+
+ {/* If group is empty, render a drop zone */} + {group.tasks.length === 0 && ( +
{ e.preventDefault(); onTaskDragOver(e, group.id, 0); }} + onDrop={e => { e.preventDefault(); onTaskDrop(e, group.id, 0); }} + > + {/* Drop indicator at the end of the group */} + {hoveredGroupId === group.id && hoveredTaskIdx === group.tasks.length && ( +
+
+
+ )} + {(isOwnerOrAdmin || isProjectManager) && !showNewCardTop && !showNewCardBottom && ( + + )} + {showNewCardTop && } +
+ )} + + {/* Drop indicator at the top of the group */} + {hoveredGroupId === group.id && hoveredTaskIdx === 0 && ( +
+
+
+ )} + + {group.tasks.map((task, idx) => ( + + + + ))} + {(isOwnerOrAdmin || isProjectManager) && !showNewCardTop && !showNewCardBottom && group.tasks.length > 0 && ( + + )} + {showNewCardBottom && } + + {/* Drop indicator at the end of the group */} + {hoveredGroupId === group.id && hoveredTaskIdx === group.tasks.length && ( +
+
+
+ )} +
+
+ ); }); KanbanGroup.displayName = 'KanbanGroup';