mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 13:31: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,181 @@
|
||||
# Module: `src/features/settings/SettingsPage.tsx`
|
||||
|
||||
> **Source**: `src/features/settings/SettingsPage.tsx` (107 lines)
|
||||
> **Topo batch**: B4 (depends on B3: `api/client`, `types/index`)
|
||||
|
||||
## Purpose
|
||||
|
||||
The per-tenant configuration screen. Three side-by-side panels:
|
||||
**Tenant** (system settings: military unit, name, default camera
|
||||
width and FoV), **Directories** (server-side filesystem paths for
|
||||
videos, images, labels, results, thumbnails, GPS sat / route),
|
||||
and **Aircrafts** (read-only list with star-toggle for default
|
||||
selection). Replaces the legacy WPF `SettingsWindow.xaml` plus the
|
||||
"system" tab (`_docs/legacy/wpf-era.md` §4).
|
||||
|
||||
Available to every authenticated user — `Header` does not gate
|
||||
`/settings` behind a permission check. The backend is the authority
|
||||
for who is allowed to write.
|
||||
|
||||
## Public interface
|
||||
|
||||
```ts
|
||||
export default function SettingsPage(): JSX.Element
|
||||
```
|
||||
|
||||
No props.
|
||||
|
||||
## Internal logic
|
||||
|
||||
- **State**:
|
||||
- `system: SystemSettings | null` — loaded from
|
||||
`GET /api/annotations/settings/system`. `null` until the GET
|
||||
resolves; the panel does not render until then (`{system && (...)}`).
|
||||
- `dirs: DirectorySettings | null` — analogous, from
|
||||
`GET /api/annotations/settings/directories`.
|
||||
- `aircrafts: Aircraft[]` — from `GET /api/flights/aircrafts`.
|
||||
- `saving: boolean` — disables the two Save buttons during a PUT.
|
||||
- **Bootstrap effect** (`useEffect([])`):
|
||||
|
||||
```ts
|
||||
api.get<SystemSettings>('/api/annotations/settings/system').then(setSystem).catch(() => {})
|
||||
api.get<DirectorySettings>('/api/annotations/settings/directories').then(setDirs).catch(() => {})
|
||||
api.get<Aircraft[]>('/api/flights/aircrafts').then(setAircrafts).catch(() => {})
|
||||
```
|
||||
|
||||
Three independent calls, all silently swallowed on error. Empty UI
|
||||
on failure (no error banner). Flag for Step 4.
|
||||
- **`saveSystem()`**:
|
||||
1. Guard: `if (!system) return`.
|
||||
2. `setSaving(true)`.
|
||||
3. `await api.put('/api/annotations/settings/system', system)`.
|
||||
4. `setSaving(false)`.
|
||||
|
||||
No optimistic update needed (the PUT body **is** the local state).
|
||||
No re-fetch — assumes the server echoes the same values. **Error
|
||||
path is missing**: a thrown PUT leaves `saving: true` permanently
|
||||
(no `try/finally`). Flag for Step 4.
|
||||
- **`saveDirs()`** — analogous against
|
||||
`PUT /api/annotations/settings/directories`. Same missing
|
||||
`try/finally` issue.
|
||||
- **`handleToggleDefault(a)`** — duplicate of the same handler in
|
||||
`AdminPage`: `PATCH /api/flights/aircrafts/${a.id}` with
|
||||
`{ isDefault: !a.isDefault }` then optimistic local flip. Two copies
|
||||
of the same logic in two pages — extract to a shared helper or to
|
||||
`FlightContext` in Step 8 (the legacy WPF had a single
|
||||
`AircraftService.SetDefault(...)`).
|
||||
- **`field(label, value, onChange, type='text')`** — local helper that
|
||||
renders a labeled `<input>`. Always controlled (`value={value ?? ''}`).
|
||||
Converts numeric inputs via `parseInt(v) || 0` /
|
||||
`parseFloat(v) || 0` at the call site. Note: `parseInt` / `parseFloat`
|
||||
on an empty string returns `NaN`, and `NaN || 0` is `0` — so
|
||||
clearing a numeric field silently writes `0`, not `null`. Flag for
|
||||
Step 4 against the `SystemSettings` type which permits `null`.
|
||||
- **Layout** — three independent flex children:
|
||||
- Tenant (`w-[300px]` shrink-0): `field()` × 4 + Save.
|
||||
- Directories (`w-[300px]` shrink-0): `field()` × 7 + Save.
|
||||
- Aircrafts (`flex-1 max-w-sm`): list with star toggle.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Internal**:
|
||||
- `../../api/client` — `api`.
|
||||
- `../../types` — `SystemSettings`, `DirectorySettings`, `Aircraft`.
|
||||
- **External**: `react` (`useState`, `useEffect`),
|
||||
`react-i18next` (`useTranslation`).
|
||||
|
||||
## Consumers (intra-repo)
|
||||
|
||||
- `src/App.tsx` — mounted at the `/settings` route inside the
|
||||
protected tree.
|
||||
|
||||
## Data models
|
||||
|
||||
- `SystemSettings` includes `id, name, militaryUnit,
|
||||
defaultCameraWidth, defaultCameraFoV, thumbnailWidth, thumbnailHeight,
|
||||
thumbnailBorder, generateAnnotatedImage, silentDetection`. The page
|
||||
exposes only the first four — every other field is read on GET and
|
||||
echoed back on PUT untouched. Flag if a future cycle needs to expose
|
||||
thumbnails / silent-detection toggles.
|
||||
- `DirectorySettings` has all 7 directory fields exposed as text
|
||||
inputs. Path validation is server-side only.
|
||||
- `Aircraft` (`id, model, type, isDefault`) — same shape as in
|
||||
`AdminPage`.
|
||||
|
||||
## Configuration
|
||||
|
||||
- **i18n keys consumed**: `settings.tenant`, `settings.directories`,
|
||||
`settings.aircrafts`, `settings.save`. (Confirmed in
|
||||
`src/i18n/en.json`.) Hardcoded English labels for every form field
|
||||
("Military Unit", "Name", "Default Camera Width", "Default Camera
|
||||
FoV", "Videos Dir", "Images Dir", "Labels Dir", "Results Dir",
|
||||
"Thumbnails Dir", "GPS Sat Dir", "GPS Route Dir"). Flag for Step 4.
|
||||
- **Tailwind tokens**: `bg-az-panel`, `bg-az-bg`, `bg-az-orange`,
|
||||
`text-az-{text,muted,orange}`, `bg-az-blue/20`, `bg-az-green/20`,
|
||||
`text-az-{blue,green}`, `border-az-border`. Defined in
|
||||
`src/index.css`.
|
||||
|
||||
## External integrations
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/annotations/settings/system` | Load tenant config |
|
||||
| `PUT` | `/api/annotations/settings/system` | Save tenant config |
|
||||
| `GET` | `/api/annotations/settings/directories` | Load directory paths |
|
||||
| `PUT` | `/api/annotations/settings/directories` | Save directory paths |
|
||||
| `GET` | `/api/flights/aircrafts` | Load aircraft list |
|
||||
| `PATCH` | `/api/flights/aircrafts/{id}` | Toggle `isDefault` |
|
||||
|
||||
Routed by `nginx.conf` to `annotations/` and `flights/` backends.
|
||||
|
||||
## Security
|
||||
|
||||
- **No client-side write authorization check** — the page renders the
|
||||
Save buttons for every user. The backend (`annotations/` service)
|
||||
is the authority. Document in `security_approach.md` (Step 6).
|
||||
- **Hardcoded internal directory paths** are **not** present in this
|
||||
file (unlike `AdminPage`'s hardcoded GPS device IP) — every value
|
||||
is server-supplied. Good.
|
||||
- **Path inputs are free-text** — server-side path traversal
|
||||
validation is mandatory. Verify the `annotations/` service rejects
|
||||
e.g. `../../etc/passwd`. Flag for Step 6.
|
||||
- **`saving` state can stick on PUT failure** because there is no
|
||||
`try/finally`, leaving the Save button permanently disabled. Step 4
|
||||
candidate (matches the `AdminPage` "AI/GPS save buttons do
|
||||
nothing" pattern — there is a clear lack of UX-level error
|
||||
handling across both admin/settings pages).
|
||||
|
||||
## Tests
|
||||
|
||||
None.
|
||||
|
||||
## Notes / open questions
|
||||
|
||||
- **Numeric clear-to-zero pitfall**: `parseInt('') || 0` writes `0`
|
||||
for an empty input, not `null`. This silently overwrites a
|
||||
legitimate `null` (per the `SystemSettings` type, all four numeric
|
||||
fields are nullable). Step 4 fix: detect empty input and pass
|
||||
`null`.
|
||||
- **Aircraft toggle handler is duplicated** between `AdminPage` and
|
||||
`SettingsPage` — extract to a shared helper. Step 8.
|
||||
- **No optimistic concurrency**: two admins editing system settings
|
||||
simultaneously will overwrite each other. The `id` field is sent
|
||||
back on PUT but no `version` / `etag` / `If-Match` header. Flag for
|
||||
Step 6 problem-extraction. Backend may not support optimistic
|
||||
concurrency yet.
|
||||
- **The Aircrafts panel is read-only-ish here** but allows the
|
||||
star-toggle, same as `AdminPage`. The duplication suggests an open
|
||||
question: is this intentional (settings is "personal preferences",
|
||||
admin is "global config", but default-aircraft is a *global*
|
||||
setting) or accidental? Surface to the user in Step 6 problem
|
||||
extraction.
|
||||
- **`saving` is a single global flag** even though the page has two
|
||||
independent Save buttons (system / dirs). A user who clicks
|
||||
"Save System" then quickly clicks "Save Dirs" while the first PUT
|
||||
is in flight will see the Dirs button disabled too. Acceptable
|
||||
given the latency budget; flag if both saves become slow.
|
||||
- **`thumbnailWidth/Height/Border` and `generateAnnotatedImage`,
|
||||
`silentDetection`** in `SystemSettings` are not exposed in the UI
|
||||
but are echoed back on PUT. If a future cycle adds them, ensure the
|
||||
GET → PUT round-trip preserves any concurrent change made by another
|
||||
client between the GET and the PUT.
|
||||
Reference in New Issue
Block a user