fix(bulk-upload): handle network errors and partial upload success

save() now wraps each chunk fetch in try/catch — a thrown network error
marks all files in that chunk as errored. Also handles HTTP 200 responses
with a non-empty errors array (partial success): only the named filenames
are marked as errored rather than all files in the chunk. Navigation is
suppressed whenever any file fails.

Tests added:
- network error marks all chunk files as errored, no navigation
- HTTP 200 with errors array marks only affected files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-25 11:09:49 +02:00
committed by marcel
parent 899508f9ca
commit ed0d0bf331
2 changed files with 72 additions and 7 deletions

View File

@@ -111,19 +111,29 @@ async function save() {
// Raw fetch is intentional: SvelteKit form actions can't stream chunked
// FormData with per-chunk progress. Session cookie is sent automatically
// by the browser for same-origin requests.
const res = await fetch('/api/documents/quick-upload', { method: 'POST', body: formData });
if (!res.ok) {
hadErrors = true;
try {
const res = await fetch('/api/documents/quick-upload', { method: 'POST', body: formData });
const body = await res.json().catch(() => ({ errors: [] }));
const errorFilenames = new Set<string>(
(body.errors ?? []).map((err: { filename: string }) => err.filename)
);
for (const entry of chunk) {
if (errorFilenames.has(entry.file.name)) {
const e = files.get(entry.id);
if (e) files.set(entry.id, { ...e, status: 'error' });
if (!res.ok || errorFilenames.size > 0) {
hadErrors = true;
for (const entry of chunk) {
// When backend names specific files, mark only those; otherwise mark all.
const isError = errorFilenames.size > 0 ? errorFilenames.has(entry.file.name) : true;
if (isError) {
const e = files.get(entry.id);
if (e) files.set(entry.id, { ...e, status: 'error' });
}
}
}
} catch {
hadErrors = true;
for (const entry of chunk) {
const e = files.get(entry.id);
if (e) files.set(entry.id, { ...e, status: 'error' });
}
}
chunkProgress = { done: i + 1, total: chunks.length };
}