Skip to content

fix(web): keep rule dialog permissions settled while scope resolves - #719

Merged
Menci merged 2 commits into
mainfrom
fix/permission-dialog-scope-flash
Nov 13, 2026
Merged

Menci merged 2 commits into
mainfrom
fix/permission-dialog-scope-flash

Conversation

@Menci

@Menci Menci commented Nov 13, 2026 •

Copy link
Copy Markdown
Owner

Problem

Switching the scope in the new/edit rule dialog rendered the new scope's permission fields before their inherited values were resolved, so every field briefly flashed Inherit (unknown).

Fix

  • The dialog renders permission fields from the last settled target: scope + its resolved inherited values (or error). Until the new resolution arrives, the previous fields stay as they were; then the new fields render once, already in their final state. Explicit values outside the new scope are pruned in that same transition.
  • While a resolution is pending, the permission fieldset is aria-busy and its radios are disabled, so no choice is made against a stale target. Saving is a no-op in that window; the save button stays enabled so it does not flicker on each scope change.
  • useAccessResolution keys its result on the target independently of enabled, replacing the separate last-request state that kept a closing dialog from dropping its result.

Tests

  • record-dialog_test.tsx: observes every DOM mutation across two scope switches with the resolution held back, asserting no unknown-inheritance state ever renders and only the old or final field sets appear (fails on main); while pending, fields are busy and disabled and clicking save sends no write.
  • resolution_test.tsx: disabling keeps the result for an unchanged target and drops it when the target changes.

Verification

  • pnpm exec vitest run over record-dialog_test.tsx, resolution_test.tsx, permission-fields_test.tsx and dashboard-admin-permissions_test.tsx — 59 passed
  • pnpm --filter @floway-dev/web run typecheck
  • pnpm exec eslint over the changed sources and tests
  • CI (Verify) on the PR head

Changing the scope in the rule dialog re-rendered the permission fields for
the new scope immediately, while its inherited values were still being
resolved. Every field briefly showed "Inherit (unknown)" before the
resolution landed.

The dialog now renders permission fields from the last settled target: the
scope together with its resolved inherited values (or resolution error).
The fields keep the previous target until the replacement resolves, then
switch in a single render. Explicit values outside the new scope are pruned
at that same moment.

The resolution hook now keys its result on the target independently of
`enabled`, which replaces the separate last-request bookkeeping that kept a
closing dialog from dropping its result.
The settled permission fields stay visible while a new scope resolves,
but choices made against them would be judged by inherited values of a
target that is no longer selected, and choices for permissions the new
scope lacks would be silently pruned.

The permission fieldset is now marked busy and its radios disabled until
the resolution settles. Saving is a no-op in that window; the save button
itself stays enabled so it does not flicker on every scope change.
@Menci
Menci merged commit 3af1088 into main Nov 13, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant