diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD.tsx
new file mode 100644
index 00000000..568c0cab
--- /dev/null
+++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD.tsx
@@ -0,0 +1,208 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { RootState } from '@/app/store';
+import { ITaskListGroup } from '@/types/tasks/taskList.types';
+import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
+import './EnhancedKanbanBoard.css';
+import './EnhancedKanbanGroup.css';
+import './EnhancedKanbanTaskCard.css';
+
+// Minimal task card for prototype (reuse your styles)
+const TaskCard: React.FC<{
+ task: IProjectTask;
+ 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;
+ groupId: string;
+ isDropIndicator: boolean;
+}> = ({ task, onTaskDragStart, onTaskDragOver, onTaskDrop, groupId, isDropIndicator }) => {
+ const themeMode = useSelector((state: RootState) => state.themeReducer.mode);
+ const background = themeMode === 'dark' ? '#23272f' : '#fff';
+ const color = themeMode === 'dark' ? '#fff' : '#23272f';
+ return (
+ <>
+ {isDropIndicator && (
+
+ )}
+ onTaskDragStart(e, task.id!, groupId)}
+ onDragOver={e => onTaskDragOver(e, groupId, -1)}
+ onDrop={e => onTaskDrop(e, groupId, -1)}
+ style={{ background, color }}
+ >
+
+
{task.name}
+
{task.task_key}
+
+ {task.assignees?.map(a => a.name).join(', ')}
+
+
+
+ >
+ );
+};
+
+// Minimal group column for prototype
+const KanbanGroup: React.FC<{
+ 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, onGroupDragStart, onGroupDragOver, onGroupDrop, onTaskDragStart, onTaskDragOver, onTaskDrop, hoveredTaskIdx, hoveredGroupId }) => (
+
+
onGroupDragStart(e, group.id)}
+ onDragOver={onGroupDragOver}
+ onDrop={e => onGroupDrop(e, group.id)}
+ >
+
{group.name}
+ {group.tasks.length}
+
+
onTaskDragOver(e, group.id, 0)}
+ onDrop={e => onTaskDrop(e, group.id, 0)}
+ >
+ {/* 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 && (
+
+ )}
+
+
+);
+
+const EnhancedKanbanBoardNativeDnD: React.FC<{ projectId: string }> = ({ projectId }) => {
+ // Get initial groups from Redux
+ const reduxGroups = useSelector((state: RootState) => state.enhancedKanbanReducer.taskGroups);
+ // Local state for groups/tasks
+ const [groups, setGroups] = useState([]);
+ // Drag state
+ const [draggedGroupId, setDraggedGroupId] = useState(null);
+ const [draggedTaskId, setDraggedTaskId] = useState(null);
+ const [draggedTaskGroupId, setDraggedTaskGroupId] = useState(null);
+ const [hoveredGroupId, setHoveredGroupId] = useState(null);
+ const [hoveredTaskIdx, setHoveredTaskIdx] = useState(null);
+ const [dragType, setDragType] = useState<'group' | 'task' | null>(null);
+
+ // Sync local state with Redux on mount or when reduxGroups or projectId change
+ useEffect(() => {
+ setGroups(reduxGroups.map(g => ({ ...g, tasks: [...g.tasks] })));
+ }, [reduxGroups, projectId]);
+
+ // Group drag handlers
+ const handleGroupDragStart = (e: React.DragEvent, groupId: string) => {
+ setDraggedGroupId(groupId);
+ setDragType('group');
+ e.dataTransfer.effectAllowed = 'move';
+ };
+ const handleGroupDragOver = (e: React.DragEvent) => {
+ if (dragType !== 'group') return;
+ e.preventDefault();
+ };
+ const handleGroupDrop = (e: React.DragEvent, targetGroupId: string) => {
+ if (dragType !== 'group') return;
+ e.preventDefault();
+ if (!draggedGroupId || draggedGroupId === targetGroupId) return;
+ const updated = [...groups];
+ const fromIdx = updated.findIndex(g => g.id === draggedGroupId);
+ const [moved] = updated.splice(fromIdx, 1);
+ const toIdx = updated.findIndex(g => g.id === targetGroupId);
+ updated.splice(toIdx, 0, moved);
+ setGroups(updated);
+ setDraggedGroupId(null);
+ setDragType(null);
+ };
+
+ // Task drag handlers
+ const handleTaskDragStart = (e: React.DragEvent, taskId: string, groupId: string) => {
+ setDraggedTaskId(taskId);
+ setDraggedTaskGroupId(groupId);
+ setDragType('task');
+ e.dataTransfer.effectAllowed = 'move';
+ };
+ const handleTaskDragOver = (e: React.DragEvent, groupId: string, taskIdx: number) => {
+ if (dragType !== 'task') return;
+ e.preventDefault();
+ if (draggedTaskId) {
+ setHoveredGroupId(groupId);
+ setHoveredTaskIdx(taskIdx);
+ }
+ };
+ const handleTaskDrop = (e: React.DragEvent, targetGroupId: string, targetTaskIdx: number) => {
+ if (dragType !== 'task') return;
+ e.preventDefault();
+ if (!draggedTaskId || !draggedTaskGroupId || hoveredGroupId === null || hoveredTaskIdx === null) return;
+ const updated = [...groups];
+ const sourceGroup = updated.find(g => g.id === draggedTaskGroupId);
+ const targetGroup = updated.find(g => g.id === targetGroupId);
+ if (!sourceGroup || !targetGroup) return;
+ // Remove from source
+ const taskIdx = sourceGroup.tasks.findIndex(t => t.id === draggedTaskId);
+ if (taskIdx === -1) return;
+ const [movedTask] = sourceGroup.tasks.splice(taskIdx, 1);
+ // Insert into target at the correct index
+ let insertIdx = targetTaskIdx;
+ if (sourceGroup.id === targetGroup.id && taskIdx < insertIdx) {
+ insertIdx--;
+ }
+ if (insertIdx < 0) insertIdx = 0;
+ if (insertIdx > targetGroup.tasks.length) insertIdx = targetGroup.tasks.length;
+ targetGroup.tasks.splice(insertIdx, 0, movedTask);
+ setGroups(updated);
+ setDraggedTaskId(null);
+ setDraggedTaskGroupId(null);
+ setHoveredGroupId(null);
+ setHoveredTaskIdx(null);
+ setDragType(null);
+ };
+
+ return (
+
+
+ {groups.map(group => (
+
+ ))}
+
+
+ );
+};
+
+export default EnhancedKanbanBoardNativeDnD;
\ No newline at end of file
diff --git a/worklenz-frontend/src/pages/projects/projectView/enhancedBoard/project-view-enhanced-board.tsx b/worklenz-frontend/src/pages/projects/projectView/enhancedBoard/project-view-enhanced-board.tsx
index 704802bd..27db13e4 100644
--- a/worklenz-frontend/src/pages/projects/projectView/enhancedBoard/project-view-enhanced-board.tsx
+++ b/worklenz-frontend/src/pages/projects/projectView/enhancedBoard/project-view-enhanced-board.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { useAppSelector } from '@/hooks/useAppSelector';
import EnhancedKanbanBoard from '@/components/enhanced-kanban/EnhancedKanbanBoard';
+import EnhancedKanbanBoardNativeDnD from '@/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD';
const ProjectViewEnhancedBoard: React.FC = () => {
const { project } = useAppSelector(state => state.projectReducer);
@@ -15,7 +16,8 @@ const ProjectViewEnhancedBoard: React.FC = () => {
return (
-
+ {/* */}
+
);
};