From 47a9ba98cb564e03f0e9a85222f67aba256c247e Mon Sep 17 00:00:00 2001 From: null Date: Sat, 4 Jul 2026 23:49:43 -0500 Subject: [PATCH] =?UTF-8?q?docs(ts):=20mark=20Phase=20B=20complete=20?= =?UTF-8?q?=E2=80=94=20every=20client=20.jsx=20converted=20to=20.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records the final 5-page batch (Snowball, BankTransactions, Spending, Bills, Subscriptions) and the dead-total-state find TypeScript surfaced. Co-Authored-By: Claude Opus 4.8 --- HISTORY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 505fcae..72c27bb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,7 +11,7 @@ - **[Client] Phase A — typed the API response shapes with branded money** — new `client/types.ts` domain module: every money field branded (`Dollars` for bill/payment/tracker/summary amounts; **`Cents` for raw bank-transaction amounts, which stay cents on the wire** — the complement that keeps `formatCentsUSD` and `formatUSD` from being swapped). Reverse-engineered from the server serializers: `TrackerResponse` (summary/bank_tracking/cashflow/rows), `Bill`, `Payment`, `Category`, `DriftBill`, `AutopaySuggestion`, `AmountSuggestion`, `AutopayStats`, `BankTransaction`, `TimelineBill`. `api.tracker/bills/payments/categories/…` now return these, so the brand flows from the fetch layer into `trackerUtils` (row money upgraded from `number` → `Dollars`; `paymentSummary` re-brands via `asDollars`). -- **[Client] Phase B — converting components/pages `.jsx` → `.tsx` (in progress, ~80/129 files)** — done: **all 22 `ui/*` primitives** + `ThemeContext`; the **entire `components/tracker/` dir** (18 files — the home page) where `fmt(row.expected_amount)` now type-checks against `Dollars`; the **entire `components/bill-modal/` dir + `BillModal.tsx`** (the 1092-line add/edit form reached from 9 entry points); the **app shell** (`App`/`main`/`useAuth`/`Layout`/`Sidebar`/routing; `index.html` entry → `main.tsx`); app-wide commons (`ErrorBoundary` class, `PageTransition`, `StatusBadge`, `MarkdownText`, `SummaryCard`, `ReleaseNotesDialog`); the `snowball/` + `transactions/` dirs; the **entire `components/admin/` dir** (11 cards incl. the OIDC/auth-methods form, bank-sync, backup manager); the **entire `components/data/` dir** (14 files — the Data page's SimpleFIN connect/sync workbench, transaction-matching, and the CSV / OFX / XLSX-spreadsheet / SQLite import flows, with `Cents`-branded bank-transaction amounts flowing through); and several pages. **A required infra fix:** Tailwind's `content` glob was `./client/**/*.{js,jsx}` — it never scanned `.tsx`, so any class used *only* in a converted file generated **no CSS** (the release-notes dialog's `left-[50%] top-[50%]` centering vanished → modal rendered off-screen). typecheck/build/48-tests all passed; only the **e2e probe** caught it. Fixed to `{js,jsx,ts,tsx}`. Remaining: ~18 pages + a few loose components — all mechanical. +- **[Client] Phase B — converted every component + page `.jsx` → `.tsx` (COMPLETE — `find client -name '*.jsx'` returns 0)** — done: **all 22 `ui/*` primitives** + `ThemeContext`; the **entire `components/tracker/` dir** (18 files — the home page) where `fmt(row.expected_amount)` now type-checks against `Dollars`; the **entire `components/bill-modal/` dir + `BillModal.tsx`** (the 1092-line add/edit form reached from 9 entry points); the **app shell** (`App`/`main`/`useAuth`/`Layout`/`Sidebar`/routing; `index.html` entry → `main.tsx`); app-wide commons (`ErrorBoundary` class, `PageTransition`, `StatusBadge`, `MarkdownText`, `SummaryCard`, `ReleaseNotesDialog`); the `snowball/` + `transactions/` dirs; the **entire `components/admin/` dir** (11 cards incl. the OIDC/auth-methods form, bank-sync, backup manager); the **entire `components/data/` dir** (14 files — the Data page's SimpleFIN connect/sync workbench, transaction-matching, and the CSV / OFX / XLSX-spreadsheet / SQLite import flows, with `Cents`-branded bank-transaction amounts flowing through); and several pages. **A required infra fix:** Tailwind's `content` glob was `./client/**/*.{js,jsx}` — it never scanned `.tsx`, so any class used *only* in a converted file generated **no CSS** (the release-notes dialog's `left-[50%] top-[50%]` centering vanished → modal rendered off-screen). typecheck/build/48-tests all passed; only the **e2e probe** caught it. Fixed to `{js,jsx,ts,tsx}`. **The final page batch** finished the job: the 5 largest/most-stateful pages — `SnowballPage`, `BankTransactionsPage` (the `Cents`-based bank ledger, with a local `Tx` structurally compatible with `BankTransaction` so it still satisfies `MatchBillDialog`), `SpendingPage` (a `RealCatEntry` narrowing splits the budget-indexed real categories from the `null`/`'other'` pseudo-rows), `BillsPage`, and `SubscriptionsPage` (1814 lines — `useOptimistic` reducer, the virtualizer's `FlatItem` discriminated union, and drag-reorder typed via the shared `DragProps`/`MoveControls`). Every page: local domain interfaces, React Query `setQueryData` for the optimistic caches, cast-at-boundary for `api.*` (`Promise`) results, and the local `fmt(v: number|null|undefined)` branded-`Dollars` wrapper so page money still type-checks. Verified: `npm run typecheck` (0) + `npm run lint` (0 errors) + `npm run build` + the 17/17 e2e probe (incl. axe on `/`, `/spending`, `/bills`, `/snowball`, `/summary`, `/analytics`, `/categories`, `/data`). ### 🏆 What the TypeScript migration caught (real defects, not just types) @@ -23,6 +23,7 @@ - **A CSS build gap invisible to typecheck/build/tests** — the Tailwind `.tsx` glob omission above; the **e2e probe** was the only gate that caught it, reinforcing that a passing build ≠ runtime-safe for a `.tsx` rename touching many files. - **Dead code with broken calls** — `PrivacyAdminCard` was imported nowhere yet called non-existent `api.privacySettings`/`setPrivacySettings` (the geolocation toggle actually lives on `/profile/settings`). Deleted rather than converted. - **A missing import that crashed a live view** — `ImportTransactionCsvSection` rendered `` **6×** (in the CSV preview + results panels) but never imported it. In the `.jsx` this is a `ReferenceError: CountPill is not defined` the moment you preview a CSV — a runtime crash `react/jsx-no-undef` wasn't configured to catch, but `tsc` flagged immediately as `TS2304: Cannot find name 'CountPill'`. Added the import. (Same file also carried two genuinely-unused imports — `CountPill`/`fmt` in the XLSX section — that TS surfaced and we dropped.) +- **A dead `total` state on the Spending page** — `IncomeSection` kept a `const [total, setTotal] = useState(0)` and dutifully `setTotal(d.total || 0)` on every income-page load, but nothing ever read `total` — a pointless render-triggering write. The `noUnusedLocals`-adjacent lint surfaced it during the `.tsx` conversion; removed the state and its setter. (The rest of the final 5-page batch type-checked clean on the first or second pass — a good signal the pages were already sound.) ### 🧪 Money-service test coverage