# Proposal — web-ui-rework

> Phase: **proposal**. Grounded in `openspec/changes/web-ui-rework/exploration.md` (verified inventory). No code changes.
> Product/UX questions for the user are collected in **§8 — Proposal question round** and must be answered before this proposal is treated as locked.

## 1. Problem / motivation

The Manage frontend is **mid-migration**: the app shell (`App.tsx`) and the new `ObservabilityPage.tsx` already live on shadcn/ui + Tailwind + lucide-react, but **22 files still import `@mui/material`, 2 use `@mui/x-data-grid`, and 2 use `@mui/icons-material`** (10 distinct icons). The result is a split-feeling product: two design languages, two theming systems (MUI `@emotion` vs the Tailwind v4 `@theme` tokens in `index.css`), and a no-op `theme.ts` shim left behind. Orphaned charting deps (`recharts`, `d3`) linger in `package.json` even though **no in-app charting exists**.

This half-finished state collides with a second driver: Manage has moved to a **thin-dashboard observability model** where charts/metrics/logs live in external Grafana, and in-app surfaces show only Alertmanager alerts + Prometheus target health + Grafana deep-links. Finishing the migration is therefore not cosmetic — it is how we *codify* the decoupling (remove the charting deps, retire the last MUI surface, make the external-link affordance consistent) and land a coherent visual redesign on one design system.

## 2. Target outcome / product vision

A single, coherent Manage UI on **shadcn/ui + Tailwind v4 + lucide-react**, visually consistent with the already-migrated shell and ObservabilityPage:

- Every page uses the same tokens (primary `#4f8cff`, Inter, `rounded-lg`, one border weight) and the same layout primitives — `Card` over ad-hoc `Paper`, Tailwind flex/grid over MUI `Grid`/`Stack`.
- Observability stays first-class and **thin**: status as numbers/Badges plus outbound Grafana kiosk deep-links; **no in-app charts**.
- DataGrid surfaces (Media, FileBrowser) become TanStack Table on the shadcn `Table` primitive, preserving pagination / row-selection / row-click / column-visibility.
- A reconciled information architecture (Backups surfaced in nav; one name for Media/Applications).
- `@mui/*`, `@emotion/*`, `recharts`, `d3`, and the `theme.ts` shim are gone, and `docs/REQUIREMENTS.md` reflects the new UX.

## 3. Scope (in)

- Finish MUI v9 → shadcn/ui + Tailwind + lucide-react across all remaining consumers in `frontend/src` (22 material files + 2 DataGrid files + 2 icon files).
- Add the missing shadcn primitives (`tabs table dialog input label checkbox switch progress separator avatar textarea dropdown-menu scroll-area`) and `@tanstack/react-table`.
- Migrate both `@mui/x-data-grid` DataGrid usages to TanStack Table + shadcn `Table`.
- Deliver a visual/UX redesign aligned to the thin-dashboard model (density, typography ramp, status Badge semantics including a success variant, consistent external-link affordance).
- Remove orphaned `recharts`, `d3`, the `theme.ts` shim, and all `@mui/*` + `@emotion/*` deps.
- Reconcile IA: surface Backups in nav; resolve Media vs Applications naming.
- Update `docs/REQUIREMENTS.md`.

## 4. Non-goals

- **No in-app charting** — no recharts/d3 re-introduction, no custom charts; metrics visualisation stays in Grafana.
- **No new design system** — shadcn/ui + the existing Tailwind v4 `@theme` tokens only.
- **No backend API changes** — frontend `types/*` contracts stay unless a UI simplification forces one, which must be flagged separately.
- **No MUI retention** — all `@mui/*` and `@emotion/*` removed by the final slice.
- **No auth/routing model change** — OIDC (`auth.ts`, `react-oidc-context`) and `react-router-dom` structure unchanged; only nav *items*/IA may shift.
- **No form-library introduction** — keep current uncontrolled/`useState` form parity.

## 5. Key technical risks

- **DataGrid → TanStack Table (high).** The two DataGrid usages (`Media.tsx`, `FileBrowser.impl.tsx`) must reproduce pagination, row-selection, row-click, and column-visibility. Whether to also carry over client-side **resizing/sorting** is an open product question (exploration §4) and is the single highest-risk slice — sequenced **last** so the shared `Table` primitive and tokens are already settled.
- **UsersPage size (high).** `pages/UsersPage.impl.tsx` is the largest consumer (25 MUI components + 9 icons + `Drawer` + `Table` + a rich-text compose UI with formatting actions and attachments). Likely needs a sub-split to stay under the 400-line slice budget.
- **Cross-slice visual drift (medium).** 22 files over ~8 slices; mitigated by migrating shared building blocks first (exploration §9 slice 2) and documenting the Tailwind layout patterns up front.
- **lucide-react v1 pin (low–medium).** `^1.14.0` is an unusual major; the 10 mapped icon names must be verified to exist before the UsersPage slice.
- **No component test harness today (medium).** See exploration §11; whether to add Vitest in slice 1 is an open question.

