feat(enhanced-kanban): implement group reordering and improve drag-and-drop functionality

- Added support for reordering kanban groups via drag-and-drop, enhancing user experience.
- Updated EnhancedKanbanBoard and EnhancedKanbanGroup components to handle group dragging and state management.
- Introduced visual feedback for dragging groups and tasks, improving usability.
- Refined CSS styles for better layout and responsiveness during drag operations.
This commit is contained in:
shancds
2025-06-23 11:37:40 +05:30
parent 67c26a973e
commit b3d39b65b0
5 changed files with 284 additions and 52 deletions

View File

@@ -49,12 +49,12 @@ interface EnhancedKanbanState {
groupBy: IGroupBy;
isSubtasksInclude: boolean;
fields: ITaskListSortableColumn[];
// Task data
taskGroups: ITaskListGroup[];
loadingGroups: boolean;
error: string | null;
// Filters
taskAssignees: ITaskListMemberFilter[];
loadingAssignees: boolean;
@@ -63,12 +63,12 @@ interface EnhancedKanbanState {
loadingLabels: boolean;
priorities: string[];
members: string[];
// Performance optimizations
virtualizedRendering: boolean;
taskCache: Record<string, IProjectTask>;
groupCache: Record<string, ITaskListGroup>;
// Performance monitoring
performanceMetrics: {
totalTasks: number;
@@ -78,7 +78,7 @@ interface EnhancedKanbanState {
lastUpdateTime: number;
virtualizationEnabled: boolean;
};
// Drag and drop state
dragState: {
activeTaskId: string | null;
@@ -86,7 +86,7 @@ interface EnhancedKanbanState {
overId: string | null;
isDragging: boolean;
};
// UI state
selectedTaskIds: string[];
expandedSubtasks: Record<string, boolean>;
@@ -137,7 +137,7 @@ const calculatePerformanceMetrics = (taskGroups: ITaskListGroup[]) => {
const groupSizes = taskGroups.map(group => group.tasks.length);
const largestGroupSize = Math.max(...groupSizes, 0);
const averageGroupSize = groupSizes.length > 0 ? totalTasks / groupSizes.length : 0;
return {
totalTasks,
largestGroupSize,
@@ -234,6 +234,35 @@ export const reorderEnhancedKanbanTasks = createAsyncThunk(
}
);
// Group reordering
export const reorderEnhancedKanbanGroups = createAsyncThunk(
'enhancedKanban/reorderGroups',
async (
{
fromIndex,
toIndex,
reorderedGroups,
}: {
fromIndex: number;
toIndex: number;
reorderedGroups: ITaskListGroup[];
},
{ rejectWithValue }
) => {
try {
// Optimistic update - return immediately for UI responsiveness
return {
fromIndex,
toIndex,
reorderedGroups,
};
} catch (error) {
logger.error('Reorder Enhanced Kanban Groups', error);
return rejectWithValue('Failed to reorder groups');
}
}
);
const enhancedKanbanSlice = createSlice({
name: 'enhancedKanbanReducer',
initialState,
@@ -327,7 +356,7 @@ const enhancedKanbanSlice = createSlice({
// Status updates
updateTaskStatus: (state, action: PayloadAction<ITaskListStatusChangeResponse>) => {
const { id: task_id, status_id } = action.payload;
// Update in all groups
state.taskGroups.forEach(group => {
group.tasks.forEach(task => {
@@ -342,7 +371,7 @@ const enhancedKanbanSlice = createSlice({
updateTaskPriority: (state, action: PayloadAction<ITaskListPriorityChangeResponse>) => {
const { id: task_id, priority_id } = action.payload;
// Update in all groups
state.taskGroups.forEach(group => {
group.tasks.forEach(task => {
@@ -358,12 +387,12 @@ const enhancedKanbanSlice = createSlice({
// Task deletion
deleteTask: (state, action: PayloadAction<string>) => {
const taskId = action.payload;
// Remove from all groups
state.taskGroups.forEach(group => {
group.tasks = group.tasks.filter(task => task.id !== taskId);
});
// Remove from caches
delete state.taskCache[taskId];
state.selectedTaskIds = state.selectedTaskIds.filter(id => id !== taskId);
@@ -383,10 +412,10 @@ const enhancedKanbanSlice = createSlice({
.addCase(fetchEnhancedKanbanGroups.fulfilled, (state, action) => {
state.loadingGroups = false;
state.taskGroups = action.payload;
// Update performance metrics
state.performanceMetrics = calculatePerformanceMetrics(action.payload);
// Update caches
action.payload.forEach(group => {
state.groupCache[group.id] = group;
@@ -394,7 +423,7 @@ const enhancedKanbanSlice = createSlice({
state.taskCache[task.id!] = task;
});
});
// Initialize column order if not set
if (state.columnOrder.length === 0) {
state.columnOrder = action.payload.map(group => group.id);
@@ -406,20 +435,33 @@ const enhancedKanbanSlice = createSlice({
})
.addCase(reorderEnhancedKanbanTasks.fulfilled, (state, action) => {
const { activeGroupId, overGroupId, updatedSourceTasks, updatedTargetTasks } = action.payload;
// Update groups
const sourceGroupIndex = state.taskGroups.findIndex(group => group.id === activeGroupId);
const targetGroupIndex = state.taskGroups.findIndex(group => group.id === overGroupId);
if (sourceGroupIndex !== -1) {
state.taskGroups[sourceGroupIndex].tasks = updatedSourceTasks;
state.groupCache[activeGroupId] = state.taskGroups[sourceGroupIndex];
}
if (targetGroupIndex !== -1 && activeGroupId !== overGroupId) {
state.taskGroups[targetGroupIndex].tasks = updatedTargetTasks;
state.groupCache[overGroupId] = state.taskGroups[targetGroupIndex];
}
})
.addCase(reorderEnhancedKanbanGroups.fulfilled, (state, action) => {
const { fromIndex, toIndex, reorderedGroups } = action.payload;
// Update groups
state.taskGroups = reorderedGroups;
state.groupCache = reorderedGroups.reduce((cache, group) => {
cache[group.id] = group;
return cache;
}, {} as Record<string, ITaskListGroup>);
// Update column order
state.columnOrder = reorderedGroups.map(group => group.id);
});
},
});