feat(upload): wire A8 local track upload to backend

Implement the A8 upload screen against the existing /upload contract:
- UploadResponse type ({track_id, title, already_exists}) + mutation typed to it
- buildUploadFormData helper (single file under field `file`, per FastAPI)
- UploadPage: drag-and-drop + file picker, client-side queue with
  concurrency cap (3), per-file status badges, retry on error,
  already_exists -> "Already in library", deep-link to A7 metadata editor
- i18n upload.* section (en/ru) incl. "metadata pending" hint

Indeterminate spinner per file; percent progress is a follow-up
(needs an XHR baseQuery — fetchBaseQuery gives no upload progress).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Senko-san
2026-06-07 18:47:59 +03:00
parent aed0572071
commit 61dbb1abd2
5 changed files with 385 additions and 5 deletions
+12 -2
View File
@@ -1,9 +1,19 @@
import { api } from '../index';
import type { Track } from '../types';
import type { UploadResponse } from '../types';
/**
* Build the multipart body for `/upload`. The backend expects exactly one file
* per request under the field name `file` (anything else → FastAPI 422).
*/
export function buildUploadFormData(file: File): FormData {
const fd = new FormData();
fd.append('file', file);
return fd;
}
export const uploadApi = api.injectEndpoints({
endpoints: (build) => ({
uploadTrack: build.mutation<Track, FormData>({
uploadTrack: build.mutation<UploadResponse, FormData>({
query: (formData) => ({
url: '/upload',
method: 'POST',