# Exploration — web-ui-rework > Phase: **explore**. Evidence-grounded mapping of the rework surface. No code changes. > Recovered by the orchestrator after the `sdd-explore` subagent hit an MCP-bridge heap OOM; > all figures below were re-derived directly from source with multiline-aware parsing and verified with `grep`. ## 1. Scope and non-goals ### In scope - Finish the **MUI v9 → shadcn/ui + Tailwind CSS + lucide-react** migration for every remaining `@mui/*` consumer in `frontend/src`. - Deliver a **visual/UX redesign** on top of the migrated primitives, consistent with the already-migrated style references: `frontend/src/App.tsx` (shell + sidebar) and `frontend/src/components/ObservabilityPage.tsx`. - Align the UI to the **new observability model**: Manage is a *thin dashboard*; charts/metrics/logs live in external, decoupled Grafana. In-app surfaces show Alertmanager alerts + Prometheus target health + Grafana deep-links only. - Remove orphaned charting deps (`recharts`, `d3`) and the no-op `theme.ts` shim. - Add the missing shadcn primitives and `@tanstack/react-table` needed for the migration. - Update `docs/REQUIREMENTS.md` to reflect the UX/architecture change (per `AGENTS.md`). ### Non-goals - **No in-app charting.** Do not re-introduce recharts/d3 or build custom charts; metrics visualisation stays in Grafana. - **No new design system.** Use shadcn/ui + the existing Tailwind v4 `@theme` token system; do not adopt a different component library. - **No backend API changes** as part of this rework. The data contracts in `frontend/src/types/*` are unchanged unless a UI simplification forces one, which must then be flagged separately. - **No MUI retention.** `@mui/material`, `@mui/icons-material`, `@mui/x-data-grid`, `@emotion/react`, `@emotion/styled` are all removed by the final slice. - **No auth/routing model change.** OIDC flow (`auth.ts`, `react-oidc-context`) and `react-router-dom` structure stay as-is; only nav *items*/IA may shift. ## 2. Extended component inventory (verified from source) Parsing note: MUI imports are multiline (`import {\n Box,\n Card,\n} from "@mui/material"`); figures below use a brace-bounded multiline parser and were cross-checked with `grep -rlE '@mui/(material|icons-material|x-data-grid)' src`. ### 2a. `@mui/material` — 22 files, 37 distinct components | File | # | Components | |---|---|---| | `pages/UsersPage.impl.tsx` | 25 | Alert, Avatar, Box, Button, Checkbox, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Drawer, IconButton, LinearProgress, Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Tooltip, Typography | | `pages/Dashboard.tsx` | 20 | Alert, Box, Button, Card, CardContent, Chip, Dialog, DialogContent, DialogTitle, FormControl, FormControlLabel, FormHelperText, Grid, InputLabel, MenuItem, Select, Stack, Switch, TextField, Typography | | `pages/Actions.tsx` | 19 | Alert, Box, Button, Card, CardContent, Chip, Dialog, DialogContent, DialogTitle, Divider, FormControl, InputLabel, MenuItem, Select, Stack, Tab, Tabs, TextField, Typography | | `pages/Settings.tsx` | 18 | Alert, Box, Button, Card, CardContent, Checkbox, Chip, Dialog, DialogContent, DialogTitle, FormControlLabel, Grid, MenuItem, Stack, Switch, Tab, TextField, Typography | | `pages/FileBrowser.impl.tsx` | 15 | Alert, Box, Button, Card, CardContent, Chip, FormControl, Grid, InputLabel, MenuItem, Select, Stack, Tab, TextField, Typography | | `pages/Media.tsx` | 14 | Alert, Box, Button, Card, CardContent, FormControl, Grid, InputLabel, LinearProgress, MenuItem, Select, Stack, TextField, Typography | | `components/BackupRunsTable.tsx` | 12 | Chip, FormControl, InputLabel, MenuItem, Paper, Select, Table, TableBody, TableCell, TableContainer, TableHead, TableRow | | `components/SessionActivityPanel.tsx` | 10 | Button, Chip, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography | | `components/BackupAlertsTable.tsx` | 9 | Button, Chip, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow | | `pages/Applications.tsx` | 9 | Alert, Box, Card, CardContent, Chip, Grid, Stack, Tab, Typography | | `components/BackupJobsTable.tsx` | 8 | Chip, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow | | `components/DiskSpaceCard.tsx` | 7 | Box, Card, CardContent, Grid, LinearProgress, Stack, Typography | | `components/BackupDashboardWidget.tsx` | 5 | Box, Card, CardContent, Chip, Typography | | `components/ConfirmDialog.tsx` | 5 | Dialog, DialogContent, DialogTitle, Stack, Typography | | `components/LibraryOverview.tsx` | 5 | Card, CardContent, Grid, Stack, Typography | | `components/SectionCard.tsx` | 5 | Box, Card, CardContent, Stack, Typography | | `components/BackupsPage.tsx` | 4 | Box, Tab, Tabs, Typography | | `components/SelectionRailCard.tsx` | 4 | Box, Card, CardContent, Typography | | `components/TabbedCard.tsx` | 4 | Box, Card, CardContent, Tabs | | `components/DialogFooter.tsx` | 3 | Box, Button, DialogActions | | `components/MetricCard.tsx` | 3 | Card, CardContent, Typography | | `components/HoverEditButton.tsx` | 1 | IconButton | **Component frequency (migration sizing):** Typography 16 · Box 14 · Card/CardContent 13 · Chip 11 · Stack 11 · Button 9 · Grid 7 · Alert 7 · MenuItem 6 · TextField 6 · Paper/Table*/FormControl/InputLabel/Select/Tab 5 · Dialog/DialogContent/DialogTitle 5 · Tabs 3 · LinearProgress 3 · DialogActions/IconButton/Divider/FormControlLabel/Switch/Checkbox 2 · FormHelperText/Avatar/Drawer/Tooltip 1. ### 2b. `@mui/x-data-grid` — 2 files (DataGrid) - `pages/Media.tsx` — `DataGrid`, `GridColDef` - `pages/FileBrowser.impl.tsx` — `DataGrid`, `GridColDef`, `GridRowSelectionModel` ### 2c. `@mui/icons-material` — 2 files, 10 distinct icons (default-import-per-icon style) | File | Icons → lucide-react equivalent | |---|---| | `components/HoverEditButton.tsx` | `EditOutlined` → `Pencil` | | `pages/UsersPage.impl.tsx` | `Close`→`X` · `AttachFile`→`Paperclip` · `FormatBold`→`Bold` · `FormatItalic`→`Italic` · `Link`→`Link` · `FormatListBulleted`→`List` · `MailOutlined`→`Mail` · `Send`→`Send` · `DeleteOutlined`→`Trash2` | > **lucide caveat:** `package.json` pins `lucide-react: ^1.14.0`, an unusual major. The named icons above (`Pencil`, `X`, `Paperclip`, `Bold`, `Italic`, `Link`, `List`, `Mail`, `Send`, `Trash2`) are standard lucide exports, but the version pin must be verified to export them before the UsersPage slice. ### 2d. Orphaned / unused deps (verified zero imports) - `recharts: ^3.8.1` — **no `from "recharts"` anywhere.** Removable. - `d3: ^7.9.0` — **no `from "d3"` / `from "d3-*"` anywhere.** Removable. - `frontend/src/theme.ts` — no-op shim (`getAppTheme` returns `{}`); safe to delete once no caller remains. ### 2e. Already-migrated style references (the design targets) - `frontend/src/App.tsx` — shell: fixed sidebar (`w-16`/`w-60` collapse), `TopBar` (sticky, backdrop-blur, version chips), `MobileDrawer` (shadcn `Sheet`), `useDarkMode` toggles `dark` class on ``. - `frontend/src/components/ObservabilityPage.tsx` (575 lines) — uses shadcn `Card/Badge/Alert/Button/Select/Skeleton/Collapsible` + 12 lucide icons; Grafana deep-links via kiosk URLs (`/d/node-exporter-overview/...?kiosk&var-instance=…`) and Explore. ## 3. MUI → shadcn/ui + Tailwind mapping table | MUI | shadcn/ui + Tailwind | Primitive status | |---|---|---| | `Card` / `CardContent` | `Card` / `CardContent` (`@/components/ui/card`) | **present** | | `Typography` | semantic `
`/`` + Tailwind `text-* font-*` | none needed |
| `Box` | ` ` | **add** `Label` |
| `DataGrid` (`@mui/x-data-grid`) | TanStack Table + shadcn `Table` (see §4) | **add** `@tanstack/react-table` |
## 4. DataGrid decision — TanStack Table (KEY TECHNICAL RISK)
**Recommendation:** migrate both DataGrid usages to **`@tanstack/react-table`** (headless) styled with the shadcn `Table` primitive. Rationale: headless (no opinionated styling, fits Tailwind), first-class sorting/filtering/pagination/selection/column-visibility, actively maintained, and the project already uses `@tanstack/react-query` so the dependency family is consistent.
**Feature parity that MUST be preserved** (derived from source):
| Page | Columns (`field`) | Features in use |
|---|---|---|
| `pages/Media.tsx` | title, series, season, episode, type, year, runtime_min, size, bitrate, hdr, video, resolution, date_added, library, path | pagination, row selection, row click (opens file browser), column visibility management, stable row id |
| `pages/FileBrowser.impl.tsx` | type, name, ext, size, modified | row selection, row click (selects file for ffprobe), column visibility management |
**Open parity questions for the proposal phase:**
- Does the redesign keep client-side column *resizing*, or only *visibility*? (Source scan shows visibility; resizing usage is ambiguous and must be confirmed against the live DataGrid props.)
- Sorting/filtering: Media has external controls already (its own filter UI); decide whether TanStack sorting is added or deferred to keep slice size ≤400 lines.
This is the single highest-risk slice; it is sequenced **last** so the shared Table primitive and design tokens are already settled.
## 5. shadcn primitives gap list
Add via `npx shadcn@latest add