feat(task-management): enhance task grouping and localization support

- Implemented unmapped task grouping for better organization of tasks without valid phases.
- Updated task distribution logic to handle unmapped tasks and added a corresponding group in the response.
- Enhanced localization by adding translations for "noTasksInGroup" in multiple languages.
- Improved task list components to support custom columns and better task management features.
- Refactored task management slice to include loading states for columns and custom columns.
This commit is contained in:
chamikaJ
2025-07-04 20:41:03 +05:30
parent 9e29031703
commit f30fde553d
23 changed files with 1560 additions and 380 deletions

View File

@@ -79,7 +79,7 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
return (
<div
ref={setNodeRef}
className={`inline-flex w-max items-center px-4 py-2 cursor-pointer hover:opacity-80 transition-opacity duration-200 ease-in-out border-b border-gray-200 dark:border-gray-700 rounded-t-md ${
className={`inline-flex w-max items-center px-4 cursor-pointer hover:opacity-80 transition-opacity duration-200 ease-in-out border-b border-gray-200 dark:border-gray-700 rounded-t-md ${
isOver ? 'ring-2 ring-blue-400 ring-opacity-50' : ''
}`}
style={{
@@ -87,7 +87,10 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
color: headerTextColor,
position: 'sticky',
top: 0,
zIndex: 20 // Higher than sticky columns (zIndex: 1) and column headers (zIndex: 2)
zIndex: 25, // Higher than task rows but lower than column headers (z-30)
height: '36px',
minHeight: '36px',
maxHeight: '36px'
}}
onClick={onToggle}
>
@@ -95,18 +98,22 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
<div style={{ width: '32px' }} className="flex items-center justify-center">
{/* Chevron button */}
<button
className="p-1 rounded-md hover:bg-opacity-20 transition-colors"
style={{ backgroundColor: headerBackgroundColor, color: headerTextColor, borderColor: headerTextColor, border: '1px solid' }}
className="p-1 rounded-md hover:shadow-lg hover:scale-105 transition-all duration-300 ease-out"
style={{ backgroundColor: 'transparent', color: headerTextColor }}
onClick={(e) => {
e.stopPropagation();
onToggle();
}}
>
{isCollapsed ? (
<ChevronRightIcon className="h-4 w-4" style={{ color: headerTextColor }} />
) : (
<ChevronDownIcon className="h-4 w-4" style={{ color: headerTextColor }} />
)}
<div
className="transition-transform duration-300 ease-out"
style={{
transform: isCollapsed ? 'rotate(0deg)' : 'rotate(90deg)',
transformOrigin: 'center'
}}
>
<ChevronRightIcon className="h-3.5 w-3.5" style={{ color: headerTextColor }} />
</div>
</button>
</div>
@@ -124,12 +131,12 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
</div>
{/* Group indicator and name */}
<div className="ml-2 flex items-center gap-3 flex-1">
<div className="ml-1 flex items-center gap-2 flex-1">
{/* Color indicator (removed as full header is colored) */}
{/* Group name and count */}
<div className="flex items-center flex-1">
<span className="text-sm font-medium">
<span className="text-sm font-semibold">
{group.name} ({group.count})
</span>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import React, { memo, useMemo, useCallback, useState } from 'react';
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { CheckCircleOutlined, HolderOutlined, CloseOutlined, DownOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons';
import { CheckCircleOutlined, HolderOutlined, CloseOutlined, DownOutlined, RightOutlined, DoubleRightOutlined, ArrowsAltOutlined } from '@ant-design/icons';
import { Checkbox, DatePicker } from 'antd';
import { dayjs, taskManagementAntdConfig } from '@/shared/antd-imports';
import { Task } from '@/types/task-management.types';
@@ -35,6 +35,7 @@ interface TaskRowProps {
isSticky?: boolean;
}>;
isSubtask?: boolean;
updateTaskCustomColumnValue?: (taskId: string, columnKey: string, value: string) => void;
}
interface TaskLabelsCellProps {
@@ -91,7 +92,7 @@ const formatDate = (dateString: string): string => {
}
};
const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumns, isSubtask = false }) => {
const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumns, isSubtask = false, updateTaskCustomColumnValue }) => {
const dispatch = useAppDispatch();
const task = useAppSelector(state => selectTaskById(state, taskId));
const isSelected = useAppSelector(state => selectIsTaskSelected(state, taskId));
@@ -277,7 +278,7 @@ const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumn
case 'taskKey':
return (
<div className="flex items-center" style={baseStyle}>
<span className="text-sm font-medium text-gray-900 dark:text-white whitespace-nowrap">
<span className="text-sm font-medium text-gray-900 dark:text-white whitespace-nowrap badge badge-primary">
{task.task_key || 'N/A'}
</span>
</div>
@@ -294,21 +295,21 @@ const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumn
{!isSubtask && (
<button
onClick={handleToggleExpansion}
className={`flex h-4 w-4 items-center justify-center rounded-sm text-xs mr-2 hover:border hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors ${
className={`flex h-4 w-4 items-center justify-center rounded-sm text-xs mr-2 hover:border hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-900/20 hover:scale-110 transition-all duration-300 ease-out ${
task.sub_tasks_count && task.sub_tasks_count > 0
? 'opacity-100'
: 'opacity-0 group-hover:opacity-100'
}`}
>
{task.sub_tasks_count && task.sub_tasks_count > 0 ? (
task.show_sub_tasks ? (
<DownOutlined className="text-gray-600 dark:text-gray-400" />
) : (
<RightOutlined className="text-gray-600 dark:text-gray-400" />
)
) : (
<div
className="transition-transform duration-300 ease-out"
style={{
transform: task.show_sub_tasks ? 'rotate(90deg)' : 'rotate(0deg)',
transformOrigin: 'center'
}}
>
<RightOutlined className="text-gray-600 dark:text-gray-400" />
)}
</div>
</button>
)}
@@ -333,13 +334,14 @@ const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumn
</div>
<button
className="opacity-0 group-hover:opacity-100 transition-opacity duration-200 ml-2 px-2 py-1 text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 border-none bg-transparent cursor-pointer"
className="opacity-0 group-hover:opacity-100 transition-all duration-200 ml-2 mr-2 px-3 py-1.5 text-xs text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/20 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 cursor-pointer rounded-md shadow-sm hover:shadow-md flex items-center gap-1"
onClick={(e) => {
e.stopPropagation();
dispatch(setSelectedTaskId(task.id));
dispatch(setShowTaskDrawer(true));
}}
>
<ArrowsAltOutlined />
{t('openButton')}
</button>
</div>
@@ -642,9 +644,11 @@ const TaskRow: React.FC<TaskRowProps> = memo(({ taskId, projectId, visibleColumn
isDragging ? 'shadow-lg border border-blue-300' : ''
}`}
>
{visibleColumns.map((column, index) =>
renderColumn(column.id, column.width, column.isSticky, index)
)}
{visibleColumns.map((column, index) => (
<React.Fragment key={column.id}>
{renderColumn(column.id, column.width, column.isSticky, index)}
</React.Fragment>
))}
</div>
);
});

View File

@@ -19,6 +19,7 @@ interface TaskRowWithSubtasksProps {
width: string;
isSticky?: boolean;
}>;
updateTaskCustomColumnValue?: (taskId: string, columnKey: string, value: string) => void;
}
interface AddSubtaskRowProps {
@@ -140,7 +141,8 @@ AddSubtaskRow.displayName = 'AddSubtaskRow';
const TaskRowWithSubtasks: React.FC<TaskRowWithSubtasksProps> = memo(({
taskId,
projectId,
visibleColumns
visibleColumns,
updateTaskCustomColumnValue
}) => {
const task = useAppSelector(state => selectTaskById(state, taskId));
const isLoadingSubtasks = useAppSelector(state => selectSubtaskLoading(state, taskId));
@@ -162,6 +164,7 @@ const TaskRowWithSubtasks: React.FC<TaskRowWithSubtasksProps> = memo(({
taskId={taskId}
projectId={projectId}
visibleColumns={visibleColumns}
updateTaskCustomColumnValue={updateTaskCustomColumnValue}
/>
{/* Subtasks and add subtask row when expanded */}
@@ -182,6 +185,7 @@ const TaskRowWithSubtasks: React.FC<TaskRowWithSubtasksProps> = memo(({
projectId={projectId}
visibleColumns={visibleColumns}
isSubtask={true}
updateTaskCustomColumnValue={updateTaskCustomColumnValue}
/>
</div>
))}