Skip to content

Conversation

@rahul1841
Copy link
Contributor

@rahul1841 rahul1841 commented Sep 26, 2025

Description

  • make global state to show the upload of the kb files/folder
  • refactor the ui of kb

Testing

  • tested locally

Summary by CodeRabbit

  • New Features

    • Redesigned upload UI with ADD FILES / UPLOAD LARGE FOLDER actions, conditional drag area, per-file queue with icons, previews, size or unsupported-format indicators, per-item remove, Clear All, and a bottom UPLOAD ITEMS action.
    • New persistent upload progress widget (progress bar, cancel with confirmation, expandable All/Uploaded/Failed list).
  • Refactor

    • Centralized, app-wide upload progress management replacing local upload state.
  • Style

    • Optional headers in upload skeleton for cleaner layouts.
  • Chores

    • Uploads now support cancellation; toasts show for 10s; batch size and file limits reduced.

make global state for uploading files in kb and refactor ui for kb upload
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Walkthrough

Adds a centralized UploadProgressContext with provider and widget, refactors route-level upload flows to use the context, revamps the file upload UI (queue, file-type icons, folder support, preview), makes skeleton headers optional, sets toast duration to 10s, and wires the provider/widget into the app main.

Changes

Cohort / File(s) Summary
Upload Progress System
frontend/src/contexts/UploadProgressContext.tsx, frontend/src/components/UploadProgressWidget.tsx, frontend/src/main.tsx, frontend/src/routes/_authenticated/knowledgeManagement.tsx
Adds UploadProgressContext, types and hook; implements UploadProgressProvider with start/update/finish/cancel/get APIs; new UploadProgressWidget renders upload progress, tabs, and cancel flow; main app wrapped with provider; knowledgeManagement refactored to use context APIs and derived upload state; batch limits lowered.
File Upload UI Revamp
frontend/src/components/ClFileUpload.tsx, frontend/src/components/FileUploadSkeleton.tsx
Replaces drag-area UI with header buttons (ADD FILES / UPLOAD LARGE FOLDER), introduces hidden file/folder inputs and consolidated handlers, adds per-file queue UI with getFileIcon/isValidFile integration, unsupported-format indicators, per-item remove and Clear All, scrollable list and upload action; SelectedFile gains optional preview. FileUploadSkeleton receives optional showHeaders?: boolean to conditionally render headers.
File utils / API
frontend/src/utils/fileUtils.ts
uploadFileBatch signature now accepts optional abortSignal and forwards it to fetch for request cancellation.
File-type icons & detection
frontend/src/lib/common.tsx
Adds getFileType(file) and getFileIcon(file) to determine file types by MIME/extension and return type-specific icons; imports related icons and mappings.
Toast tweak
frontend/src/components/ui/toast.tsx
Sets ToastPrimitives.Root duration to 10000 ms (10s).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant ClFileUpload
  participant KnowledgeRoute as KnowledgeRoute/UI
  participant UploadCtx as UploadProgressProvider
  participant Server
  participant Widget as UploadProgressWidget

  User->>ClFileUpload: select files / add folder / drag-drop
  ClFileUpload->>KnowledgeRoute: submit files + metadata
  KnowledgeRoute->>UploadCtx: startUpload(files, meta) → { uploadId, AbortController }
  UploadCtx-->>Widget: publish currentUpload (isUploading=true)

  loop per batch (<= configured limits)
    KnowledgeRoute->>Server: uploadFileBatch(batchFiles, collectionId, parentId, abortSignal)
    Server-->>KnowledgeRoute: batch response (per-file results)
    KnowledgeRoute->>UploadCtx: updateFileStatus(fileId, status[, error])
    KnowledgeRoute->>UploadCtx: updateProgress(batchIndex, fileIndex, totals)
    UploadCtx-->>Widget: progress & status updates
  end

  alt user cancels
    Widget->>UploadCtx: cancelUpload(uploadId)
    UploadCtx->>UploadCtx: abort controller -> reset currentUpload
  else completed
    KnowledgeRoute->>UploadCtx: finishUpload(uploadId)
  end
  UploadCtx-->>Widget: currentUpload=null (widget hides)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • zereraz
  • shivamashtikar
  • junaid-shirur
  • devesh-juspay

Poem

I hop with files in fluffy rows, 🐇
Buttons gleam where the folder grows.
Icons sparkle, queues align,
I nibble bugs and stitch each sign.
Uploads hum — a carrot-coded line. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title follows the conventional commit style and clearly highlights the main change—introducing a global upload state for the knowledge base—without extraneous details, making it concise and directly related to the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/AddglobalKbStateAndUi

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d18444 and 753cf41.

📒 Files selected for processing (2)
  • frontend/src/components/ClFileUpload.tsx (2 hunks)
  • frontend/src/lib/common.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/components/ClFileUpload.tsx (2)
server/shared/fileUtils.ts (1)
  • isValidFile (10-46)
frontend/src/lib/common.tsx (1)
  • getFileIcon (190-209)
frontend/src/lib/common.tsx (2)
server/shared/types.ts (2)
  • MIME_TYPE_MAPPINGS (168-191)
  • EXTENSION_MAPPINGS (194-201)
frontend/src/components/ChatBox.tsx (1)
  • getFileIcon (90-142)

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @rahul1841, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience for file and folder uploads within the Knowledge Base feature. By centralizing upload state management, users now benefit from a consistent, real-time progress display through a new dedicated widget. The file upload interface has also been modernized with a clearer layout, dynamic file type icons, and improved feedback mechanisms, making the upload process more intuitive and informative.

