Web UI rework lifecycle: verify + sync phases. - verify-report.md: PASS verdict (23/64 vitest, 5/5 node, zero @mui in src, 71/71 tasks, all spec scenarios green). Non-blocking findings recorded: slice-5 review-budget overage (5a/5b not split); node --test tests cmd typo. - sync-report.md + openspec/specs/web-ui/spec.md: canonical domain spec distilled from the verified change (design system, thin-dashboard model, TanStack visibility-only tables, reconciled IA, removed deps, Vitest harness). - Change-side delta spec under openspec/changes/web-ui-rework/specs/.
13 KiB
Web UI — Delta (web-ui-rework)
Change:
web-ui-rework· Domain:web-ui· Phase: spec (reconciled duringsdd-sync). Distilled from the verified flatspec.md+design.mdof changeweb-ui-rework. Captures the durable, post-change end-state contracts of the Manage web frontend, not the migration steps (the per-slice delivery strategy and per-slice build/lint gate are intentionally excluded as migration-process contracts; they remain documented in the changespec.md/tasks.md).
ADDED Requirements
The canonical
openspec/specs/web-ui/spec.mddid not exist before this change. All requirements below are therefore ADDED to a newweb-uidomain;sdd-synccopies them verbatim into the canonical spec (native helper rule: when the canonical spec does not exist, the change spec becomes the new canonical spec).
Requirement: Single design system
The frontend MUST render every surface using only shadcn/ui primitives, Tailwind v4 @theme
tokens (configured in frontend/src/index.css), and lucide-react icons. The application MUST NOT
introduce any additional component or styling library, and MUST NOT retain any @mui/* or
@emotion/* import or dependency.
Scenario: No MUI or Emotion remains in source or dependencies
- GIVEN the
web-ui-reworkchange is fully applied - WHEN a recursive search of
frontend/srcis performed for imports from@mui/material,@mui/icons-material,@mui/x-data-grid,@emotion/react, or@emotion/styled - THEN the search returns zero matches
- AND none of
@mui/material,@mui/icons-material,@mui/x-data-grid,@emotion/react, or@emotion/styledis listed infrontend/package.json
Scenario: No additional design system adopted
- GIVEN the rework is applied
- WHEN
frontend/package.jsondependencies are inspected - THEN no component or styling library other than the existing shadcn/ui + Tailwind v4 + lucide-react + Radix primitives stack is present
Requirement: No in-app charting and orphaned charting dependencies removed
The frontend MUST NOT render any in-app chart, sparkline, time-series canvas, or log-stream
visualization. The unused recharts and d3 dependencies MUST be absent from
frontend/package.json, and the no-op frontend/src/theme.ts shim MUST be deleted. Observability
metric surfaces MUST be expressed only as numeric values, status Badges, or outbound Grafana
deep-link affordances.
Scenario: Charting dependencies are gone
- GIVEN the rework is applied
- WHEN
frontend/package.jsonis inspected - THEN neither
rechartsnord3is listed as a dependency - AND a recursive search of
frontend/srcfor imports fromrecharts,d3, ord3-*returns zero matches
Scenario: No chart component exists
- GIVEN the rework is applied
- WHEN the migrated pages and components are inspected
- THEN no component renders an in-app chart, sparkline, or graph canvas
- AND metric surfaces render only as numeric values, status Badges, or outbound Grafana deep-links
Scenario: Theme shim removed
- GIVEN the foundation slice is applied
- WHEN
frontend/src/theme.tsis checked for existence - THEN it does not exist
- AND no import of
themeorgetAppThemeremains infrontend/src
Requirement: Design tokens — primary color and repurposed status cues
The primary color MUST resolve to #4f8cff in both the light and dark themes. The existing
chart-1 through chart-5 CSS tokens in frontend/src/index.css MUST be retained and documented
as the single source of truth for status / Grafana-link semantic color cues (info / success /
warning / destructive / neutral-accent), and MUST NOT be dropped. The Badge component MUST expose
success and warning variants that draw color from these cues.
Scenario: Primary color unchanged
- GIVEN the rework is applied
- WHEN
frontend/src/index.cssis inspected - THEN the primary color token resolves to
#4f8cffin both the@theme(light) and.darkblocks
Scenario: Chart tokens retained and semantically repurposed
- GIVEN the rework is applied
- WHEN
frontend/src/index.cssis inspected - THEN the
chart-1throughchart-5tokens are still defined in both blocks - AND they are documented/applied as status / Grafana-link semantic cues (info, success, warning, destructive, neutral-accent)
- AND status Badges draw their variant colors from these cues
Requirement: Comfortable visual density with no compact mode
Every surface MUST use comfortable density: page padding p-4 md:p-6 (matching the migrated shell
<main>) and card spacing gap-4. The application MUST NOT introduce a compact or dense mode for
any table, list, or panel.
Scenario: Comfortable density on every page
- GIVEN each migrated page is rendered
- WHEN the page content container is inspected
- THEN it uses comfortable padding consistent with the shell (
p-4 md:p-6) and card gaps (gap-4)
Scenario: No compact mode exists
- GIVEN the rework is applied
- WHEN the frontend is inspected for a density/compact toggle or compact spacing classes on tables
- THEN no compact or dense mode exists for Media, Users, Backups runs, or any other surface
Requirement: Status Badge semantic variants
The Badge component MUST provide variants covering success (healthy/OK), info (default), neutral
(secondary), warning, and destructive (error / firing alert). Status displays across Backups,
Observability, and Users MUST map status values to these variants consistently via the repurposed
chart-* cues.
Scenario: Healthy status uses a success cue
- GIVEN a monitoring target or backup run is in a healthy/OK state
- WHEN its status is rendered
- THEN it displays a Badge using the success color cue (from
chart-2)
Scenario: Firing alert uses a destructive cue
- GIVEN an Alertmanager alert is firing
- WHEN its status is rendered
- THEN it displays a destructive Badge (from
chart-4)
Requirement: Information architecture — Backups top-level navigation
The Backups surface MUST be exposed as a top-level navigation item in the sidebar, linking to the
existing /backups route.
Scenario: Backups is reachable from the sidebar
- GIVEN the application shell is rendered
- WHEN the sidebar navigation items are inspected
- THEN a top-level "Backups" item is present
- AND selecting it navigates to
/backupsand renders the Backups page
Requirement: Information architecture — Media route and legacy redirects
The Media/Applications surface MUST be named "Media" going forward, and its canonical route MUST
be /media. The legacy /applications route MUST redirect (replace) to /media, mirroring the
existing /monitoring → /observability redirect. The OIDC authentication model and the
react-router-dom routing structure MUST be otherwise unchanged.
Scenario: Media route is the canonical entry
- GIVEN the rework is applied
- WHEN the sidebar "Media" item is selected
- THEN the browser navigates to
/media - AND the Media surface is rendered under the name "Media"
Scenario: Legacy /applications redirects to /media
- GIVEN the application is running
- WHEN a user navigates directly to
/applications - THEN the client router issues a replace redirect to
/media - AND the Media surface is rendered (same pattern as
/monitoring→/observability)
Scenario: Auth and routing model unchanged
- GIVEN the rework is applied
- WHEN the authentication and routing setup is inspected
- THEN the OIDC flow (
auth.ts,react-oidc-context) and thereact-router-domstructure are unchanged - AND only nav items, the
/mediaroute, and the/applicationsredirect differ from the prior state
Requirement: Data tables use TanStack Table with visibility-only features
The Media and FileBrowser data grids MUST be built on @tanstack/react-table behind a shared
DataTable wrapper styled with the shadcn Table primitive. The rebuilt tables MUST reproduce only
the features in use: column visibility, pagination (where present), row selection, and
row click. The tables MUST NOT add column sorting, and MUST NOT add column resizing.
Scenario: No sorting added
- GIVEN the rebuilt Media and FileBrowser tables are rendered
- WHEN the table headers and column definitions are inspected
- THEN no
getSortedRowModel, sortable-column behavior, or sort affordance is present
Scenario: No column resizing added
- GIVEN the rebuilt Media and FileBrowser tables are rendered
- WHEN the column edges are inspected
- THEN no column-resize handles or resizing behavior are present
Requirement: Data table interaction parity preserved
The rebuilt tables MUST preserve their pre-rework interaction behavior: column visibility (Media's 15 columns and FileBrowser's 5 columns), pagination (Media, server-driven via the query layer), row selection, and row click.
Scenario: Column visibility preserved
- GIVEN the rebuilt tables are rendered
- WHEN a user toggles the visibility of a column
- THEN on Media the toggleable columns match
title, series, season, episode, type, year, runtime, size, bitrate, hdr, video, resolution, date_added, library, path - AND on FileBrowser the toggleable columns match
type, name, ext, size, modified
Scenario: Media pagination parity
- GIVEN the rebuilt Media table is rendered with more rows than one page
- WHEN a user changes the page size and navigates between pages
- THEN the visible rows, total count, and page index update consistently with the pre-rework behavior (page index/size drive the query layer's limit/offset)
Scenario: Row selection preserved
- GIVEN the rebuilt tables are rendered
- WHEN a user selects and deselects rows via the selection control
- THEN the selection state is maintained across pagination and matches the pre-rework model
Scenario: Row click behavior preserved
- GIVEN the rebuilt tables are rendered
- WHEN a user clicks a Media row
- THEN navigation occurs to the file browser at the clicked item's path
- AND WHEN a user clicks a FileBrowser row
- THEN that file is selected for ffprobe preview
Requirement: Thin-dashboard observability parity
The Observability surface MUST continue to render Alertmanager alerts, Prometheus target health, monitoring-machine status, and Grafana deep-link cards (kiosk iframe URLs and Explore links). The rework MUST NOT introduce any in-app chart, and MUST preserve all existing Grafana deep-link affordances.
Scenario: Alerts and target health still shown
- GIVEN the Observability page is rendered with Alertmanager and Prometheus data available
- THEN Alertmanager alerts and Prometheus target health are displayed as before the rework
Scenario: Grafana deep-links preserved
- GIVEN the Observability page is rendered
- WHEN a Grafana deep-link card is inspected
- THEN the outbound kiosk/explore URL with instance variables is preserved
- AND a consistent external-link affordance is present across the surface
Scenario: No in-app chart on observability
- GIVEN the Observability page is rendered
- WHEN metric surfaces are inspected
- THEN no in-app chart is rendered; metrics are numbers, status Badges, or Grafana deep-links only
Requirement: Frontend component test harness
A Vitest + @testing-library/react component test harness MUST be present and configured.
Migrated components MUST be covered by behavioral component tests. The legacy
frontend/tests/*.mjs node --test suites MUST continue to run and pass.
Scenario: Vitest harness is present
- GIVEN the rework is applied
- WHEN
frontend/package.jsonand config are inspected - THEN Vitest and
@testing-library/reactare installed - AND a Vitest test script is configured
Scenario: Legacy node:test suites keep passing
- GIVEN the rework is applied
- WHEN the
frontend/tests/*.mjssuites are executed withnode --test - THEN all pre-existing assertions still pass
Requirement: Documentation reflects the post-rework architecture
docs/REQUIREMENTS.md MUST reflect the post-rework UX and architecture: the single design system
(shadcn/ui + Tailwind + lucide-react), the thin-dashboard observability model (no in-app charts),
TanStack tables, the /media route with /applications redirect, the Backups top-level nav item,
and the removal of @mui/*, @emotion/*, recharts, and d3.
Scenario: Requirements doc reflects the rework
- GIVEN the final slice is applied
- WHEN
docs/REQUIREMENTS.mdis inspected - THEN it documents the shadcn/ui + Tailwind + lucide-react stack, the thin-dashboard observability model (no in-app charts), the
/mediaroute with/applicationsredirect, the Backups top-level nav item, and the removed dependencies
Requirement: No backend API or frontend data-contract changes
The rework MUST NOT change any backend API contract or any type in frontend/src/types/*. If a UI
simplification forces a contract change, that change MUST be flagged and approved separately.
Scenario: Frontend data contracts unchanged
- GIVEN the rework is applied
- WHEN
frontend/src/types/*and the backend API surface are compared to the pre-rework state - THEN no backend endpoint, response shape, or shared frontend type is altered by this change