Skip to content

feat(dashboard): unify mutation feedback into global flash toasts#582

Merged
SantiagoDePolonia merged 4 commits into
mainfrom
feat/flash-messages
Jul 24, 2026
Merged

feat(dashboard): unify mutation feedback into global flash toasts#582
SantiagoDePolonia merged 4 commits into
mainfrom
feat/flash-messages

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the dashboard's per-page notice/error banner pattern (hand-rolled $state fields, inline alert markup, and page-specific gating repeated across 19 files) with a single flash-message mechanism:

  • New flash store (src/lib/stores/flash.svelte.js): flash.success(msg) / flash.error(msg) queue toasts with timed auto-dismiss (5s success / 8s error), manual dismiss, and dedup (an identical toast still on screen restarts instead of stacking).
  • New FlashMessages organism mounted once in App.svelte: fixed bottom-right stack, rendered above modal shells so feedback fired from a dialog stays visible, with consistent role="status"/role="alert" + aria-live in one place.
  • All 10 page stores (budgets, rate limits, MCP servers, provider credentials, guardrails, workflows, virtual models, pricing overrides, failover, API keys) and 4 settings sections (budget settings, tagging, pricing recalculation, runtime refresh) migrated; every per-store notice field is deleted.
  • Toasts fire immediately when the mutation response arrives — before the follow-up list refetch — so feedback is instant instead of trailing the refetch by seconds.

