11 KiB
WebUI Import Migration Plan
Snapshot date: 2026-05-24
Status
- Initial implementation completed on 2026-05-15.
importis now React-owned in the shell route manifest.- The legacy import page DOM has been removed from
webui/index.html. - Legacy import activation has been removed from
webui/static/init.js. - A React route subtree now owns import rendering, nested route state, album matching, singles matching, auto-import controls, and the client-side processing queue.
- The old
tab=URL contract has been replaced by/import/album,/import/singles, and/import/auto, with/importredirecting to/import/album. - Route-local workflow state lives in
webui/src/routes/import/-import.store.ts, which keeps draft matching, selection, and queue state alive while navigating within the import route. - The old import-page-specific functions have already been removed from
webui/static/stats-automations.js; any remainingimportreferences there belong to the broader automation feature set, not this page migration. - Backend routes are already grouped around
/api/import/*and/api/auto-import/*.
Goal
- Migrate
importinto a React-owned route without changing the user workflow. - Preserve manual album matching, singles matching, auto-import review, and the processing queue.
- Keep staging-folder and import-processing behavior backed by the existing API routes.
- Use the completed
issuesandstatsroutes as the structural reference for route slices, API helpers, shell gating, and tests.
Why import Was The Right Next Route
- It is the safest remaining route after excluding
helpandhydrabase. - It has real workflows, so it gives the migration program more signal than another mostly-static page.
- The backend API boundary is already clearer than the broad dashboard, library, discover, sync, or settings surfaces.
- The page is important enough to validate mutation, polling, and route-local reducer patterns before larger operational pages.
- It does not need a visual redesign or a new shell abstraction to migrate cleanly.
Current Legacy Shape
Page surface in webui/index.html:
- Header
- import folder path
- file count and total size
- refresh action
- Processing queue
- per-job progress
- partial error display
- clear-finished action
- Tabs
- auto-import
- albums
- singles
- Auto-import tab
- enable toggle
- status text
- confidence and interval settings
- scan-now action
- live scan progress
- result filters
- approve, reject, approve-all, and clear-completed actions
- Albums tab
- auto-group suggestions from staging
- album search
- album result cards
- track matching view
- drag/drop and tap-to-assign overrides
- unmatched file pool
- process album action
- Singles tab
- staging file list
- select all / per-file selection
- per-file track search
- manual match selection
- process selected action
Legacy JS responsibilities in webui/static/stats-automations.js:
initializeImportPage- staging fetch and refresh
- tab switching
- auto-import polling
- auto-import mutations
- staging group and suggestion rendering
- album search and match POSTs
- drag/drop assignment state
- singles search and selection state
- client-side processing queue
- sequential album/singles processing requests
Backend endpoints already available:
GET /api/import/staging/filesGET /api/import/staging/groupsGET /api/import/staging/hintsGET /api/import/staging/suggestionsGET /api/import/search/albumsPOST /api/import/album/matchPOST /api/import/album/processGET /api/import/search/tracksPOST /api/import/singles/processGET /api/auto-import/statusPOST /api/auto-import/toggleGET /api/auto-import/settingsPOST /api/auto-import/settingsGET /api/auto-import/resultsPOST /api/auto-import/approve/:idPOST /api/auto-import/reject/:idPOST /api/auto-import/scan-nowPOST /api/auto-import/approve-allPOST /api/auto-import/clear-completed
Implemented Route Slice
webui/src/routes/import/
route.tsx
index.tsx
album.tsx
auto.tsx
singles.tsx
-import.types.ts
-import.api.ts
-import.helpers.ts
-import.store.ts
-route.test.tsx
-ui/
import-page.tsx
album-import-tab.tsx
auto-import-tab.tsx
singles-import-tab.tsx
import-shared.tsx
Implemented Route Responsibilities
route.tsx
- declare
/import - gate route through
bridge.isPageAllowed('import') - preload shell context
- prefetch the staging-files query without blocking on transient fetch failure
index.tsx
- redirect
/importto/import/album
album.tsx
- prefetch album staging groups and suggestions
auto.tsx
- validate the
autoFiltersearch param - keep route-driven filter changes in the URL while leaving the rest of the workflow state local
singles.tsx
- mount the singles import tab without extra route-level loader work
-import.types.ts
- search param schema
- API response types
- staging file, staging group, album result, track result, match, auto-import result, and queue item types
-import.api.ts
- query options for staging, groups, suggestions, auto-import status, auto-import settings, auto-import results, album search, and track search
- mutation helpers for album match, album process, singles process, auto-import actions, and settings writes
- invalidation helpers for broad route refreshes, staging-only refreshes, and auto-import-only refreshes
-import.helpers.ts
- byte-size formatting
- album and track display labels
- confidence class/label mapping
- staging match normalization
- auto-import result filtering and counters
-import.store.ts
- album search state, selected album, auto-group file paths, and match overrides
- selected single-file state and manual matches
- queue job state and queue entry updates
- single-search draft state
- draft state survival across nested route remounts
-ui/import-page.tsx
- page chrome and nested route navigation
- queue summary and queue-item rendering
- nested route outlet for album, singles, and auto views
Search Params
Use nested route paths for durable, shareable tab state:
/import/album- default landing route
/import/singles/import/autoautoFilter- values:
all,pending,imported,failed - default:
all
- values:
Keep these local to React state:
- album search text
- track search text
- selected album
- match overrides
- selected singles
- processing queue jobs
Reasoning:
- The tab choice belongs in the path, which keeps deep links simple and avoids an extra
tab=query param. - The auto-import filter is still useful after reloads.
- The matching workflow is ephemeral and should not create fragile URLs with file indexes or local staging paths.
Query Model
Critical route-loader data:
importStagingFilesQueryOptions()
Useful prefetch data:
importStagingGroupsQueryOptions()importStagingSuggestionsQueryOptions()
Nested-route data:
autoImportStatusQueryOptions()autoImportSettingsQueryOptions()autoImportResultsQueryOptions(autoFilter)
Lazy search data:
importAlbumSearchQueryOptions(query)importTrackSearchQueryOptions(query)
Mutation-style actions:
- album match draft
- process one album track
- process one single file
- toggle auto-import
- save auto-import settings
- scan now
- approve/reject auto-import result
- approve all
- clear completed
Invalidation rules:
- Processing album or singles files invalidates staging files, staging groups, staging suggestions, auto-import results, and any route-local queue completion summary.
- Auto-import actions invalidate auto-import status and results.
- Auto-import settings writes invalidate settings and status.
- Refresh invalidates staging files, groups, and suggestions.
Incremental Migration Order
Recommended order:
- Add route slice, types, API helpers, reducer, and helper tests.
- Build the React route shell with header, tabs, and staging summary.
- Port the Albums tab search and suggestions, but keep processing disabled until match rendering is covered.
- Port the album matching view, including drag/drop and tap assignment.
- Port the processing queue and album/singles process mutations.
- Port the Singles tab.
- Port the Auto tab and polling behavior.
- Flip
importfromlegacytoreactin the shell route manifest. - Remove the legacy
import-pageDOM fromwebui/index.html. - Remove import-specific legacy functions from
webui/static/stats-automations.js.
This order gives us a visible React page early while delaying the highest-risk file-processing actions until the state model is tested.
The implemented route keeps the same overall migration shape, but the final URL contract uses nested route paths instead of a tab= search param.
Testing Sketch
Unit tests:
- route path and filter defaults
- staging summary formatting
- auto-import counters
- confidence labels
- reducer assignment behavior
- reducer queue transitions
API tests:
- staging files success and error
- staging groups success and error
- album search success and empty result
- track search success and empty result
- album match success and failure
- album process success and partial error
- singles process success and partial error
- auto-import status/results/settings/actions
Route / component tests:
- unauthorized users redirect to profile home
- default route redirects to
/import/album /import/singlesrenders the Singles tab/import/auto?autoFilter=pendingrenders pending auto-import results- refresh invalidates staging queries
- album selection opens the match view
- drag/drop and tap assignment update track matches
- processing queue advances and refreshes staging on completion
- client workflow drafts survive page remounts
Playwright can wait until after route ownership flips.
Risks
- The processing queue is client-side and long-running.
- Auto-import polling must stop when leaving the auto subroute or route.
- File indexes can become stale after staging refreshes.
- Album matching depends on preserving source, album name, and album artist from search results.
- The page currently shares a large legacy module with stats and automations code, so cleanup should be careful and incremental.
Decisions To Keep Simple
- Keep the current visual language.
- Keep the existing backend endpoints.
- Keep the processing queue client-side for the first migration.
- Keep file matching state local to the route.
- Do not extract shared workflow primitives until a second migrated route needs them.
Outcome
- The route now serves as the first React-owned workflow migration.
- The implementation uses nested route paths plus a validated
autoFiltersearch param. - The route uses TanStack Query for staging data, suggestions, auto-import polling, mutations, and invalidation.
- Tests cover shell ownership, nested route state, album match payload preservation, and auto-import rendering.
Recommendation
Treat remaining work as cleanup and hardening rather than route selection.
Follow-up work should optimize for:
- shrinking
stats-automations.jsafter cutover - adding E2E coverage around full album and singles processing
- considering route-level code splitting once more large React routes land
It should not optimize for:
- redesign
- backend reshaping
- shared queue abstractions
- migrating
automationsat the same time