## 6. High-level slice strategy

Chained PRs, ≤400 changed lines each, each leaving `npm run build` + `npm run lint` green. Full table and sequencing rationale live in exploration.md **§9**; summary:

1. **Foundation** — shadcn primitives + `@tanstack/react-table`, remove `recharts`/`d3`, delete `theme.ts`.
2. **Shared components** — `SectionCard`, `SelectionRailCard`, `TabbedCard`, `MetricCard`, `DiskSpaceCard`, `HoverEditButton`, `DialogFooter`, `ConfirmDialog`, `LibraryOverview`, `NowPlaying`/`SessionActivityPanel`.
3. **Backups cluster** (+ surface `/backups` in nav).
4. **Dashboard + Applications**.
5. **Settings + Actions**.
6. **Users** (largest; may sub-split).
7. **DataGrid → TanStack Table** — Media + FileBrowser (highest risk; last).
8. **Cleanup + docs** — remove `@mui/*` + `@emotion/*`, final typecheck/lint, update `docs/REQUIREMENTS.md`.

Slices 6 and 7 are the most likely to need sub-splitting to stay ≤400 lines.

## 7. Success criteria

- `npm run build` (tsc -b + vite build) and `npm run lint` green; existing `node --test` suites pass.
- **Zero `@mui/*` or `@emotion/*` imports remain** in `frontend/src` (verified by grep); `recharts`, `d3`, and `theme.ts` removed.
- Both DataGrid surfaces rebuilt on TanStack Table with pagination / row-selection / row-click / column-visibility parity.
- **ObservabilityPage parity preserved** (Alertmanager alerts + Prometheus target health + Grafana deep-links); no in-app charts introduced.
- Visual consistency with the migrated shell + ObservabilityPage; Backups surfaced in nav; Media/Applications naming resolved.
- `docs/REQUIREMENTS.md` updated to reflect the UX/architecture change.

## 8. Proposal question round

The following product/UX questions need user answers before this proposal is treated as locked. They are intended to uncover business rules, IA intent, and scope boundaries — not delivery mechanics. *(Parent: ask these verbatim.)*

**Q1 — Information architecture (parent-ask):** Today the sidebar lists Dashboard, Observability, Media, Files, Users, Actions, Settings — but "Media" routes to `/applications` (component `Applications`), and a working **Backups** page (`/backups`) is hidden from the nav. Do you want (a) **Backups as its own top-level nav item**, (b) Backups grouped under a "Media ops" section, or (c) left as URL-only? And should the Media/Applications surface be named **"Media"** or **"Applications"** going forward?

**Q2 — Visual density (parent-ask):** The migrated shell uses **comfortable** spacing (`p-4 md:p-6`, `gap-4`). Should the redesign keep comfortable density across all 22 pages, or do some dense tables (Media, Users, Backups runs) need a **compact** mode to fit more rows on screen?

**Q3 — TanStack Table feature parity (parent-ask):** The current DataGrids use column **visibility**; **resizing** and **sorting** usage is ambiguous. For the rebuilt tables, do you want (a) **visibility-only** parity (smallest scope), (b) add **sorting**, or (c) add **sorting + column resizing** (closest to a full DataGrid)?

**Q4 — Test harness (parent-ask):** There is **no component test harness** today (only `tsc -b`, `eslint`, and `node --test` on pure-JS transforms). Should slice 1 introduce a **minimal Vitest + Testing Library** harness for the migrated components, or proceed on **static + manual smoke** evidence per slice?

**Q5 — Palette (parent-ask):** The current primary is **`#4f8cff`** with `chart-1..5` tokens defined but unused. Do you want to (a) **keep `#4f8cff`** and repurpose the chart tokens as Grafana-link color cues, (b) **shift the primary** to a new accent (please specify), or (c) keep the palette as-is and **drop** the unused chart tokens entirely?

---

**Recommended next phase:** `spec` — after the §8 questions are answered, refine scope/token/IA decisions and lock the slice contracts.
