Files
manage/openspec/changes/web-ui-rework/proposal.md
T
Developer dd778d8850 feat(frontend): slice 1 — foundation for MUI->shadcn migration
Web UI rework (openspec/changes/web-ui-rework). Foundation slice:
- Add @tanstack/react-table; remove orphaned recharts, d3
- Vendor shadcn primitives: tabs table dialog input label checkbox
  switch progress separator avatar textarea dropdown-menu scroll-area
- Add Vitest + @testing-library/react + jsdom harness; npm test script
- Delete no-op theme.ts shim; remove getAppTheme references
- Smoke test proves the harness

Gate: build + lint + test green.
2026-06-17 12:11:05 +00:00

90 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (lowmedium).** `^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.