# Module: `src/features/dataset/DatasetPage.tsx`

> Compact module doc. Spec: `_docs/ui_design/README.md` (Dataset Explorer Layout) and parent suite `../../../../_docs/09_dataset_explorer.md` (API contract). Imports `CanvasEditor` from `../annotations/` — see cross-feature edge in `00_discovery.md §8`.

## Purpose

Owns the `/dataset` route. Read-side surface for the `Annotations` table: paginated thumbnail grid, date / class / status / objects-only / name filters, inline editing through the Annotations Tab's `CanvasEditor`, and a class-distribution chart. Mirrors the legacy `Azaion.Dataset.DatasetExplorer` window (`_docs/legacy/wpf-era.md §5`).

## Public interface

Default-exported page component, no props. Mounts under `/dataset` in `App.tsx`.

## Internal logic

- **Tabs**: `'annotations'` (grid, default) | `'editor'` (hidden until a thumbnail is double-clicked) | `'distribution'` (bar chart). State held in component.
- **Filters**: `fromDate`, `toDate`, `statusFilter` (`AnnotationStatus`), `selectedClassNum` (from `DetectionClasses`), `objectsOnly` (boolean), `search` (400 ms debounced via `useDebounce`).
- **Pagination**: client `page` state, server `pageSize` fixed at 20, `totalPages = ceil(totalCount / pageSize)`.
- **Selection**: `Set<annotationId>`. Plain click replaces; Ctrl+click toggles. Validate button appears when set is non-empty.
- **Validate**: `POST endpoints.annotations.datasetBulkStatus()` (= `/api/annotations/dataset/bulk-status`) with `{ annotationIds[], status: Validated }`.
- **Distribution**: lazy-loaded on tab switch via `GET endpoints.annotations.datasetClassDistribution()` (= `/api/annotations/dataset/class-distribution`).
- **Editor tab** mounts `<CanvasEditor>` with a synthesised `Media` stub built from `editorAnnotation.mediaId` (most fields blank). Used so `CanvasEditor` can fetch the image via `endpoints.annotations.annotationImage(id)` (= `/api/annotations/annotations/{id}/image`).

## Dependencies

- Internal: `api` (barrel — `api`, `endpoints`, since AZ-485 / F4 + AZ-486 / F7), `useDebounce`, `useResizablePanel` (left panel 250 / 200–400), `FlightContext`, `DetectionClasses`, `ConfirmDialog` (imported but not used here — see Findings), `features/annotations/CanvasEditor`, `types/index`.
- External: `react`, `react-i18next`.

## External integrations

| Builder → Path | Where | Notes |
|---|---|---|
| `endpoints.annotations.dataset(qs)` → `/api/annotations/dataset?...` | `fetchItems` | Filters: `page`, `pageSize`, `fromDate`, `toDate`, `flightId`, `status`, `classNum`, `hasDetections`, `name` (caller builds `URLSearchParams.toString()`). |
| `endpoints.annotations.datasetItem(id)` → `/api/annotations/dataset/{id}` | `handleDoubleClick` | Loads full `AnnotationListItem` for the editor tab. |
| `endpoints.annotations.datasetBulkStatus()` → `/api/annotations/dataset/bulk-status` | `handleValidate` | Body: `{ annotationIds, status }`. |
| `endpoints.annotations.datasetClassDistribution()` → `/api/annotations/dataset/class-distribution` | `loadDistribution` | Class histogram. |
| `endpoints.annotations.annotationThumbnail(id)` → `/api/annotations/annotations/{id}/thumbnail` | grid tile `<img src=>` | One HTTP per visible tile. |
| `endpoints.annotations.annotationImage(id)` → `/api/annotations/annotations/{id}/image` | `CanvasEditor` (delegated) | When the editor is open. |

Path builders live in `src/api/endpoints.ts` (since AZ-486 / F7).

Spec contract is in parent suite `_docs/09_dataset_explorer.md`.

## Findings carried into Step 4 / 6 / 8

1. **No keyboard shortcuts implemented**: spec demands `1–9` (class), Enter (save), Del (delete), X (delete all), Esc (close editor), V (validate selected), Up/Down/PageUp/PageDown (navigate). Only Ctrl+click for multi-select works. The editor tab has no Save / Delete buttons either. Step 6 problem extraction.
2. **`Refresh thumbnails` button is missing** — spec calls for a status-bar action to regenerate the thumbnail database with progress. Step 6.
3. **No virtualisation in the grid**: spec says "virtualized grid" — current code renders all `items` returned by the page (max 20 due to `pageSize`). For larger pages performance would degrade. Today not a problem because `pageSize=20`. Step 8.
4. **Editor tab does not Save**: opening an annotation in the editor lets the user edit `editorDetections` locally but there's no Save / Cancel control wired up. Edits are discarded when the user changes tab. Step 6.
5. **`editorMedia` synthetic stub** — `mediaType: 1` is a magic number (`MediaType.Image = 1`); should reuse the enum import already present. Step 4.
6. **`ConfirmDialog` imported but never used** — dead import. Step 4 cleanup.
7. **`fetchItems` does `catch {}`** silently — same `coderule.mdc` violation as elsewhere. Step 4.
8. **Empty-state**: when `items.length === 0` the grid renders nothing; no "No matches" message. Step 4.
9. **`thumbnail` URL does not include a cache-buster**; if a thumbnail is regenerated server-side, the browser will keep the stale image. Step 4 / Step 8.
10. **`isSeed` red-ring is correctly rendered**, but `isSeed` is not in `AnnotationStatus` filters — the user can't filter to seeds-only. Step 6 if intended.
11. **Date inputs accept any `<input type="date">` string**; no validation that `fromDate ≤ toDate`. Step 4.
12. **Status filter buttons skip status `None`** (spec lists None as one of the four buttons) — current code shows `All`, `Created`, `Edited`, `Validated`, conflating `All` with "include None". Step 4.
13. **`selectedClassNum=0` is the "no filter" sentinel** but also a real class number (class 0 exists per `_docs/ui_design/README.md` → military vehicle). Selecting class 0 in `DetectionClasses` is indistinguishable from "no filter". Step 6.
14. **Inherits one cross-cutting UI fix and one cross-repo parent-doc fix** (resolved with user 2026-05-10):
    - **[STEP 4 — UI FIX]** `AnnotationStatus` values must change from UI `0/1/2` to spec `0/10/20/30/40` (`src/types/index.ts:23`). Every status filter button in this page (`null`/`null`/`Created`/`Edited`/`Validated`) currently sends a wrong integer. The "All" button is value `null` (no filter) — keep, but also expose a real "None" filter using the new `AnnotationStatus.None=0`. **PRIORITY** for Step 4. See annotations doc finding #27.
    - **[RESOLVED 2026-05-10]** `DatasetItem.isSplit` is correct on UI; parent `../../../../_docs/09_dataset_explorer.md §1` response schema now includes it. See annotations doc finding #33.

## Tests

None.

## Cross-doc references

- Parent suite Dataset Explorer API: `../../../../_docs/09_dataset_explorer.md`
- UI spec: `../../ui_design/README.md` (Dataset Explorer Layout, Keyboard Shortcuts).
- Imports `CanvasEditor` from `features/annotations/` — see `00_discovery.md §8` cross-feature edge.
- Legacy WPF reference: `../../legacy/wpf-era.md §5` (Dataset Explorer window).
