diff --git a/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx b/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx
index ac5227fe..c208882f 100644
--- a/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx
+++ b/worklenz-frontend/src/components/task-list-v2/TaskListV2Table.tsx
@@ -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 AddTaskRow from './components/AddTaskRow';
import { AddCustomColumnButton, CustomColumnHeader } from './components/CustomColumnComponents';
+import TaskListSkeleton from './components/TaskListSkeleton';
// Hooks and utilities
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
@@ -597,7 +598,9 @@ const TaskListV2Section: React.FC = () => {
);
// Loading and error states
- if (loading || loadingColumns) return ;
+ if (loading || loadingColumns) {
+ return ;
+ }
if (error)
return (
diff --git a/worklenz-frontend/src/components/task-list-v2/components/TaskListSkeleton.tsx b/worklenz-frontend/src/components/task-list-v2/components/TaskListSkeleton.tsx
new file mode 100644
index 00000000..54351f60
--- /dev/null
+++ b/worklenz-frontend/src/components/task-list-v2/components/TaskListSkeleton.tsx
@@ -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
= ({ 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) => (
+
+ {columns.map((column, colIndex) => {
+ const columnStyle = {
+ width: column.width,
+ flexShrink: 0,
+ };
+
+ return (
+
+ {column.id === 'dragHandle' || column.id === 'checkbox' ? (
+
+ ) : column.id === 'title' ? (
+
+
+
+ ) : column.id === 'assignees' ? (
+
+
+
+
+ ) : (
+
+ )}
+
+ );
+ })}
+
+ ));
+
+ return (
+
+
+ {/* Table Container */}
+
+ {/* Skeleton Content */}
+
+ {/* Skeleton Column Headers */}
+
+
+ {columns.map((column, index) => {
+ const columnStyle = {
+ width: column.width,
+ flexShrink: 0,
+ };
+
+ return (
+
+ {column.id === 'dragHandle' || column.id === 'checkbox' ? (
+
+ ) : (
+
+ )}
+
+ );
+ })}
+ {/* Add Custom Column Button Skeleton */}
+
+
+
+
+
+
+ {/* Skeleton Group Headers and Rows */}
+
+ {/* First Group */}
+
+ {/* Group Header Skeleton */}
+
+
+ {/* Group Tasks Skeleton */}
+ {skeletonRows.slice(0, 3)}
+
+
+ {/* Second Group */}
+
+ {/* Group Header Skeleton */}
+
+
+ {/* Group Tasks Skeleton */}
+ {skeletonRows.slice(3, 6)}
+
+
+ {/* Third Group */}
+
+ {/* Group Header Skeleton */}
+
+
+ {/* Group Tasks Skeleton */}
+ {skeletonRows.slice(6, 8)}
+
+
+
+
+
+
+ );
+};
+
+export default TaskListSkeleton;
\ No newline at end of file