Merge branch 'fix/enhanced-task-board' into fix/get-pull-6-26
This commit is contained in:
@@ -45,6 +45,7 @@ import alertService from '@/services/alerts/alertService';
|
|||||||
import { IGroupBy } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
import { IGroupBy } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
import EnhancedKanbanCreateSection from './EnhancedKanbanCreateSection';
|
import EnhancedKanbanCreateSection from './EnhancedKanbanCreateSection';
|
||||||
import ImprovedTaskFilters from '../task-management/improved-task-filters';
|
import ImprovedTaskFilters from '../task-management/improved-task-filters';
|
||||||
|
import { fetchStatusesCategories } from '@/features/taskAttributes/taskStatusSlice';
|
||||||
|
|
||||||
// Import the TaskListFilters component
|
// Import the TaskListFilters component
|
||||||
const TaskListFilters = React.lazy(() => import('@/pages/projects/projectView/taskList/task-list-filters/task-list-filters'));
|
const TaskListFilters = React.lazy(() => import('@/pages/projects/projectView/taskList/task-list-filters/task-list-filters'));
|
||||||
@@ -66,7 +67,7 @@ const EnhancedKanbanBoard: React.FC<EnhancedKanbanBoardProps> = ({ projectId, cl
|
|||||||
const { teamId } = useAppSelector((state: RootState) => state.auth);
|
const { teamId } = useAppSelector((state: RootState) => state.auth);
|
||||||
const groupBy = useSelector((state: RootState) => state.enhancedKanbanReducer.groupBy);
|
const groupBy = useSelector((state: RootState) => state.enhancedKanbanReducer.groupBy);
|
||||||
const project = useAppSelector((state: RootState) => state.projectReducer.project);
|
const project = useAppSelector((state: RootState) => state.projectReducer.project);
|
||||||
|
const { statusCategories, status: existingStatuses } = useAppSelector((state) => state.taskStatusReducer);
|
||||||
// Local state for drag overlay
|
// Local state for drag overlay
|
||||||
const [activeTask, setActiveTask] = useState<any>(null);
|
const [activeTask, setActiveTask] = useState<any>(null);
|
||||||
const [activeGroup, setActiveGroup] = useState<any>(null);
|
const [activeGroup, setActiveGroup] = useState<any>(null);
|
||||||
@@ -86,6 +87,9 @@ const EnhancedKanbanBoard: React.FC<EnhancedKanbanBoardProps> = ({ projectId, cl
|
|||||||
if (projectId) {
|
if (projectId) {
|
||||||
dispatch(fetchEnhancedKanbanGroups(projectId) as any);
|
dispatch(fetchEnhancedKanbanGroups(projectId) as any);
|
||||||
}
|
}
|
||||||
|
if (!statusCategories.length) {
|
||||||
|
dispatch(fetchStatusesCategories() as any);
|
||||||
|
}
|
||||||
}, [dispatch, projectId]);
|
}, [dispatch, projectId]);
|
||||||
|
|
||||||
// Get all task IDs for sortable context
|
// Get all task IDs for sortable context
|
||||||
@@ -385,10 +389,10 @@ const EnhancedKanbanBoard: React.FC<EnhancedKanbanBoardProps> = ({ projectId, cl
|
|||||||
<>
|
<>
|
||||||
{/* Task Filters */}
|
{/* Task Filters */}
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<React.Suspense fallback={<div>Loading filters...</div>}>
|
<React.Suspense fallback={<div>Loading filters...</div>}>
|
||||||
<ImprovedTaskFilters position="board" />
|
<ImprovedTaskFilters position="board" />
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
</div>
|
</div>
|
||||||
<div className={`enhanced-kanban-board ${className}`}>
|
<div className={`enhanced-kanban-board ${className}`}>
|
||||||
{/* Performance Monitor - only show for large datasets */}
|
{/* Performance Monitor - only show for large datasets */}
|
||||||
{/* {performanceMetrics.totalTasks > 100 && <PerformanceMonitor />} */}
|
{/* {performanceMetrics.totalTasks > 100 && <PerformanceMonitor />} */}
|
||||||
|
|||||||
@@ -38,9 +38,11 @@ const EnhancedKanbanCreateTaskCard: React.FC<EnhancedKanbanCreateTaskCardProps>
|
|||||||
const groupBy = useAppSelector(state => state.enhancedKanbanReducer.groupBy);
|
const groupBy = useAppSelector(state => state.enhancedKanbanReducer.groupBy);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const createRequestBody = (): ITaskCreateRequest | null => {
|
const createRequestBody = (): ITaskCreateRequest | null => {
|
||||||
@@ -66,18 +68,30 @@ const EnhancedKanbanCreateTaskCard: React.FC<EnhancedKanbanCreateTaskCardProps>
|
|||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetForNextTask = () => {
|
||||||
|
setNewTaskName('');
|
||||||
|
setCreatingTask(false);
|
||||||
|
// Keep the card visible for creating the next task
|
||||||
|
setTimeout(() => {
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
const handleAddTask = async () => {
|
const handleAddTask = async () => {
|
||||||
if (creatingTask || !projectId || !currentSession || newTaskName.trim() === '') return;
|
if (creatingTask || !projectId || !currentSession || newTaskName.trim() === '') return;
|
||||||
setCreatingTask(true);
|
|
||||||
const body = createRequestBody();
|
const body = createRequestBody();
|
||||||
if (!body) return;
|
if (!body) {
|
||||||
|
setCreatingTask(true);
|
||||||
|
setShowNewCard(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Real-time socket event handler
|
// Real-time socket event handler
|
||||||
const eventHandler = (task: IProjectTask) => {
|
const eventHandler = (task: IProjectTask) => {
|
||||||
setCreatingTask(false);
|
|
||||||
dispatch(addTaskToGroup({ sectionId, task: { ...task, id: task.id || nanoid(), name: task.name || newTaskName.trim() } }));
|
dispatch(addTaskToGroup({ sectionId, task: { ...task, id: task.id || nanoid(), name: task.name || newTaskName.trim() } }));
|
||||||
socket?.off(SocketEvents.QUICK_TASK.toString(), eventHandler);
|
socket?.off(SocketEvents.QUICK_TASK.toString(), eventHandler);
|
||||||
resetForm();
|
resetForNextTask();
|
||||||
};
|
};
|
||||||
socket?.once(SocketEvents.QUICK_TASK.toString(), eventHandler);
|
socket?.once(SocketEvents.QUICK_TASK.toString(), eventHandler);
|
||||||
socket?.emit(SocketEvents.QUICK_TASK.toString(), JSON.stringify(body));
|
socket?.emit(SocketEvents.QUICK_TASK.toString(), JSON.stringify(body));
|
||||||
@@ -89,6 +103,13 @@ const EnhancedKanbanCreateTaskCard: React.FC<EnhancedKanbanCreateTaskCardProps>
|
|||||||
setCreatingTask(false);
|
setCreatingTask(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
if (newTaskName.trim() === '') {
|
||||||
|
setCreatingTask(false);
|
||||||
|
setShowNewCard(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
ref={cardRef}
|
ref={cardRef}
|
||||||
@@ -111,9 +132,11 @@ const EnhancedKanbanCreateTaskCard: React.FC<EnhancedKanbanCreateTaskCardProps>
|
|||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
autoFocus
|
||||||
value={newTaskName}
|
value={newTaskName}
|
||||||
onChange={e => setNewTaskName(e.target.value)}
|
onChange={e => setNewTaskName(e.target.value)}
|
||||||
onPressEnter={handleAddTask}
|
onPressEnter={handleAddTask}
|
||||||
|
onBlur={handleBlur}
|
||||||
placeholder={t('newTaskNamePlaceholder')}
|
placeholder={t('newTaskNamePlaceholder')}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|||||||
@@ -7,18 +7,19 @@ import { fetchStatuses, fetchStatusesCategories } from '@/features/taskAttribute
|
|||||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||||
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||||
import { fetchTaskGroups } from '@/features/tasks/tasks.slice';
|
import { fetchTaskGroups } from '@/features/tasks/tasks.slice';
|
||||||
import { fetchBoardTaskGroups } from '@/features/board/board-slice';
|
|
||||||
import { deleteStatusToggleDrawer } from '@/features/projects/status/DeleteStatusSlice';
|
import { deleteStatusToggleDrawer } from '@/features/projects/status/DeleteStatusSlice';
|
||||||
import { Drawer, Alert, Card, Select, Button, Typography, Badge } from 'antd';
|
import { Drawer, Alert, Card, Select, Button, Typography, Badge } from 'antd';
|
||||||
import { DownOutlined } from '@ant-design/icons';
|
import { DownOutlined } from '@ant-design/icons';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
deleteSection,
|
deleteSection,
|
||||||
IGroupBy,
|
IGroupBy,
|
||||||
} from '@features/board/board-slice';
|
} from '@features/board/board-slice';
|
||||||
import { statusApiService } from '@/api/taskAttributes/status/status.api.service';
|
import { statusApiService } from '@/api/taskAttributes/status/status.api.service';
|
||||||
import { phasesApiService } from '@/api/taskAttributes/phases/phases.api.service';
|
import { phasesApiService } from '@/api/taskAttributes/phases/phases.api.service';
|
||||||
import logger from '@/utils/errorLogger';
|
import logger from '@/utils/errorLogger';
|
||||||
|
import { fetchEnhancedKanbanGroups } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
@@ -43,8 +44,8 @@ const DeleteStatusDrawer: React.FC = () => {
|
|||||||
|
|
||||||
const refreshTasks = useCallback(() => {
|
const refreshTasks = useCallback(() => {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
const fetchAction = projectView === 'list' ? fetchTaskGroups : fetchBoardTaskGroups;
|
const fetchAction = projectView === 'list' ? fetchTaskGroups : fetchEnhancedKanbanGroups;
|
||||||
dispatch(fetchAction(projectId));
|
dispatch(fetchAction(projectId) as any);
|
||||||
}, [projectId, projectView, dispatch]);
|
}, [projectId, projectView, dispatch]);
|
||||||
|
|
||||||
const handleDrawerOpenChange = () => {
|
const handleDrawerOpenChange = () => {
|
||||||
@@ -71,7 +72,7 @@ const DeleteStatusDrawer: React.FC = () => {
|
|||||||
dispatch(fetchStatuses(projectId));
|
dispatch(fetchStatuses(projectId));
|
||||||
refreshTasks();
|
refreshTasks();
|
||||||
dispatch(fetchStatusesCategories());
|
dispatch(fetchStatusesCategories());
|
||||||
} else{
|
} else {
|
||||||
console.error('Error deleting status', res);
|
console.error('Error deleting status', res);
|
||||||
}
|
}
|
||||||
} else if (groupBy === IGroupBy.PHASE) {
|
} else if (groupBy === IGroupBy.PHASE) {
|
||||||
@@ -83,7 +84,7 @@ const DeleteStatusDrawer: React.FC = () => {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error deleting section', error);
|
logger.error('Error deleting section', error);
|
||||||
}finally {
|
} finally {
|
||||||
setDeletingStatus(false);
|
setDeletingStatus(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -98,7 +99,7 @@ const DeleteStatusDrawer: React.FC = () => {
|
|||||||
open={isDelteStatusDrawerOpen}
|
open={isDelteStatusDrawerOpen}
|
||||||
afterOpenChange={handleDrawerOpenChange}
|
afterOpenChange={handleDrawerOpenChange}
|
||||||
>
|
>
|
||||||
<Alert type="warning" message={selectedForDelete?.message.replace("$","")} />
|
<Alert type="warning" message={selectedForDelete?.message.replace("$", "")} />
|
||||||
|
|
||||||
<Card className="text-center" style={{ marginTop: 16 }}>
|
<Card className="text-center" style={{ marginTop: 16 }}>
|
||||||
<Title level={5}>{selectedForDelete?.name}</Title>
|
<Title level={5}>{selectedForDelete?.name}</Title>
|
||||||
|
|||||||
Reference in New Issue
Block a user