import { Button, Collapse, CollapseProps, Flex, Skeleton, Tooltip, Typography, Upload } from 'antd'; import React, { useEffect, useState, useRef } from 'react'; import { ReloadOutlined } from '@ant-design/icons'; import DescriptionEditor from './description-editor'; import SubTaskTable from './subtask-table'; import DependenciesTable from './dependencies-table'; import { useAppSelector } from '@/hooks/useAppSelector'; import TaskDetailsForm from './task-details-form'; import { fetchTask } from '@/features/tasks/tasks.slice'; import { useAppDispatch } from '@/hooks/useAppDispatch'; import { TFunction } from 'i18next'; import { subTasksApiService } from '@/api/tasks/subtasks.api.service'; import { ISubTask } from '@/types/tasks/subTask.types'; import { ITaskDependency } from '@/types/tasks/task-dependency.types'; import { taskDependenciesApiService } from '@/api/tasks/task-dependencies.api.service'; import logger from '@/utils/errorLogger'; import { getBase64 } from '@/utils/file-utils'; import { ITaskAttachment, ITaskAttachmentViewModel, } from '@/types/tasks/task-attachment-view-model'; import taskAttachmentsApiService from '@/api/tasks/task-attachments.api.service'; import AttachmentsGrid from './attachments/attachments-grid'; import TaskComments from './comments/task-comments'; import { ITaskCommentViewModel } from '@/types/tasks/task-comments.types'; import taskCommentsApiService from '@/api/tasks/task-comments.api.service'; interface TaskDrawerInfoTabProps { t: TFunction; } const TaskDrawerInfoTab = ({ t }: TaskDrawerInfoTabProps) => { const dispatch = useAppDispatch(); const { projectId } = useAppSelector(state => state.projectReducer); const { taskFormViewModel, loadingTask, selectedTaskId } = useAppSelector( state => state.taskDrawerReducer ); const [subTasks, setSubTasks] = useState([]); const [loadingSubTasks, setLoadingSubTasks] = useState(false); const [taskDependencies, setTaskDependencies] = useState([]); const [loadingTaskDependencies, setLoadingTaskDependencies] = useState(false); const [processingUpload, setProcessingUpload] = useState(false); const selectedFilesRef = useRef([]); const [taskAttachments, setTaskAttachments] = useState([]); const [loadingTaskAttachments, setLoadingTaskAttachments] = useState(false); const [taskComments, setTaskComments] = useState([]); const [loadingTaskComments, setLoadingTaskComments] = useState(false); const handleFilesSelected = async (files: File[]) => { if (!taskFormViewModel?.task?.id || !projectId) return; if (!processingUpload) { setProcessingUpload(true); try { const filesToUpload = [...files]; selectedFilesRef.current = filesToUpload; // Upload all files and wait for all promises to complete await Promise.all( filesToUpload.map(async file => { const base64 = await getBase64(file); const body: ITaskAttachment = { file: base64 as string, file_name: file.name, task_id: taskFormViewModel?.task?.id || '', project_id: projectId, size: file.size, }; await taskAttachmentsApiService.createTaskAttachment(body); }) ); } finally { setProcessingUpload(false); selectedFilesRef.current = []; // Refetch attachments after all uploads are complete fetchTaskAttachments(); } } }; const fetchTaskData = () => { if (!loadingTask && selectedTaskId && projectId) { dispatch(fetchTask({ taskId: selectedTaskId, projectId })); } }; const panelStyle: React.CSSProperties = { border: 'none', paddingBlock: 0, }; // Define all info items const allInfoItems: CollapseProps['items'] = [ { key: 'details', label: {t('taskInfoTab.details.title')}, children: , style: panelStyle, className: 'custom-task-drawer-info-collapse', }, { key: 'description', label: {t('taskInfoTab.description.title')}, children: ( ), style: panelStyle, className: 'custom-task-drawer-info-collapse', }, { key: 'subTasks', label: {t('taskInfoTab.subTasks.title')}, extra: (