Skip to content

🥂 feat: Toasts on All Save Actions#75

Open
dustinhealy wants to merge 3 commits into
worktree-radix-dismissable-dedupefrom
feat/save-action-toasts
Open

🥂 feat: Toasts on All Save Actions#75
dustinhealy wants to merge 3 commits into
worktree-radix-dismissable-dedupefrom
feat/save-action-toasts

Conversation

@dustinhealy

@dustinhealy dustinhealy commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Wires @clickhouse/click-ui's Toast into every save mutation in the admin panel so admins get visible feedback on both success and failure. Before this PR most save sites silently swallowed errors to console.error and showed nothing on success.

Stacked on #74 (Radix react-dismissable-layer dedupe). The dedupe is a hard prerequisite. Without it click-ui's toast auto-dismiss timer is killed by the shared-layer-set race as soon as any modal opens or closes. Once #74 merges to main this PR rebases cleanly onto main.

  • Add notifySuccess(title) and notifyError(title) helpers in src/utils/toast.ts that delegate to click-ui's exported createToast. ClickUIProvider already mounts the matching ToastProvider, so no extra wiring is needed at the root.
  • Fire a success/error toast from every save site: config editor (per-field, bulk save, YAML import), groups create/edit/delete, roles create/edit/delete, users invite/delete, role + group assignment in UserDetailDialog, user-profile create/delete, system grants in EditCapabilitiesDialog, and per-field profile values in useProfileMutations.
  • Pass the affected resource through each mutation's variables rather than reading it from closed-over component state in onSuccess, so the toast still renders the correct name when the confirm dialog has cleared its target by the time the response lands.
  • Delete the unused .config-toast* CSS block and ToastState type left over from the old ConfigPage-local toast portal.
  • New i18n keys under com_toast_* for the per-resource success messages (com_toast_group_deleted, com_toast_role_updated, etc.).

Change Type

  • New feature (non-breaking change which adds functionality)

Implementation

  • src/utils/toast.ts: five-line module exporting notifySuccess and notifyError over createToast({ type: 'success' | 'danger', title }). Toasts inherit click-ui's brand colours, WCAG contrast, dismiss button, swipe-to-dismiss, ARIA live region, keyboard accessibility, and 5s auto-dismiss from the library.
  • Mutation variables carry the resource name. GroupsTab, RolesTab, UsersPage delete-mutation mutationFn now takes the full target object; UserDetailDialog's add/remove role/group mutations take { id, name }; EditCapabilitiesDialog's save passes { name: principalName }. Each onSuccess reads from the mutation's variables arg so the toast cannot read an empty string when the confirm dialog has already closed.
  • ConfigPage drops its local useState<ToastState> + portal and instead calls notifySuccess and notifyError directly. The notifySaving intermediate state is gone since the bulk save flow already shows pending state via ConfirmSaveDialog's saving prop and the StickyActionBar's disabled save button.

Testing

  • SESSION_SECRET=12345678901234567890123456789012 bun run test: 675 tests pass across 17 files.
  • bun run lint: clean.
  • SESSION_SECRET=12345678901234567890123456789012 bun run build: production build green.
  • Manual: triggered each save mutation listed above and confirmed a click-ui toast appears with the resource name and click-ui's brand styling, auto-dismisses after 5s, can be dismissed early via the close button or swipe-right.

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • My changes do not introduce new warnings
  • Local unit tests pass with my changes

Note

Low Risk
UI-only feedback and mutation variable wiring; no changes to authorization or server APIs.

Overview
Adds click-ui toasts for admin save/delete flows via new notifySuccess / notifyError helpers in src/utils/toast.ts, replacing silent failures and the ConfigPage custom toast portal.

Success and error toasts now fire across groups/roles (create, edit, delete), users (invite, delete, role/group/profile assignment), system capabilities, config bulk save/import/validation, and per-field profile overrides in useProfileMutations. New com_toast_* i18n strings name the affected resource.

Mutations pass resource identity through variables (e.g. full group/role/user on delete, { name } on create/update) so toast text stays correct after confirm dialogs clear state. Several sites route API errors to toasts instead of inline setError or ConfirmDialog error props (RolesTab drops deleteError).

Removes dead .config-toast* CSS and the ToastState type. ConfigPage no longer shows a separate “Saving…” toast; pending state remains on the save dialog and sticky bar.

Reviewed by Cursor Bugbot for commit 8419332. Bugbot is set up for automated code reviews on this repo. Configure here.

Wires click-ui's Toast component into every save mutation in the admin
panel: config editor (per-field, bulk, YAML import), groups (create /
edit / delete), roles (create / edit / delete), users (invite, delete,
role / group assignment, user profile create / delete), system grants
(EditCapabilitiesDialog), and per-field profile mutations
(useProfileMutations).

Uses click-ui's official createToast directly; ClickUIProvider already
mounts the matching ToastProvider, so the global createToast routes
through it. Toasts inherit click-ui brand colours, WCAG contrast,
keyboard accessibility, swipe-to-dismiss, close button, ARIA live
region, and 5s auto-dismiss from the library.

src/utils/toast.ts is a five-line wrapper exposing notifySuccess and
notifyError that delegate to click-ui's createToast. Call sites pass
the affected resource through mutation variables (not closed-over
component state) so the toast renders the correct name even when the
confirm dialog has already cleared its target by the time the response
lands.

Refs AI-1206.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

Comment thread src/components/access/EditGroupDialog.tsx
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 3cc8172526

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…tions

EditGroupDialog, EditRoleDialog and EditCapabilitiesDialog mutationFns
used to bail with an empty return when their target (group, role,
principal) was missing, which React Query treats as success and which
caused the new onSuccess handlers to fire a success toast and close
the dialog without anything having been persisted.

Throw a localised error in the unavailable case so onError fires
instead, and add a matching guard at each call site so mutate is
never invoked with a missing target in the first place.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: db90e9b412

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit db90e9b. Configure here.

Comment thread src/components/access/CreateGroupDialog.tsx
The five create and edit dialogs used to read the resource name from
component state inside onSuccess instead of from the value that was
submitted with the mutation. If the user edited the name field while
the request was in flight (or the dialog reset before the toast
fired), the toast could render an empty or wrong name even though the
server saved the original value.

Pass the submitted name through the mutation variables, use it for
the actual API call, and read it back from the mutation's data or
variables argument in onSuccess so the toast always reflects what
was persisted.
@dustinhealy

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 8419332bc2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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