Wire toasts into existing user actions #35

Closed
opened 2026-07-16 14:25:34 +02:00 by Sirttas · 0 comments
Owner

Summary

Now that the global toast component exists (#33), wire it into the app's existing user actions
so writes confirm success and failures are surfaced. Today none of these give any user-facing
feedback — errors only reach log.error in service.ts, and several mutation call sites don't
even .catch, so a failed request silently leaves the UI as if it succeeded.

The approach is a global error net plus per-action success toasts: one generic error toast in
the mammon axios response interceptor catches every unhandled API failure once, and individual
actions add only their success (or immediate "started") toasts. This avoids threading .catch
through every call site.

Follow the wording/promise pattern already established for rule-book save in
pages/rules/EditRuleBook.vue (.then(toast.success)), minus the per-action .catch (the net
handles errors).

Scope

1. Global error net (interceptor)

  • Add a single generic toast.error(...) at the final rejection point of the mammon axios
    response interceptor
    in mammon/mammonService.ts — i.e. after the 401→silent-refresh→retry
    logic, on the branch that ultimately Promise.reject(error)s.
  • Fixed generic message (e.g. "Something went wrong"). Do not read/surface the server's
    error body.
  • Suppress 401 and 403: a 401 ends in the auth-expiry redirect to login (a toast there is
    noise mid-redirect); a 403 is likewise swallowed silently. Every other final rejection
    (other 4xx, 5xx, network/no-response) toasts once.
  • Applies to mammonAxiosInstance only. The separate credentialedClient (silent refresh,
    logout) is left untouched — refresh failure is quiet by design, logout failure is low-value.
  • Because the net covers all read/fetch failures, the previously-listed "error toasts only"
    actions (market scan, type scan, appraise/compare, load acquisitions) need no per-action
    code
    — they are covered automatically. Do not add .catch toasts to them.

2. Per-action success toasts (mutations)

Add a success toast on completion. No per-action error .catch (net handles it).

  • Buy acquisition — market/acquisition/BuyModal.vue (addAcquiredType)
  • Sell acquisition — market/acquisition/SellModal.vue (remove / removeAcquiredType): the
    method loops over acquisition lots (one call per lot). Await the whole loop, then show one
    aggregate success toast
    (e.g. "Acquisition sold"). On a mid-loop failure the loop aborts
    (the awaited call throws) and the global net surfaces the error. The loop itself is a smell —
    tracked separately in mammon#68 (batch the sell server-side); do not refactor it here.
  • Create ledger — ledger/EditLedgerModal.vue (createMain / createCombined)
  • Update ledger — ledger/EditLedgerModal.vue (updateMain / updateCombined)
  • Process activities — pages/ledger/ListLedgers.vue (processActivities): an immediate
    toast.info(...) (e.g. "Processing started") on click. The completion toast is owned by
    #13 (see Related).
  • Add character — pages/Characters.vue / auth.ts (addCharacter): no success toast
    (success is a redirect); error is covered by the global net.

3. Clipboard

  • Add a toast.info('Copied') on clipboard copy — components/ClipboardButton.vue,
    market/type/MarketTypeInput.vue, utils.ts (copyToClipboard). (Included despite the
    frequency concern.)

Explicitly out of scope

  • The SSE processing-completion toast — owned by #13.
  • Action buttons / undo inside toasts — out of scope for the toast component (#33).
  • Refactoring the SellModal per-lot loop — spun off to mammon#68.
  • Surfacing server-provided error messages — the net uses a fixed generic string.

Acceptance criteria

  • The mammon response interceptor shows a single generic error toast on any final rejection
    except 401/403, on mammonAxiosInstance only.
  • Each mutation in §2 shows a success toast on completion (add-character excepted — redirect).
  • Sell shows exactly one aggregate success toast after its loop completes.
  • "Process Activities" shows an immediate info toast on click.
  • Clipboard copy shows a 'Copied' info toast.
  • No mutation call site swallows a rejected promise silently, and read/fetch actions are not
    given redundant per-action error toasts (the net covers them).

Related

  • Depends on #33 (global toast component — done).
  • Related to #13: #13 owns the toast fired when an async processing run completes (via the SSE
    stream); this issue only covers the immediate "Processing started" toast on the button click.
  • Spun off mammon#68 (batch the acquisition sell server-side).
## Summary Now that the global toast component exists (#33), wire it into the app's existing user actions so writes confirm success and failures are surfaced. Today none of these give any user-facing feedback — errors only reach `log.error` in `service.ts`, and several mutation call sites don't even `.catch`, so a failed request silently leaves the UI as if it succeeded. The approach is **a global error net plus per-action success toasts**: one generic error toast in the mammon axios response interceptor catches every unhandled API failure once, and individual actions add only their success (or immediate "started") toasts. This avoids threading `.catch` through every call site. Follow the wording/promise pattern already established for rule-book save in `pages/rules/EditRuleBook.vue` (`.then(toast.success)`), minus the per-action `.catch` (the net handles errors). ## Scope ### 1. Global error net (interceptor) - Add a single generic `toast.error(...)` at the final rejection point of the **mammon axios response interceptor** in `mammon/mammonService.ts` — i.e. after the 401→silent-refresh→retry logic, on the branch that ultimately `Promise.reject(error)`s. - **Fixed generic message** (e.g. `"Something went wrong"`). Do **not** read/surface the server's error body. - **Suppress `401` and `403`**: a 401 ends in the auth-expiry redirect to login (a toast there is noise mid-redirect); a 403 is likewise swallowed silently. Every other final rejection (other 4xx, 5xx, network/no-response) toasts once. - Applies to **`mammonAxiosInstance` only**. The separate `credentialedClient` (silent refresh, logout) is left untouched — refresh failure is quiet by design, logout failure is low-value. - Because the net covers all read/fetch failures, the previously-listed "error toasts only" actions (market scan, type scan, appraise/compare, load acquisitions) need **no per-action code** — they are covered automatically. Do not add `.catch` toasts to them. ### 2. Per-action success toasts (mutations) Add a success toast on completion. No per-action error `.catch` (net handles it). - Buy acquisition — `market/acquisition/BuyModal.vue` (`addAcquiredType`) - Sell acquisition — `market/acquisition/SellModal.vue` (`remove` / `removeAcquiredType`): the method loops over acquisition lots (one call per lot). Await the whole loop, then show **one aggregate success toast** (e.g. `"Acquisition sold"`). On a mid-loop failure the loop aborts (the awaited call throws) and the global net surfaces the error. The loop itself is a smell — tracked separately in **mammon#68** (batch the sell server-side); do not refactor it here. - Create ledger — `ledger/EditLedgerModal.vue` (`createMain` / `createCombined`) - Update ledger — `ledger/EditLedgerModal.vue` (`updateMain` / `updateCombined`) - Process activities — `pages/ledger/ListLedgers.vue` (`processActivities`): an immediate `toast.info(...)` (e.g. `"Processing started"`) on click. The *completion* toast is owned by #13 (see Related). - Add character — `pages/Characters.vue` / `auth.ts` (`addCharacter`): **no success toast** (success is a redirect); error is covered by the global net. ### 3. Clipboard - Add a `toast.info('Copied')` on clipboard copy — `components/ClipboardButton.vue`, `market/type/MarketTypeInput.vue`, `utils.ts` (`copyToClipboard`). (Included despite the frequency concern.) ## Explicitly out of scope - The SSE processing-completion toast — owned by #13. - Action buttons / undo inside toasts — out of scope for the toast component (#33). - Refactoring the SellModal per-lot loop — spun off to mammon#68. - Surfacing server-provided error messages — the net uses a fixed generic string. ## Acceptance criteria - [x] The mammon response interceptor shows a single generic error toast on any final rejection except `401`/`403`, on `mammonAxiosInstance` only. - [x] Each mutation in §2 shows a success toast on completion (add-character excepted — redirect). - [x] Sell shows exactly one aggregate success toast after its loop completes. - [x] "Process Activities" shows an immediate info toast on click. - [x] Clipboard copy shows a `'Copied'` info toast. - [x] No mutation call site swallows a rejected promise silently, and read/fetch actions are not given redundant per-action error toasts (the net covers them). ## Related - Depends on #33 (global toast component — done). - Related to #13: #13 owns the toast fired when an async processing run *completes* (via the SSE stream); this issue only covers the immediate "Processing started" toast on the button click. - Spun off mammon#68 (batch the acquisition sell server-side).
Sirttas added the
Priority
High
2
Status
To Refine
Kind/Enhancement
labels 2026-07-16 14:25:34 +02:00
Sirttas removed the
Status
To Refine
label 2026-07-16 14:32:13 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: eveal/gemory#35