Highlights

  • Global Upload State Management: Introduced a new React Context (UploadProgressContext) to manage the state of file and folder uploads globally across the application, replacing previous local state and local storage mechanisms.
  • Real-time Upload Progress Widget: Added a new UploadProgressWidget component that provides a persistent, real-time view of ongoing uploads, including overall progress, individual file statuses (pending, uploading, uploaded, failed), and the ability to cancel uploads.
  • Refactored File Upload UI: The ClFileUpload component has been significantly refactored to improve user experience, featuring separate buttons for adding files and uploading folders, a dedicated 'Upload Queue' section, and dynamic file type icons.
  • Enhanced File Type Recognition: Implemented helper functions to determine file types based on MIME types and extensions, and integrated new SVG icons for various document, image, PDF, spreadsheet, and presentation file formats.
  • Batch Upload Configuration Adjustment: The createBatches function now uses smaller batch sizes (5MB payload, 5 files per batch) to potentially improve stability and performance for uploads.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a major refactoring of the file upload functionality in the knowledge management section. It replaces a localStorage-based progress tracking system with a much more robust and user-friendly global state managed by a React Context. This enables a new upload progress widget that provides detailed, real-time feedback. The file upload UI has also been significantly improved. The changes are well-structured and greatly enhance the user experience. I've included a few suggestions to improve responsiveness, maintainability, and performance.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
frontend/src/components/ui/toast.tsx (1)

63-71: Set a default duration without shadowing consumer overrides.

Right now duration={10000} is overridden by {...props}. Make the 10s value a default while still allowing callers to override it.

Apply this diff:

