# Slice 3 Review — Media table mobile layout (`mobile-responsive-parity`) Reviewer: fresh adversarial pass. Scope: unstaged diff on `frontend/src/pages/Media.tsx` and `frontend/src/pages/__tests__/Media.test.tsx`. ## Verification commands run | Command | Result | |---|---| | `npm run lint` | pass (0 errors; 2 pre-existing warnings in `UsersPage.impl.tsx`, unrelated to Slice 3) | | `npm run build` | pass (`tsc -b` + vite, built in 854ms) | | `npm run test` | pass (25 files, 94 tests) | ## Point-by-point ### 1. Desktop non-regression — CONFIRMED CORRECT The diff is a clean branch-add, not a rewrite. The `DataTable` block was moved into the `else` of `isMobile ? : ` with every prop byte-for- byte identical to the pre-change version (`Media.tsx:671-697`): `columns`, `data`, `getRowId`, `enableRowSelection`, `rowSelection`, `onRowSelectionChange`, `onRowClick`, `enableColumnVisibilityToggle`, `columnVisibility`, `onColumnVisibilityChange`, `enablePagination`, `manualPagination`, `pagination`, `onPaginationChange`, `pageSizeOptions`, `rowCount`, `emptyMessage`. The wrapping `
` and the `status?.exists` gate are preserved on both branches. No desktop prop was dropped, renamed, or reordered. R3.6 / R10.1 satisfied. ### 2. Mobile card fields — CONFIRMED CORRECT `mediaCardFields` (`Media.tsx:83-96`) matches the real `MediaItem` type (`types/index.ts:274`), not the design doc's illustrative field names: - `title` (string) — primary ✓ - `size` → `r.size || "-"` (string, null-safe) ✓ - `hdr` → `r.hdr || "-"` (string, null-safe) ✓ - `library` → `r.library || "-"` (string, null-safe) ✓ - `year` (`number | null`) → `r.year != null ? String(r.year) : "-"` ✓ explicitly null-safe 5 fields total (1 primary + 4), inside the spec's 3–5 range (R3.2). No undefined access possible — every field guards against empty/null. The design example used `size_display`/`is_hdr`/`library_name` (illustrative); the worker correctly used the real keys. Good. ### 3. Pagination duplication — NOT A BUG; acceptable tech debt `MediaMobilePagination` (`Media.tsx:107-188`) duplicates `DataTablePagination` (`data-table.tsx`). I verified the semantics match exactly: | Concern | DataTable | MediaMobilePagination | Match | |---|---|---|---| | Rows count | `rowCount ?? 0` (manual) | `totalRows` = `total` (`queryResult?.total ?? 0`) | ✓ | | pageCount | `Math.max(1, Math.ceil(rowCount/pageSize))` | `totalPages` = `Math.max(1, Math.ceil(total/pageSize))` (`Media.tsx:403`) | ✓ | | Page-size change | `table.setPageSize()` → resets `pageIndex:0` | `onPaginationChange(() => ({pageIndex:0, pageSize:Number(value)}))` | ✓ | | Prev disabled | `!getCanPreviousPage()` = `pageIndex>0` inverted | `pageIndex <= 0` | ✓ | | Next disabled | `!getCanNextPage()` = `pageIndex>=pageCount-1` inverted | `pageIndex >= pageCount - 1` | ✓ | | Page indicator | `Page {pageIndex+1} of {pageCount}` | same | ✓ | No off-by-one, no missing clamp, no stale state. The mobile component reads `pageIndex`/`pageSize` derived the same way as the controlled `pagination` state fed to DataTable (`Media.tsx:355-356`), so the two paths can't drift on values. Could they reuse DataTable's pagination by extracting it? That would require editing the shared `data-table.tsx` (export `DataTablePagination` or split a `TablePagination`), which is explicitly out of scope for Slice 3 and would risk R3.6/R10.1 (the shared component powers the desktop path). Acceptable to defer to a follow-up refactor slice. **Non-blocking smell, not a must-fix.** ### 4. Row click navigation — CONFIRMED CORRECT `handleRowClick` (`Media.tsx:398-400`) is passed unchanged to `MobileCardRow.onRowClick` (`Media.tsx:659`). `MobileCardRow` makes the whole card a `