diff --git a/frontend/app/upload/[provider]/page.tsx b/frontend/app/upload/[provider]/page.tsx index c964fef9c..0c2de0e70 100644 --- a/frontend/app/upload/[provider]/page.tsx +++ b/frontend/app/upload/[provider]/page.tsx @@ -29,11 +29,16 @@ import { } from "@/components/ui/tooltip"; import { useTask } from "@/contexts/task-context"; import { useSessionIngestSettings } from "@/hooks/useSessionIngestSettings"; -import { duplicateCheck } from "@/lib/upload-utils"; // Connectors that sync entire buckets/repositories without a file picker const DIRECT_SYNC_PROVIDERS = ["ibm_cos", "aws_s3"]; +interface ConnectorDuplicateCheckResponse { + duplicate_names?: string[]; + duplicate_count?: number; + non_duplicate_files?: CloudFile[]; +} + // --------------------------------------------------------------------------- // Shared bucket view — used by both IBM COS and S3 // --------------------------------------------------------------------------- @@ -419,6 +424,7 @@ export default function UploadProviderPage() { allFiles: CloudFile[]; nonDuplicateFiles: CloudFile[]; duplicateNames: string[]; + duplicateCount: number; } | null>(null); const isOverwriteConfirmedRef = useRef(false); @@ -514,24 +520,27 @@ export default function UploadProviderPage() { throw new Error(`Duplicate check failed: ${checkResponse.statusText}`); } - const checkData = await checkResponse.json(); + const checkData = + (await checkResponse.json()) as ConnectorDuplicateCheckResponse; const duplicateNames = checkData.duplicate_names || []; - const totalFiles = checkData.total_files || 0; + const duplicateCount = + typeof checkData.duplicate_count === "number" + ? checkData.duplicate_count + : duplicateNames.length; - if (duplicateNames.length === 0) { + if (duplicateCount === 0) { submitSync(connector, selectedFiles, false); return; } - // If all files are duplicates, we set nonDuplicateFiles to empty so it toasts "Nothing was synced" on skip - const isAllDuplicate = duplicateNames.length === totalFiles; - const nonDuplicateFiles = isAllDuplicate ? [] : selectedFiles; + const nonDuplicateFiles = checkData.non_duplicate_files || []; setPendingSync({ connector, allFiles: selectedFiles, nonDuplicateFiles, duplicateNames, + duplicateCount, }); setDuplicateDialogOpen(true); } catch (err) { @@ -559,12 +568,12 @@ export default function UploadProviderPage() { // "skip duplicates" branch. isOverwriteConfirmedRef.current = false; } else { - const { connector, nonDuplicateFiles, duplicateNames } = pendingSync; + const { connector, nonDuplicateFiles, duplicateCount } = pendingSync; if (nonDuplicateFiles.length > 0) { submitSync(connector, nonDuplicateFiles, false); } else { toast.info( - `All ${duplicateNames.length} selected file(s) already exist. Nothing was synced.`, + `All ${duplicateCount} selected file(s) already exist. Nothing was synced.`, ); } } @@ -788,6 +797,7 @@ export default function UploadProviderPage() { onOverwrite={handleOverwriteDuplicates} isLoading={isIngesting} duplicateNames={pendingSync?.duplicateNames} + duplicateCount={pendingSync?.duplicateCount} /> ); diff --git a/frontend/components/duplicate-handling-dialog.tsx b/frontend/components/duplicate-handling-dialog.tsx index 76b5a84c9..7f5e747db 100644 --- a/frontend/components/duplicate-handling-dialog.tsx +++ b/frontend/components/duplicate-handling-dialog.tsx @@ -41,9 +41,12 @@ export const DuplicateHandlingDialog: React.FC< }; const namesProvided = duplicateNames && duplicateNames.length > 0; - const effectiveCount = namesProvided - ? duplicateNames!.length - : duplicateCount; + const effectiveCount = + typeof duplicateCount === "number" + ? duplicateCount + : namesProvided + ? duplicateNames!.length + : undefined; const description = typeof effectiveCount === "number" @@ -79,8 +82,8 @@ export const DuplicateHandlingDialog: React.FC< {namesProvided && (