# Azaion UI — Data Model > Synthesis output of `/document` Step 3c. Consolidated from `src/types/index.ts`, > per-component data sections, and the parent suite spec at > `_docs/legacy/wpf-era.md` §8 + the suite-level docs (cross-referenced). > > The UI does NOT own a database. The entities below are the **contract shapes** > the UI consumes from suite REST + SSE endpoints. The authoritative schema is > server-side; this document captures the **client-side type expectations** and > the **mismatches** between those expectations and the suite spec (enum drift, > shape drift) for resolution at autodev Step 4. ## 1. Entities by component | Entity | Component | Backing service | Wire shape | |--------|-----------|-----------------|------------| | `AuthUser` | `02_auth` | `admin/` | `{id, email, name, role, permissions[]}` | | `User` | `08_admin` | `admin/` | `{id, name, email, role, isActive}` | | `Aircraft` | `08_admin` / `09_settings` | `admin/` | `{id, model, type:'Plane'\|'Copter', isDefault}` | | `Flight` | `05_flights` | `flights/` | `{id, name, createdDate, aircraftId\|null}` | | `Waypoint` | `05_flights` | `flights/` | UI sends: `{id, flightId, name, latitude, longitude, order}`. Spec wants: `{Geopoint:{Lat,Lon,MGRS}, Source, Objective, OrderNum, Height}` — **drift, finding #20** | | `Media` | `06_annotations` | `annotations/` | `{id, name, path, mediaType, mediaStatus, duration, annotationCount, waypointId, userId}` | | `AnnotationListItem` | `06_annotations` | `annotations/` | `{id, mediaId, time, createdDate, userId, source, status, isSplit, splitTile, detections[]}` | | `Detection` | `06_annotations` | `annotations/` (storage) + `detect/` (production) | `{id, classNum, label, confidence, affiliation, combatReadiness, centerX, centerY, width, height}` | | `DetectionClass` | `08_admin` (write) + `06_annotations` (read) | `admin/` (write) + `annotations/` (read) | `{id, name, shortName, color, maxSizeM, photoMode}` | | `DatasetItem` | `07_dataset` | `annotations/` | `{annotationId, imageName, thumbnailPath, status, createdDate, createdEmail, flightName, source, isSeed, isSplit}` | | `ClassDistributionItem` | (currently unused — backs missing chart) | `annotations/` | `{classNum, label, color, count}` | | `SystemSettings` | `09_settings` | `admin/` | `{id, name, militaryUnit, defaultCameraWidth, defaultCameraFoV, thumbnailWidth, thumbnailHeight, thumbnailBorder, generateAnnotatedImage, silentDetection}` | | `DirectorySettings` | `09_settings` | `admin/` | `{id, videosDir, imagesDir, labelsDir, resultsDir, thumbnailsDir, gpsSatDir, gpsRouteDir}` | | `CameraSettings` | `09_settings` | `admin/` | `{id, altitude, focalLength, sensorWidth}` | | `UserSettings` | `09_settings` | `admin/` | `{id, userId, selectedFlightId, annotationsLeftPanelWidth, annotationsRightPanelWidth, datasetLeftPanelWidth, datasetRightPanelWidth}` | | `PaginatedResponse` | shared (`00_foundation`) | every list endpoint | `{items[], totalCount, page, pageSize}` | ## 2. Enums (numeric wire format) > The suite uses **numeric** wire values for every enum. The UI types in `src/types/index.ts` > match in *shape* but several have **wrong values** vs. spec. The state-of-the-world is captured > in `state.json::notes[]` (2026-05-10 02:13Z entries) — Step 4 will fix the UI side. | Enum | UI values today | Spec values | Status | |------|-----------------|-------------|--------| | `MediaType` | `None=0, Image=1, Video=2` | matches | ✓ | | `MediaStatus` | `New=0, AiProcessing=1, AiProcessed=2, ManualCreated=3` | also has `None`, `Confirmed`, `Error` | **drift** — UI cannot render error state. Step 4 fix. | | `AnnotationSource` | `AI=0, Manual=1` | matches numerically; spec doc shows strings (cross-repo doc fix replayed 2026-05-10) | ✓ (after parent-doc fix) | | `AnnotationStatus` | `Created=0, Edited=1, Validated=2` | spec is `None=0, Created=10, Edited=20, Validated=30, Deleted=40` | **drift, severe** — wire payloads will be wrong. Step 4 PRIORITY. | | `Affiliation` | `Unknown=0, Friendly=1, Hostile=2` | spec also has `None` | **drift** — Step 4. | | `CombatReadiness` | `NotReady=0, Ready=1` | spec also has `Unknown` | **drift** — Step 4. | ## 3. Entity-relationship diagram ```mermaid erDiagram User ||--o{ Flight : "creates" Flight ||--o{ Waypoint : "has" Flight ||--o{ Media : "captures" Media ||--o{ AnnotationListItem : "annotated by" AnnotationListItem ||--o{ Detection : "contains" DetectionClass ||--o{ Detection : "classifies (by classNum)" Aircraft }o--o| Flight : "default for" User ||--|| AuthUser : "session view of" User ||--|| UserSettings : "preferences" User ||--o{ DatasetItem : "validated by" SystemSettings ||--|| Aircraft : "may default to" Detection { string id int classNum "raw int including PhotoMode offset" string label float confidence Affiliation affiliation CombatReadiness combatReadiness float centerX "normalized 0..1" float centerY "normalized 0..1" float width "normalized 0..1" float height "normalized 0..1" } DetectionClass { int id string name string shortName string color "hex" float maxSizeM "GSD constraint" int photoMode "0=Regular, 1=Winter (+20), 2=Night (+40)" } Waypoint { string id string flightId string name float latitude "wire shape: drift — see finding 20" float longitude int order } AnnotationListItem { string id string mediaId string time "video timestamp HH:MM:SS or null" string createdDate "ISO 8601" string userId AnnotationSource source AnnotationStatus status bool isSplit string splitTile "YOLO label string or null" } Media { string id string name string path MediaType mediaType MediaStatus mediaStatus string duration "HH:MM:SS or null" int annotationCount string waypointId string userId } DatasetItem { string annotationId string imageName string thumbnailPath AnnotationStatus status string createdDate string createdEmail string flightName AnnotationSource source bool isSeed bool isSplit } ``` ## 4. Migration / schema-evolution strategy The UI does NOT own a database, so there is no client-side migration to run. However: - **Enum drift** above is effectively a "client-side schema migration" — every drifted enum must be aligned with the suite spec in Step 4 (or in the case of `AnnotationSource`, the parent-suite doc is what's wrong and was already fixed via cross-repo doc patch on 2026-05-10). - **Backwards compatibility** is the suite's responsibility. The UI assumes the latest contract; if the suite needs to roll out a breaking change, it must coordinate with the UI version (typically by gating the change behind a feature flag in the admin service, then deploying both at once). - **`UserSettings.{annotationsLeftPanelWidth, ...}`** — the type exists; the wire endpoint exists; the UI does not write these today (`useResizablePanel` finding #11). The fix is purely client-side wiring. ## 5. Seed data observations The UI has no seed data of its own. Two sources of "default" data are observable: 1. **`FALLBACK_CLASS_NAMES`** (`11_class-colors`) — 12 generic English labels (Car, Person, Truck, …) shown only when admin classes failed to load. These are NOT a seed for the admin service; they are a defensive UI fallback only. 2. **`Constants.DefaultAnnotationClasses`** in the legacy WPF — referenced in `_docs/legacy/wpf-era.md §10`. The React UI does NOT carry an equivalent (admin classes are always fetched). Acceptable. ## 6. Validation rules (client-side) | Field | Rule | Source | Defect | |-------|------|--------|--------| | Login email | non-empty | `LoginPage` | none observed | | Login password | non-empty | `LoginPage` | none observed | | Numeric settings | `parseInt(v) \|\| 0` | `SettingsPage` | clearing field silently writes 0 — finding B4 | | Waypoint lat/lng | implicit (Leaflet bounds) | `FlightsPage` | no explicit clamp — Step 4 | | Detection bbox normalized 0..1 | implicit (CanvasEditor scaling) | `CanvasEditor` | normalized-coordinate clamping mentioned in `_docs/legacy/wpf-era.md §10` — verify in Step 4 | | Class 1–9 keyboard pick | `classes[idx + photoMode]` | `DetectionClasses` | ordering vs admin contract unverified — Step 4 | | File upload size | server cap 500 MB (nginx) | `nginx.conf` | client does not pre-validate — Step 4 cosmetic | ## 7. Wire-format gotchas - **`time` as a string** in `AnnotationListItem` (e.g., `"00:01:23.456"`) is parsed/formatted client-side; precision is millisecond. Used for video annotation overlay window math (50 ms before / 150 ms after — currently wrong, finding #6). - **`splitTile` as a YOLO label string** is the raw ` ` text, parsed client-side in `CanvasEditor.tsx`. Format mismatch is a Step 4 verification candidate. - **`PaginatedResponse` ceiling** — `pageSize=1000` is hardcoded for flights (finding B3). Other lists vary; no canonical default. - **Date strings** — assumed ISO 8601; no timezone handling beyond what the browser's `Date` constructor implies. Step 4 verification when timezone bugs surface.