Commit Graph
1050 Commits
Author SHA1 Message Date
Lucas BergerandClaude Sonnet 4.6 ee04aee4fb fix(20): CR-02 derive editorMember from live query data
Store only the member id in editorMemberId state rather than the full
AdminMember object. Derive editorMember from membersQuery.data after each
render, so query invalidations following a per-section save always give
MemberEditorSheet fresh server state — eliminating the stale-reference
silent-overwrite race.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:00:47 -04:00
Lucas BergerandClaude Sonnet 4.6 72977334fc fix(20): CR-01 WR-06 atomic last-admin guard + empty-body 400
Wrap the last-admin check and UPDATE in a db.transaction with a
SELECT...FOR UPDATE locking read so concurrent PATCH demotions
serialise and cannot both pass the guard, eliminating the TOCTOU
race (CR-01).

Add a .refine() to updateMemberSchema requiring at least one field,
returning 400 via noEchoHook instead of crashing Drizzle with an
empty SET clause (WR-06).

Add Test H asserting empty {} -> 400 { error: 'Invalid request' }.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:59:56 -04:00
Lucas Berger 5c74ada48b docs(20): add deep code review report 2026-06-18 17:57:08 -04:00
Lucas BergerandClaude Opus 4.8 f656a0c77b docs(phase-20): evolve PROJECT.md after phase completion
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 17:48:23 -04:00
Lucas BergerandClaude Opus 4.8 dec8220da3 docs(phase-20): complete phase execution — verification passed (9/9), advisory review
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 17:47:51 -04:00
Lucas Berger 80fd683877 docs(20-03): complete member editor + declutter plan 2026-06-18 17:40:52 -04:00
Lucas Berger 9b62887f0f feat(20-03): unify member editor + declutter admin members panel
- playwright-cli verified: Members tab shows tappable rows, no retired buttons
- Row tap opens 'Edit member' sheet; per-section saves keep sheet open
- 'Add member' trigger opens 'Add member' sheet in create mode
- Profile save fires 'Profile saved.' toast; sheet stays open (D-05)
- eslint + prettier + typecheck + vitest (275 tests) all pass
- Fix pre-existing prettier drift in docs/*, CLAUDE.md, README.md, api/admin.ts
2026-06-18 17:39:00 -04:00
Lucas Berger 9e6b004541 feat(20-03): rework AdminPage MemberRow + Add-member trigger; remove old surfaces
- MemberRow: whole-row role=button, aria-label='Edit {name}', trailing ChevronRight
- Admin badge on member.isAdmin (12px/600, --color-member-0 on --color-surface-dim)
- 'Add member' ghost trigger with Plus icon opens MemberEditorSheet in create mode
- Single MemberEditorSheet replaces CredentialSheet + ResetPasswordSheet mounts
- Removed: inline Local Accounts add-form, ResetPasswordSheet definition, create-form state
- Empty state: 'No members yet' / 'Add a member to get started.' (UI-SPEC copy)
2026-06-18 17:35:37 -04:00
Lucas Berger b125a69b58 feat(20-03): add MemberEditorSheet with edit/create modes and per-section saves
- Edit mode: Profile (display name + admin toggle), Set new password, App password sections
- Create mode: single form with display name, username, initial/confirm password
- Per-section saves keep sheet open; create success closes sheet (D-05, D-07)
- Admin toggle role=switch, aria-checked; last-admin 409 shows inline error + reverts (D-03)
- Section 2 gated on member.hasLocalCredential; passwords never prefilled (T-20-07)
- App password save routes through saveCredential -> CalDAV validation (T-20-08)
- No Rotate/Add credential/Reset password copy (D-06)
2026-06-18 17:31:46 -04:00
Lucas Berger 10149a5966 docs(phase-20): update tracking after wave 1 2026-06-18 17:23:49 -04:00
Lucas Berger 258a188bde chore: merge executor worktree (worktree-agent-af493496867aaca6d) 2026-06-18 17:21:59 -04:00
Lucas Berger b377e9cb0a chore: merge executor worktree (worktree-agent-afcc2c886302ea642) 2026-06-18 17:21:59 -04:00
Lucas Berger 4a179a943d docs(20-01): complete member-profile update plan — SUMMARY + all CI gates green 2026-06-18 17:21:30 -04:00
Lucas BergerandClaude Sonnet 4.6 618991cf13 docs(20-02): complete plan 20-02 summary
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:21:26 -04:00
Lucas BergerandClaude Sonnet 4.6 5bcd8180c1 feat(20-02): add updateMemberProfile fetcher + AdminMember.isAdmin
- Add isAdmin: boolean to AdminMember interface (after color, before
  hasCredential) — feeds the Phase 20 editor toggle initial state (D-02)
- Add updateMemberProfile(memberId, body) fetcher: PATCH /api/admin/members/:id,
  credentials:include, redirect:manual, JSON body
- Maps 401/opaqueredirect → SessionExpiredError (existing convention)
- Maps 409/422 → Error('last-admin') sentinel (D-03 last-admin guard)
- Maps other non-ok → generic error
- All 50 tests pass; eslint + prettier + tsc --noEmit exit 0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:20:13 -04:00
Lucas Berger bc48632756 feat(20-01): add PATCH /members/:id member-profile update with last-admin guard
- Add isAdmin field to GET /members select and mapped member object (D-02)
- Add updateMemberSchema (displayName optional string, isAdmin optional boolean)
- Register adminRouter.patch('/members/:id') with noEchoHook and requireAdmin (inherited)
- Handler: parsePositiveIntParam id validation (400), existence check (404),
  D-03 last-admin guard via COUNT(*) query (409), partial set() update (200)
- Fix Test D: switch to adminId2 for GET after self-demotion (adminId1 no longer admin)
- All 44 tests green including 7 new PATCH/isAdmin tests
2026-06-18 17:19:43 -04:00
Lucas BergerandClaude Sonnet 4.6 18da7e9476 test(20-02): add failing tests for updateMemberProfile + AdminMember.isAdmin
- 8 RED tests covering: PATCH URL contract, credentials/redirect shape,
  void on 200, SessionExpiredError on 401/opaqueredirect, last-admin
  sentinel on 409 and 422, generic error on 500
- 1 compile-time shape test for AdminMember.isAdmin: boolean
- All new tests fail (updateMemberProfile is not a function); 42 existing pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:18:21 -04:00
Lucas Berger a0a82ac9b6 test(20-01): add failing tests for member-profile update + last-admin guard + isAdmin read
- Test A: PATCH displayName happy path → 200, GET reflects change
- Test B: PATCH isAdmin promote → 200, GET shows isAdmin true
- Test C: last-admin guard → 409 when only admin demotes self
- Test D: self-demotion → 200 when second admin exists
- Test E: non-admin PATCH → 403 (requireAdmin boundary)
- Test F: wrong-type body → 400 { error: "Invalid request" }; malformed :id → 400
- Test G: non-existent member id → 404
- Test H: GET /members includes boolean isAdmin per member
All 7 new tests fail RED for the right reasons (route 404 / isAdmin missing)
2026-06-18 17:18:14 -04:00
Lucas Berger 9b8b84edbe docs(20): finalize phase plan (state, roadmap waves, patterns) 2026-06-18 17:06:40 -04:00
Lucas Berger 666845a192 docs(20): create phase plan 2026-06-18 17:04:21 -04:00
Lucas Berger a3d89d0da0 docs(20): UI design contract 2026-06-18 16:51:46 -04:00
Lucas Berger 96193831c4 docs(20): UI design contract 2026-06-18 16:50:31 -04:00
Lucas Berger fe0325ec43 docs(state): record phase 20 context session 2026-06-18 16:42:23 -04:00
Lucas Berger 91948919a8 docs(20): capture phase context 2026-06-18 16:42:19 -04:00
Lucas Berger 51ec9c3e82 docs(roadmap): add Phase 20 — Admin Member Editor & Form Declutter 2026-06-18 15:14:25 -04:00
Lucas Berger b8d4a69b73 docs(phase-17): add security threat verification (9 closed, 0 open)
CI / changes (pull_request) Successful in 4s
CI / fast-checks (pull_request) Failing after 1m41s
CI / api (pull_request) Successful in 2m2s
CI / security (pull_request) Successful in 54s
CI / gate (pull_request) Has been cancelled
CI / harness (pull_request) Has been cancelled
2026-06-18 14:33:10 -04:00
Lucas Berger 881f2d2d18 test(17): complete UAT - 9 passed, 0 issues (playwright-cli verified) 2026-06-18 14:31:32 -04:00
Lucas Berger 6fa6725fe8 docs(17): add code review fix report (auto-loop, 3 iterations) 2026-06-18 14:06:03 -04:00
Lucas Berger 287ecae2f7 fix(17): CR-01 make focus-trap visibility filter tolerant of jsdom; IN-01 drop inert containment guard 2026-06-18 14:04:33 -04:00
Lucas Berger 89dee4f586 fix(17): IN-03 render visible Redirecting status during OIDC redirect 2026-06-18 13:57:31 -04:00
Lucas Berger f7575ea2c3 fix(17): IN-02 document hand-maintained favicon.ico coupling 2026-06-18 13:56:48 -04:00
Lucas Berger 7578d48d3d fix(17): IN-01 pull focus back into dialog when activeElement escapes 2026-06-18 13:56:32 -04:00
Lucas Berger 5b4625b41d fix(17): WR-01 exclude hidden/zero-size nodes from focus-trap boundaries 2026-06-18 13:56:23 -04:00
Lucas Berger 4bc1e2a820 fix(17): IN-05 normalize AdminPage JSX formatting with Prettier 2026-06-18 13:48:34 -04:00
Lucas Berger 11b6b36cb9 fix(17): IN-07 memoize Intl.DateTimeFormat timezone resolution 2026-06-18 13:47:37 -04:00
Lucas Berger 2317833b74 fix(17): IN-04 remove dead brand placeholder tokens 2026-06-18 13:46:47 -04:00
Lucas Berger 3f4b7eac73 fix(17): IN-02 useCallback handleClose, drop exhaustive-deps disables 2026-06-18 13:46:27 -04:00
Lucas Berger dd0b76128d fix(17): WR-08 replace cp icon chain with cross-platform Node copy script 2026-06-18 13:45:36 -04:00
Lucas Berger 1c0f35748d fix(17): WR-06 WR-07 clamp tz combobox active index; Tab-to-commit, blur-timer unmount cleanup 2026-06-18 13:44:44 -04:00
Lucas Berger f601c0c408 fix(17): WR-02 WR-03 WR-04 IN-06 admin tab Home/End+wrap; toast re-announce, wrap, z-index 400 2026-06-18 13:43:10 -04:00
Lucas Berger fb30800e9a fix(17): WR-01 trap focus in modal dialogs via useFocusTrap 2026-06-18 13:41:33 -04:00
Lucas Berger a4a7438641 fix(17): WR-05 IN-03 IN-01 resize-aware useIsPhone hook; OidcRedirect side-effect in effect 2026-06-18 13:40:00 -04:00
Lucas Berger c2ceebf130 docs(17): add deep code review report 2026-06-18 13:35:10 -04:00
Lucas BergerandClaude Opus 4.8 a986c74963 docs(phase-17): mark phase complete (pending human verification)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:11:35 -04:00
Lucas BergerandClaude Opus 4.8 fcc02e3833 docs(17): add phase verification report (10/10 decisions delivered; human_needed for device-only checks)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:11:23 -04:00
Lucas BergerandClaude Opus 4.8 9730d3dcdb docs(17): add code review report
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:06:20 -04:00
Lucas BergerandClaude Opus 4.8 ea49a4dc83 docs(phase-17): update tracking after wave 2
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:03:12 -04:00
Lucas BergerandClaude Opus 4.8 b5fcd1d172 fix(17): resolve post-merge wave 2 integration issues
- SettingsSheet: void the navigate('/login') promise (react-router v7 returns Promise) — eslint no-floating-promises
- InstructionSheet.test: wrap SettingsSheet render in MemoryRouter — 17-05 added useNavigate() which needs Router context

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:01:39 -04:00
Lucas Berger 4887ba1b5f chore: merge executor worktree (worktree-agent-a12887546a61a8053) 2026-06-18 12:58:59 -04:00
Lucas Berger aaf754d938 chore: merge executor worktree (worktree-agent-a65077bbd9885bf7e) 2026-06-18 12:58:58 -04:00