Adopt TanStack Query: provider, timeouts, useBudget, Budget page

QueryClientProvider (30s staleTime, 1 retry) + Sonner Toaster at the
root. api.ts gains AbortSignal support and a default 30s timeout
(FE-24); query cancellation kills the stale-response races (FE-01,
FE-03). useBudget is now queries + mutations with targeted ['budget']
invalidation and success/error toasts on every mutation (ARCH-13,
UX-01); month navigation keeps the previous month visible while
fetching. Budget's transaction list is a cancellable query with
placeholder data; deferred-toggle gets feedback (UX-05); both panels
render a shared ErrorState with retry instead of failing silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-06-10 16:24:48 -06:00
parent 396c492f27
commit 65dffdac06
8 changed files with 234 additions and 117 deletions

View File

@@ -10,8 +10,13 @@ class ApiError extends Error {
interface RequestConfig {
params?: Record<string, string | number | boolean | undefined>;
/** Caller cancellation (TanStack Query passes this); combined with the
* default 30s timeout. */
signal?: AbortSignal;
}
const REQUEST_TIMEOUT_MS = 30_000;
async function request<T>(
method: string,
url: string,
@@ -38,11 +43,17 @@ async function request<T>(
fetchBody = JSON.stringify(body);
}
const timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
const signal = config?.signal
? AbortSignal.any([config.signal, timeout])
: timeout;
const res = await fetch(fullUrl, {
method,
headers,
body: fetchBody,
credentials: "same-origin",
signal,
});
if (res.status === 401) {