->({ className, variant, children, ...props }, ref) => {
+>({ className, variant, children, duration = 10000, ...props }, ref) => {
@@
-    className={cn(toastVariants({ variant }), className)}
-    duration={10000}
+    className={cn(toastVariants({ variant }), className)}
+    duration={duration}
     {...props}
frontend/src/components/ClFileUpload.tsx (2)

16-35: Deduplicate file type detection by reusing the shared utility.

getFileType is already implemented in frontend/src/utils/fileUtils.ts. Duplicating this logic here risks drift.

Apply this diff to remove the local helper:

-// Helper function to determine FileType from a file
-const getFileType = (file: File): FileType => {
-  // First check by MIME type
-  for (const [fileType, mimeTypes] of Object.entries(MIME_TYPE_MAPPINGS)) {
-    if ((mimeTypes as readonly string[]).includes(file.type)) {
-      return fileType as FileType
-    }
-  }
-
-  // Fallback to extension checking
-  const fileName = file.name.toLowerCase()
-  for (const [fileType, extensions] of Object.entries(EXTENSION_MAPPINGS)) {
-    if ((extensions as readonly string[]).some(ext => fileName.endsWith(ext))) {
-      return fileType as FileType
-    }
-  }
-
-  // Default fallback
-  return FileType.FILE
-}

And add this import near the top of the file:

import { getFileType } from "@/utils/fileUtils"

Based on relevant code snippets.


224-231: Add a non-WebKit fallback for folder drops.

Some browsers don’t support webkitGetAsEntry. If filePromises is empty, fall back to dataTransfer.files so plain file drops still work.

Apply this diff:

-      Promise.all(filePromises)
-        .then((filesArrays) => {
-          onFilesSelect(filesArrays.flat())
-        })
-        .catch((err) => {
-          console.error("Error traversing file tree:", err)
-        })
+      if (filePromises.length > 0) {
+        Promise.all(filePromises)
+          .then((filesArrays) => {
+            onFilesSelect(filesArrays.flat())
+          })
+          .catch((err) => {
+            console.error("Error traversing file tree:", err)
+          })
+      } else {
+        // Fallback: treat as regular files drop
+        const droppedFiles = Array.from(e.dataTransfer.files).filter(
+          (file) => !file.name.startsWith("."),
+        )
+        if (droppedFiles.length) onFilesSelect(droppedFiles)
+      }
frontend/src/components/UploadProgressWidget.tsx (2)

19-21: Guard and clamp progress percentage.

Avoid NaN/Infinity and overshoot; clamp to [0, 100] with a zero-total guard.

Apply this diff:

-  const progressPercentage = Math.round((batchProgress.current / batchProgress.total) * 100)
+  const progressPercentage = batchProgress.total > 0
+    ? Math.min(100, Math.max(0, Math.round((batchProgress.current / batchProgress.total) * 100)))
+    : 0

70-75: Optional: Add accessible progress semantics.

Expose progress to assistive tech via aria attributes.

Example:

<div
  className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2 mb-3"
  role="progressbar"
  aria-valuemin={0}
  aria-valuemax={100}
  aria-valuenow={progressPercentage}
  aria-label="Upload progress"
>
  <div
    className="bg-gray-500 h-2 rounded-full transition-all duration-300 ease-out"
    style={{ width: `${progressPercentage}%` }}
  />
</div>
frontend/src/routes/_authenticated/knowledgeManagement.tsx (1)

734-760: Duplicate pattern - consider extracting shared logic.

The file status update logic is duplicated between new collection uploads (lines 553-576) and existing collection uploads (lines 734-760). Consider extracting this into a shared helper function to reduce code duplication.

+const updateBatchFileStatuses = (uploadId: string, batchFiles: File[], uploadResult: any, updateFileStatus: Function) => {
+  if (uploadResult.results) {
+    uploadResult.results.forEach((result: any, index: number) => {
+      const file = batchFiles[index]
+      if (result.success) {
+        updateFileStatus(uploadId, file.name, 'uploaded')
+      } else {
+        updateFileStatus(uploadId, file.name, 'failed', result.error || 'Upload failed')
+      }
+    })
+  } else {
+    // Fallback: mark all as uploaded if no individual results available
+    batchFiles.forEach(file => {
+      updateFileStatus(uploadId, file.name, 'uploaded')
+    })
+  }
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 101a49c and 8248ae4.

⛔ Files ignored due to path filters (6)
  • frontend/src/assets/collectionIcons/document.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/image.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/pdf.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/ppt.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/spreadsheet.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/text.svg is excluded by !**/*.svg
📒 Files selected for processing (7)
  • frontend/src/components/ClFileUpload.tsx (2 hunks)
  • frontend/src/components/FileUploadSkeleton.tsx (1 hunks)
  • frontend/src/components/UploadProgressWidget.tsx (1 hunks)
  • frontend/src/components/ui/toast.tsx (1 hunks)
  • frontend/src/contexts/UploadProgressContext.tsx (1 hunks)
  • frontend/src/main.tsx (2 hunks)
  • frontend/src/routes/_authenticated/knowledgeManagement.tsx (14 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
frontend/src/main.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • UploadProgressProvider (52-147)
frontend/src/components/UploadProgressWidget.tsx (1)
  • UploadProgressWidget (9-205)
frontend/src/components/UploadProgressWidget.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • useUploadProgress (40-46)
frontend/src/components/ui/confirmModal.tsx (1)
  • ConfirmModal (23-59)
frontend/src/routes/_authenticated/knowledgeManagement.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • useUploadProgress (40-46)
frontend/src/utils/fileUtils.ts (1)
  • uploadFileBatch (205-252)
frontend/src/components/ClFileUpload.tsx (3)
frontend/src/utils/fileUtils.ts (1)
  • getFileType (9-31)
server/shared/types.ts (2)
  • MIME_TYPE_MAPPINGS (168-191)
  • EXTENSION_MAPPINGS (194-201)
server/shared/fileUtils.ts (1)
  • isValidFile (10-46)
🔇 Additional comments (29)
frontend/src/components/ClFileUpload.tsx (1)

5-7: Verify shared imports across frontend/server boundaries build in all environments.

Directly importing from ../../../server/shared/* in a frontend component relies on tooling allowing cross‑package/dir imports (Vite/TS config, aliasing). Please confirm CI/prod builds resolve these paths and don’t pull server-only code.

If helpful, I can scan the repo for tsconfig/vite aliases and produce a quick check script to validate resolution.

frontend/src/main.tsx (1)

32-36: LGTM: Properly wires the provider and widget at the app root.

Provider placement and widget rendering look correct alongside RouterProvider and Toaster.

frontend/src/components/FileUploadSkeleton.tsx (1)

18-20: Fix potential crash when processedFiles exceeds totalFiles.

skeletonCount can become negative, causing Array.from({ length: skeletonCount }) to throw. Clamp to 0–4.

Apply this diff:

-  const skeletonCount = Math.min(4, totalFiles - processedFiles)
+  const skeletonCount = Math.max(0, Math.min(4, totalFiles - processedFiles))

Likely an incorrect or invalid review comment.

frontend/src/routes/_authenticated/knowledgeManagement.tsx (13)

60-60: LGTM! Clean integration with global upload context.

The import statement properly adds the useUploadProgress hook from the new global upload context.


382-390: Clean migration from localStorage to global context.

The code correctly replaces localStorage-based upload state management with the global upload context. The derived state variables are properly extracted from currentUpload.


553-576: Excellent per-file status tracking implementation.

The code properly tracks individual file upload statuses through the global context, providing detailed feedback for each file in the batch. The fallback handling when individual results aren't available is also well implemented.


584-586: Progress updates correctly implemented.

The progress calculation and update through the global context is accurate and provides real-time feedback to users.


675-675: Proper cleanup with finishUpload.

The upload cleanup is correctly handled by calling finishUpload with the upload ID, maintaining consistency with the global state management.


717-720: Consistent pattern for existing collection uploads.

The implementation follows the same pattern as new collection uploads, correctly differentiating between new and existing collection scenarios through the isNewCollection parameter.


861-861: Consistent cleanup pattern.

The finishUpload call maintains consistency with the new collection upload flow.


1581-1606: Proper skeleton display for new collection uploads.

The conditional rendering correctly shows the upload skeleton only for new collection uploads, with appropriate progress indicators and headers.


1836-1848: Proper skeleton display for existing collection uploads.

The conditional rendering correctly shows the upload skeleton for existing collection uploads, properly filtering by collection ID and hiding headers for consistency with the existing UI.


1966-1967: Modal positioning updated appropriately.

The modal positioning changes appear to be minor UI adjustments that align with the new upload flow design.


1991-1998: Label text updated for consistency.

The label text changes from "Collection title" to "Collection name" provide better consistency throughout the UI.


534-537: createBatches enforces reduced limits
It uses MAX_PAYLOAD_SIZE = 5 MiB and MAX_FILES_PER_BATCH = 5, so batches adhere to the updated constraints.


2092-2093: Verify reduced batch limits against real-world uploads.
Reducing MAX_PAYLOAD_SIZE from 45 MB to 5 MB and MAX_FILES_PER_BATCH from 50 to 5 may degrade performance for users uploading many or large files—load-test with representative payloads and adjust limits based on observed throughput and timeout behavior.

frontend/src/contexts/UploadProgressContext.tsx (13)

1-1: LGTM! Appropriate React and TypeScript imports.

The imports correctly include all necessary React hooks and types for context implementation.


3-8: Well-structured interface for batch progress tracking.

The UploadBatchProgress interface provides comprehensive tracking for batch upload operations with all necessary fields.


10-16: Comprehensive file status tracking interface.

The UploadFileStatus interface properly tracks individual file upload states with appropriate status types and optional error handling.


18-26: Well-designed upload task structure.

The UploadTask interface effectively combines all upload-related information, supporting both new and existing collection scenarios through the isNewCollection and targetCollectionId fields.


28-36: Comprehensive context interface design.

The UploadProgressContextType interface provides all necessary methods for managing upload lifecycle, with appropriate parameter types and return values.


40-46: Standard React context hook pattern.

The useUploadProgress hook follows React best practices with proper error handling for usage outside the provider.


55-82: Robust upload initiation implementation.

The startUpload function properly generates unique IDs, initializes file statuses, and creates the upload task with all required fields. The use of useCallback is appropriate for performance optimization.


84-97: Efficient progress update implementation.

The updateProgress function correctly updates batch progress while preserving other upload task properties. The null check ensures safe updates.


99-112: Proper file status update with immutable patterns.

The updateFileStatus function correctly updates individual file statuses using immutable update patterns, ensuring React re-renders properly.


114-119: Clean upload completion handling.

The finishUpload function properly clears the current upload, maintaining clean state management.


121-126: Consistent cancel upload implementation.

The cancelUpload function follows the same pattern as finishUpload, providing consistent cleanup behavior.


128-130: Simple and effective upload retrieval.

The getUploadProgress function provides a straightforward way to retrieve upload progress by ID.


132-147: Complete provider implementation.

The UploadProgressProvider component properly provides all context methods and maintains the upload state. The value object correctly exposes all required functionality.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (12)
frontend/src/components/ui/toast.tsx (1)

70-70: Consider making the toast duration configurable.

Hardcoding the toast duration to 10 seconds may not be appropriate for all types of toasts. Some messages (errors, warnings) might need longer display times while others (success confirmations) could be shorter.

Consider accepting the duration as a prop with 10000ms as the default:

-duration={10000}
+duration={props.duration ?? 10000}
frontend/src/components/FileUploadSkeleton.tsx (1)

35-35: Simplify the conditional class application.

The empty string in the ternary operator is unnecessary.

-<div className={showHeaders ? "mt-2" : ""}>
+<div className={showHeaders ? "mt-2" : undefined}>
frontend/src/components/UploadProgressWidget.tsx (1)

159-168: Consider using icon components instead of inline SVGs.

The inline SVGs for status indicators could be replaced with icon components from lucide-react for better consistency and maintainability. Based on learnings, the project already uses lucide-react v0.475.0.

-{file.status === 'uploaded' && (
-  <svg width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M1 5L5 9L13 1" stroke="#10B981" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
-  </svg>
-)}
-{file.status === 'failed' && (
-  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <path d="M3 3L11 11M11 3L3 11" stroke="#EF4444" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
-  </svg>
-)}
+{file.status === 'uploaded' && (
+  <Check className="h-3.5 w-3.5 text-green-500" />
+)}
+{file.status === 'failed' && (
+  <X className="h-3.5 w-3.5 text-red-500" />
+)}

Don't forget to import the Check icon at the top:

-import { X, ChevronUp, ChevronDown } from 'lucide-react'
+import { X, ChevronUp, ChevronDown, Check } from 'lucide-react'
frontend/src/routes/_authenticated/knowledgeManagement.tsx (3)

568-583: Consider extracting the file status update logic into a helper function.

The logic for updating file statuses based on upload results is duplicated between handleUpload and handleAddFilesToCollection. This could lead to inconsistencies if one location is updated but not the other.

Extract into a helper function:

const updateFileStatuses = (
  uploadId: string,
  batchFiles: File[],
  uploadResult: any,
  updateFileStatus: (uploadId: string, fileName: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => void
) => {
  if (uploadResult.results) {
    uploadResult.results.forEach((result: any, index: number) => {
      const file = batchFiles[index]
      if (result.success) {
        updateFileStatus(uploadId, file.name, 'uploaded')
      } else {
        updateFileStatus(uploadId, file.name, 'failed', result.error || 'Upload failed')
      }
    })
  } else {
    // Fallback: mark all as uploaded if no individual results available
    batchFiles.forEach(file => {
      updateFileStatus(uploadId, file.name, 'uploaded')
    })
  }
}

Then use it in both places:

-// Update individual file statuses based on results
-if (uploadResult.results) {
-  uploadResult.results.forEach((result: any, index: number) => {
-    const file = batchFiles[index]
-    if (result.success) {
-      updateFileStatus(uploadId, file.name, 'uploaded')
-    } else {
-      updateFileStatus(uploadId, file.name, 'failed', result.error || 'Upload failed')
-    }
-  })
-} else {
-  // Fallback: mark all as uploaded if no individual results available
-  batchFiles.forEach(file => {
-    updateFileStatus(uploadId, file.name, 'uploaded')
-  })
-}
+updateFileStatuses(uploadId, batchFiles, uploadResult, updateFileStatus)

1989-1990: Inconsistent modal positioning between different modals.

The upload modal is positioned differently (pt-24 pr-24) compared to other modals in the component which are centered. This could create an inconsistent user experience.

Consider using consistent positioning for all modals:

-<div className="fixed inset-0 bg-black bg-opacity-50 flex items-start justify-end pt-24 pr-24 z-50">
+<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">

2123-2124: Document the batch configuration constants.

The batch size limits are important configuration that affects upload behavior. These should be documented for future maintainability.

Add documentation:

 const BATCH_CONFIG = {
+  // Maximum payload size per batch (5MB)
   MAX_PAYLOAD_SIZE: 5 * 1024 * 1024,
+  // Maximum number of files per batch
   MAX_FILES_PER_BATCH: 5,
 }
frontend/src/components/ClFileUpload.tsx (5)

5-7: Avoid duplicating file-type detection logic; import the shared helper.

You already have getFileType in frontend/src/utils/fileUtils.ts. Import it and drop local MIME/extension mappings here to reduce coupling to server/shared and keep one source of truth.

Apply this diff:

-import { FileType, MIME_TYPE_MAPPINGS, EXTENSION_MAPPINGS } from "../../../server/shared/types"
-import { isValidFile } from "../../../server/shared/fileUtils"
+import { FileType } from "../../../server/shared/types"
+import { getFileType } from "@/utils/fileUtils"
+import { isValidFile } from "../../../server/shared/fileUtils"

16-35: Remove in-file getFileType (now imported).

Drops duplicated logic and relies on the centralized helper.

Apply this diff:

-// Helper function to determine FileType from a file
-const getFileType = (file: File): FileType => {
-  // First check by MIME type
-  for (const [fileType, mimeTypes] of Object.entries(MIME_TYPE_MAPPINGS)) {
-    if ((mimeTypes as readonly string[]).includes(file.type)) {
-      return fileType as FileType
-    }
-  }
-
-  // Fallback to extension checking
-  const fileName = file.name.toLowerCase()
-  for (const [fileType, extensions] of Object.entries(EXTENSION_MAPPINGS)) {
-    if ((extensions as readonly string[]).some(ext => fileName.endsWith(ext))) {
-      return fileType as FileType
-    }
-  }
-
-  // Default fallback
-  return FileType.FILE
-}

91-101: Stabilize drag-highlight to prevent flicker (nested dragenter/dragleave).

Counting enter/leave events with a ref avoids the highlight toggling rapidly over children.

Apply this diff:

   const [isDragging, setIsDragging] = useState(false)
   const fileInputRef = useRef<HTMLInputElement>(null)
   const folderInputRef = useRef<HTMLInputElement>(null)
+  const dragCounterRef = useRef(0)

-  const handleDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
-    e.preventDefault()
-    e.stopPropagation()
-    if (!isUploading) {
-      setIsDragging(true)
-    }
-  }
+  const handleDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
+    e.preventDefault()
+    e.stopPropagation()
+    if (isUploading) return
+    dragCounterRef.current += 1
+    setIsDragging(true)
+  }

-  const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
-    e.preventDefault()
-    e.stopPropagation()
-    if (!isUploading) {
-      setIsDragging(false)
-    }
-  }
+  const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
+    e.preventDefault()
+    e.stopPropagation()
+    if (isUploading) return
+    dragCounterRef.current = Math.max(0, dragCounterRef.current - 1)
+    if (dragCounterRef.current === 0) setIsDragging(false)
+  }

   const handleDrop = useCallback(
     (e: React.DragEvent<HTMLDivElement>) => {
       e.preventDefault()
       e.stopPropagation()
-      setIsDragging(false)
+      setIsDragging(false)
+      dragCounterRef.current = 0

Also applies to: 103-109, 116-123


214-231: Add cross-browser fallback when DataTransferItem.webkitGetAsEntry is unavailable.

Firefox and some environments don’t expose webkitGetAsEntry. Fall back to e.dataTransfer.files so simple file drops still work.

Apply this diff:

-      Promise.all(filePromises)
+      if (filePromises.length === 0) {
+        const files = Array.from(e.dataTransfer.files).filter(
+          (file) => !file.name.startsWith("."),
+        )
+        onFilesSelect(files)
+        return
+      }
+      Promise.all(filePromises)
         .then((filesArrays) => {
           onFilesSelect(filesArrays.flat())
         })
         .catch((err) => {
           console.error("Error traversing file tree:", err)
         })

392-441: Minor: remove unused map index parameter.

index isn’t used; drop it to satisfy stricter linters and reduce noise.

Apply this diff:

-              {selectedFiles.map((selectedFile, index) => {
+              {selectedFiles.map((selectedFile) => {
frontend/src/contexts/UploadProgressContext.tsx (1)

84-97: Clamp progress to avoid overflows.

Defensive bounds prevent UI showing >100% or invalid batch numbers.

Apply this diff:

   const updateProgress = useCallback((uploadId: string, current: number, batch: number) => {
     setCurrentUpload(prev => {
       if (!prev || prev.id !== uploadId) return prev
-      
-      return {
+      const nextCurrent = Math.min(Math.max(current, 0), prev.batchProgress.total)
+      const nextBatch = Math.min(Math.max(batch, 0), prev.batchProgress.totalBatches)
+      return {
         ...prev,
         batchProgress: {
           ...prev.batchProgress,
-          current,
-          batch
+          current: nextCurrent,
+          batch: nextBatch
         }
       }
     })
   }, [])
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 101a49c and f8a44f9.

⛔ Files ignored due to path filters (6)
  • frontend/src/assets/collectionIcons/document.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/image.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/pdf.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/ppt.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/spreadsheet.svg is excluded by !**/*.svg
  • frontend/src/assets/collectionIcons/text.svg is excluded by !**/*.svg
📒 Files selected for processing (7)
  • frontend/src/components/ClFileUpload.tsx (2 hunks)
  • frontend/src/components/FileUploadSkeleton.tsx (1 hunks)
  • frontend/src/components/UploadProgressWidget.tsx (1 hunks)
  • frontend/src/components/ui/toast.tsx (1 hunks)
  • frontend/src/contexts/UploadProgressContext.tsx (1 hunks)
  • frontend/src/main.tsx (2 hunks)
  • frontend/src/routes/_authenticated/knowledgeManagement.tsx (14 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
frontend/src/components/UploadProgressWidget.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • useUploadProgress (40-46)
frontend/src/components/ui/confirmModal.tsx (1)
  • ConfirmModal (23-59)
frontend/src/routes/_authenticated/knowledgeManagement.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • useUploadProgress (40-46)
frontend/src/utils/fileUtils.ts (1)
  • uploadFileBatch (205-252)
frontend/src/main.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • UploadProgressProvider (52-147)
frontend/src/components/UploadProgressWidget.tsx (1)
  • UploadProgressWidget (9-205)
frontend/src/components/ClFileUpload.tsx (3)
frontend/src/utils/fileUtils.ts (1)
  • getFileType (9-31)
server/shared/types.ts (2)
  • MIME_TYPE_MAPPINGS (168-191)
  • EXTENSION_MAPPINGS (194-201)
server/shared/fileUtils.ts (1)
  • isValidFile (10-46)
🪛 GitHub Actions: TypeScript Build Check
frontend/src/routes/_authenticated/knowledgeManagement.tsx

[error] 389-389: TS6133: 'cancelUpload' is declared but its value is never read.

frontend/src/components/ClFileUpload.tsx

[error] 2-2: TS6133: 'Upload' is declared but its value is never read.


[error] 2-2: TS6133: 'Trash2' is declared but its value is never read.

🔇 Additional comments (5)
frontend/src/components/FileUploadSkeleton.tsx (1)

8-8: LGTM! Clean implementation of the conditional header display.

The optional showHeaders prop with a sensible default of true allows flexible usage of the skeleton component.

Also applies to: 16-16

frontend/src/main.tsx (1)

10-11: LGTM! Clean integration of the global upload progress system.

The upload progress provider and widget are correctly integrated at the app level, ensuring all components have access to the upload context.

Also applies to: 32-36

frontend/src/routes/_authenticated/knowledgeManagement.tsx (1)

1605-1605: No changes needed for skeleton loader logic. The startUpload call initializes isNewCollection on the upload task and all subsequent updates (updateProgress/updateFileStatus) spread the previous state, so the skeleton only appears for new-collection uploads.

frontend/src/components/ClFileUpload.tsx (1)

315-347: LGTM: drag-and-drop surface and accessibility basics are solid.

Visible labels, alt text for icons, and disabled states are handled well.

frontend/src/contexts/UploadProgressContext.tsx (1)

52-82: LGTM: simple, single-upload global state with immutable updates.

API is coherent; startUpload returns an id and initializes per-file status cleanly.

Confirm the product requirement is “only one upload globally at a time.” If multiple uploads can start concurrently, we should guard startUpload (reject or queue).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/src/routes/_authenticated/knowledgeManagement.tsx (2)

734-737: Apply the same progress fix in add-to-collection flow.

-      let totalSuccessful = 0
-      let totalSkipped = 0
-      let totalFailed = 0
+      let totalSuccessful = 0
+      let totalSkipped = 0
+      let totalFailed = 0
+      let processed = 0
@@
-        // Update progress
-        const newCurrent = (i + 1) * batchFiles.length
-        updateProgress(uploadId, newCurrent, i + 1)
+        // Update progress
+        processed += batchFiles.length
+        updateProgress(uploadId, processed, i + 1)

Also applies to: 776-779


686-693: Revoke object URLs to avoid memory leaks.

URLs created for previews aren’t revoked on remove/clear/close.

 const handleFilesSelect = (files: File[]) => {
   const newFiles: FileUploadSelectedFile[] = files.map((file) => ({
     file,
     id: Math.random().toString(),
     preview: URL.createObjectURL(file),
   }))
   setSelectedFiles((prev) => [...prev, ...newFiles])
 }
 
 const handleRemoveFile = (id: string) => {
-  setSelectedFiles((prev) => prev.filter((f) => f.id !== id))
+  setSelectedFiles((prev) => {
+    const toRemove = prev.find((f) => f.id === id)
+    if (toRemove?.preview) URL.revokeObjectURL(toRemove.preview)
+    return prev.filter((f) => f.id !== id)
+  })
 }
 
 const handleRemoveAllFiles = () => {
-  setSelectedFiles([])
+  setSelectedFiles((prev) => {
+    prev.forEach((f) => f.preview && URL.revokeObjectURL(f.preview))
+    return []
+  })
 }
 const handleCloseModal = () => {
   setShowNewCollection(false)
   setAddingToCollection(null)
   setTargetFolder(null)
   setCollectionName("")
-  setSelectedFiles([])
+  setSelectedFiles((prev) => {
+    prev.forEach((f) => f.preview && URL.revokeObjectURL(f.preview))
+    return []
+  })
   setOpenDropdown(null)
 }

Also applies to: 695-701, 699-701, 510-517

🧹 Nitpick comments (5)
frontend/src/components/ClFileUpload.tsx (4)

16-35: DRY: Reuse centralized getFileType instead of redefining it here.

This duplicates logic already available in frontend/src/utils/fileUtils.ts, increasing drift risk.

Apply this diff:

+import { getFileType } from "@/utils/fileUtils"
 
-// Helper function to determine FileType from a file
-const getFileType = (file: File): FileType => {
-  // First check by MIME type
-  for (const [fileType, mimeTypes] of Object.entries(MIME_TYPE_MAPPINGS)) {
-    if ((mimeTypes as readonly string[]).includes(file.type)) {
-      return fileType as FileType
-    }
-  }
-
-  // Fallback to extension checking
-  const fileName = file.name.toLowerCase()
-  for (const [fileType, extensions] of Object.entries(EXTENSION_MAPPINGS)) {
-    if ((extensions as readonly string[]).some(ext => fileName.endsWith(ext))) {
-      return fileType as FileType
-    }
-  }
-
-  // Default fallback
-  return FileType.FILE
-}

5-7: Align imports to shared alias to avoid brittle relative paths.

Use the same alias style as elsewhere (e.g., knowledgeManagement.tsx uses "shared/types").

-import { FileType, MIME_TYPE_MAPPINGS, EXTENSION_MAPPINGS } from "../../../server/shared/types"
+import { FileType, MIME_TYPE_MAPPINGS, EXTENSION_MAPPINGS } from "shared/types"

If available, also prefer importing isValidFile via the shared alias.


451-456: Prevent uploading unsupported files.

Disable the Upload button if any selected file is invalid per isValidFile.

-                disabled={
-                  selectedFiles.length === 0 ||
-                  isUploading ||
-                  !collectionName.trim()
-                }
+                disabled={
+                  selectedFiles.length === 0 ||
+                  isUploading ||
+                  !collectionName.trim() ||
+                  selectedFiles.some(sf => !isValidFile(sf.file))
+                }

239-243: Optional: Filter out unsupported files at selection time.

Drop hidden/unsupported files early to keep the queue clean (you already show “UNSUPPORTED FORMAT”, so treat this as optional).

-      const filteredFiles = Array.from(e.target.files).filter(
-        (file) => !file.name.startsWith("."),
-      )
+      const filteredFiles = Array.from(e.target.files).filter(
+        (file) => !file.name.startsWith(".") && isValidFile(file),
+      )
-      const filteredFiles = Array.from(e.target.files).filter(
-        (file: File) => !file.name.startsWith("."),
-      )
+      const filteredFiles = Array.from(e.target.files).filter(
+        (file: File) => !file.name.startsWith(".") && isValidFile(file),
+      )

Also applies to: 252-256

frontend/src/components/UploadProgressWidget.tsx (1)

20-21: Clamp progress to 0–100 as a guard.

Defensive clamp avoids overshoot if current > total.

-  const progressPercentage = batchProgress.total > 0 ? Math.round((batchProgress.current / batchProgress.total) * 100) : 0
+  const progressPercentage = batchProgress.total > 0
+    ? Math.min(100, Math.max(0, Math.round((batchProgress.current / batchProgress.total) * 100)))
+    : 0
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8a44f9 and d7ec937.

📒 Files selected for processing (4)
  • frontend/src/components/ClFileUpload.tsx (2 hunks)
  • frontend/src/components/UploadProgressWidget.tsx (1 hunks)
  • frontend/src/contexts/UploadProgressContext.tsx (1 hunks)
  • frontend/src/routes/_authenticated/knowledgeManagement.tsx (15 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
frontend/src/routes/_authenticated/knowledgeManagement.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • useUploadProgress (40-46)
frontend/src/utils/fileUtils.ts (1)
  • uploadFileBatch (205-252)
frontend/src/components/ClFileUpload.tsx (3)
frontend/src/utils/fileUtils.ts (1)
  • getFileType (9-31)
server/shared/types.ts (2)
  • MIME_TYPE_MAPPINGS (168-191)
  • EXTENSION_MAPPINGS (194-201)
server/shared/fileUtils.ts (1)
  • isValidFile (10-46)
frontend/src/components/UploadProgressWidget.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • useUploadProgress (40-46)
frontend/src/components/ui/confirmModal.tsx (1)
  • ConfirmModal (23-59)
🔇 Additional comments (1)
frontend/src/contexts/UploadProgressContext.tsx (1)

28-36: Use fileId only for status updates (avoid name collisions and redundant params).

File names collide across folders; id is sufficient and stable. Simplify API and internals.

 interface UploadProgressContextType {
   currentUpload: UploadTask | null
   startUpload: (collectionName: string, files: { file: File; id: string }[], totalBatches: number, isNewCollection: boolean, targetCollectionId?: string) => string
   updateProgress: (uploadId: string, current: number, batch: number) => void
-  updateFileStatus: (uploadId: string, fileName: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => void
+  updateFileStatus: (uploadId: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => void
   finishUpload: (uploadId: string) => void
   cancelUpload: (uploadId: string) => void
   getUploadProgress: (uploadId: string) => UploadTask | null
 }
-  const updateFileStatus = useCallback((uploadId: string, fileName: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => {
+  const updateFileStatus = useCallback((uploadId: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => {
     setCurrentUpload(prev => {
       if (!prev || prev.id !== uploadId) return prev
       
       return {
         ...prev,
-        files: prev.files.map(file => 
-          file.name === fileName && file.id === fileId
-            ? { ...file, status, error }
-            : file
-        )
+        files: prev.files.map(file => file.id === fileId ? { ...file, status, error } : file)
       }
     })
   }, [])

Run to find and patch call sites:

#!/bin/bash
rg -nP --type=ts -C2 '\bupdateFileStatus\s*\('

Also applies to: 99-112

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
frontend/src/utils/fileUtils.ts (1)

209-210: Thread cancellation across helper APIs for consistency.

addFilesToExistingCollection doesn’t allow passing an AbortSignal, so any callers using it can’t cancel in-flight uploads. Consider adding an optional abortSignal param and threading it to uploadFileBatch.

Example:

export const addFilesToExistingCollection = async (
  files: SelectedFile[],
  collectionId: string,
  parentId?: string | null,
  abortSignal?: AbortSignal,
): Promise<any> => {
  return uploadFileBatch(
    files.map((f) => f.file),
    collectionId,
    parentId,
    abortSignal,
  )
}
frontend/src/routes/_authenticated/knowledgeManagement.tsx (1)

541-545: Propagate cancelation to collection creation.

createCollection isn’t cancelable, so pressing Cancel during the “create” step won’t abort that request. Pass the same AbortSignal to createCollection (and update its signature to accept it) to make the whole flow cancelable.

Proposed call-site change:

-const cl = await createCollection(collectionName.trim(), "")
+const cl = await createCollection(collectionName.trim(), "", abortController.signal)

And update createCollection to accept abortSignal?: AbortSignal and pass signal: abortSignal to authFetch (in fileUtils.ts).

frontend/src/contexts/UploadProgressContext.tsx (1)

141-149: Memoize provider value to avoid needless rerenders.

Wrap the value object in useMemo with [currentUpload, startUpload, updateProgress, updateFileStatus, finishUpload, cancelUpload, getUploadProgress].

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7ec937 and 3d18444.

📒 Files selected for processing (3)
  • frontend/src/contexts/UploadProgressContext.tsx (1 hunks)
  • frontend/src/routes/_authenticated/knowledgeManagement.tsx (15 hunks)
  • frontend/src/utils/fileUtils.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/routes/_authenticated/knowledgeManagement.tsx (2)
frontend/src/contexts/UploadProgressContext.tsx (1)
  • useUploadProgress (41-47)
frontend/src/utils/fileUtils.ts (1)
  • uploadFileBatch (205-254)
🔇 Additional comments (9)
frontend/src/routes/_authenticated/knowledgeManagement.tsx (6)

389-396: LGTM on wiring the global upload context.

Destructure looks correct; derived state matches the provider shape.


562-567: Prefer fileId-only status updates to avoid filename collisions.

Requiring both name and id couples UI updates to a mutable/display field. Using only file.id is sufficient and robust when duplicate names exist.

If adopting, first change the context API to updateFileStatus(uploadId, fileId, status, error?), then drop file.file.name here:

- updateFileStatus(uploadId, file.file.name, file.id, 'uploading')
+ updateFileStatus(uploadId, file.id, 'uploading')

(similarly for 'uploaded' and 'failed')

Also applies to: 569-577, 579-583, 764-773, 775-779


592-595: Progress calc fix looks good.

Switching to a running processed count addresses variable batch sizes correctly.

Also applies to: 788-791


734-738: Optional: short‑circuit loops on cancel to avoid extra UI work.

Even with abort, add a guard to cease per-file status updates ASAP when canceled.

Example:

-const { currentUpload, startUpload, updateProgress, updateFileStatus, finishUpload } = useUploadProgress()
+const { currentUpload, startUpload, updateProgress, updateFileStatus, finishUpload, getUploadProgress } = useUploadProgress()
@@
-for (let i = 0; i < batches.length; i++) {
+for (let i = 0; i < batches.length; i++) {
+  if (!getUploadProgress(uploadId)) break
   const batchFiles = batches[i].map((f) => ({ file: f.file, id: f.id }))
   // Mark batch files as uploading
-  batchFiles.forEach(file => {
-    updateFileStatus(uploadId, file.file.name, file.id, 'uploading')
-  })
+  batchFiles.forEach(file => {
+    if (!getUploadProgress(uploadId)) return
+    updateFileStatus(uploadId, file.file.name, file.id, 'uploading')
+  })

Also applies to: 749-756, 757-762


1625-1648: Progress skeleton UX: good conditional rendering.

New/Existing collection gating and header toggling read well.

Also applies to: 1880-1892


2144-2146: Verify new batching limits with backend constraints.

Dropping to 5MB/5 files per batch may impact throughput; ensure server accepts many small multipart posts and any reverse proxies (e.g., Nginx, CDN) won’t 413 on cumulative requests.

frontend/src/contexts/UploadProgressContext.tsx (2)

26-27: Cancellation via AbortController: solid implementation.

Provider tracks an AbortController per upload and exposes it to callers; cancelUpload aborts and clears state. Looks good.

Also applies to: 31-37, 56-66, 67-85, 124-135


33-37: Key by fileId only in updateFileStatus (avoid name coupling).

Matching on both name and id can fail if the name changes or duplicates exist. Id alone is sufficient.

Apply:

-  updateFileStatus: (uploadId: string, fileName: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => void
+  updateFileStatus: (uploadId: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => void
@@
-const updateFileStatus = useCallback((uploadId: string, fileName: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => {
+const updateFileStatus = useCallback((uploadId: string, fileId: string, status: 'pending' | 'uploading' | 'uploaded' | 'failed', error?: string) => {
   setCurrentUpload(prev => {
     if (!prev || prev.id !== uploadId) return prev
     return {
       ...prev,
       files: prev.files.map(file =>
-        file.name === fileName && file.id === fileId
+        file.id === fileId
           ? { ...file, status, error }
           : file
       )
     }
   })
}, [])

Then update call sites accordingly.

Also applies to: 102-115

frontend/src/utils/fileUtils.ts (1)

209-210: Confirmed signal forwarding in authFetch. authFetch spreads init into both fetch calls (including signal), so aborting the passed AbortSignal will cancel the request as expected.

kalpadhwaryu
kalpadhwaryu previously approved these changes Sep 26, 2025
Himanshvarma
Himanshvarma previously approved these changes Sep 27, 2025
@rahul1841 rahul1841 dismissed stale reviews from Himanshvarma and kalpadhwaryu via 753cf41 September 29, 2025 06:53
@junaid-shirur
Copy link
Collaborator

Maybe we can start adding global state management instead of creating wrapper components

CC : @shivamashtikar @rahul1841

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants