mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 09:21:10 +00:00
[AZ-447] autodev Steps 1-4 baseline: docs, tests, refactor specs
Captures the full output of autodev existing-code Phase A through Step 4 (Code Testability Revision) for the Azaion UI workspace: - Step 1 Document: _docs/02_document/ (FINAL_report, architecture, glossary, components/, modules/, diagrams/, system-flows, module-layout) plus _docs/00_problem/ + _docs/01_solution/ + _docs/legacy/ + _docs/how_to_test + README. - Step 2 Architecture Baseline: architecture_compliance_baseline.md. - Step 3 Test Spec: _docs/02_document/tests/ (environment, test-data, blackbox/performance/resilience/security/ resource-limit tests, traceability-matrix), enum_spec_snapshot, expected_results/results_report.md (98 rows), plus the run-tests.sh + run-performance-tests.sh runners. - Step 4 Code Testability Revision: 01-testability-refactoring/ run dir (list-of-changes C01-C07, deferred_to_refactor, analysis/research_findings + refactoring_roadmap) and the 7 child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/ plus _dependencies_table.md. - _docs/_autodev_state.md pins the cursor at Step 4 / refactor Phase 4 entry so /autodev resumes cleanly. Epic AZ-447 (UI testability gates) tracks the 7 child tasks that will land in subsequent commits. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# Module: `src/components/DetectionClasses.tsx`
|
||||
|
||||
> **Source**: `src/components/DetectionClasses.tsx` (99 lines)
|
||||
> **Topo batch**: B3 (depends on B2 leaves: `api/client`, `features/annotations/classColors`, `types/index`)
|
||||
|
||||
## Purpose
|
||||
|
||||
A side-panel widget that lets the annotator pick a detection class (1–9 or click) and a photo mode (`Regular`, `Winter`, `Night`). Loads the class catalogue from the backend and falls back to a hardcoded list when the API returns empty / errors. Replaces the legacy WPF detection-class picker referenced in `_docs/legacy/wpf-era.md` §"What survived".
|
||||
|
||||
## Public interface
|
||||
|
||||
```ts
|
||||
interface Props {
|
||||
selectedClassNum: number
|
||||
onSelect: (classNum: number) => void
|
||||
photoMode: number
|
||||
onPhotoModeChange: (mode: number) => void
|
||||
}
|
||||
export default function DetectionClasses(props: Props): JSX.Element
|
||||
```
|
||||
|
||||
Fully controlled — the parent owns `selectedClassNum` and `photoMode`. The current contract for `photoMode` values is integer offsets `0` (Regular), `20` (Winter), `40` (Night), matching the `photoMode` field of `DetectionClass` (`src/types/index.ts`) and the offsets baked into `FALLBACK_CLASSES` below.
|
||||
|
||||
## Internal logic
|
||||
|
||||
- **Class catalogue load** (mount-only `useEffect`):
|
||||
- `api.get<DetectionClass[]>('/api/annotations/classes')`.
|
||||
- On a non-empty array → `setClasses(list)`.
|
||||
- On an empty array OR a thrown error → `setClasses(FALLBACK_CLASSES)`.
|
||||
- **`FALLBACK_CLASSES`** is a module-private 3 × |`FALLBACK_CLASS_NAMES`| matrix:
|
||||
- For each mode offset in `[0, 20, 40]`, build one class entry per name in `FALLBACK_CLASS_NAMES` (imported from `features/annotations/classColors.ts`).
|
||||
- Each entry: `{ id: i + modeOffset, name, shortName: name.slice(0, 3), color: getClassColor(i), maxSizeM: 10, photoMode: modeOffset }`.
|
||||
- This means: in offline / API-down mode, the ID range is `0–N-1` (Regular), `20–20+N-1` (Winter), `40–40+N-1` (Night). The backend's class IDs MUST follow the same convention or fallback↔backend handoff yields ID collisions. Confirm in Step 4 via the `annotations/` service contract.
|
||||
- **Numeric hotkeys 1–9** (effect keyed on `[classes, photoMode, onSelect]`):
|
||||
- `keydown` on `window`. `parseInt(e.key)` → if 1–9, picks `classes[(num - 1) + photoMode]`.
|
||||
- **Bug-shaped**: `photoMode` is `0 | 20 | 40` (an *offset*), not a row count. `classes[idx + 0]` is correct for Regular; for Winter/Night the index `idx + 20` / `idx + 40` is meaningful only when `classes` is in the contiguous `[0..N-1, 20..20+N-1, 40..40+N-1]` shape that `FALLBACK_CLASSES` produces. **If the backend returns its classes in a different order**, hotkey 1–9 will pick the wrong class. Verify backend ordering in Step 4 — this is the principal correctness risk in this module. Flag.
|
||||
- **Auto-select first class on mode change** (effect keyed on `[classes, photoMode, selectedClassNum, onSelect]`):
|
||||
- Filter `classes` to the active `photoMode`.
|
||||
- If `selectedClassNum` is not within that filtered set, call `onSelect(modeClasses[0].id)`.
|
||||
- This guarantees the parent always holds a class ID consistent with the selected photo mode.
|
||||
- **Render**:
|
||||
- Class list — only entries whose `photoMode` matches the active mode.
|
||||
- Photo-mode bar — three buttons (Sunny / Snowflake / Moon icons) for Regular / Winter / Night.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Internal**:
|
||||
- `../api/client` — `api.get<T>()`.
|
||||
- `../features/annotations/classColors` — `getClassColor(i)`, `FALLBACK_CLASS_NAMES`.
|
||||
- `../types` — `DetectionClass` type.
|
||||
- **External**: `react`, `react-i18next`, `react-icons/md`, `react-icons/fa`.
|
||||
|
||||
## Consumers (intra-repo)
|
||||
|
||||
From the §7a dependency graph:
|
||||
|
||||
- `src/features/annotations/AnnotationsPage.tsx`
|
||||
- `src/features/dataset/DatasetPage.tsx`
|
||||
|
||||
This is the **canonical example** of the cross-layer import flagged in `_docs/02_document/00_discovery.md` §8: a `components/` (shared) module importing from `features/annotations/`. Two clean fixes are in scope for Step 4 / Step 8:
|
||||
|
||||
1. Lift `classColors.ts` into `src/components/detection/` (or `src/shared/`) and update the two consumers.
|
||||
2. Or: move `DetectionClasses` itself into `src/features/annotations/` since both consumers are in `features/`.
|
||||
|
||||
## Data models
|
||||
|
||||
`DetectionClass` (from `src/types/index.ts`) — see `_docs/02_document/modules/src__types__index.md`.
|
||||
|
||||
`FALLBACK_CLASSES` is module-private; see Internal logic above.
|
||||
|
||||
## Configuration
|
||||
|
||||
Endpoint: `/api/annotations/classes` — string-literal URL (testability fix scheduled for Step 4).
|
||||
|
||||
Photo-mode value set is `{0, 20, 40}` — hardcoded, mirrored by `FALLBACK_CLASSES`. If the backend grows a fourth mode (e.g. thermal, IR), every consumer of `photoMode` will need a coordinated change.
|
||||
|
||||
Tailwind tokens: `bg-az-orange` (Regular), `bg-az-blue` (Winter), `bg-purple-600` (Night). Defined in `src/index.css`.
|
||||
|
||||
## External integrations
|
||||
|
||||
- HTTP `GET /api/annotations/classes` → `DetectionClass[]`. Backed by the `annotations/` service (`.NET`) per `nginx.conf`.
|
||||
|
||||
## Security
|
||||
|
||||
- **Silent failure on class load**: `.catch(() => setClasses(FALLBACK_CLASSES))` swallows the error and the user sees the fallback. Acceptable for UX continuity, but the lack of any user-visible signal means a misconfigured `/api/annotations/classes` deploy could go unnoticed in prod. Flag for Step 6 `security_approach.md` / Step 8.
|
||||
- No input that flows back to the server here.
|
||||
- The `getClassColor(i)` palette is deterministic; no PII.
|
||||
|
||||
## Tests
|
||||
|
||||
None.
|
||||
|
||||
## Notes / open questions
|
||||
|
||||
- **`getPhotoModeSuffix` redundancy** (carry-over from B1): `classColors.ts` exposes `getPhotoModeSuffix()` that derives the same suffix the typed `DetectionClass.photoMode` field already encodes. Once `classColors.ts` is repositioned (see Consumers), `getPhotoModeSuffix` is a deletion candidate. Defer to Step 8.
|
||||
- **`FALLBACK_CLASS_NAMES.length`** is implicitly assumed to be ≤ 9 by the hotkey code (only 1–9 are bound). If the catalogue grows to 10+ entries, hotkeys can no longer cover the tail. Acceptable for now.
|
||||
- **Mode-button colours don't use `az-` tokens for Night** (`bg-purple-600`, `text-purple-400` instead of an `az-purple` token). Cosmetic inconsistency; flag for Step 4 against `_docs/ui_design/README.md` colour palette.
|
||||
- The class list is rendered with `1.`, `2.`, … prefixes derived from `i+1` — so the hotkey number always matches the visible label inside the active mode. Good.
|
||||
- Does not handle modifier keys (`Shift`, `Ctrl`) on numeric hotkeys; pressing `Shift+5` will trigger the `5` branch. Fine for now.
|
||||
Reference in New Issue
Block a user