feat(frontend): implement file upload with drag-and-drop and per-file progress

- client.ts: add uploadWithProgress() using XHR for upload progress events
- FileUpload.svelte: drag-drop zone wrapper, multi-file queue with individual
  progress bars, success/error status, MIME rejection message, dismiss panel
- Header.svelte: optional onUpload prop renders upload icon button
- files/+page.svelte: wire upload button, prepend uploaded files to grid
- vite-mock-plugin.ts: handle POST /files, unshift new file into mock array
- Fix crypto.randomUUID() crash on non-secure HTTP context (use Date.now + Math.random)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-05 14:02:26 +03:00
parent a5b610d472
commit b9cace2997
5 changed files with 462 additions and 21 deletions
@@ -10,6 +10,7 @@
onSortChange: (sort: string) => void;
onOrderToggle: () => void;
onFilterToggle: () => void;
onUpload?: () => void;
}
let {
@@ -20,6 +21,7 @@
onSortChange,
onOrderToggle,
onFilterToggle,
onUpload,
}: Props = $props();
</script>
@@ -32,6 +34,15 @@
{$selectionActive ? 'Cancel' : 'Select'}
</button>
{#if onUpload}
<button class="upload-btn icon-btn" onclick={onUpload} title="Upload files">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path d="M8 2v9M4 6l4-4 4 4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 13h12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</svg>
</button>
{/if}
<div class="controls">
<select
class="sort-select"