User-visible impact

  • Mutation feedback (saves, deletes, resets, toggles, reconnects) now appears as bottom-right toasts that auto-dismiss, instead of inline banners that persisted until the next action and were sometimes hidden by gating conditions (e.g. the failover editor's "Generated N fallback models" message previously rendered nowhere visible on the Models page).
  • Deliberately unchanged: load failures, in-form validation/submit errors, "feature is unavailable" capability warnings, confirm-dialog errors, and the one-time issued-API-key banner remain inline — transient toasts would be wrong for persistent state.
  • "Generated N drafts" no longer duplicates the failover drafts modal's own list/empty state.

Testing

  • svelte-check: 0 errors / 0 warnings; make test-dashboard: 316/316.
  • Live headless-browser (CDP) verification: toast text/role/styling, 5s auto-dismiss, manual dismiss, and instant appearance on budget create; embedded dist rebuilt and in sync.

Summary by CodeRabbit

  • New Features
    • Added global toast notifications for dashboard success and error messages, including fixed toast stacking, auto-dismiss, animations, accessibility live regions, and manual dismissal.
  • Bug Fixes
    • Unified user feedback across settings, budgets, guardrails, models, providers, rate limits, workflows, failover, and API key actions by replacing per-page success “notice” banners with flash toasts.
    • Improved consistency: identical messages are de-duplicated, and success banners are removed where they were previously incorrectly shown/hidden during form interactions.

Replace the per-page notice/error banner pattern (hand-rolled state
fields, inline alert markup, and page-specific gating across 19 files)
with one flash store and a FlashMessages toast stack mounted in App:

- flash.success()/flash.error() queue toasts top-right with timed
  auto-dismiss (5s/8s), manual dismiss, and identical-toast dedup
- mutation feedback (saves, deletes, resets, toggles, reconnects) now
  flashes globally; success messages no longer vanish on next action
- persistent state stays inline: load failures, in-form validation and
  submit errors, feature-unavailable warnings, confirm-dialog errors,
  and the one-time issued-API-key banner
- consistent aria (role=status/alert + aria-live) in one place
Copilot AI review requested due to automatic review settings July 24, 2026 15:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@SantiagoDePolonia, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fc8c75ad-e4db-4ed3-a708-7a9ce0d1eca3

📥 Commits

Reviewing files that changed from the base of the PR and between a4cfc88 and b1431c8.

⛔ Files ignored due to path filters (2)
  • internal/admin/dashboard/static/dist/assets/index-DmWIREH6.js is excluded by !**/dist/**
  • internal/admin/dashboard/static/dist/index.html is excluded by !**/dist/**
📒 Files selected for processing (12)
  • web/dashboard/src/pages/auth-keys/authKeys.svelte.js
  • web/dashboard/src/pages/budgets/budgets.svelte.js
  • web/dashboard/src/pages/guardrails/guardrails.svelte.js
  • web/dashboard/src/pages/mcp-servers/mcpServers.svelte.js
  • web/dashboard/src/pages/models/failover.svelte.js
  • web/dashboard/src/pages/models/pricingOverrides.svelte.js
  • web/dashboard/src/pages/models/virtualModels.svelte.js
  • web/dashboard/src/pages/providers-config/providersConfig.svelte.js
  • web/dashboard/src/pages/settings/BudgetSettings.svelte
  • web/dashboard/src/pages/settings/PricingRecalculation.svelte
  • web/dashboard/src/pages/settings/TaggingSettings.svelte
  • web/dashboard/src/pages/workflows/workflows.svelte.js
📝 Walkthrough

Walkthrough

The dashboard adds a global flash-message store and toast component, then migrates page-level mutation feedback from local notices and errors to shared success/error notifications across keys, budgets, guardrails, servers, models, settings, and workflows.

Changes

Global flash notification migration

Layer / File(s) Summary
Flash store and notification surface
web/dashboard/src/lib/stores/flash.svelte.js, web/dashboard/src/lib/components/organisms/FlashMessages.svelte, web/dashboard/src/App.svelte
Adds reactive toast state with timers, deduplication, dismissal, transitions, accessibility attributes, styling, and root-level rendering.
Core page feedback migration
web/dashboard/src/pages/auth-keys/*, web/dashboard/src/pages/budgets/*, web/dashboard/src/pages/guardrails/*
Routes mutation outcomes through flash, retains relevant inline errors, and removes local success-notice rendering.
Service configuration and limits migration
web/dashboard/src/pages/mcp-servers/*, web/dashboard/src/pages/providers-config/*, web/dashboard/src/pages/rate-limits/*
Uses shared flash notifications for server, provider, and rate-limit actions while preserving load, availability, and confirmation handling.
Model operations migration
web/dashboard/src/pages/models/*
Replaces failover, pricing override, and virtual-model notices with flash success/error messages and removes corresponding banner UI.
Settings and workflow migration
web/dashboard/src/pages/settings/*, web/dashboard/src/pages/workflows/*
Moves save, reset, refresh, recalculation, tagging, and workflow feedback to flash or confirmation-dialog errors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DashboardPage
  participant flash
  participant App
  participant FlashMessages
  DashboardPage->>flash: emit mutation result
  flash->>App: update reactive toast state
  App->>FlashMessages: render toast stack
  FlashMessages->>flash: dismiss toast
Loading

Suggested reviewers: copilot

Poem

A rabbit hops through toast-filled air,
Success and errors sparkle there.
Local notices fade from view,
One shared flash comes bouncing through.
“Hop, hop!” the dashboard sings,
With tiny animated wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately summarizes the main dashboard feedback change.
Description check ✅ Passed It includes a clear summary of what changed, why, impact, and testing, which covers the template's required content.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/flash-messages

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown

Confidence Score: 3/5

The PR needs fixes before merging because successful mutations can produce contradictory failure feedback and successful settings saves can leave obsolete error alerts visible.

Success is announced before fallible refetches complete, and two settings save paths retain persistent load errors, causing the dashboard to communicate incorrect operation outcomes.

web/dashboard/src/pages/budgets/budgets.svelte.js, web/dashboard/src/pages/settings/BudgetSettings.svelte, web/dashboard/src/pages/settings/TaggingSettings.svelte

T-Rex T-Rex Logs

What T-Rex did

  • I reproduced a refetch mismatch where a successful PUT is followed by a failed GET, leaving the success toast and the mutation form error visible after the form closes.
  • I used the Playwright harness against the real dashboard API to render the Settings page, where a 503 on the initial GET coexists with a 200 on the PUT, producing both the retained load alert and the success toast.
  • I documented the toast lifecycle and confirmed that refetches returned HTTP 200 after changes, while the prior inline load error and the new success toast can coexist; the previously observed refetch failure did not reappear.

View all artifacts

T-Rex Ran code and verified through T-Rex

Sequence Diagram

sequenceDiagram
  participant U as Operator
  participant P as Dashboard page
  participant A as Admin API
  participant F as Flash store
  U->>P: Submit mutation
  P->>A: Mutation request
  A-->>P: Success
  P->>F: Queue success toast
  P->>A: Refetch page data
  A-->>P: Updated data or load failure
  F-->>U: Render global toast
Loading

Reviews (1): Last reviewed commit: "feat(dashboard): unify mutation feedback..." | Re-trigger Greptile

Comment on lines 191 to 192
flash.success("Budget saved.");
await this.fetchBudgets();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Refetch failures misreport mutations

When a mutation succeeds but the subsequent list refetch fails, this code has already announced success and the shared catch then reports that the mutation failed. Operators consequently receive contradictory feedback even though the server-side change succeeded; separate the mutation result from refresh errors or defer success feedback until the refresh outcome is known.

Artifacts

Repro: focused executable Svelte-store harness with transport and feedback assertions

  • Evidence file captured while the check ran.

Repro: successful PUT, failed follow-up GET, and contradictory feedback sequence

  • The full command output behind this check.

Budget Initial Load Error

  • What the screen looked like at this point in the check.

After: Budget Stale Error Save

  • What the screen looked like at this point in the check.

▶ budget-stale-error-flow.webm

  • The flow T-Rex ran, recorded end to end.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines 57 to 59
return;
}
saving = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Successful saves retain load errors

When the initial settings load fails and a later save succeeds, the save path no longer clears error, so the success toast appears while the obsolete load-failure alert remains rendered indefinitely. Clear the retained load error when beginning or completing a successful save; the same stale-state behavior exists in TaggingSettings.svelte.

Artifacts

Repro: Playwright harness controlling the real dashboard API responses and exercising the save flow.

  • Evidence file captured while the check ran.

Repro: executed harness output confirming the success toast and retained load error were both visible.

  • The full command output behind this check.

Repro: browser console and network trace showing the 503 GET followed by the 200 PUT and visibility assertions.

  • Evidence file captured while the check ran.

Budget Initial Load Error

  • What the screen looked like at this point in the check.

After: Budget Stale Error Save

  • What the screen looked like at this point in the check.

▶ Recording of the check

  • The flow T-Rex ran, recorded end to end.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines 57 to 59
return;
}
saving = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Successful saves retain load errors

When the initial settings load fails and a later save succeeds, the save path no longer clears error, so the success toast appears while the obsolete load-failure alert remains rendered indefinitely. Clear the retained load error when beginning or completing a successful save; the same stale-state behavior exists in TaggingSettings.svelte.

Artifacts

Repro: Playwright harness controlling the real dashboard API responses and exercising the save flow.

  • Evidence file captured while the check ran.

Repro: executed harness output confirming the success toast and retained load error were both visible.

  • The full command output behind this check.

Repro: browser console and network trace showing the 503 GET followed by the 200 PUT and visibility assertions.

  • Evidence file captured while the check ran.

Budget Initial Load Error

  • What the screen looked like at this point in the check.

After: Budget Stale Error Save

  • What the screen looked like at this point in the check.

▶ Recording of the check

  • The flow T-Rex ran, recorded end to end.

View artifacts

T-Rex Ran code and verified through T-Rex

Slide up from the bottom edge with a slight overshoot (backOut, 360ms)
instead of the subtle 160ms side-slide, and add a fading glow ring in
the toast's accent color on arrival. Glow is disabled under
prefers-reduced-motion.
Copilot AI review requested due to automatic review settings July 24, 2026 16:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Anchor the toast stack over the main section (offset past the sidebar,
tracking its collapsed/mobile 60px width) and drop toasts in from above.
Toasts now size to their text up to 480px.
Copilot AI review requested due to automatic review settings July 24, 2026 16:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/dashboard/src/lib/components/organisms/FlashMessages.svelte`:
- Line 18: Update the toast transitions in FlashMessages, including the fly
entry and fade exit, to honor prefers-reduced-motion by using the no-transition
parameter shape or zero duration when reduced motion is enabled, while
preserving the existing animations otherwise.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3197946d-1530-426e-843e-767ae3bf0c4a

📥 Commits

Reviewing files that changed from the base of the PR and between c32677f and a4cfc88.

⛔ Files ignored due to path filters (3)
  • internal/admin/dashboard/static/dist/assets/index-BOce0cPe.js is excluded by !**/dist/**
  • internal/admin/dashboard/static/dist/assets/index-cOpxxzyI.css is excluded by !**/dist/**
  • internal/admin/dashboard/static/dist/index.html is excluded by !**/dist/**
📒 Files selected for processing (1)
  • web/dashboard/src/lib/components/organisms/FlashMessages.svelte

class:flash-toast-error={toast.kind === "error"}
role={toast.kind === "error" ? "alert" : "status"}
aria-live={toast.kind === "error" ? "assertive" : "polite"}
in:fly={{ y: -24, duration: 360, easing: backOut }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="web/dashboard/src/lib/components/organisms/FlashMessages.svelte"
if [ -f "$file" ]; then
  echo "== file exists =="
  wc -l "$file"
  echo "== relevant lines =="
  sed -n '1,160p' "$file" | nl -ba
else
  echo "missing $file"
  echo "== flash messages candidates =="
  fd -i 'FlashMessages' .
fi

echo "== reduced-motion references =="
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'prefers-reduced-motion|reduced motion|transition:|fade|fly|glow' web/dashboard/src || true

Repository: ENTERPILOT/GoModel

Length of output: 303


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="web/dashboard/src/lib/components/organisms/FlashMessages.svelte"

echo "== relevant file content =="
if command -v nl >/dev/null 2>&1; then
  nl -ba "$file" | sed -n '1,160p'
else
  awk '{printf "%6d  %s\n", NR, $0}' "$file" | sed -n '1,160p'
fi

echo "== reduced-motion / transition references ==="
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'prefers-reduced-motion|reduced motion|transition:|fade|fly|glow' web/dashboard/src || true
else
  grep -RIn --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=build 'prefers-reduced-motion\|reduced motion\|transition:\|fade\|fly\|glow' web/dashboard/src || true
fi

Repository: ENTERPILOT/GoModel

Length of output: 10118


Honor reduced-motion for the Svelte transitions.

The prefers-reduced-motion: reduce rule only disables the glow animation; the fly entry and fade exit still run for users who prefer reduced motion. Use no-transition parameter shape or zero-duration transitions under reduced-motion conditions for these toasts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/dashboard/src/lib/components/organisms/FlashMessages.svelte` at line 18,
Update the toast transitions in FlashMessages, including the fly entry and fade
exit, to honor prefers-reduced-motion by using the no-transition parameter shape
or zero duration when reduced motion is enabled, while preserving the existing
animations otherwise.

- Post-success list refreshes are now fire-and-forget (void) instead of
  awaited inside the mutation's try block, so a refresh failure can
  never be misreported as a failed mutation; it surfaces only as the
  list's own load state. (The one-shot loads in the conversation drawer
  and the types fetch inside fetchPage stay awaited.)
- BudgetSettings/TaggingSettings clear a retained load error on a
  successful save, so an obsolete load-failure alert no longer sticks
  around next to a success toast.
Copilot AI review requested due to automatic review settings July 24, 2026 16:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@SantiagoDePolonia

Copy link
Copy Markdown
Contributor Author

Addressed both review findings in b1431c8:

  • Refetch failures misreport mutations — post-success list refreshes are now fire-and-forget (void) rather than awaited inside the mutation's try block (26 sites), so a refresh failure can only surface as the list's own load state, never as mutation feedback. Toast timing is unaffected since feedback already fired before the refresh.
  • Successful saves retain load errorsBudgetSettings and TaggingSettings now clear a retained load error on a successful save, since the save proves the endpoint works and returned fresh data.

@SantiagoDePolonia
SantiagoDePolonia merged commit aaa7f26 into main Jul 24, 2026
18 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.

3 participants