Files
ui/_docs/02_document/components/08_admin/description.md
T
Oleksandr Bezdieniezhnykh ecacfa8b43 [AZ-512] Admin class inline edit form + PATCH wiring (cy4 batch 16)
Implements the cycle-3-deferred AZ-512 task under the user-authorized
Option B path (MSW-stubbed; live deploy gates at Step 16 on AZ-513).

Code:
- src/features/admin/AdminPage.tsx — inline edit affordance:
  editingId/editForm state, handleStartEdit/Cancel/Update, Enter+Escape
  keyboard handling, colspan row swap when editing, pencil (✎) button
  per row. Full-body PATCH (Risk 2). Single editingId enforces the
  one-row-at-a-time invariant (Risk 3). Disabled buttons during the
  in-flight PATCH (Risk 4). Inline role="alert" on validation/server
  errors (no alert() per Finding B4 anti-pattern).
- src/i18n/{en,ua}.json — `admin.classes` flat → nested with `title`
  + 6 new keys (edit, save, cancel, nameRequired,
  maxSizeMustBePositive, updateFailed). Parity gate FT-P-22 PASS.

Test infrastructure:
- tests/msw/handlers/admin.ts — PATCH /api/admin/classes/:id
  partial-merge handler.
- tests/admin_class_edit.test.tsx — 12 tests covering AC-1..AC-6
  + AC-8 (AC-7 satisfied by static FT-P-22 gate).
- tests/destructive_ux.test.tsx — adjacent-hygiene selector fix
  at 3 call sites: the new ✎ button moved the first-button
  position; targeting × explicitly preserves the existing
  it.fails()/control semantics.

Docs:
- _docs/02_document/components/08_admin/description.md — recorded
  edit affordance + PATCH wiring + AZ-513 cross-workspace note.
- _docs/03_implementation/batch_16_cycle4_report.md
- _docs/03_implementation/implementation_report_admin_class_edit_cycle4.md
- _docs/02_tasks/todo → done — AZ-512 archived.

Quality gates: 32 files / 243 tests / 13 quarantined skips PASS;
all 35 static checks PASS (FT-P-22/23, STC-ARCH-01/02, STC-SEC*,
banned-deps incl. SEC1B/C/D).

Cross-workspace dependency: admin/ AZ-513 (POST + PATCH + DELETE
/classes routes) NOT yet shipped. Step 11 (Run Tests) passes on
stubs; Step 16 (Deploy) holds until AZ-513 lands live. Leftover
record at _docs/_process_leftovers/2026-05-13_az-512-admin-
classes-prereq.md stays open.

Discovered pre-existing bug (NOT bundled): tests/msw/handlers/
admin.ts returns paginate(seedUsers) for GET /api/admin/users,
but AdminPage consumes as flat User[] → users.map crash. Test
files use the same flat-array workaround
destructive_ux.test.tsx documented. Flagged in batch + impl
reports for separate triage.

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

3.8 KiB

08 — Admin

1. High-Level Overview

Purpose: Operator-only configuration page. User management, detection-class management, AI Settings, GPS Settings, aircraft default.

Architectural Pattern: Single-page feature, large monolithic component (~215 lines pre-consolidation per state.json).

Upstream dependencies: 00_foundation, 01_api-transport, 03_shared-ui (ConfirmDialog).

Downstream consumers: 10_app-shell (routed at /admin, currently with no role-based guard — see #1 caveat below).

2. Internal Interfaces

Export Notes
AdminPage() Top-level route component. Sub-sections: Users, Detection Classes, AI Settings, GPS Settings, Aircraft default. Detection Classes table supports the full CRUD surface — add, edit (AZ-512 inline form on row click of the ✎ button; PATCH /api/admin/classes/{id} with full body per Risk-2 mitigation; Enter saves, Escape cancels; inline validation for empty name and non-positive maxSizeM; closes Architecture Vision P12), delete.

3. External API Specification

Method Path Purpose
GET / POST / PUT / DELETE /api/admin/users User CRUD
GET /api/annotations/classes Read class list (note: read uses annotations/, write uses admin/)
POST / PATCH / DELETE /api/admin/classes Class CRUD. PATCH /api/admin/classes/{id} powers the inline edit affordance (AZ-512) and accepts a full or partial body of { name?, shortName?, color?, maxSizeM? }. Cross-workspace note: as of AZ-512 ship, the live admin/ service still owes the write routes (POST + PATCH + DELETE) per AZ-513 on admin/; UI ships against MSW stubs until that lands.
GET / PUT /api/admin/settings/ai AI service config
GET / PUT /api/admin/settings/gps GPS device config
GET / PUT /api/admin/settings/aircraft-default Aircraft default

5. Implementation Details

State Management: Page-local React state per sub-form. No global form library.

Findings (B4, copied from state.json):

  1. PRIORITY (security): no role-based route guard on /admin — anyone authenticated can access. Server-enforced 403 protects the data, but UI does not gate. Surface in Step 6 problem-extraction. Cross-link with 10_app-shell.
  2. AI Settings & GPS Settings forms render with defaultValue only — NO state, NO submit handler, the Save button does nothing. PRIORITY surface in Step 6.
  3. Hardcoded GPS device default '192.168.1.100' / port '5535' shipped in production bundle. Step 4.
  4. handleDeleteClass has NO ConfirmDialog despite being destructive. Step 4 vs ui_design/README.md.
  5. Service split mismatch: detection-class read uses /api/annotations/classes (annotations service) but write uses /api/admin/classes (admin service). Verify with suite ADRs in Step 3a.
  6. handleToggleDefault duplicated between AdminPage and SettingsPage; aircraft default is global config but page exists in both /admin and /settings — surface intent in Step 6.
  7. Many hardcoded English strings. Step 4 i18n.

Key Dependencies: 03_shared-ui/ConfirmDialog (used for some destructive actions; missing on handleDeleteClass).

7. Caveats & Edge Cases

  • The broken Save button is the most user-visible bug.
  • The annotations/admin service split for class CRUD looks like a copy-paste residue but may be deliberate; verify in Step 3a.
  • No optimistic concurrency / version check for any settings — last writer wins.

8. Dependency Graph

Must be implemented after: 00_foundation, 01_api-transport, 03_shared-ui.

Can be implemented in parallel with: every other feature page.

Blocks: 10_app-shell.

Module Inventory

Path Module Doc
src/features/admin/AdminPage.tsx _docs/02_document/modules/src__features__admin__AdminPage.md