feat(task-list): add TaskListSkeleton component for improved loading state
- Introduced TaskListSkeleton to provide a visual loading state for the task list, enhancing user experience during data fetching. - Updated TaskListV2Table to utilize TaskListSkeleton instead of a generic Skeleton component, allowing for a more tailored loading interface. - The new skeleton component includes customizable column widths and multiple rows to better represent the task list structure while loading.
This commit is contained in:
@@ -63,6 +63,7 @@ import OptimizedBulkActionBar from '@/components/task-management/optimized-bulk-
|
|||||||
import CustomColumnModal from '@/pages/projects/projectView/taskList/task-list-table/custom-columns/custom-column-modal/custom-column-modal';
|
import CustomColumnModal from '@/pages/projects/projectView/taskList/task-list-table/custom-columns/custom-column-modal/custom-column-modal';
|
||||||
import AddTaskRow from './components/AddTaskRow';
|
import AddTaskRow from './components/AddTaskRow';
|
||||||
import { AddCustomColumnButton, CustomColumnHeader } from './components/CustomColumnComponents';
|
import { AddCustomColumnButton, CustomColumnHeader } from './components/CustomColumnComponents';
|
||||||
|
import TaskListSkeleton from './components/TaskListSkeleton';
|
||||||
|
|
||||||
// Hooks and utilities
|
// Hooks and utilities
|
||||||
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
|
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
|
||||||
@@ -597,7 +598,9 @@ const TaskListV2Section: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Loading and error states
|
// Loading and error states
|
||||||
if (loading || loadingColumns) return <Skeleton style={{ marginTop: 8 }} active />;
|
if (loading || loadingColumns) {
|
||||||
|
return <TaskListSkeleton visibleColumns={visibleColumns} />;
|
||||||
|
}
|
||||||
if (error)
|
if (error)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Skeleton } from 'antd';
|
||||||
|
import ImprovedTaskFilters from '@/components/task-management/improved-task-filters';
|
||||||
|
|
||||||
|
interface TaskListSkeletonProps {
|
||||||
|
visibleColumns?: Array<{
|
||||||
|
id: string;
|
||||||
|
width: string;
|
||||||
|
isSticky?: boolean;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TaskListSkeleton: React.FC<TaskListSkeletonProps> = ({ visibleColumns }) => {
|
||||||
|
// Default columns if none provided
|
||||||
|
const defaultColumns = [
|
||||||
|
{ id: 'dragHandle', width: '40px' },
|
||||||
|
{ id: 'checkbox', width: '40px' },
|
||||||
|
{ id: 'taskKey', width: '100px' },
|
||||||
|
{ id: 'title', width: '300px' },
|
||||||
|
{ id: 'assignees', width: '120px' },
|
||||||
|
{ id: 'status', width: '120px' },
|
||||||
|
{ id: 'priority', width: '100px' },
|
||||||
|
{ id: 'dueDate', width: '120px' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = visibleColumns || defaultColumns;
|
||||||
|
|
||||||
|
// Generate multiple skeleton rows
|
||||||
|
const skeletonRows = Array.from({ length: 8 }, (_, index) => (
|
||||||
|
<div key={index} className="flex items-center min-w-max px-1 py-3 border-b border-gray-100 dark:border-gray-800">
|
||||||
|
{columns.map((column, colIndex) => {
|
||||||
|
const columnStyle = {
|
||||||
|
width: column.width,
|
||||||
|
flexShrink: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${index}-${column.id}`}
|
||||||
|
className="border-r border-gray-200 dark:border-gray-700 flex items-center px-2"
|
||||||
|
style={columnStyle}
|
||||||
|
>
|
||||||
|
{column.id === 'dragHandle' || column.id === 'checkbox' ? (
|
||||||
|
<Skeleton.Button size="small" shape="circle" active />
|
||||||
|
) : column.id === 'title' ? (
|
||||||
|
<div className="w-full">
|
||||||
|
<Skeleton.Input size="small" active style={{ width: '80%' }} />
|
||||||
|
</div>
|
||||||
|
) : column.id === 'assignees' ? (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Skeleton.Avatar size="small" active />
|
||||||
|
<Skeleton.Avatar size="small" active />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Skeleton.Button size="small" active style={{ width: '70%' }} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-col bg-white dark:bg-gray-900 h-full overflow-hidden">
|
||||||
|
{/* Table Container */}
|
||||||
|
<div
|
||||||
|
className="border border-gray-200 dark:border-gray-700 rounded-lg"
|
||||||
|
style={{
|
||||||
|
height: 'calc(100vh - 240px)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Skeleton Content */}
|
||||||
|
<div
|
||||||
|
className="flex-1 bg-white dark:bg-gray-900 relative"
|
||||||
|
style={{
|
||||||
|
overflowX: 'auto',
|
||||||
|
overflowY: 'auto',
|
||||||
|
minHeight: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Skeleton Column Headers */}
|
||||||
|
<div
|
||||||
|
className="sticky top-0 z-30 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700"
|
||||||
|
style={{ width: '100%', minWidth: 'max-content' }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="flex items-center px-1 py-3 w-full"
|
||||||
|
style={{ minWidth: 'max-content', height: '44px' }}
|
||||||
|
>
|
||||||
|
{columns.map((column, index) => {
|
||||||
|
const columnStyle = {
|
||||||
|
width: column.width,
|
||||||
|
flexShrink: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`header-${column.id}`}
|
||||||
|
className="border-r border-gray-200 dark:border-gray-700 flex items-center px-2"
|
||||||
|
style={columnStyle}
|
||||||
|
>
|
||||||
|
{column.id === 'dragHandle' || column.id === 'checkbox' ? (
|
||||||
|
<span></span>
|
||||||
|
) : (
|
||||||
|
<Skeleton.Button size="small" active style={{ width: '60%' }} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{/* Add Custom Column Button Skeleton */}
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-center px-2 border-r border-gray-200 dark:border-gray-700"
|
||||||
|
style={{ width: '50px', flexShrink: 0 }}
|
||||||
|
>
|
||||||
|
<Skeleton.Button size="small" shape="circle" active />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Skeleton Group Headers and Rows */}
|
||||||
|
<div style={{ minWidth: 'max-content' }}>
|
||||||
|
{/* First Group */}
|
||||||
|
<div className="mt-2">
|
||||||
|
{/* Group Header Skeleton */}
|
||||||
|
<div className="flex items-center px-4 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<Skeleton.Button size="small" shape="circle" active />
|
||||||
|
<div className="ml-3 flex-1">
|
||||||
|
<Skeleton.Input size="small" active style={{ width: '150px' }} />
|
||||||
|
</div>
|
||||||
|
<Skeleton.Button size="small" active style={{ width: '30px' }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Group Tasks Skeleton */}
|
||||||
|
{skeletonRows.slice(0, 3)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Second Group */}
|
||||||
|
<div className="mt-2">
|
||||||
|
{/* Group Header Skeleton */}
|
||||||
|
<div className="flex items-center px-4 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<Skeleton.Button size="small" shape="circle" active />
|
||||||
|
<div className="ml-3 flex-1">
|
||||||
|
<Skeleton.Input size="small" active style={{ width: '150px' }} />
|
||||||
|
</div>
|
||||||
|
<Skeleton.Button size="small" active style={{ width: '30px' }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Group Tasks Skeleton */}
|
||||||
|
{skeletonRows.slice(3, 6)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Third Group */}
|
||||||
|
<div className="mt-2">
|
||||||
|
{/* Group Header Skeleton */}
|
||||||
|
<div className="flex items-center px-4 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
||||||
|
<Skeleton.Button size="small" shape="circle" active />
|
||||||
|
<div className="ml-3 flex-1">
|
||||||
|
<Skeleton.Input size="small" active style={{ width: '150px' }} />
|
||||||
|
</div>
|
||||||
|
<Skeleton.Button size="small" active style={{ width: '30px' }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Group Tasks Skeleton */}
|
||||||
|
{skeletonRows.slice(6, 8)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskListSkeleton;
|
||||||
Reference in New Issue
Block a user