mirror of
https://github.com/stack-auth/stack.git
synced 2026-07-20 21:29:36 +08:00
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/hexclave/hexclave/blob/dev/CONTRIBUTING.md
-->
## Summary
This PR fixes the Users dashboard delete-dialog click behavior and makes
user mutations refresh the Users page without requiring a manual browser
reload.
The main user-facing changes are:
1. Opening the delete dialog from a Users table row no longer lets
dialog clicks fall through into row navigation.
2. Creating, deleting, or updating a user from the Users page now
refreshes the table, total-user count, and KPI cards automatically.
3. Users table profile links avoid expensive profile prefetching, so
refreshes no longer fan out into many unnecessary per-user profile
requests.
---
## Delete Dialog Click Behavior
The Users page has two delete-user entry points:
- the user profile action menu
- the Users table row action menu
Before this PR, the profile-page flow behaved correctly, but the
table-row flow could accidentally trigger row navigation while the
delete dialog was open. For example, clicking dialog content, footer
whitespace, the confirmation label, or the overlay could navigate to the
user's profile instead of simply interacting with or closing the dialog.
This PR fixes that by treating the row action area as non-row-click
territory and stopping click/double-click propagation from the action
surface.
Behavior after this change:
- Clicking outside the dialog closes it and returns to the page that
opened it.
- Clicking inside the dialog no longer navigates through the underlying
row.
- The confirmation label still toggles the required delete
acknowledgement.
- The explicit user identifier inside the dialog is the only navigation
target to that user's profile.
- The delete confirmation copy is shorter and uses a non-empty display
fallback:
- display name
- primary email
- user ID
---
## Users Page Refresh
Previously, creating or deleting a user through the Users page changed
backend state, but the table and KPI surfaces could stay stale until the
user manually refreshed the page or clicked the reload button.
This PR adds an explicit Users-page mutation refresh path:
- `UserDialog` can notify the page after create/edit succeeds.
- `DeleteUserDialog` can notify the page after delete succeeds.
- `UserTable` exposes its existing `useDataSource().reload()` function
to the page.
- The Users page refreshes the table and the metrics/count surfaces
after successful user mutations.
- The row action menu also refreshes after removing 2FA, so the row no
longer keeps showing a stale 2FA action after the update succeeds.
---
## Refresh Performance
The existing manual reload path used the broad `_refreshUsers()` SDK
invalidation, which refreshes more than this page needs. In local
testing, that broad path could combine with profile prefetching and
trigger many per-user requests for profile details, contact channels,
and OAuth providers.
This PR narrows the automatic Users-page refresh:
- table rows reload through the table data source
- total-user counts refresh through the user-count metrics endpoint
- KPI cards refresh through the metrics endpoint
- table/profile links can opt out of dashboard `UrlPrefetcher` with
`prefetch={false}`
- dense Users table profile links use `prefetch={false}`
- the delete-dialog profile link also opts out of prefetching
This keeps the generic `DataGrid` component unchanged. The Users page
owns the fact that a user mutation happened, and the table only exposes
the reload function it already has.
---
## KPI / Navigation Consistency
The Users page now keeps a page-local metrics snapshot for the total
count and KPI cards. It refreshes that snapshot after mutations and on
page restore, so navigating into a user profile and then returning with
browser back does not leave the KPI cards showing old counts.
Passive page-load/page-restore metrics refreshes use non-alert async
handling, so a background metrics failure is reported normally instead
of showing a blocking browser alert. User-triggered refreshes and
mutation-triggered refreshes still use the dashboard's alert-backed
async handling.
---
## Screenshots of the Delete User Dialog
Before
<img width="524" height="302" alt="old-user-delete-dialog"
src="https://github.com/user-attachments/assets/d75f25d3-e462-4ae3-ac3b-d133f6ddbbf6"
/>
After
<img width="520" height="281" alt="new-user-delete-dialog"
src="https://github.com/user-attachments/assets/4783f61e-76b6-45c5-8952-0f76fa48eba1"
/>
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes delete dialog click-through and makes the Users page auto-refresh
the table and metrics without flicker. KPIs and total users render
instantly from a snapshot and refresh in the background for smoother
navigation.
- **Bug Fixes**
- Block row navigation from all row-action and delete-dialog clicks; add
a non-prefetching profile link in `DeleteUserDialog` that closes the
dialog on click. Dialog now accepts `profileHref`, fires `onDeleted`,
and URL-encodes `projectId`/`user.id`; `redirectTo` still works.
- Safer links: `Link` supports `prefetch={false}` and disables internal
prefetch; applied to profile links in the table and dialogs.
- **Refactors**
- Instant metrics: preload a snapshot via
`fetchMetricsOrThrow`/`fetchMetricsUserCountsOrThrow` and pass it to
Total Users and KPI cards; auto-refresh on tab restore and after user
mutations.
- Targeted reloads: `UserTable` exposes `onReloadChange`; page, dialogs,
and 2FA removal call `onUserMutated` to reload rows and refresh metrics.
The refresh button uses the same path.
- Internals: add `sendRequest` and schema-validated metrics fetchers
with backward-compatible defaults.
<sup>Written for commit
|
||
|---|---|---|
| .. | ||
| public | ||
| scripts | ||
| src | ||
| .env | ||
| .env.development | ||
| .eslintrc.cjs | ||
| .gitignore | ||
| .npmrc | ||
| components.json | ||
| DESIGN-GUIDE.md | ||
| instrumentation-client.ts | ||
| LICENSE | ||
| next.config.mjs | ||
| package.json | ||
| postcss.config.js | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vitest.config.ts | ||