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).
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.
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 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).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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.errorinservice.ts, and several mutation call sites don'teven
.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
.catchthrough 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 nethandles errors).
Scope
1. Global error net (interceptor)
toast.error(...)at the final rejection point of the mammon axiosresponse interceptor in
mammon/mammonService.ts— i.e. after the 401→silent-refresh→retrylogic, on the branch that ultimately
Promise.reject(error)s."Something went wrong"). Do not read/surface the server'serror body.
401and403: a 401 ends in the auth-expiry redirect to login (a toast there isnoise mid-redirect); a 403 is likewise swallowed silently. Every other final rejection
(other 4xx, 5xx, network/no-response) toasts once.
mammonAxiosInstanceonly. The separatecredentialedClient(silent refresh,logout) is left untouched — refresh failure is quiet by design, logout failure is low-value.
actions (market scan, type scan, appraise/compare, load acquisitions) need no per-action
code — they are covered automatically. Do not add
.catchtoasts to them.2. Per-action success toasts (mutations)
Add a success toast on completion. No per-action error
.catch(net handles it).market/acquisition/BuyModal.vue(addAcquiredType)market/acquisition/SellModal.vue(remove/removeAcquiredType): themethod 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.
ledger/EditLedgerModal.vue(createMain/createCombined)ledger/EditLedgerModal.vue(updateMain/updateCombined)pages/ledger/ListLedgers.vue(processActivities): an immediatetoast.info(...)(e.g."Processing started") on click. The completion toast is owned by#13 (see Related).
pages/Characters.vue/auth.ts(addCharacter): no success toast(success is a redirect); error is covered by the global net.
3. Clipboard
toast.info('Copied')on clipboard copy —components/ClipboardButton.vue,market/type/MarketTypeInput.vue,utils.ts(copyToClipboard). (Included despite thefrequency concern.)
Explicitly out of scope
Acceptance criteria
except
401/403, onmammonAxiosInstanceonly.'Copied'info toast.given redundant per-action error toasts (the net covers them).
Related
stream); this issue only covers the immediate "Processing started" toast on the button click.