Files
familysync/.planning/phases/20-admin-member-editor-form-declutter/20-02-SUMMARY.md
T
2026-06-18 17:21:26 -04:00

5.0 KiB

phase, plan, subsystem, tags, status, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags status dependency_graph tech_stack key_files decisions metrics
20-admin-member-editor-form-declutter 02 pwa-api-client
api-client
types
fetcher
admin
tdd
complete
requires provides affects
20-01: PATCH /api/admin/members/:id route (Plan 20-01, parallel worktree)
updateMemberProfile fetcher consumed by Plan 20-03 MemberEditorSheet
AdminMember.isAdmin field for editor toggle initial state
apps/pwa/src/api/client.ts
apps/pwa/src/api/client.test.ts
added patterns
SessionExpiredError sentinel (existing convention) extended to new fetcher
last-admin sentinel error (new: mirrors existing 'conflict' pattern at line 206)
TDD RED→GREEN cycle on client.ts behavior
modified
path change
apps/pwa/src/api/client.ts Added isAdmin: boolean to AdminMember interface; added updateMemberProfile fetcher
path change
apps/pwa/src/api/client.test.ts Added 9 TDD tests: 8 for updateMemberProfile behavior + 1 for AdminMember.isAdmin shape
Followed PATCH verb for the member-profile update (idiomatic REST, consistent with updateEvent at line 496)
last-admin sentinel maps both 409 and 422 — PATTERNS.md notes server may return either; both handled
isAdmin placed after color and before hasCredential in AdminMember — matches PATTERNS.md excerpt
duration_minutes completed_date tasks_completed files_modified
4 2026-06-18 2 2

Phase 20 Plan 02: PWA API Client — updateMemberProfile Fetcher + AdminMember.isAdmin Summary

One-liner: Thin API client glue: updateMemberProfile PATCH fetcher with last-admin 409/422 sentinel + isAdmin: boolean on AdminMember, enabling the Plan 20-03 editor.

What Was Built

Added two changes to apps/pwa/src/api/client.ts:

  1. AdminMember.isAdmin: boolean — new required field on the AdminMember interface (after color, before hasCredential). Feeds the editor toggle's initial state once Plan 20-01 lands (the GET /api/admin/members route already returns it after that plan's changes). No consumer code changes needed; Plan 20-03 reads it directly.

  2. updateMemberProfile(memberId, body) — exported async function that issues PATCH /api/admin/members/${memberId} with credentials: 'include', redirect: 'manual', Content-Type: application/json, and JSON-stringified body { displayName?, isAdmin? }. Error mapping:

    • opaqueredirect or 401SessionExpiredError (existing re-auth flow convention)
    • 409 or 422new Error('last-admin') (D-03 sentinel; editor branches on this message)
    • other non-ok → generic Error
    • 200 → resolves void

TDD Gate Compliance

Gate Commit Notes
RED 18da7e9 8 updateMemberProfile behavior tests + 1 AdminMember.isAdmin shape test — all fail with updateMemberProfile is not a function; 42 existing tests pass
GREEN 5bcd818 All 50 tests pass after implementation; eslint + prettier + tsc --noEmit exit 0
REFACTOR N/A No refactor needed — the implementation was minimal and clean on the first pass

Task Summary

Task Name Commit Files
RED Add failing tests for updateMemberProfile + AdminMember.isAdmin 18da7e9 client.test.ts
GREEN Add updateMemberProfile fetcher + AdminMember.isAdmin 5bcd818 client.ts, client.test.ts

Verification

  • grep -n "updateMemberProfile" apps/pwa/src/api/client.ts → line 249 (export), line 263 (error throw)
  • grep -n "PATCH" apps/pwa/src/api/client.ts → line 254 (method: 'PATCH')
  • grep -nE "isAdmin: boolean" apps/pwa/src/api/client.ts → line 597 (AdminMember)
  • grep -n "last-admin" apps/pwa/src/api/client.ts → line 262 (the sentinel throw)
  • pnpm --filter @familysync/pwa exec tsc --noEmit → exit 0
  • pnpm --filter @familysync/pwa exec eslint src/api/client.ts src/api/client.test.ts → exit 0
  • pnpm exec prettier --check apps/pwa/src/api/client.ts apps/pwa/src/api/client.test.ts → exit 0
  • 50/50 tests pass

Deviations from Plan

None — plan executed exactly as written.

  • The PATTERNS.md excerpt was followed verbatim for the function shape.
  • The eslint require-await issue in the test file was caught and fixed during Task 2 CI gates (test function did not need async — removed it). Not a plan deviation; it was a CI gate finding during Task 2 as specified.

Known Stubs

None. This plan delivers typed wire code only; no UI rendering or data display.

Threat Flags

No new security-relevant surface introduced. updateMemberProfile reuses the existing session-expiry path (T-20-05 mitigated) and does not introduce new trust boundaries (T-20-06 accepted per plan threat model).

Self-Check: PASSED

Check Result
apps/pwa/src/api/client.ts exists FOUND
apps/pwa/src/api/client.test.ts exists FOUND
20-02-SUMMARY.md exists FOUND
RED commit 18da7e9 FOUND
GREEN commit 5bcd818 FOUND