# UMM UX Flows — Implemented & Tested **Last verified:** 2026-04-22 (deployed build, backend healthy) ## Navigation | Route | Page | Nav Label | |-------|------|-----------| | `/` | Dashboard | Dashboard | | `/library` | Library | Library | | `/library/:type/:id` | Media Detail | (via Library click) | | `/discover` | Discover | Discover | | `/calendar` | Calendar | Calendar | | `/queue` | Download Queue | Queue | | `/search` | Search Indexers | Search | | `/activity` | Activity | Activity | | `/requests` | Requests | Requests | | `/blocklist` | Blocklist | Blocklist | | `/settings` | Settings | Settings | --- ## Page-by-Page Flows ### Dashboard (`/`) **API:** `GET /api/dashboard` (200 OK) - 8 stat cards: Movies, TV Series, Music, Books, Active Downloads, Missing, Upgrades Available, Total Storage - Auto-fetches on mount - Skeleton loading while fetching - Error banner with retry on failure ### Library (`/library`) **API:** `GET /api/media?page=1&page_size=50` (200 OK) - Search input with 300ms debounce - Type filter: All, Movie, Series, Music, Book, Audiobook, Podcast, Photo, Other - Status filter: All, Available, Unavailable, Downloading, Failed - Paginated table (50 per page) - Each row: Title (click → detail), Type, Status badge, Quality, Monitor toggle - Monitor toggle: optimistic update, rollback on error with toast - Empty state messages for no results vs empty library ### Media Detail (`/library/:type/:id`) **API:** `GET /api/media/:type/:id/detail` (200 OK) 5 tabs (4 for non-series): **Overview tab:** - Poster image (or letter placeholder) - Title, year, original title - Status badge, monitored indicator - TMDB rating, genres - Quality profile name - Current quality / desired quality - Synopsis text **Search tab:** - Auto-searches indexers for media title - Results via `ReleaseSearchResults` component - Sortable by quality, size, seeders, age **Files tab:** - File table: name, quality, size, source, codec, subtitles - Per-file subtitle badges (language code, SDH, Forced, source) - "Search" button per file → subtitle search modal - "Download" button per subtitle result - "Extract All Subtitles" button (extracts embedded subs) **Episodes tab (series only):** - Episodes grouped by season - Columns: episode #, title, status, monitored, has file, quality **History tab:** - Timeline with color-coded badges: Grab, Import, Download, Failed, Upgrade, Blocked, Error - Relative timestamps (Xm ago, Xh ago, Xd ago) **Header actions:** - "Refresh Metadata" button → `POST /api/media/:type/:id/refresh-metadata` - "Delete" button → confirmation modal → `DELETE /api/media/:type/:id` → redirect to Library ### Discover (`/discover`) **API:** `GET /api/discover/trending?type=movie&page=1` (401 — requires API key auth) **API:** `GET /api/discover/popular?type=movie&page=1` (401 — requires API key auth) - Trending / Popular tab toggle - Movie / Series type filter - Poster grid with hover overlay (title, year, rating, "Add to Library" button) - Green checkmark badge for items already in library - "Add to Library" → `POST /api/discover/add` → toast feedback - Paginated (prev/next, up to page 10) **Note:** Requires API key authentication. Frontend does not currently send API keys, so this flow will 401 in production. ### Calendar (`/calendar`) **API:** `GET /api/calendar?month=2026-04` (200 OK) - Monthly grid (6 rows × 7 cols) - Navigation: prev/next month, "Today" button - Events color-coded by type: movie (indigo), series (emerald), music (amber), audiobook (rose), podcast (violet) - Click event → navigate to media detail - "Today" date highlighted with indigo ring - "+N more" for days with >3 events ### Download Queue (`/queue`) **API:** `GET /api/queue` (200 OK) - Tabs: All, Downloading, Pending, Failed, Imported, History - Auto-refresh every 5 seconds - Per-item: release title, status badge, download client, quality, size - Progress bar for downloading items - Error message display for failed items - Actions: Cancel (with confirmation modal), Retry, Retry All Failed, Clear Completed - "Check for Completed Downloads" → `POST /api/imports/trigger` - Import History tab with pagination (`GET /api/imports/history`) ### Search Indexers (`/search`) **API:** `GET /api/releases/search?query=...` (500 — "no enabled indexers available" when none configured) - Free-text search across all enabled indexers - Sortable columns: Quality, Title, Size, Indexer, Seeders, Age - "Select & Grab" button per result → opens media selector modal - Modal: debounced library search - Select media item → `POST /api/releases/grab` → toast - Empty state for no query, no results, loading skeleton **Note:** Returns 500 when no indexers are configured. This is expected behavior. ### Activity (`/activity`) **API:** `GET /api/activity?page=1&page_size=50` (200 OK) - Paginated event log (50 per page) - Type filter dropdown: All, Grabs, Imports, Downloads, Failures, Upgrades, Safety Blocks, Errors - Color-coded badges: Grab (blue), Import/Download (green), Failed/Error (red), Upgrade (purple), Blocked (orange) - Relative timestamps ### Requests (`/requests`) **API:** `GET /api/requests?page=1&page_size=20` (401 — requires API key auth) - Filter tabs: All, Pending, Approved, Fulfilled, Rejected - "+ New Request" modal: - Title (required), Type (7 options), Year, Quality Profile dropdown, Root Folder dropdown - Submit → `POST /api/requests` - Per-request card: title, year, status badge, requester, time ago, quality profile, root folder - Actions for pending: Approve (green) → `PUT /api/requests/:id/approve`, Reject (red, with confirmation) → `PUT /api/requests/:id/reject` - Withdraw link for pending/approved → `DELETE /api/requests/:id` **Note:** Requires API key authentication. Frontend does not currently send API keys. ### Blocklist (`/blocklist`) **API:** `GET /api/blocklist?page=1&page_size=50` (200 OK) - Paginated table (50 per page) - Checkboxes for bulk selection (select all toggle) - Per-row: release title, indexer, quality, reason, date, delete button - Bulk "Delete Selected" → `DELETE /api/blocklist/:id` per item - "Clear Expired" → `DELETE /api/blocklist/expired` - "Clear All" → confirmation modal → `DELETE /api/blocklist` ### Settings (`/settings`) 8 sections in 2-column grid layout: #### Notifications **API:** `GET /api/notifications/channels` (200 OK) - List channels with enable/disable dot, name, type badge (webhook/telegram), config preview - Inline edit: name, URL/bot token/chat ID, event type checkboxes (8 types), save/cancel/test/delete - Add channel: name, type selector, URL or bot token + chat ID, event type checkboxes - Test button → `POST /api/notifications/channels/:id/test` #### Indexers **API:** `GET /api/indexers` (200 OK) - List with enable/disable dot, name, implementation badge (torznab/newznab/cardigann), URL - Inline edit: name, URL, API key (masked), enable toggle, save/cancel/test/delete - Add indexer: name, implementation selector (Newznab, Torznab, Cardigann) - Cardigann: YAML textarea, validate button → `POST /api/indexers/validate-cardigann`, settings fields from definition - Test → `POST /api/indexers/:id/test` #### Download Clients **API:** `GET /api/download-clients` (200 OK) - List with enable/disable dot, name, implementation, URL - Inline edit: name, URL, API key, category, priority, enable toggle, save/cancel/test/delete - Add: name, implementation (SABnzbd, qBittorrent), URL, API key - Test → `POST /api/download-clients/:id/test` #### Quality Profiles **API:** `GET /api/quality-profiles` (200 OK) - List with name, media type badges - Inline edit: name, cutoff quality, allowed qualities (comma-sep), save/cancel/delete - Add: name, media types (comma-sep), cutoff quality, allowed qualities #### Root Folders **API:** `GET /api/root-folders` (200 OK) - List with path, media type, free space - Inline edit: path, media type (delete + re-create pattern), save/cancel/delete - Add: path, media type #### Tags **API:** `GET /api/tags` (200 OK — fixed) - List with name, color swatch - Inline edit: name, color, save/cancel/delete - Add: name, color (hex) #### Tasks **API:** `GET /api/workers` (401 — requires API key auth, different from `/api/tasks`) - Scheduled tasks list with name, cron expression, enabled toggle, last run, next run - Enable/disable toggle per task **Note:** Frontend calls `/api/tasks` but router registers `/api/workers`. May result in 404. #### Metadata **API:** `POST /api/media/refresh-all` (200 OK, no auth required) - Description text about refresh scope - "Refresh All Metadata" button → confirmation modal → POST - Loading state while refreshing --- ## Shared Components | Component | Used By | Purpose | |-----------|---------|---------| | `Toast` | All pages | Success/error feedback (top-right, auto-dismiss) | | `ConfirmModal` | Queue, Blocklist, Media Detail, Settings, Requests | Confirmation for destructive/batch actions | | `Pagination` | Library, Queue, Activity, Blocklist, Requests | Page navigation with total/pages display | | `StatusBadge` | Library, Queue, Media Detail, Requests | Color-coded status labels | | `ErrorBanner` | All pages | Error display with retry button | | `Loading` | Dashboard, Queue, Activity, Blocklist, Settings | Skeleton/spinner loading states | | `ReleaseSearchResults` | Media Detail, Search | Release result table with grab actions | --- ## API Test Results (2026-04-22) ### Working (200 OK) | Endpoint | Status | Notes | |----------|--------|-------| | `GET /health/live` | 200 | `{"status":"alive"}` | | `GET /api/dashboard` | 200 | Stats returned, all zeros (empty DB) | | `GET /api/media` | 200 | Paginated, empty | | `GET /api/queue` | 200 | Empty | | `GET /api/blocklist` | 200 | Paginated, empty | | `GET /api/activity` | 200 | Paginated, empty | | `GET /api/indexers` | 200 | Empty | | `GET /api/download-clients` | 200 | Empty | | `GET /api/quality-profiles` | 200 | Empty | | `GET /api/root-folders` | 200 | Empty | | `GET /api/tags` | 200 | Empty (fixed: removed `created_at` column ref) | | `GET /api/notifications/channels` | 200 | Empty | | `GET /api/calendar` | 200 | Empty events | | `GET /api/search` | 200 | Empty results | | `GET /api/imports/history` | 200 | Empty | | `POST /api/media/refresh-all` | 200 | Triggers refresh | ### Auth-Required (401 Unauthorized) | Endpoint | Notes | |----------|-------| | `GET /api/requests` | API key auth middleware | | `GET /api/discover/trending` | API key auth middleware | | `GET /api/discover/popular` | API key auth middleware | | `GET /api/workers` | API key auth middleware | **Root cause:** Frontend `client.ts` does not send `X-API-Key` header or `api_key` query param. These flows (Requests, Discover, Tasks) will 401 in production unless auth is configured or the frontend is updated to pass API keys. ### Expected Errors (500) | Endpoint | Error | Cause | |----------|-------|-------| | `GET /api/releases/search` | "no enabled indexers available" | No indexers configured yet | --- ## Known Issues 1. **Auth-protected endpoints unreachable from frontend** — Requests, Discover, and Tasks pages will show errors because the API client doesn't pass API keys. The `newAPIKeyAuth` middleware requires `X-API-Key` header or `api_key` query param. 2. **Tasks page route mismatch** — Frontend calls `/api/tasks` but router registers workers at `/api/workers`. Needs alignment. 3. **No indexers configured** — Release search (used by Search page and Media Detail Search tab) returns 500. Expected until indexers are added via Settings. 4. **Frontend CORS** — CORS is configured to allow `cfg.FrontendURL` only. Requests from other origins will be rejected.