+ {uploadTasks.map((task) => {
+ const shouldShowProgress = [
+ 'preparing',
+ 'uploading',
+ 'training',
+ ].includes(task.status);
+ const rawProgress = Math.min(Math.max(task.progress ?? 0, 0), 100);
+ const formattedProgress = Math.round(rawProgress);
+ const progressOffset = PROGRESS_CIRCUMFERENCE * (1 - rawProgress / 100);
+ const isCollapsed = collapsedTasks[task.id] ?? false;
+
+ return (
+
+
+
+
+ {getStatusHeading(task.status)}
+
+
+
+ {(task.status === 'completed' ||
+ task.status === 'failed') && (
+
+ )}
+
+
+
+
+
+
+
+ {task.fileName}
+
+
+
+ {shouldShowProgress && (
+
+ )}
+
+ {task.status === 'completed' && (
+

+ )}
+
+ {task.status === 'failed' && (
+

+ )}
+
+
+
+ {task.status === 'failed' && task.errorMessage && (
+
+ {task.errorMessage}
+
+ )}
+
+
+
+
+ );
+ })}
+
+ {uploadTasks.some(
+ (task) => task.status === 'completed' || task.status === 'failed',
+ ) && (
+
+ )}
+
+ );
+}
diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx
index 19170c2d..72e6a7f2 100644
--- a/frontend/src/upload/Upload.tsx
+++ b/frontend/src/upload/Upload.tsx
@@ -1,4 +1,5 @@
-import { useCallback, useEffect, useRef, useState } from 'react';
+import { useCallback, useState } from 'react';
+import { nanoid } from '@reduxjs/toolkit';
import { useDropzone } from 'react-dropzone';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
@@ -24,6 +25,8 @@ import {
getIngestorSchema,
IngestorOption,
} from '../upload/types/ingestor';
+import { addUploadTask, updateUploadTask } from './uploadSlice';
+
import { FormField, IngestorConfig, IngestorType } from './types/ingestor';
import { FilePicker } from '../components/FilePicker';
@@ -259,15 +262,8 @@ function Upload({
config: {},
}));
- const [progress, setProgress] = useState<{
- type: 'UPLOAD' | 'TRAINING';
- percentage: number;
- taskId?: string;
- failed?: boolean;
- }>();
-
const { t } = useTranslation();
- const setTimeoutRef = useRef