From 65c61d71fac56a9f2b8a2d2a9b6f013f8207cecb Mon Sep 17 00:00:00 2001 From: null Date: Fri, 3 Jul 2026 19:58:54 -0500 Subject: [PATCH] docs(history): React correctness audit + ESLint enforcement (R1-R4) Co-Authored-By: Claude Opus 4.8 --- HISTORY.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index c58ee00..3c44230 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,6 +9,14 @@ - **[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 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: + - **Real latent bugs (were runtime-throwing / dead):** `ImportTransactionCsvSection` called `importErrorState()` without importing it (its own error handler would `ReferenceError` on a failed import); a dead duplicate `client/components/MobileTrackerRow.jsx` (unused, with undefined-setter refs) was deleted; `StatusPage`'s `dbOk` had a dead `?? true` branch; plus orphaned dead logic (an unrendered autopay-badge `useMemo`, an unwired categories keyboard handler). + - **All 13 `exhaustive-deps` fixed**, incl. a real stale-value bug: `BankSyncSection`'s save read `btLateGraceDays` outside its `useCallback` deps, so it could persist a stale late-grace value. Others stabilized derived arrays/objects feeding `useMemo` (TrackerPage `filters`/`rows`, HealthPage `bills`, BankTransactionsPage `transactions`) and documented intentional id/filter-scoped effects. + - **Out-of-order response guards (R3):** the month/filter-driven loaders on Analytics, Summary, and Spending now use the same request-sequence guard as the Bank ledger, so a slow response for old params can't overwrite fresher data on rapid navigation. + - Verified-clean (no change needed): event-listener cleanup is balanced everywhere, the always-mounted dialogs' `open`-guarded reset effects are correct, and the CSV preview's index-keys are fine (read-only sample). *(The larger React Query migration of the 7 manual-fetch pages remains as a follow-up enhancement — the async-race correctness is already handled.)* + ### 🧹 Bill modal decompose - **[Bill modal] Broke the 1,733-line God-component into focused sub-components** — `BillModal.jsx` did bill fields + payment CRUD + linked-transaction unmatch + merchant rules + templates + debt/autopay all in one file. Extracted seven presentational components under `client/components/bill-modal/` (`DebtDetailsSection`, `AutopayTrustIndicator`, `PaymentHistoryList`, `PaymentFormFields`, `UnmatchDialogs`, `LinkedTransactionsSection`, `TemplateSection`) plus a shared `transactionDisplay.js` helper module, threading state/handlers via props. State stays in the parent (the save action is unchanged), so it's **behavior-preserving** — each extraction was its own commit, verified with `npm run build` + `npm run test:client`. BillModal is now **1,090 lines (from 1,733, −37%)** and the big interwoven blocks (payments, linked transactions, unmatch flows, debt) are each their own testable file. (Tracker BM2)