diff --git a/HISTORY.md b/HISTORY.md index 3c44230..ff70ca5 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,6 +9,10 @@ - **[Tracker] Killed the getTracker N+1 (was ~2–3 DB round-trips × N bills every home-page load)** — inside `bills.map`, `getTracker` ran a payments query per bill (`fetchPaymentsForBillCycle`) plus `computeAmountSuggestion` per bill, and the suggestion alone fired up to 12 queries per bill (6 months × 2) — roughly 70–450 queries for a 35-bill account on every Tracker load. Now one query fetches all bills' cycle payments (grouped in JS by each bill's own range) and two queries compute all amount suggestions (`computeAmountSuggestionsBatch`), replacing the per-bill loops. Behavior-preserving — `tests/amountSuggestionService.test.js` pins the batched suggestion to be byte-identical to the per-bill function, and the `trackerService` tests still pass unchanged. (Tracker P1) +### ⚛️ React Query migration (all manual-fetch pages) + +- **[Client] Moved the 7 remaining manual-fetch pages onto React Query** — Analytics, Bills, Subscriptions, Summary, Snowball, Spending, and Bank transactions each fetched with hand-rolled `useEffect` + `load()` + `useState(data/loading/error)`. Migrated them all to `useQuery` hooks (in `client/hooks/useQueries.js`) whose keys encode the params, so React Query now provides caching, request dedup, cancellation, and out-of-order-response safety for free — the manual request-sequence guards added earlier were removed. `keepPreviousData` keeps the last result visible while a new month/filter/page loads. Mutations that previously called `load()` now `invalidateQueries`, and optimistic edits (list reorder/delete, categorize, budget/plan changes) write through `queryClient.setQueryData` wrappers so every existing call site works unchanged. Editable form fields seeded from the data (Summary starting-amounts/income, Snowball settings, Spending budgets) are now seeded via a data-synced effect; pagination (Spending/Bank) is a page-keyed query. Bonus: because Bills reuses the shared `['bills']` cache, bill mutations there now also refresh the Tracker/overdue badge live. Each page was its own commit, verified with `npm run lint` (0 errors) + `npm run test:client` + `npm run build`. (R5) + ### ✅ React correctness audit + ESLint enforcement - **[Client] Added ESLint + react-hooks enforcement and fixed everything it found** — there was no linting at all, so nothing enforced the two rules that catch real React bugs across 125 components/pages: **rules-of-hooks** (conditional-hook crashes) and **exhaustive-deps** (stale closures). Added an ESLint 9 flat config (`eslint.config.mjs`) with `eslint-plugin-react-hooks`/`react-refresh`, an `npm run lint` script, and wired it into `npm run ci`. First run: **0 rules-of-hooks violations** (clean), and the rest fixed: