[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>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-13 04:35:13 +03:00
parent ef56d9c207
commit ecacfa8b43
10 changed files with 718 additions and 14 deletions
@@ -0,0 +1,89 @@
# Batch Report
**Batch**: 16
**Cycle**: 4 (autodev existing-code Step 10)
**Tasks**: [AZ-512]
**Date**: 2026-05-13
**Reactivation context**: AZ-512 was deferred to backlog at the end of cycle 3 (Cross-Workspace Verification BLOCKING gate failed — `admin/` service does not expose `/classes` write routes). User authorized **Option B** (MSW-stubbed UI ahead of admin/ AZ-513 shipping) at cycle 4 entry. Task moved `backlog/``todo/` in commit `ef56d9c`.
## Task Results
| Task | Status | Files Modified | Tests | AC Coverage | Issues |
|------|--------|---------------|-------|-------------|--------|
| AZ-512_admin_edit_detection_class | Done | 5 production + test + 1 doc | 12 passed | 8/8 ACs covered | 1 noted (pre-existing) |
### Files modified
| Path | Type | Change |
|------|------|--------|
| `src/features/admin/AdminPage.tsx` | OWNED (08_admin) | Added inline edit affordance: `editingId` / `editForm` / `editError` / `editSaving` state; handlers (`handleStartEdit`, `handleCancelEdit`, `handleUpdateClass`, `handleEditKeyDown`); colspan row swap when editing; pencil (✎) button on read-only rows. Updated `t('admin.classes')``t('admin.classes.title')`. |
| `src/i18n/en.json` | spec-authorized (00_foundation) | Restructured `admin.classes` from flat string to nested object (`title` + 6 new keys: `edit`, `save`, `cancel`, `nameRequired`, `maxSizeMustBePositive`, `updateFailed`). |
| `src/i18n/ua.json` | spec-authorized (00_foundation) | Ukrainian mirror of the same 7 keys (FT-P-22 parity gate PASS). |
| `tests/msw/handlers/admin.ts` | test-infra | Added `http.patch('/api/admin/classes/:id', ...)` partial-merge handler; existing PUT handler retained (dead code, not introduced by this task). |
| `tests/admin_class_edit.test.tsx` | new | 12 tests covering AC-1..AC-6, AC-8 (AC-7 covered by static FT-P-22 gate). |
| `tests/destructive_ux.test.tsx` | adjacent hygiene | Fixed `firstRow.querySelector('button')` selector at 3 call sites — my ✎ button became the first button in the row; replaced with `Array.from(querySelectorAll('button')).find(b => b.textContent === '×')` to deliberately target the delete (×) button. Pre-existing `it.fails()` semantics preserved. |
| `_docs/02_document/components/08_admin/description.md` | spec-authorized (per task Scope.Included) | Recorded edit affordance + PATCH wiring in Internal Interfaces table and External API table; cross-referenced AZ-513 prerequisite. |
### Files NOT modified (scope discipline)
| Path | Reason |
|------|--------|
| `src/api/endpoints.ts` | Task constraint: reuse existing `endpoints.admin.class(id)` builder; no new endpoint helper for PATCH (same URL as DELETE). |
| `src/api/client.ts` | `api.patch()` helper already exists. |
| `_docs/02_document/architecture.md` | Architecture-level wire-shape table update belongs in Step 13 (Update Docs), not Step 10. |
| AdminPage delete-confirm wiring | Out of scope (Finding B4 — explicitly excluded per task spec Scope.Excluded). |
| Settings/Users sections | Out of scope (separate concerns per task spec Scope.Excluded). |
## AC Test Coverage: All covered (8 of 8)
| AC | Test name | Notes |
|----|-----------|-------|
| AC-1 | `renders a pencil button per row` | One edit affordance per class row |
| AC-2 | `row 1 enters edit mode with name="class-a"; other rows stay read-only` + `single-row invariant` | Seeded values + Risk 3 mitigation |
| AC-3 | `Save button → one PATCH with full body, row re-renders, form closes` + `Enter key inside form behaves like Save` | Risk 2 mitigation: full-body always |
| AC-4 | `Cancel button → no PATCH; row reverts` + `Escape key inside form behaves like Cancel` | No network in either path |
| AC-5 | `empty name → no PATCH; nameRequired error visible` + `non-positive maxSizeM → no PATCH; maxSizeMustBePositive error visible` | Validation-before-submit |
| AC-6 | `PATCH 500 → form stays open; updateFailed error visible; no alert() called` | Risk 4 mitigation: disabled buttons during PATCH; spy on `window.alert` |
| AC-7 | (static) `FT-P-22 (key parity): PASS` | `scripts/check-i18n-coverage.mjs --parity-only` |
| AC-8 | `Add posts to /api/admin/classes and refetches the list` + `Delete sends DELETE and removes the row optimistically` | Regression guards |
## Code Review Verdict: PASS (inline self-review)
A formal `/code-review` skill run was not invoked for this single-task batch (3 pts, tight scope, all spec ACs verified). The self-review checked: file ownership respected, no silent error swallowing, no `alert()` usage (STC-SEC7 confirms), no banned-deps literals (STC-SEC1B/C/D confirm), i18n parity + coverage (FT-P-22/23 confirm), architecture compliance (STC-ARCH-01/02 confirm), single-responsibility handlers, no spec drift, no dependencies on un-shipped admin/ work in the test layer.
If a cumulative review is required at Step 14.5 (every K=3 batches), this is the 1st batch of cycle 4 — cumulative review fires at batch 18.
## Auto-Fix Attempts: 0
No PASS-with-warnings or FAIL findings during self-review.
## Stuck Agents: None
Single task, ~7 file edits, no rewrites without progress. The one i18n-coverage failure (3 raw English aria-labels) was fixed in a single targeted swap (aria-label → data-field) without regressing the spec's aria-label-on-edit-button NFR.
## Test Suite Result
| Suite | Result |
|-------|--------|
| `bun run test` (full vitest) | **32 files passed, 243 tests passed, 13 quarantined skips** (cycle 3 baseline preserved) |
| `bash scripts/run-tests.sh --static-only` | **All 35 static checks PASS** including FT-P-22, FT-P-23, STC-ARCH-01/02, STC-SEC1/2/3/4/7/8/13/14, STC-SEC1B/C/D, banned-deps, etc. |
## Pre-existing bug noted (NOT fixed this batch)
While writing the new test file, I discovered that `tests/msw/handlers/admin.ts` returns `paginate(seedUsers)` (= `{ items, totalCount, page, pageSize }`) for `GET /api/admin/users`, but `AdminPage.tsx:19` does `api.get<User[]>(...).then(setUsers)` expecting a flat array. The catch swallows fetch errors but NOT the subsequent `users.map is not a function` render error.
- **Impact in tests**: any test that mounts the full `<AdminPage />` without overriding the users handler crashes. Today, `destructive_ux.test.tsx:50-59` already overrides `/api/admin/users` with `jsonResponse([])` and documents the drift with the same comment shape; my new `tests/admin_class_edit.test.tsx` adds the same override (`stubUsersAsPlainArray()`).
- **Impact in production**: depends on what the live `admin/` service actually returns (flat or paginated). If paginated, the Users table is broken end-to-end against the live service — analogous to the pre-existing AZ-513 add/delete situation. If flat, only the test fixture is wrong.
- **Recommendation**: a separate UI-workspace ticket to either (a) align the MSW handler with the live admin/ shape (and fix `AdminPage.users` consumption if needed), or (b) introduce a paginated-response unwrap in the api client. NOT bundled with AZ-512 per scope discipline (`coderule.mdc`).
## Cross-workspace dependency reminder
AZ-512 ships in this batch but the **live admin/ service does not yet expose** `POST | PATCH | DELETE /api/admin/classes(/{id})` (verified 2026-05-13: zero `MapPost|MapPatch|MapDelete` against `classes` in `admin/Azaion.AdminApi/Program.cs`). Per the user-chosen Option B path:
- **Step 11 (Run Tests)** passes on MSW stubs.
- **Step 16 (Deploy)** gates on **AZ-513** landing on the admin/ workspace AND that build being deployed to whichever environment(s) the UI is promoted into. The leftover record at `_docs/_process_leftovers/2026-05-13_az-512-admin-classes-prereq.md` remains open until that point.
- The existing pre-existing-broken Add and Delete affordances on `AdminPage`'s class table also start working end-to-end the moment AZ-513 ships.
## Next Batch
None planned in this cycle (cycle 4 was entered for AZ-512 reactivation only). After Step 11 (Run Tests) confirms the test suite still passes, autodev auto-chains through Steps 12 → 13 → 14 → 15 → 16 → 17. The Deploy gate (Step 16) will surface the admin/ AZ-513 dependency before any prod cutover.