Phase B cycle 1 was a structural refactor only: F4 (barrel imports + STC-ARCH-01) and F7 (endpoint builders + STC-ARCH-02). This commit brings docs in line with source after the cycle, no code changes. Module docs (12 consumers): swap every /api/<service>/... literal in code snippets and integration tables for the matching endpoints.* builder; note the barrel import migration in Dependencies. New module doc: src__api__endpoints.md (public surface, F4 barrel re-export note, STC-ARCH-02 enforcement, contract-test reference). Architecture compliance baseline: mark F4 + F7 CLOSED with commit hashes (23746ec,8a461a2). 01_api-transport component description: add endpoints.ts + barrel to Internal Interfaces, close the F7 caveat, extend Module Inventory. ripple_log_cycle1.md: Task Step 0.5 reverse-dep analysis records the import-graph closure (no extra docs needed beyond the direct set). Carry-over reports landed alongside the docs: - test_run_report_phase_b_cycle1.md (Step 11 outcome) - implementation_report_refactor_phase_b_cycle1.md (cycle summary) State file: trimmed to the autodev <30-line target; Steps 14 + 15 recorded as SKIPPED with rationale (no security or perf surface changed in this cycle); pointer moved to Step 16 (Deploy). Co-authored-by: Cursor <cursoragent@cursor.com>
6.8 KiB
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). ImportsCanvasEditorfrom../annotations/— see cross-feature edge in00_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(fromDetectionClasses),objectsOnly(boolean),search(400 ms debounced viauseDebounce). - Pagination: client
pagestate, serverpageSizefixed 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 synthesisedMediastub built fromeditorAnnotation.mediaId(most fields blank). Used soCanvasEditorcan fetch the image viaendpoints.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
- 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. Refresh thumbnailsbutton is missing — spec calls for a status-bar action to regenerate the thumbnail database with progress. Step 6.- No virtualisation in the grid: spec says "virtualized grid" — current code renders all
itemsreturned by the page (max 20 due topageSize). For larger pages performance would degrade. Today not a problem becausepageSize=20. Step 8. - Editor tab does not Save: opening an annotation in the editor lets the user edit
editorDetectionslocally but there's no Save / Cancel control wired up. Edits are discarded when the user changes tab. Step 6. editorMediasynthetic stub —mediaType: 1is a magic number (MediaType.Image = 1); should reuse the enum import already present. Step 4.ConfirmDialogimported but never used — dead import. Step 4 cleanup.fetchItemsdoescatch {}silently — samecoderule.mdcviolation as elsewhere. Step 4.- Empty-state: when
items.length === 0the grid renders nothing; no "No matches" message. Step 4. thumbnailURL does not include a cache-buster; if a thumbnail is regenerated server-side, the browser will keep the stale image. Step 4 / Step 8.isSeedred-ring is correctly rendered, butisSeedis not inAnnotationStatusfilters — the user can't filter to seeds-only. Step 6 if intended.- Date inputs accept any
<input type="date">string; no validation thatfromDate ≤ toDate. Step 4. - Status filter buttons skip status
None(spec lists None as one of the four buttons) — current code showsAll,Created,Edited,Validated, conflatingAllwith "include None". Step 4. selectedClassNum=0is the "no filter" sentinel but also a real class number (class 0 exists per_docs/ui_design/README.md→ military vehicle). Selecting class 0 inDetectionClassesis indistinguishable from "no filter". Step 6.- Inherits one cross-cutting UI fix and one cross-repo parent-doc fix (resolved with user 2026-05-10):
- [STEP 4 — UI FIX]
AnnotationStatusvalues must change from UI0/1/2to spec0/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 valuenull(no filter) — keep, but also expose a real "None" filter using the newAnnotationStatus.None=0. PRIORITY for Step 4. See annotations doc finding #27. - [RESOLVED 2026-05-10]
DatasetItem.isSplitis correct on UI; parent../../../../_docs/09_dataset_explorer.md §1response schema now includes it. See annotations doc finding #33.
- [STEP 4 — UI FIX]
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
CanvasEditorfromfeatures/annotations/— see00_discovery.md §8cross-feature edge. - Legacy WPF reference:
../../legacy/wpf-era.md §5(Dataset Explorer window).