feat(task-management): enhance task localization and progress visualization
- Added localization entries for task statuses (To Do, In Progress, Done) across multiple languages including Albanian, German, Spanish, Portuguese, and Chinese. - Updated the GroupProgressBar component to improve visual representation of task progress with distinct color coding for each status. - Enhanced TaskGroupHeader to calculate and display group progress dynamically based on task completion and status distribution. - Integrated a new Convert To Subtask Drawer for improved task management functionality.
This commit is contained in:
@@ -17,5 +17,22 @@
|
|||||||
"renamePhase": "Riemërto Fazën",
|
"renamePhase": "Riemërto Fazën",
|
||||||
"changeCategory": "Ndrysho Kategorinë",
|
"changeCategory": "Ndrysho Kategorinë",
|
||||||
"clickToEditGroupName": "Kliko për të ndryshuar emrin e grupit",
|
"clickToEditGroupName": "Kliko për të ndryshuar emrin e grupit",
|
||||||
"enterGroupName": "Shkruani emrin e grupit"
|
"enterGroupName": "Shkruani emrin e grupit",
|
||||||
|
"todo": "Për t'u bërë",
|
||||||
|
"inProgress": "Në progres",
|
||||||
|
"done": "E kryer",
|
||||||
|
|
||||||
|
"indicators": {
|
||||||
|
"tooltips": {
|
||||||
|
"subtasks": "{{count}} nën-detyrë",
|
||||||
|
"subtasks_plural": "{{count}} nën-detyra",
|
||||||
|
"comments": "{{count}} koment",
|
||||||
|
"comments_plural": "{{count}} komente",
|
||||||
|
"attachments": "{{count}} bashkëngjitje",
|
||||||
|
"attachments_plural": "{{count}} bashkëngjitje",
|
||||||
|
"subscribers": "Detyra ka abonentë",
|
||||||
|
"dependencies": "Detyra ka varësi",
|
||||||
|
"recurring": "Detyrë përsëritëse"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,5 +17,22 @@
|
|||||||
"renamePhase": "Phase umbenennen",
|
"renamePhase": "Phase umbenennen",
|
||||||
"changeCategory": "Kategorie ändern",
|
"changeCategory": "Kategorie ändern",
|
||||||
"clickToEditGroupName": "Klicken Sie, um den Gruppennamen zu bearbeiten",
|
"clickToEditGroupName": "Klicken Sie, um den Gruppennamen zu bearbeiten",
|
||||||
"enterGroupName": "Gruppennamen eingeben"
|
"enterGroupName": "Gruppennamen eingeben",
|
||||||
|
"todo": "Zu erledigen",
|
||||||
|
"inProgress": "In Bearbeitung",
|
||||||
|
"done": "Erledigt",
|
||||||
|
|
||||||
|
"indicators": {
|
||||||
|
"tooltips": {
|
||||||
|
"subtasks": "{{count}} Unteraufgabe",
|
||||||
|
"subtasks_plural": "{{count}} Unteraufgaben",
|
||||||
|
"comments": "{{count}} Kommentar",
|
||||||
|
"comments_plural": "{{count}} Kommentare",
|
||||||
|
"attachments": "{{count}} Anhang",
|
||||||
|
"attachments_plural": "{{count}} Anhänge",
|
||||||
|
"subscribers": "Aufgabe hat Abonnenten",
|
||||||
|
"dependencies": "Aufgabe hat Abhängigkeiten",
|
||||||
|
"recurring": "Wiederkehrende Aufgabe"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
"changeCategory": "Change Category",
|
"changeCategory": "Change Category",
|
||||||
"clickToEditGroupName": "Click to edit group name",
|
"clickToEditGroupName": "Click to edit group name",
|
||||||
"enterGroupName": "Enter group name",
|
"enterGroupName": "Enter group name",
|
||||||
|
"todo": "To Do",
|
||||||
|
"inProgress": "Doing",
|
||||||
|
"done": "Done",
|
||||||
|
|
||||||
"indicators": {
|
"indicators": {
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
|
|||||||
@@ -17,5 +17,22 @@
|
|||||||
"renamePhase": "Renombrar Fase",
|
"renamePhase": "Renombrar Fase",
|
||||||
"changeCategory": "Cambiar Categoría",
|
"changeCategory": "Cambiar Categoría",
|
||||||
"clickToEditGroupName": "Haz clic para editar el nombre del grupo",
|
"clickToEditGroupName": "Haz clic para editar el nombre del grupo",
|
||||||
"enterGroupName": "Ingresa el nombre del grupo"
|
"enterGroupName": "Ingresa el nombre del grupo",
|
||||||
|
"todo": "Por Hacer",
|
||||||
|
"inProgress": "En Progreso",
|
||||||
|
"done": "Hecho",
|
||||||
|
|
||||||
|
"indicators": {
|
||||||
|
"tooltips": {
|
||||||
|
"subtasks": "{{count}} subtarea",
|
||||||
|
"subtasks_plural": "{{count}} subtareas",
|
||||||
|
"comments": "{{count}} comentario",
|
||||||
|
"comments_plural": "{{count}} comentarios",
|
||||||
|
"attachments": "{{count}} adjunto",
|
||||||
|
"attachments_plural": "{{count}} adjuntos",
|
||||||
|
"subscribers": "La tarea tiene suscriptores",
|
||||||
|
"dependencies": "La tarea tiene dependencias",
|
||||||
|
"recurring": "Tarea recurrente"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,5 +17,22 @@
|
|||||||
"renamePhase": "Renomear Fase",
|
"renamePhase": "Renomear Fase",
|
||||||
"changeCategory": "Alterar Categoria",
|
"changeCategory": "Alterar Categoria",
|
||||||
"clickToEditGroupName": "Clique para editar o nome do grupo",
|
"clickToEditGroupName": "Clique para editar o nome do grupo",
|
||||||
"enterGroupName": "Digite o nome do grupo"
|
"enterGroupName": "Digite o nome do grupo",
|
||||||
|
"todo": "A Fazer",
|
||||||
|
"inProgress": "Em Andamento",
|
||||||
|
"done": "Concluído",
|
||||||
|
|
||||||
|
"indicators": {
|
||||||
|
"tooltips": {
|
||||||
|
"subtasks": "{{count}} subtarefa",
|
||||||
|
"subtasks_plural": "{{count}} subtarefas",
|
||||||
|
"comments": "{{count}} comentário",
|
||||||
|
"comments_plural": "{{count}} comentários",
|
||||||
|
"attachments": "{{count}} anexo",
|
||||||
|
"attachments_plural": "{{count}} anexos",
|
||||||
|
"subscribers": "Tarefa tem assinantes",
|
||||||
|
"dependencies": "Tarefa tem dependências",
|
||||||
|
"recurring": "Tarefa recorrente"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
"changeCategory": "更改类别",
|
"changeCategory": "更改类别",
|
||||||
"clickToEditGroupName": "点击编辑组名称",
|
"clickToEditGroupName": "点击编辑组名称",
|
||||||
"enterGroupName": "输入组名称",
|
"enterGroupName": "输入组名称",
|
||||||
|
"todo": "待办",
|
||||||
|
"inProgress": "进行中",
|
||||||
|
"done": "已完成",
|
||||||
|
|
||||||
"indicators": {
|
"indicators": {
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
|
|||||||
@@ -38,26 +38,26 @@ const GroupProgressBar: React.FC<GroupProgressBarProps> = ({
|
|||||||
{/* Compact progress bar */}
|
{/* Compact progress bar */}
|
||||||
<div className="w-20 bg-gray-200 dark:bg-gray-700 rounded-full h-1.5 overflow-hidden">
|
<div className="w-20 bg-gray-200 dark:bg-gray-700 rounded-full h-1.5 overflow-hidden">
|
||||||
<div className="h-full flex">
|
<div className="h-full flex">
|
||||||
{/* Todo section - light gray */}
|
{/* Todo section - light green */}
|
||||||
{todoProgress > 0 && (
|
{todoProgress > 0 && (
|
||||||
<div
|
<div
|
||||||
className="bg-gray-300 dark:bg-gray-600 transition-all duration-300"
|
className="bg-green-200 dark:bg-green-800 transition-all duration-300"
|
||||||
style={{ width: `${(todoProgress / total) * 100}%` }}
|
style={{ width: `${(todoProgress / total) * 100}%` }}
|
||||||
title={`${t('todo')}: ${todoProgress}%`}
|
title={`${t('todo')}: ${todoProgress}%`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* Doing section - blue */}
|
{/* Doing section - medium green */}
|
||||||
{doingProgress > 0 && (
|
{doingProgress > 0 && (
|
||||||
<div
|
<div
|
||||||
className="bg-blue-500 transition-all duration-300"
|
className="bg-green-400 dark:bg-green-600 transition-all duration-300"
|
||||||
style={{ width: `${(doingProgress / total) * 100}%` }}
|
style={{ width: `${(doingProgress / total) * 100}%` }}
|
||||||
title={`${t('inProgress')}: ${doingProgress}%`}
|
title={`${t('inProgress')}: ${doingProgress}%`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* Done section - green */}
|
{/* Done section - dark green */}
|
||||||
{doneProgress > 0 && (
|
{doneProgress > 0 && (
|
||||||
<div
|
<div
|
||||||
className="bg-green-500 transition-all duration-300"
|
className="bg-green-600 dark:bg-green-400 transition-all duration-300"
|
||||||
style={{ width: `${(doneProgress / total) * 100}%` }}
|
style={{ width: `${(doneProgress / total) * 100}%` }}
|
||||||
title={`${t('done')}: ${doneProgress}%`}
|
title={`${t('done')}: ${doneProgress}%`}
|
||||||
/>
|
/>
|
||||||
@@ -69,19 +69,19 @@ const GroupProgressBar: React.FC<GroupProgressBarProps> = ({
|
|||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{todoProgress > 0 && (
|
{todoProgress > 0 && (
|
||||||
<div
|
<div
|
||||||
className="w-1.5 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full"
|
className="w-1.5 h-1.5 bg-green-200 dark:bg-green-800 rounded-full"
|
||||||
title={`${t('todo')}: ${todoProgress}%`}
|
title={`${t('todo')}: ${todoProgress}%`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{doingProgress > 0 && (
|
{doingProgress > 0 && (
|
||||||
<div
|
<div
|
||||||
className="w-1.5 h-1.5 bg-blue-500 rounded-full"
|
className="w-1.5 h-1.5 bg-green-400 dark:bg-green-600 rounded-full"
|
||||||
title={`${t('inProgress')}: ${doingProgress}%`}
|
title={`${t('inProgress')}: ${doingProgress}%`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{doneProgress > 0 && (
|
{doneProgress > 0 && (
|
||||||
<div
|
<div
|
||||||
className="w-1.5 h-1.5 bg-green-500 rounded-full"
|
className="w-1.5 h-1.5 bg-green-600 dark:bg-green-400 rounded-full"
|
||||||
title={`${t('done')}: ${doneProgress}%`}
|
title={`${t('done')}: ${doneProgress}%`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { getContrastColor } from '@/utils/colorUtils';
|
|||||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||||
import { selectSelectedTaskIds, selectTask, deselectTask } from '@/features/task-management/selection.slice';
|
import { selectSelectedTaskIds, selectTask, deselectTask } from '@/features/task-management/selection.slice';
|
||||||
import { selectGroups, fetchTasksV3 } from '@/features/task-management/task-management.slice';
|
import { selectGroups, fetchTasksV3, selectAllTasksArray } from '@/features/task-management/task-management.slice';
|
||||||
import { selectCurrentGrouping } from '@/features/task-management/grouping.slice';
|
import { selectCurrentGrouping } from '@/features/task-management/grouping.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';
|
||||||
@@ -43,8 +43,9 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const selectedTaskIds = useAppSelector(selectSelectedTaskIds);
|
const selectedTaskIds = useAppSelector(selectSelectedTaskIds);
|
||||||
const groups = useAppSelector(selectGroups);
|
const groups = useAppSelector(selectGroups);
|
||||||
|
const allTasks = useAppSelector(selectAllTasksArray);
|
||||||
const currentGrouping = useAppSelector(selectCurrentGrouping);
|
const currentGrouping = useAppSelector(selectCurrentGrouping);
|
||||||
const { statusCategories } = useAppSelector(state => state.taskStatusReducer);
|
const { statusCategories, status: statusList } = useAppSelector(state => state.taskStatusReducer);
|
||||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||||
const { isOwnerOrAdmin } = useAuthService();
|
const { isOwnerOrAdmin } = useAuthService();
|
||||||
|
|
||||||
@@ -67,6 +68,74 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
|
|||||||
return currentGroup?.taskIds || [];
|
return currentGroup?.taskIds || [];
|
||||||
}, [currentGroup]);
|
}, [currentGroup]);
|
||||||
|
|
||||||
|
// Calculate group progress values dynamically
|
||||||
|
const groupProgressValues = useMemo(() => {
|
||||||
|
if (!currentGroup || !allTasks.length) {
|
||||||
|
return { todoProgress: 0, doingProgress: 0, doneProgress: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const tasksInCurrentGroup = currentGroup.taskIds
|
||||||
|
.map(taskId => allTasks.find(task => task.id === taskId))
|
||||||
|
.filter(task => task !== undefined);
|
||||||
|
|
||||||
|
if (tasksInCurrentGroup.length === 0) {
|
||||||
|
return { todoProgress: 0, doingProgress: 0, doneProgress: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're grouping by status, show progress based on task completion
|
||||||
|
if (currentGrouping === 'status') {
|
||||||
|
// For status grouping, calculate based on task progress values
|
||||||
|
const progressStats = tasksInCurrentGroup.reduce((acc, task) => {
|
||||||
|
const progress = task.progress || 0;
|
||||||
|
if (progress === 0) {
|
||||||
|
acc.todo += 1;
|
||||||
|
} else if (progress === 100) {
|
||||||
|
acc.done += 1;
|
||||||
|
} else {
|
||||||
|
acc.doing += 1;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, { todo: 0, doing: 0, done: 0 });
|
||||||
|
|
||||||
|
const totalTasks = tasksInCurrentGroup.length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
todoProgress: totalTasks > 0 ? Math.round((progressStats.todo / totalTasks) * 100) : 0,
|
||||||
|
doingProgress: totalTasks > 0 ? Math.round((progressStats.doing / totalTasks) * 100) : 0,
|
||||||
|
doneProgress: totalTasks > 0 ? Math.round((progressStats.done / totalTasks) * 100) : 0,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// For priority/phase grouping, show progress based on status distribution
|
||||||
|
// Use a simplified approach based on status names and common patterns
|
||||||
|
const statusCounts = tasksInCurrentGroup.reduce((acc, task) => {
|
||||||
|
// Find the status by ID first
|
||||||
|
const statusInfo = statusList.find(s => s.id === task.status);
|
||||||
|
const statusName = statusInfo?.name?.toLowerCase() || task.status?.toLowerCase() || '';
|
||||||
|
|
||||||
|
// Categorize based on common status name patterns
|
||||||
|
if (statusName.includes('todo') || statusName.includes('to do') || statusName.includes('pending') || statusName.includes('open') || statusName.includes('backlog')) {
|
||||||
|
acc.todo += 1;
|
||||||
|
} else if (statusName.includes('doing') || statusName.includes('progress') || statusName.includes('active') || statusName.includes('working') || statusName.includes('development')) {
|
||||||
|
acc.doing += 1;
|
||||||
|
} else if (statusName.includes('done') || statusName.includes('completed') || statusName.includes('finished') || statusName.includes('closed') || statusName.includes('resolved')) {
|
||||||
|
acc.done += 1;
|
||||||
|
} else {
|
||||||
|
// Default unknown statuses to "doing" (in progress)
|
||||||
|
acc.doing += 1;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, { todo: 0, doing: 0, done: 0 });
|
||||||
|
|
||||||
|
const totalTasks = tasksInCurrentGroup.length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
todoProgress: totalTasks > 0 ? Math.round((statusCounts.todo / totalTasks) * 100) : 0,
|
||||||
|
doingProgress: totalTasks > 0 ? Math.round((statusCounts.doing / totalTasks) * 100) : 0,
|
||||||
|
doneProgress: totalTasks > 0 ? Math.round((statusCounts.done / totalTasks) * 100) : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [currentGroup, allTasks, statusList, currentGrouping]);
|
||||||
|
|
||||||
// Calculate selection state for this group
|
// Calculate selection state for this group
|
||||||
const { isAllSelected, isPartiallySelected } = useMemo(() => {
|
const { isAllSelected, isPartiallySelected } = useMemo(() => {
|
||||||
if (tasksInGroup.length === 0) {
|
if (tasksInGroup.length === 0) {
|
||||||
@@ -369,7 +438,7 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
|
|||||||
|
|
||||||
{/* Progress Bar - sticky to the right edge during horizontal scroll */}
|
{/* Progress Bar - sticky to the right edge during horizontal scroll */}
|
||||||
{(currentGrouping === 'priority' || currentGrouping === 'phase') &&
|
{(currentGrouping === 'priority' || currentGrouping === 'phase') &&
|
||||||
(group.todo_progress || group.doing_progress || group.done_progress) && (
|
(groupProgressValues.todoProgress || groupProgressValues.doingProgress || groupProgressValues.doneProgress) && (
|
||||||
<div
|
<div
|
||||||
className="flex items-center bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-md shadow-sm px-3 py-1.5 ml-auto"
|
className="flex items-center bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-md shadow-sm px-3 py-1.5 ml-auto"
|
||||||
style={{
|
style={{
|
||||||
@@ -381,9 +450,9 @@ const TaskGroupHeader: React.FC<TaskGroupHeaderProps> = ({ group, isCollapsed, o
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GroupProgressBar
|
<GroupProgressBar
|
||||||
todoProgress={group.todo_progress || 0}
|
todoProgress={groupProgressValues.todoProgress}
|
||||||
doingProgress={group.doing_progress || 0}
|
doingProgress={groupProgressValues.doingProgress}
|
||||||
doneProgress={group.done_progress || 0}
|
doneProgress={groupProgressValues.doneProgress}
|
||||||
groupType={group.groupType || currentGrouping || ''}
|
groupType={group.groupType || currentGrouping || ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ import CustomColumnModal from '@/pages/projects/projectView/taskList/task-list-t
|
|||||||
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';
|
import TaskListSkeleton from './components/TaskListSkeleton';
|
||||||
|
import ConvertToSubtaskDrawer from '@/components/task-list-common/convert-to-subtask-drawer/convert-to-subtask-drawer';
|
||||||
|
|
||||||
// Hooks and utilities
|
// Hooks and utilities
|
||||||
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
|
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
|
||||||
@@ -766,6 +767,9 @@ const TaskListV2Section: React.FC = () => {
|
|||||||
|
|
||||||
{/* Custom Column Modal */}
|
{/* Custom Column Modal */}
|
||||||
{createPortal(<CustomColumnModal />, document.body, 'custom-column-modal')}
|
{createPortal(<CustomColumnModal />, document.body, 'custom-column-modal')}
|
||||||
|
|
||||||
|
{/* Convert To Subtask Drawer */}
|
||||||
|
{createPortal(<ConvertToSubtaskDrawer />, document.body, 'convert-to-subtask-drawer')}
|
||||||
</div>
|
</div>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import {
|
|||||||
toggleTaskExpansion,
|
toggleTaskExpansion,
|
||||||
updateTaskAssignees,
|
updateTaskAssignees,
|
||||||
} from '@/features/task-management/task-management.slice';
|
} from '@/features/task-management/task-management.slice';
|
||||||
import { deselectAll } from '@/features/projects/bulkActions/bulkActionSlice';
|
import { deselectAll, selectTasks } from '@/features/projects/bulkActions/bulkActionSlice';
|
||||||
import { setConvertToSubtaskDrawerOpen } from '@/features/task-drawer/task-drawer.slice';
|
import { setConvertToSubtaskDrawerOpen } from '@/features/tasks/tasks.slice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||||
import {
|
import {
|
||||||
@@ -412,7 +412,45 @@ const TaskContextMenu: React.FC<TaskContextMenuProps> = ({
|
|||||||
key: 'convertToSubTask',
|
key: 'convertToSubTask',
|
||||||
label: (
|
label: (
|
||||||
<button
|
<button
|
||||||
onClick={() => dispatch(setConvertToSubtaskDrawerOpen(true))}
|
onClick={() => {
|
||||||
|
// Convert task to the format expected by bulkActionSlice
|
||||||
|
const projectTask = {
|
||||||
|
id: task.id,
|
||||||
|
name: task.title || task.name || '',
|
||||||
|
task_key: task.task_key,
|
||||||
|
status: task.status,
|
||||||
|
status_id: task.status,
|
||||||
|
priority: task.priority,
|
||||||
|
phase_id: task.phase,
|
||||||
|
phase_name: task.phase,
|
||||||
|
description: task.description,
|
||||||
|
start_date: task.startDate,
|
||||||
|
end_date: task.dueDate,
|
||||||
|
total_hours: task.timeTracking?.estimated || 0,
|
||||||
|
total_minutes: task.timeTracking?.logged || 0,
|
||||||
|
progress: task.progress,
|
||||||
|
sub_tasks_count: task.sub_tasks_count || 0,
|
||||||
|
assignees: task.assignees?.map((assigneeId: string) => ({
|
||||||
|
id: assigneeId,
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
avatar_url: '',
|
||||||
|
team_member_id: assigneeId,
|
||||||
|
project_member_id: assigneeId,
|
||||||
|
})) || [],
|
||||||
|
labels: task.labels || [],
|
||||||
|
manual_progress: false,
|
||||||
|
created_at: task.createdAt,
|
||||||
|
updated_at: task.updatedAt,
|
||||||
|
sort_order: task.order,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Select the task in bulk action reducer
|
||||||
|
dispatch(selectTasks([projectTask]));
|
||||||
|
|
||||||
|
// Open the drawer
|
||||||
|
dispatch(setConvertToSubtaskDrawerOpen(true));
|
||||||
|
}}
|
||||||
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 w-full text-left"
|
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 w-full text-left"
|
||||||
>
|
>
|
||||||
<DoubleRightOutlined className="text-gray-500 dark:text-gray-400" />
|
<DoubleRightOutlined className="text-gray-500 dark:text-gray-400" />
|
||||||
|
|||||||
Reference in New Issue
Block a user