e805c624b2
Below md, the machine editor Dialog renders as a SheetForm (triggered by the same Edit/Add buttons via machineDialogOpen state). The shared MachineEditor body (fields + SSH validate button) renders inside the sheet; the ConfirmDialog is a sibling outside. Desktop Dialog is byte-for-byte identical. No navigation needed on close -- the Settings page content (tabbed cards, machine list) is always visible behind the sheet, so there is no stranding risk (unlike ServicePage where the sheet was the whole page). Added saveDisabled prop to SheetForm (additive, default false) so the machine editor can gate Save on required fields (name + host for SSH mode), matching the desktop DialogFooter confirmDisabled semantics. Scope note: SSHKeyManager is an inline two-panel layout (SelectionRailCard + SectionCard), not a dialog, and already stacks responsively via grid-cols-1 md:grid-cols-[...]. Wrapping it in SheetForm would break its always-visible selection rail. Left as-is. Tests: 3 new mobile cases (SheetForm render, save payload, cancel closes) + desktop unchanged. 113 tests pass; lint/build green. Refs openspec/changes/mobile-responsive-parity/ (spec R4, tasks slice 7).
Manage Frontend
React + TypeScript SPA for Manage, consuming the FastAPI backend.
Tech Stack
- Vite — Build tool
- React 18+ — UI framework
- TypeScript — Type safety
- @tanstack/react-query — Data fetching/caching
- @tanstack/react-table — Data tables (media, file browser)
- react-router-dom — Client-side routing
- Tailwind CSS + shadcn/ui — Styling
Setup
cd frontend
npm install
Development
npm run dev
Runs on http://localhost:5173 with API requests proxied to http://localhost:8000.
Make sure the backend is running:
cd ../backend
uvicorn media_library_viewer_api.main:app --reload --port 8000
Build
npm run build
Output goes to frontend/dist/.
Pages
- Dashboard (
/) — Now playing, library stats, configurable widgets and shortcuts, frontend/backend version chips in the shell header - Observability (
/observability) — Thin dashboard: Alertmanager alerts, Prometheus target health, machine status, and Grafana deep-links (no in-app charting) - Media (
/media) — Full-library table with sort/filter/search - Users (
/users) — Read-only Jellyfin user list with optional Jellyseerr enrichment - File Browser (
/files) — Remote directory browsing, ffprobe preview, jobs - Settings (
/settings) — Persistent monitoring machine definitions, SSH keys, and setup workflow - Actions (
/actions) — Saved server tasks (shell/python) targeting ssh_tasks services
Environment Variables
Set VITE_API_URL and any OIDC variables directly in your shell or Compose build args if the API is not at http://localhost:8000.
The frontend version defaults to the package.json version and can be overridden with VITE_APP_VERSION and VITE_APP_BUILD_INFO when you need explicit deployed labels.
VITE_API_URL=http://your-backend-host:8000
Configuration workflow examples
- Local development: run
docker compose -f docker-compose.dev.yml up --build, then open the app and add monitoring machines in the Settings tab. - Production: export the required Compose variables in your shell, run
docker compose up --build, and manage local/remote machines from Settings. - Observability: Manage only deploys backend + frontend. It connects to existing Grafana/Prometheus/Alertmanager instances; see
docker-compose.observability.ymlfor an optional standalone example stack. A machine can belocal(the API host itself) orssh(a remote host), and the UI treats both the same after configuration.
In development, the Vite proxy handles /api requests automatically. Observability services (Grafana, Prometheus, Alertmanager) are configured in the app on the Services page — there are no observability env vars.