feat(web): FileDropZone component with upload progress #14

Closed
opened 2026-02-07 02:20:13 +00:00 by forbes · 0 comments
Owner

Context

The CreateItemPane redesign needs drag-and-drop file upload with progress tracking. Files are uploaded directly to MinIO via presigned URLs (see #12 for backend).

This component can be built and tested with mock upload functions before the backend is ready.

Component: FileDropZone

File: web/src/components/items/FileDropZone.tsx

Props

interface FileDropZoneProps {
  files: PendingAttachment[];
  onFilesAdded: (files: PendingAttachment[]) => void;
  onFileRemoved: (index: number) => void;
  accept?: string;  // e.g. '.FCStd,.step,.stl,.pdf,.png,.jpg'
}

interface PendingAttachment {
  file: File;
  objectKey: string;
  uploadProgress: number;     // 0-100
  uploadStatus: 'pending' | 'uploading' | 'complete' | 'error';
  error?: string;
}

Drop Zone UI

  • Dashed 2px border using --ctp-surface1, border-radius: 0.5rem
  • Centered text: 'Drop files here or browse'
  • Accepted formats hint in --ctp-overlay0 at 0.75rem
  • Default: dashed --ctp-surface1 border
  • Drag over: dashed --ctp-mauve border, rgba(203,166,247,0.05) bg
  • Clicking opens hidden <input type='file' multiple>

File List

Each file shows:

  • Type icon: colored rounded square. .FCStd/.step/.stl -> blue ('CAD'), .pdf -> red ('PDF'), .png/.jpg -> green ('IMG'), other -> overlay1 ('FILE')
  • File name (truncated with ellipsis)
  • File size in --ctp-overlay0
  • Upload progress bar (thin 2px, --ctp-mauve fill) when uploading
  • Remove button (x) on hover, --ctp-overlay0 -> --ctp-red

Hook: useFileUpload

File: web/src/hooks/useFileUpload.ts

function useFileUpload(): {
  upload: (file: File) => Promise<PendingAttachment>;
  uploading: boolean;
}

Upload flow:

  1. POST /api/uploads/presign with { filename, content_type, size }
  2. Backend returns { object_key, upload_url, expires_at }
  3. PUT file to presigned URL via XMLHttpRequest (for progress events)
  4. Return completed PendingAttachment with object_key

Dependencies

  • Requires #12 (presigned upload backend) for real uploads
  • Can be built and demoed with a mock upload function that simulates progress

Acceptance Criteria

  • Drop zone renders with dashed border and hint text
  • Drag-over state changes border color
  • Click opens file picker
  • Dropped/selected files appear in file list with type icons
  • Upload progress bar animates during upload
  • Remove button removes file from list
  • useFileUpload hook handles presigned URL flow
  • TypeScript compiles clean
## Context The CreateItemPane redesign needs drag-and-drop file upload with progress tracking. Files are uploaded directly to MinIO via presigned URLs (see #12 for backend). This component can be built and tested with mock upload functions before the backend is ready. ## Component: `FileDropZone` **File**: `web/src/components/items/FileDropZone.tsx` ### Props ```typescript interface FileDropZoneProps { files: PendingAttachment[]; onFilesAdded: (files: PendingAttachment[]) => void; onFileRemoved: (index: number) => void; accept?: string; // e.g. '.FCStd,.step,.stl,.pdf,.png,.jpg' } interface PendingAttachment { file: File; objectKey: string; uploadProgress: number; // 0-100 uploadStatus: 'pending' | 'uploading' | 'complete' | 'error'; error?: string; } ``` ### Drop Zone UI - Dashed 2px border using `--ctp-surface1`, `border-radius: 0.5rem` - Centered text: 'Drop files here or **browse**' - Accepted formats hint in `--ctp-overlay0` at 0.75rem - **Default**: dashed `--ctp-surface1` border - **Drag over**: dashed `--ctp-mauve` border, `rgba(203,166,247,0.05)` bg - Clicking opens hidden `<input type='file' multiple>` ### File List Each file shows: - Type icon: colored rounded square. `.FCStd/.step/.stl` -> blue ('CAD'), `.pdf` -> red ('PDF'), `.png/.jpg` -> green ('IMG'), other -> overlay1 ('FILE') - File name (truncated with ellipsis) - File size in `--ctp-overlay0` - Upload progress bar (thin 2px, `--ctp-mauve` fill) when uploading - Remove button (x) on hover, `--ctp-overlay0` -> `--ctp-red` ## Hook: `useFileUpload` **File**: `web/src/hooks/useFileUpload.ts` ```typescript function useFileUpload(): { upload: (file: File) => Promise<PendingAttachment>; uploading: boolean; } ``` Upload flow: 1. `POST /api/uploads/presign` with `{ filename, content_type, size }` 2. Backend returns `{ object_key, upload_url, expires_at }` 3. `PUT` file to presigned URL via XMLHttpRequest (for progress events) 4. Return completed PendingAttachment with object_key ### Dependencies - Requires #12 (presigned upload backend) for real uploads - Can be built and demoed with a mock upload function that simulates progress ## Acceptance Criteria - [ ] Drop zone renders with dashed border and hint text - [ ] Drag-over state changes border color - [ ] Click opens file picker - [ ] Dropped/selected files appear in file list with type icons - [ ] Upload progress bar animates during upload - [ ] Remove button removes file from list - [ ] useFileUpload hook handles presigned URL flow - [ ] TypeScript compiles clean
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kindred/silo#14