feat(task-management): enhance task list with new components and improved state management

- Introduced TaskListV2 and TaskGroupHeader components for a more organized task display.
- Implemented virtualized rendering using react-virtuoso for efficient task list handling.
- Updated Redux state management to include new selectors and improved task grouping logic.
- Added task filtering capabilities with TaskListFilters component for better user experience.
- Enhanced task selection handling and integrated drag-and-drop functionality for task rows.
- Updated package dependencies to include new libraries for icons and forms.
This commit is contained in:
chamikaJ
2025-07-03 15:31:54 +05:30
parent e26f16bbc2
commit d15c00c29b
19 changed files with 1316 additions and 602 deletions

View File

@@ -0,0 +1,261 @@
import React from 'react';
import { Task } from '@/types/task-management.types';
import Avatar from '@/components/Avatar';
import { format } from 'date-fns';
import { Bars3Icon } from '@heroicons/react/24/outline';
import { ClockIcon } from '@heroicons/react/24/outline';
interface TaskRowProps {
task: Task;
visibleColumns: Array<{
id: string;
width: string;
isSticky?: boolean;
}>;
}
const TaskRow: React.FC<TaskRowProps> = ({ task, visibleColumns }) => {
const renderColumn = (columnId: string, width: string, isSticky?: boolean, index?: number) => {
const baseStyle = {
width,
...(isSticky ? {
position: 'sticky' as const,
left: index === 0 ? 0 : index === 1 ? 32 : 132,
backgroundColor: 'inherit',
zIndex: 1,
} : {}),
};
switch (columnId) {
case 'dragHandle':
return (
<div
className="cursor-move flex items-center justify-center"
style={baseStyle}
>
<Bars3Icon className="w-4 h-4 text-gray-400" />
</div>
);
case 'taskKey':
return (
<div
className="flex items-center"
style={baseStyle}
>
<span className="text-sm font-medium text-gray-900 dark:text-white whitespace-nowrap">
{task.task_key}
</span>
</div>
);
case 'title':
return (
<div
className="flex items-center"
style={baseStyle}
>
<span className="text-sm text-gray-700 dark:text-gray-300 truncate">
{task.title || task.name}
</span>
</div>
);
case 'status':
return (
<div style={baseStyle}>
<span
className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium"
style={{
backgroundColor: task.statusColor ? `${task.statusColor}20` : 'rgb(229, 231, 235)',
color: task.statusColor || 'rgb(31, 41, 55)',
}}
>
{task.status}
</span>
</div>
);
case 'assignees':
return (
<div className="flex items-center gap-1" style={baseStyle}>
{task.assignee_names?.slice(0, 3).map((assignee, index) => (
<Avatar
key={index}
name={assignee.name || ''}
size="small"
className="ring-2 ring-white dark:ring-gray-900"
/>
))}
{(task.assignee_names?.length || 0) > 3 && (
<span className="text-xs text-gray-500 dark:text-gray-400 ml-1">
+{task.assignee_names!.length - 3}
</span>
)}
</div>
);
case 'priority':
return (
<div style={baseStyle}>
<span
className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium"
style={{
backgroundColor: task.priorityColor ? `${task.priorityColor}20` : 'rgb(229, 231, 235)',
color: task.priorityColor || 'rgb(31, 41, 55)',
}}
>
{task.priority}
</span>
</div>
);
case 'dueDate':
return (
<div style={baseStyle}>
{task.dueDate && (
<span className="text-sm text-gray-500 dark:text-gray-400">
{format(new Date(task.dueDate), 'MMM d')}
</span>
)}
</div>
);
case 'progress':
return (
<div style={baseStyle}>
<div className="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${task.progress}%` }}
/>
</div>
</div>
);
case 'labels':
return (
<div className="flex items-center gap-1" style={baseStyle}>
{task.labels?.slice(0, 2).map((label, index) => (
<span
key={index}
className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium"
style={{
backgroundColor: label.color ? `${label.color}20` : 'rgb(229, 231, 235)',
color: label.color || 'rgb(31, 41, 55)',
}}
>
{label.name}
</span>
))}
{(task.labels?.length || 0) > 2 && (
<span className="text-xs text-gray-500 dark:text-gray-400">
+{task.labels!.length - 2}
</span>
)}
</div>
);
case 'phase':
return (
<div style={baseStyle}>
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200">
{task.phase}
</span>
</div>
);
case 'timeTracking':
return (
<div className="flex items-center gap-1" style={baseStyle}>
<ClockIcon className="w-4 h-4 text-gray-400" />
<span className="text-sm text-gray-500 dark:text-gray-400">
{task.timeTracking?.logged || 0}h
</span>
{task.timeTracking?.estimated && (
<span className="text-sm text-gray-400 dark:text-gray-500">
/{task.timeTracking.estimated}h
</span>
)}
</div>
);
case 'estimation':
return (
<div style={baseStyle}>
{task.timeTracking.estimated && (
<span className="text-sm text-gray-500 dark:text-gray-400">
{task.timeTracking.estimated}h
</span>
)}
</div>
);
case 'startDate':
return (
<div style={baseStyle}>
{task.startDate && (
<span className="text-sm text-gray-500 dark:text-gray-400">
{format(new Date(task.startDate), 'MMM d')}
</span>
)}
</div>
);
case 'completedDate':
return (
<div style={baseStyle}>
{task.completedAt && (
<span className="text-sm text-gray-500 dark:text-gray-400">
{format(new Date(task.completedAt), 'MMM d')}
</span>
)}
</div>
);
case 'createdDate':
return (
<div style={baseStyle}>
{task.createdAt && (
<span className="text-sm text-gray-500 dark:text-gray-400">
{format(new Date(task.createdAt), 'MMM d')}
</span>
)}
</div>
);
case 'lastUpdated':
return (
<div style={baseStyle}>
{task.updatedAt && (
<span className="text-sm text-gray-500 dark:text-gray-400">
{format(new Date(task.updatedAt), 'MMM d')}
</span>
)}
</div>
);
case 'reporter':
return (
<div style={baseStyle}>
{task.reporter && (
<span className="text-sm text-gray-500 dark:text-gray-400">
{task.reporter}
</span>
)}
</div>
);
default:
return null;
}
};
return (
<div className="flex items-center min-w-max px-4 py-2 hover:bg-gray-50 dark:hover:bg-gray-800">
{visibleColumns.map((column, index) => renderColumn(column.id, column.width, column.isSticky, index))}
</div>
);
};
export default TaskRow;