From f48476478ab4403dc987d1da131bf728b77c0081 Mon Sep 17 00:00:00 2001 From: shancds Date: Tue, 15 Jul 2025 16:19:40 +0530 Subject: [PATCH] Implement task deletion functionality in TaskCard component - Added context menu for task deletion with confirmation modal. - Integrated localization for delete task prompts in multiple languages. - Updated TaskCard to handle task deletion logic, including dispatching actions to update the state and emit socket events for task progress. --- .../public/locales/alb/kanban-board.json | 5 + .../public/locales/de/kanban-board.json | 5 + .../public/locales/en/kanban-board.json | 5 + .../public/locales/es/kanban-board.json | 5 + .../public/locales/pt/kanban-board.json | 5 + .../public/locales/zh/kanban-board.json | 6 +- .../EnhancedKanbanBoardNativeDnD/TaskCard.tsx | 111 +++++++++++++++++- 7 files changed, 138 insertions(+), 4 deletions(-) diff --git a/worklenz-frontend/public/locales/alb/kanban-board.json b/worklenz-frontend/public/locales/alb/kanban-board.json index def705aa..71245ac4 100644 --- a/worklenz-frontend/public/locales/alb/kanban-board.json +++ b/worklenz-frontend/public/locales/alb/kanban-board.json @@ -10,6 +10,11 @@ "deleteConfirmationOk": "Po", "deleteConfirmationCancel": "Anulo", + "deleteTaskTitle": "Fshi Detyrën", + "deleteTaskContent": "Jeni i sigurt që doni të fshini këtë detyrë? Kjo veprim nuk mund të zhbëhet.", + "deleteTaskConfirm": "Fshi", + "deleteTaskCancel": "Anulo", + "dueDate": "Data e përfundimit", "cancel": "Anulo", diff --git a/worklenz-frontend/public/locales/de/kanban-board.json b/worklenz-frontend/public/locales/de/kanban-board.json index 70e1f6ca..8fc39559 100644 --- a/worklenz-frontend/public/locales/de/kanban-board.json +++ b/worklenz-frontend/public/locales/de/kanban-board.json @@ -10,6 +10,11 @@ "deleteConfirmationOk": "Ja", "deleteConfirmationCancel": "Abbrechen", + "deleteTaskTitle": "Aufgabe löschen", + "deleteTaskContent": "Sind Sie sicher, dass Sie diese Aufgabe löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.", + "deleteTaskConfirm": "Löschen", + "deleteTaskCancel": "Abbrechen", + "dueDate": "Fälligkeitsdatum", "cancel": "Abbrechen", diff --git a/worklenz-frontend/public/locales/en/kanban-board.json b/worklenz-frontend/public/locales/en/kanban-board.json index e295a6c6..f9cf0bd4 100644 --- a/worklenz-frontend/public/locales/en/kanban-board.json +++ b/worklenz-frontend/public/locales/en/kanban-board.json @@ -10,6 +10,11 @@ "deleteConfirmationOk": "Yes", "deleteConfirmationCancel": "Cancel", + "deleteTaskTitle": "Delete Task", + "deleteTaskContent": "Are you sure you want to delete this task? This action cannot be undone.", + "deleteTaskConfirm": "Delete", + "deleteTaskCancel": "Cancel", + "dueDate": "Due date", "cancel": "Cancel", diff --git a/worklenz-frontend/public/locales/es/kanban-board.json b/worklenz-frontend/public/locales/es/kanban-board.json index 6e8d5975..703afd32 100644 --- a/worklenz-frontend/public/locales/es/kanban-board.json +++ b/worklenz-frontend/public/locales/es/kanban-board.json @@ -10,6 +10,11 @@ "deleteConfirmationOk": "Sí", "deleteConfirmationCancel": "Cancelar", + "deleteTaskTitle": "Eliminar tarea", + "deleteTaskContent": "¿Estás seguro de que deseas eliminar esta tarea? Esta acción no se puede deshacer.", + "deleteTaskConfirm": "Eliminar", + "deleteTaskCancel": "Cancelar", + "dueDate": "Fecha de vencimiento", "cancel": "Cancelar", diff --git a/worklenz-frontend/public/locales/pt/kanban-board.json b/worklenz-frontend/public/locales/pt/kanban-board.json index a2034daa..3ba0626d 100644 --- a/worklenz-frontend/public/locales/pt/kanban-board.json +++ b/worklenz-frontend/public/locales/pt/kanban-board.json @@ -10,6 +10,11 @@ "deleteConfirmationOk": "Sim", "deleteConfirmationCancel": "Cancelar", + "deleteTaskTitle": "Excluir Tarefa", + "deleteTaskContent": "Tem certeza de que deseja excluir esta tarefa? Esta ação não pode ser desfeita.", + "deleteTaskConfirm": "Excluir", + "deleteTaskCancel": "Cancelar", + "dueDate": "Data de vencimento", "cancel": "Cancelar", diff --git a/worklenz-frontend/public/locales/zh/kanban-board.json b/worklenz-frontend/public/locales/zh/kanban-board.json index 7b72c5d5..51f7a171 100644 --- a/worklenz-frontend/public/locales/zh/kanban-board.json +++ b/worklenz-frontend/public/locales/zh/kanban-board.json @@ -15,5 +15,9 @@ "assignToMe": "分配给我", "archive": "归档", "newTaskNamePlaceholder": "写一个任务名称", - "newSubtaskNamePlaceholder": "写一个子任务名称" + "newSubtaskNamePlaceholder": "写一个子任务名称", + "deleteTaskTitle": "删除任务", + "deleteTaskContent": "您确定要删除此任务吗?此操作无法撤销。", + "deleteTaskConfirm": "删除", + "deleteTaskCancel": "取消" } \ No newline at end of file diff --git a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/TaskCard.tsx b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/TaskCard.tsx index bf499a12..daa8793c 100644 --- a/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/TaskCard.tsx +++ b/worklenz-frontend/src/components/enhanced-kanban/EnhancedKanbanBoardNativeDnD/TaskCard.tsx @@ -14,8 +14,11 @@ import { useSocket } from '@/socket/socketContext'; import { SocketEvents } from '@/shared/socket-events'; import { getUserSession } from '@/utils/session-helper'; import { themeWiseColor } from '@/utils/themeWiseColor'; -import { toggleTaskExpansion, fetchBoardSubTasks } from '@/features/enhanced-kanban/enhanced-kanban.slice'; +import { toggleTaskExpansion, fetchBoardSubTasks, deleteTask as deleteKanbanTask, updateEnhancedKanbanSubtask } from '@/features/enhanced-kanban/enhanced-kanban.slice'; import TaskProgressCircle from './TaskProgressCircle'; +import { Button, Modal } from 'antd'; +import { DeleteOutlined } from '@ant-design/icons'; +import { tasksApiService } from '@/api/tasks/tasks.api.service'; // Simple Portal component const Portal: React.FC<{ children: React.ReactNode }> = ({ children }) => { @@ -70,6 +73,9 @@ const TaskCard: React.FC = memo(({ const d = selectedDate || new Date(); return new Date(d.getFullYear(), d.getMonth(), 1); }); + const [contextMenu, setContextMenu] = useState<{ visible: boolean; x: number; y: number }>({ visible: false, x: 0, y: 0 }); + const contextMenuRef = useRef(null); + const [selectedTask, setSelectedTask] = useState(null); useEffect(() => { setSelectedDate(task.end_date ? new Date(task.end_date) : null); @@ -102,6 +108,21 @@ const TaskCard: React.FC = memo(({ } }, [showDatePicker]); + // Hide context menu on click elsewhere + useEffect(() => { + const handleClick = (e: MouseEvent) => { + if (contextMenuRef.current && !contextMenuRef.current.contains(e.target as Node)) { + setContextMenu({ ...contextMenu, visible: false }); + } + }; + if (contextMenu.visible) { + document.addEventListener('mousedown', handleClick); + } + return () => { + document.removeEventListener('mousedown', handleClick); + }; + }, [contextMenu]); + const handleCardClick = useCallback((e: React.MouseEvent, id: string) => { e.stopPropagation(); dispatch(setSelectedTaskId(id)); @@ -178,6 +199,48 @@ const TaskCard: React.FC = memo(({ handleSubTaskExpand(); }, [handleSubTaskExpand]); + // Delete logic (similar to task-drawer-header) + const handleDeleteTask = async (task: IProjectTask | null) => { + if (!task || !task.id) return; + Modal.confirm({ + title: t('deleteTaskTitle'), + content: t('deleteTaskContent'), + okText: t('deleteTaskConfirm'), + okType: 'danger', + cancelText: t('deleteTaskCancel'), + centered: true, + onOk: async () => { + if (!task.id) return; + const res = await tasksApiService.deleteTask(task.id); + if (res.done) { + dispatch(setSelectedTaskId(null)); + if (task.is_sub_task) { + dispatch(updateEnhancedKanbanSubtask({ + sectionId: '', + subtask: { id: task.id , parent_task_id: task.parent_task_id || '', manual_progress: false }, + mode: 'delete', + })); + } else { + dispatch(deleteKanbanTask(task.id)); + } + dispatch(setShowTaskDrawer(false)); + if (task.parent_task_id) { + socket?.emit( + SocketEvents.GET_TASK_PROGRESS.toString(), + task.parent_task_id + ); + } + } + setContextMenu({ visible: false, x: 0, y: 0 }); + setSelectedTask(null); + }, + onCancel: () => { + setContextMenu({ visible: false, x: 0, y: 0 }); + setSelectedTask(null); + }, + }); + }; + // Calendar rendering helpers const year = calendarMonth.getFullYear(); const month = calendarMonth.getMonth(); @@ -202,7 +265,37 @@ const TaskCard: React.FC = memo(({ return ( <> -
+ {/* Context menu for delete */} + {contextMenu.visible && ( +
+ +
+ )} +
{/* Progress circle at top right */}
@@ -221,6 +314,11 @@ const TaskCard: React.FC = memo(({ onDrop={e => onTaskDrop(e, groupId, idx)} onDragEnd={onDragEnd} // <-- add this onClick={e => handleCardClick(e, task.id!)} + onContextMenu={e => { + e.preventDefault(); + setContextMenu({ visible: true, x: e.clientX, y: e.clientY }); + setSelectedTask(task); + }} >
@@ -447,7 +545,14 @@ const TaskCard: React.FC = memo(({ {!task.sub_tasks_loading && Array.isArray(task.sub_tasks) && task.sub_tasks.length > 0 && (
    {task.sub_tasks.map(sub => ( -
  • handleCardClick(e, sub.id!)} className="flex items-center gap-2 px-2 py-1 rounded hover:bg-gray-50 dark:hover:bg-gray-800"> +
  • handleCardClick(e, sub.id!)} + className="flex items-center gap-2 px-2 py-1 rounded hover:bg-gray-50 dark:hover:bg-gray-800" + onContextMenu={e => { + e.preventDefault(); + setContextMenu({ visible: true, x: e.clientX, y: e.clientY }); + setSelectedTask(sub); + }}> {sub.priority_color || sub.priority_color_dark ? (