wip(mechanic): inflight damage-flow + vendor scheme + PhotoAnnotate + TT views

Маркер «вот где остановилась mechanic-сессия» — не разбито по логическим коммитам,
зафиксировано целиком чтобы не потерять при отвлечении/чекаутах.

Modified:
- pages/InspectionEditor.tsx — расширение damage-flow внутри редактора
- pages/VehicleCard.tsx — vendor data + расширение карточки
- pages/InspectionReview.tsx — обновлённый review
- api/{auth,inspections,photos,vehicles}.ts — новые endpoints для TT + photo flow
- components/damage-flow/DamageClassifyDialog.tsx — multi-step UI
- components/vehicle-scheme/VendorVehicleScheme.tsx — SVG zone interaction
- data/damageVocabulary.ts — обновлённый словарь повреждений
- frontend index.html/manifest/icons — PWA-метаданные

New (untracked):
- components/damage-flow/PhotoAnnotateStep.tsx — новый шаг flow'а
- components/tt/ — vendor TT-state UI
- pages/TtStateDetail.tsx — экран осмотра вендора
- recon/findings/damage_model.md + damage_vocabulary.json + scheme/ — recon docs

Cleanup: удалены bash-garbage 'Last' и 'dict[str' из frontend/.
This commit is contained in:
2026-05-20 11:16:21 +10:00
parent e0083b894f
commit 7e2cf0c9c8
132 changed files with 2823 additions and 106 deletions
+79
View File
@@ -0,0 +1,79 @@
# Vendor Damage Data Model — Extraction Notes
Source: `Ttc.dll` (Xamarin Android app, reverse-engineered from `assemblies.blob`)
Date: 2026-05-17
## Core Objects
### DetailDamageCoordinate
A single tap-point placed by the mechanic on the enlarged part SVG.
| Field | Type | Notes |
|-------|------|-------|
| `Id` | int/Guid | PK |
| `X` | float | Relative X on the part SVG canvas |
| `Y` | float | Relative Y on the part SVG canvas |
| `InspectionGuid` | Guid | Ties this point to an inspection session |
| `MetaId` | int | Zone ID (0..56) — which part this point belongs to |
| `GroupId` | int | Groups multiple points belonging to ONE damage annotation |
| `ImageWithLinesId` | int | FK → the photo with polyline annotation |
| `LinesId` | int | FK → the DrawingLines set |
Evidence: backing fields `<X>k__BackingField`, `<Y>k__BackingField`, `<Id>k__BackingField`, `<MetaId>k__BackingField`, `<GroupId>k__BackingField`, `<ImageWithLinesId>k__BackingField`, `<LinesId>k__BackingField` all found in contiguous block at 0x3baa00x3bd50 in Strings heap. `GetByGroupId` method confirms group-based querying.
### DetailDamageCoordinateContract (API wire format)
Same fields as above, serialized as JSON. The JSON property for severity is `"degree"` (lowercase, confirmed at 0x3d752 in Strings heap alongside `"damageDegree"` widget name).
### DetailDamageDrawPointContract
A single vertex in the finger-drawn polyline annotation on a photo.
| Field | Type | Notes |
|-------|------|-------|
| `X` | float | Point X on the photo canvas |
| `Y` | float | Point Y on the photo canvas |
The polyline is a **list** of `DetailDamageDrawPointContract` objects. The parent container is `DrawingLines` (backing field `<DrawingLines>k__BackingField` at 0x3cade). Method `DrawLine` (0x3e3c9) draws each segment. `StrokeWidth` (0x3f59a) controls line thickness.
The photo itself is stored as `Image64StringWithLines` (base64 JPEG) and `ImageBase64StringWithLines` (alternate field). The link between a damage group and its annotated photo is: `DetailDamageCoordinate.ImageWithLinesId → VehicleStatePhotoInspection.Id`.
## Multi-Point Damage Grouping
**Yes, multi-point damages are grouped via `GroupId`.** The mechanic taps N spots on the part SVG — each tap creates a `DetailDamageCoordinate` with the same `GroupId`. All points in a group share:
- One `damage_type` (the vertical list selection)
- One `degree` (severity: 0=Легкое, 1=Среднее, 2=Тяжелое)
- One `ImageWithLinesId` (one photo with optional polyline)
The `GetByGroupId` method (found in Strings heap at 0x394c0) fetches all coordinates for a group. `SaveByParts` (0x42f4f) persists the full group in one call.
## Photo + Polyline Link
```
Inspection
└─ DetailDamageCoordinate[] (grouped by GroupId)
├─ GroupId = 42 (e.g.)
├─ MetaId = 0 (front bumper)
├─ [X1,Y1], [X2,Y2], [X3,Y3] ← multiple tap points
├─ ImageWithLinesId = 7 ← FK → photo record
└─ degree = 1 (Среднее)
VehicleStatePhotoInspection (id=7)
├─ Image64StringWithLines ← base64 JPEG (the photo)
├─ DrawingLines[] ← list of DetailDamageDrawPointContract
│ └─ [{X:0.2, Y:0.4}, {X:0.5, Y:0.6}, ...] ← polyline vertices
└─ HistoryLines[] ← read-only history of old polylines
```
## Our Implementation Recommendation
1. **DamageGroup** table: `{ id, inspection_id, zone_id, damage_type, severity(0-2), photo_id, created_at }`
2. **DamagePoint** table: `{ id, group_id, x REAL, y REAL, seq_order INT }` — multiple rows per group
3. **DamagePhoto** table: `{ id, image_b64, polyline JSONB }` where `polyline` is `[[x,y], ...]`
4. Severity stored as integer 0/1/2, displayed as Легкое/Среднее/Тяжелое
This mirrors the vendor model exactly while using a standard relational schema.
## Known Unknowns
- The exact JSON shape returned by `damage/details` endpoint (API not captured live)
- Whether `GroupId` is a local UUID or server-assigned int
- Whether `DrawingLines` coordinates are normalized (0..1) or absolute pixels