diff --git a/frontend/src/components/UploadToast.tsx b/frontend/src/components/UploadToast.tsx index 1c200d29..673f0f5b 100644 --- a/frontend/src/components/UploadToast.tsx +++ b/frontend/src/components/UploadToast.tsx @@ -4,7 +4,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { selectUploadTasks, - removeUploadTask, + dismissUploadTask, clearCompletedTasks, } from '../upload/uploadSlice'; import ChevronDown from '../assets/chevron-down.svg'; @@ -30,8 +30,6 @@ export default function UploadToast() { const dispatch = useDispatch(); const uploadTasks = useSelector(selectUploadTasks); - if (uploadTasks.length === 0) return null; - const getStatusHeading = (status: string) => { switch (status) { case 'preparing': @@ -51,63 +49,64 @@ export default function UploadToast() { return (
- {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; + {uploadTasks + .filter((task) => !task.dismissed) + .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.status === 'failed' + ? 'bg-[#FBFBFB] dark:bg-[#26272E]' + : 'bg-[#FBFBFB] dark:bg-[#26272E]' + }`} + > +
+
+

+ {getStatusHeading(task.status)} +

+
+ - )} -
-
- -
-
-
-

- {task.fileName} -

- -
- {shouldShowProgress && ( - - - - - )} - - {task.status === 'completed' && ( - - )} - - {task.status === 'failed' && ( - - )} -
+
- {task.status === 'failed' && task.errorMessage && ( - - {task.errorMessage} - - )} +
+
+
+

+ {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/uploadSlice.ts b/frontend/src/upload/uploadSlice.ts index 5d18ebb2..eb9ef13c 100644 --- a/frontend/src/upload/uploadSlice.ts +++ b/frontend/src/upload/uploadSlice.ts @@ -24,6 +24,7 @@ export interface UploadTask { status: UploadTaskStatus; taskId?: string; errorMessage?: string; + dismissed?: boolean; } interface UploadState { @@ -83,10 +84,30 @@ export const uploadSlice = createSlice({ const index = state.tasks.findIndex( (task) => task.id === action.payload.id, ); + if (index !== -1) { + const updates = action.payload.updates; + + // When task completes or fails, set dismissed to false to notify user + if (updates.status === 'completed' || updates.status === 'failed') { + state.tasks[index] = { + ...state.tasks[index], + ...updates, + dismissed: false, + }; + } else { + state.tasks[index] = { + ...state.tasks[index], + ...updates, + }; + } + } + }, + dismissUploadTask: (state, action: PayloadAction) => { + const index = state.tasks.findIndex((task) => task.id === action.payload); if (index !== -1) { state.tasks[index] = { ...state.tasks[index], - ...action.payload.updates, + dismissed: true, }; } }, @@ -111,6 +132,7 @@ export const { clearAttachments, addUploadTask, updateUploadTask, + dismissUploadTask, removeUploadTask, clearCompletedTasks, } = uploadSlice.actions;