feat(client-vue): redesign bucket file upload with drag-and-drop dropzone#1681
Conversation
…zone Replace the near-invisible Bootstrap file input with a custom, theme-aware drag-and-drop dropzone for analysis file uploads: - real drag-and-drop (was file-picker only) plus click-to-browse - segmented Files / Directories mode toggle - selection summary (count, total size, clear all) + scrollable list - light/dark via --vc-color-* / --privateaim-brand-* tokens - fix pre-existing :disbaled typo and buggy vNode reset hack; type the change/drop handlers (Event/DragEvent/FileList) instead of any Also register browser globals for *.vue in eslint.config.js — the shared config only set Node globals for SFCs, so DOM-only globals (DragEvent, FileList, HTMLInputElement) tripped no-undef.
|
Note Reviews pausedUse the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough
ChangesDrag-and-drop upload and path handling
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue`:
- Around line 34-42: The addFiles function blindly appends files to
tempFiles.value without deduplication, and the file removal logic (which also
needs fixing based on the comment's scope) uses name/path matching that can
cause collisions when multiple files have the same name. Modify the addFiles
function to check if each file from the incoming FileList already exists in
tempFiles.value before adding it, using object identity comparison since
FBucketFileForm emits the same File instance. Additionally, update the file
removal logic (around lines 70-79) to use the same object identity matching
instead of relying on name or path heuristics to ensure the correct file is
removed.
- Around line 155-171: The file input element with id="files" is hidden using
`display: none` at lines 352-354, which removes it from the tab order and makes
it inaccessible to keyboard users. Replace the `display: none` CSS rule with an
alternative hiding technique that keeps the input in the tab order and
accessible to assistive technologies, such as positioning it off-screen with
`position: absolute; left: -9999px;` or using `width: 0; height: 0; overflow:
hidden;`. This allows keyboard users to tab to the input and open the file
picker without relying on pointer devices.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 38f7a430-1021-407c-99e5-1dfc579ff551
📒 Files selected for processing (2)
eslint.config.jspackages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue
…load
- send each file's relative path as the multipart filename so the storage service (busboy preservePath) keeps the directory in BucketFile.path/directory
- expand drag-dropped folders via the FileSystem Entry API (webkitGetAsEntry + recursive readEntries), deriving each path from entry.fullPath (dropped files have an empty webkitRelativePath)
- stage files as { file, path } and dedupe by path on ingest (matches the server unique bucket_id+path); remove by object identity
- render the relative path in the file lists (FBucketFile: data.path || data.name; FBucketFileForm: explicit relative-path prop)
- a11y: file input is visually hidden but kept tabbable (was display:none) with a focus-within ring on the dropzone
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue`:
- Around line 125-159: The onDrop async function lacks error handling for the
collectEntry call within the entry traversal loop. Wrap the loop that calls
collectEntry (around line 151) in a try/catch block, and when an error is caught
during File System Entry traversal, ensure a failed event is emitted to properly
notify the caller instead of silently failing. This handles permission errors
and other edge cases that may occur during the async traversal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c887c010-57c2-49c4-9761-336bbf4067ea
📒 Files selected for processing (4)
eslint.config.jspackages/client-vue/src/components/bucket-file/FBucketFile.vuepackages/client-vue/src/components/bucket-file/FBucketFileForm.vuepackages/client-vue/src/components/bucket-file/FBucketFilesUpload.vue
|
@coderabbitai pause |
✅ Action performedReviews paused. |
collectEntry() (FileSystem Entry API) can reject on read/permission errors; onDrop awaited it without a guard, so the drop would fail silently. Wrap the traversal in try/catch and emit "failed", matching the upload path's error handling.
Summary
The analysis file upload area used a bare Bootstrap
<input class="form-control">that was barely visible against the content surface. This replaces it with a custom, theme-aware drag-and-drop dropzone.FBucketFilesUpload.vue--vc-color-*/--privateaim-brand-*tokens, so it flips correctly between light and dark; respectsprefers-reduced-motion.:disbaledtypo (the input was never disabled while busy) and the buggyvNodereset hack; handlers are now properly typed (Event/DragEvent/FileList) instead ofany.eslint.config.jsglobals.browserfor**/*.vue. The shared@tada5hi/eslint-configonly sets Node globals for SFCs, so DOM-only globals (DragEvent,FileList,HTMLInputElement) trippedno-undef..tsfiles in the package already had browser globals — this closes the gap for.vue.Test plan
npm run dev --workspace=apps/client-ui→ open an analysis → Code Files → + (Add) to open the upload modal.npm run lintpasses.Notes
dataTransfer.filesdoesn't recurse); full directory-tree uploads still use Directories mode + browse (webkitdirectory), as before.closes #1186
closes #1155
closes #1223
closes #1192
Summary by CodeRabbit
Release Notes
New Features
Improvements