feat(resume): admin bulk resume import
Adds the dashboard surface for the backend's /api/admin/v1/resume/import-batches endpoints, and the multipart support it needed. Client: - rawRequest passes a FormData body through untouched and skips Content-Type, so the browser sets multipart/form-data *with the boundary*. Setting it by hand stripped the boundary and the server saw an empty upload — verified against the backend: 202 with the fix, 422 VALIDATION_ERROR without it. This was the first admin endpoint in the codebase to accept a file. - New api.upload(). fetch reports no upload progress, so upload UI stays indeterminate rather than faking a percentage. UI (/resume/import-batches, gated on admin:resume:read; writes on :manage): - Upload dialog with countryCode and dry-run checked by default — seeing the collision report before creating accounts is almost always what you want. - Detail sheet polls every 2s and stops once the batch is terminal. Clickable count tiles filter the per-resume report; step statuses surface `命中缓存` / `复用结果` so the dedup is visible. - Confirm a dry run, retry failed rows, and resolve a missing contact. IMPORT_IDENTITY_TAKEN is shown inline — a collision is expected input, not an error toast. - progress.total is 0 until unpacking finishes, so both views render 准备中… rather than "0 / 0", which would read as an empty batch. types.gen.ts regenerated from the backend's admin spec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@ -31,6 +31,14 @@ ddev exec php artisan tinker storage/app/e2e-admin-seed.php
|
||||
| `pnpm test` | vitest (API client envelope/refresh/error tests) |
|
||||
| `pnpm gen:api` | regenerate `src/api/types.gen.ts` from the backend's private admin OpenAPI spec (`../gig-platform/storage/app/api-docs/admin/openapi.yaml`; run `ddev artisan api-docs:generate` there first) |
|
||||
|
||||
## Uploads
|
||||
|
||||
`api.upload(path, formData)` is the multipart path. `rawRequest` deliberately
|
||||
does **not** set `Content-Type` for a `FormData` body — the browser must set it
|
||||
itself so the multipart boundary is included; setting it by hand makes the
|
||||
server see an empty upload (a 422 rather than a 202). `fetch` reports no upload
|
||||
progress, so upload UIs stay indeterminate.
|
||||
|
||||
## Deploy
|
||||
|
||||
The app is a client-rendered SPA served as static assets by a Cloudflare Worker
|
||||
|
||||
Reference in New Issue
Block a user