Files
ui/_docs/02_document/modules/src__components__DetectionClasses.md
T
Oleksandr Bezdieniezhnykh 09449bda2c
ci/woodpecker/push/build-arm Pipeline failed
[AZ-510][AZ-511][AZ-512][AZ-513] Cycle 3 Steps 12-15 + admin prereq
Wrap up cycle 3 across the autodev existing-code Phase B steps that
follow Implement (Steps 12-15), plus the cross-workspace prerequisite
ticket filed for AZ-512.

Step 12 - Test-Spec Sync:
- Un-quarantine FT-P-01 in traceability-matrix (closed by AZ-510)
- Add AZ-510 chained /users/me failure-path test reference under AC-23
- Note AZ-512 deferral status under O9 (P12 Phase B target)

Step 13 - Update Docs (task mode):
- Refresh src__auth__AuthContext module doc with AZ-510 wire shape
  (POST refresh + chained /users/me + bootstrapInflight guard)
- Add usersMe() to src__api__endpoints module doc + consumer note
- Rename src__features__annotations__classColors module doc to
  src__class-colors__classColors (matches AZ-511 git mv); refresh header
- Refresh src__components__DetectionClasses + src__features__annotations
  module group doc for the new class-colors barrel import path
- Update components/11_class-colors Module Inventory to point at the
  renamed module doc filename
- Rewrite system-flows.md Flow F2 (Bearer auto-refresh) with the AZ-510
  POST + chained /users/me sequence; close Finding B3 references
- Generate ripple_log_cycle3 documenting all changed source files,
  their reverse-dependency search results, and the docs touched

Step 14 - Security Audit (cycle-3 delta):
- Resume mode against cycle-2 baseline; cycle-2 artifacts untouched
- Re-run bun audit on both roots: clean (cycle-2 inline fix held)
- Re-rate OWASP A06: FAIL -> PASS; A07: PASS_WITH_KNOWN -> PASS (B3
  closed by AZ-510)
- New finding F-SAST-CY3-1 (LOW): __resetBootstrapInflightForTests
  exposed via src/auth public barrel; defer to hygiene cycle
- Verdict: FAIL -> PASS_WITH_WARNINGS; one HIGH (F-SAST-1
  mission-planner git-history key, unchanged) remains
- Add amendment banner to cycle-2 security_report.md

Step 15 - Performance Test:
- Static profile NFT-PERF-01 PASS (290 575 B gzipped vs 2 MB budget;
  ~14% of budget; no regression from AZ-510 surface additions)
- E2E profile SKIP (Playwright perf project still pending AZ-457..AZ-482);
  legitimate skip per test-run skill, gap acknowledged in report
- AZ-510 200ms p95 chain NFR verified at spec level only - no CI gate
  yet (covered by future AZ-457..AZ-482 work)

Cross-workspace prerequisite (AZ-513 just filed):
- Updated _docs/_process_leftovers/2026-05-13_az-512-admin-classes-prereq.md
  to reflect AZ-513 filing on admin/ workspace (parent epic AZ-509,
  Blocks link to AZ-512). Companion task spec added in admin/ repo
  (separate commit there, owned by admin/ workspace).

State file: advanced to Step 16 (Deploy) per autodev existing-code flow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 03:58:21 +03:00

6.8 KiB
Raw Blame History

Module: src/components/DetectionClasses.tsx

Source: src/components/DetectionClasses.tsx (99 lines) Topo batch: B3 (depends on B2 leaves: api/client, class-colors (via barrel), types/index) Last refresh: 2026-05-13 — getClassColor + FALLBACK_CLASS_NAMES import migrated from '../features/annotations/classColors' to '../class-colors' barrel by AZ-511.

Purpose

A side-panel widget that lets the annotator pick a detection class (19 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

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[]>(endpoints.annotations.classes()) (= /api/annotations/classes, since AZ-486 / F7).
    • 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 0N-1 (Regular), 2020+N-1 (Winter), 4040+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 19 (effect keyed on [classes, photoMode, onSelect]):
    • keydown on window. parseInt(e.key) → if 19, 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 19 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 (barrel) — api, endpoints. (Since AZ-485 / F4 + AZ-486 / F7.)
    • ../features/annotations/classColorsgetClassColor(i), FALLBACK_CLASS_NAMES. (Cross-component import preserved; flagged in Consumers below.)
    • ../typesDetectionClass 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: endpoints.annotations.classes()/api/annotations/classes (typed builder from ../api/endpoints, since AZ-486 / F7).

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 endpoints.annotations.classes() (= /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 19 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.