Commit Graph

86 Commits

Author SHA1 Message Date
null a31a774f95 chore: dead-code sweep — knip now clean and blocking in CI (Phase 5)
- delete root test-functional.js (imported uninstalled 'playwright' —
  could never run) and run-functional-test.js (legacy pre-Playwright)
- delete postcss.config.js + autoprefixer/postcss devDeps (tailwind v4's
  vite plugin owns the pipeline; the config had no plugins left)
- client/lib/money.ts: drop unused SUPPORTED_CURRENCIES; keep
  dollarsToCents exported + @public-tagged (API symmetry with
  centsToDollars — the sanctioned unit crossing)
- client/lib/billDrafts.ts: drop unused BillDraft type export
- move the two runtime seed JSONs (advisory filters, merchant-store
  matches; 7 MB) out of docs/ into db/data/ next to their loader;
  fresh-DB seed verified (5000+5000 rows); Dockerfile COPY . covers it
- knip.json: apply config hints, ignore the two CSS-side tailwind deps;
  npx knip exits 0 — added to CI as a BLOCKING step so unused
  files/exports/deps fail instead of rotting (this is how the WebAuthn
  ghost feature survived four QA cycles)

NOT deleted: client/public/img/doingmypart.jpg — flagged unused by the
code grep but actually referenced from HISTORY.md markdown that the
release-notes renderer serves (user catch). Lesson encoded in the QA
plan: asset-reference sweeps must include rendered markdown content.

Full ci green (252 server / 52 client / build / knip 0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:15:04 -05:00
null 522eaaf101 chore(deps)!: tailwind 3 -> 4 via @tailwindcss/vite (4i)
Official codemod (run in a clean worktree; docker-owned data/ blocks it
in-tree): tailwind.config.js inlined into client/index.css as CSS-first
@theme/@utility (config file deleted — v4 auto-detects content, which
also retires the .tsx-content-glob failure class from the TS migration),
33 template files' class renames applied, tailwindcss-animate loads via
the v4 @plugin bridge. Build runs through @tailwindcss/vite; postcss
config is now plugin-free. tailwind-merge -> 3 (v4 class model).

Visual-regression gate: only diffs were a ~2px global preflight shift +
the version string (baseline predated 0.41.1); re-baselined both login
screenshots in this commit after review. webauthn.probe confined to the
probe project (it enables WebAuthn for the seeded user, racing the
parallel UI projects' password logins).

e2e 27 passed, probe 18/18, PROD SMOKE PASS, typecheck/lint/tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:03:15 -05:00
null f587d09d4a style: apply Prettier across client + server (no behavior change)
One-time repo-wide `prettier --write` (isolated commit so future diffs
stay reviewable). Mechanical formatting only.

Verified unchanged: typecheck + typecheck:server + check:server clean;
server tests 232/232; client tests 50/50; Vite build succeeds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 14:23:53 -05:00
null c41f1a8a7b feat(tracker): status row border-left indicators, refined metric cards, search filter, and CSS polish 2026-07-06 13:47:34 -05:00
null df1f61d6cc feat(brand): render the real product glyph SVGs (not inline approximations)
BrandGlyph drew hardcoded monochrome shapes from an inline switch and
ignored the actual /brand/glyphs/*.svg files. Render the real files via a
robust <img> (fail-soft onError, intrinsic width/height so no layout
shift, alt for a11y parity) so every existing placement — PageHeader
(Tracker/About/Release), Command Palette, Release Notes, Onboarding,
Login — shows the full-color brand tiles. Deletes the ~85-line dead
BrandGlyphShape switch and the now-unused useId import.

Senior-review while-here:
- Guard test (client/lib/brand.test.ts): asserts every BrandGlyphName
  asset — and every /brand asset in brand.ts — resolves to a real,
  non-empty file on disk. Catches the "image added but path wrong /
  renamed" regression TS can't see.
- Widen vitest include to {js,jsx,ts,tsx}: TypeScript client tests were
  silently never run (client/hooks/useAutoSave.test.ts). Now 6 files / 50
  tests run (was 4 / 42).
- Remove dead brand.ts entries: legacyLogo (pointed at the OLD pre-brand
  /img/logo.png) and the unused ADMIN_GLYPH const.
- PageHeader glyph class inline-grid -> inline-block for the <img>.

Verified: typecheck + lint clean; test:client 50/50; build (glyphs land
in dist/brand/glyphs, served 200 as image/svg+xml); e2e probe 17/17;
login visual baseline unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:32:37 -05:00
null b25be070ed Add complete brand asset pack 2026-07-06 12:54:29 -05:00
null 3a14c50e08 chore(brand): update PWA icons, release notes, and About page 2026-07-06 12:13:01 -05:00
null 1c04dfdf8c feat(brand): BrandMark/Wordmark/Lockup consume the new SVGs
Brand.tsx (101 line churn) and brand.ts (47 line churn) now expose
the new SVG-first primitives:

- BrandMark: <img> with BRAND.assets.mark (vector, scales to any size).
- BrandWordmark: text fallback if the mark is unavailable.
- BrandLockup: the combined mark + wordmark for the layout chrome,
  honours the light/dark variant via CSS variables.
- BrandGlyph: unchanged but re-uses the new accent tokens.

brand.ts adds the new asset-path constants (mark, markMono, lockup,
lockupLight, socialPreview) and a tokens helper so the lockup can
switch variants without prop drilling.

This is commit 2 of the v0.42.0 follow-up batch. Depends on
720d31b (SVG assets).
2026-07-05 17:50:04 -05:00
null 4b81600afe fix(brand): finish the "BillTracker" → "Bill Tracker" rename (display copy)
The v0.42.0 brand refresh renamed client copy but missed a few user-facing
server strings:
- routes/about.js: the /api/about `name` (rendered as the About page title) was
  still 'BillTracker' → 'Bill Tracker'.
- userDbImportService.js: two import-error messages referenced 'BillTracker'.
- Remove the now-unused APP_NAME export from lib/version.ts (0 consumers;
  lib/brand.ts BRAND.name is the canonical source).

Left intentionally as identifiers (not display copy): the updateCheckService
User-Agent 'BillTracker/<version>' and all repository URLs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 17:31:14 -05:00
null 8ceea06659 chore(lib): trackerUtils + version track the brand refresh
Small follow-ups the rest of the refresh depends on:

- client/lib/trackerUtils.ts: status-tone helper now returns the
  'good' / 'warn' / 'danger' tokens the new TonedCard / ToneDot
  consume, so the per-row badges stay in sync with the new design
  tokens.
- client/lib/version.ts: release-notes shape accepts the optional
  brand_image field that HISTORY.md now uses for the 'Doing my
  part' release image.

This is commit 8 of 9 in the v0.42.0 brand refresh. Depends on
1807456 (admin + data + dialogs).
2026-07-05 17:19:10 -05:00
null dfee7f9401 feat(brand): new brand module + logo assets
Adds the canonical brand definition used by the v0.42.0 brand refresh:

- client/lib/brand.ts — single source of truth for name, tagline,
  repository URL, asset paths, navigation glyphs, and accent palette
  (BRAND_GLYPHS / accent tokens consumed by Brand.tsx + page chrome).
- client/components/brand/Brand.tsx — BrandMark (img with the
  logo asset), BrandWordmark, BrandGlyph, and a small BrandStack
  used by the layout chrome.
- client/public/brand/{logo,pwa-192,pwa-512,apple-touch-icon}.png —
  replaces the inline img-cut references in README/HISTORY with a
  canonical set; PWA manifest + favicon now point here.

This is commit 1 of 9 splitting the working-tree brand refresh into
per-layer commits. Each commit is independently buildable; the order
matches the dependency direction (lib → component → chrome → UI
primitives → page consumers → admin/dialog consumers → docs).
2026-07-05 17:18:04 -05:00
null 0fa80a77ef feat(settings): make Currency + Date-format real (plan Tier 2)
The Currency/Date-format dropdowns were decorative (nothing read them). Wire
them into the formatters, display-only (single active currency, no conversion):
- money.ts: activeCurrency/activeLocale read synchronously from localStorage at
  load (correct first paint), setMoneyFormat() write-through; formatUSD/
  formatUSDWhole/formatCentsUSD honor them. Default USD/en-US is byte-identical,
  so money.test + the probe stay green.
- utils.ts: fmtDate honors an activeDateFormat (MM/DD/YYYY | DD/MM/YYYY |
  YYYY-MM-DD) with setDateFormat() write-through.
- FormatSync (mounted in Layout) reconciles the server settings → localStorage/
  module and re-renders once only on a genuine cross-device divergence.
- Settings selects write through on change, currency list expanded (AUD/CHF/JPY/
  INR), "Regional" section, tooltips clarifying display-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 15:54:19 -05:00
null 88874f3f3d feat(tracker): export/print the month + calendar link (plan C1/C2)
- Header "Export" menu: Export CSV (via the existing /api/export?from&to date
  range for the viewed month) and Print (window.print()). Extract the shared
  downloadFile blob helper to lib/download.ts (reused by DownloadMyDataSection).
- Add api.exportRangeUrl(from, to, fmt).
- Tracker-scoped @media print CSS: hide interactive chrome (.tracker-print-hide),
  keep the month title + summary + buckets, avoid splitting a row across pages.
- Header "Calendar" button deep-links to /calendar?year&month; CalendarPage now
  reads those params as its initial month (falls back to today).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:49:09 -05:00
null 03009e2a83 refactor(client): de-duplicate parseUtc + settingEnabled to lib/utils (plan A2/A3)
- Extract parseUtc (was copy-pasted in 4 files) and settingEnabled (3 copies,
  one named settingsBool) into client/lib/utils.ts as the single source.
- Replace all local copies with the shared import.
- Add tracker_show_safe_to_spend to the tracker's local settings defaults so it
  matches SettingsPage's list (behavior unchanged; aligns the two sources).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:38:12 -05:00
null dd5bf92fc1 fix(money): manual payment path flags suspected dupes (409) w/o losing legit ones (Track A)
The deliberate manual POST /payments had no dedupe guard (double-submit
made a second payment), but silently deduping it like the auto paths
would lose a legitimately-identical same-day payment — a money bug. So
the server returns 409 DUPLICATE_SUSPECTED (existing payment attached),
and a shared client helper (client/lib/paymentActions.ts, used by the
tracker inline cell, partial-payment dialog, and bill modal) turns it
into an 'add anyway?' toast that resends with allow_duplicate. Automated
one-click paths keep deduping silently (a repeat there is a retry).

Dropped a now-unused api import in PaymentLedgerDialog (Track E sweep).
Server suite 188 green; typecheck/lint/build clean; probe 17/17.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 11:51:06 -05:00
null 2f4f76db83 refactor(ts): HealthPage → TypeScript + export makeBillDraft's SourceBill return
Annotated makeBillDraft's return as SourceBill (index-signatured) and exported it,
so its draft output stays typed; BillModal keeps initialBill: Partial<Bill> and
callers cast makeBillDraft(...) as Partial<Bill> at the boundary. typecheck 0.
2026-07-04 22:19:39 -05:00
null f4f1dadaab refactor(ts): convert MobileTrackerRow to TSX (B5)
The mobile tracker row (consumes branded TrackerRow + shared payment hooks).
Supporting: TrackerRow.sparkline typed number[]|null; ROW_STATUS_CLS typed
Record<string,string> (it has no `skipped` key, so indexing by TrackerStatus
needed a string index). DragProps/MoveControls interfaces exported. Notable: the
HTML5 drag handlers conflict with framer-motion's own onDrag* gesture types on
motion.div — cast via ComponentProps<typeof motion.div>; quick-pay/remaining
amounts need asDollars at the boundary. typecheck 0, build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 19:36:01 -05:00
null 1f18a720c4 refactor(ts): convert all UI primitives + ThemeContext to TSX (B2)
Foundational for phase B: the ui/* primitives were untyped .jsx, and TS infers
React-19 ref-as-prop components as *requiring* a ref, so every consumer errored.
Converted all 22 ui primitives (button, input, card, dialog, select, table,
dropdown-menu, tabs, badge, checkbox, switch, tooltip, separator, label,
Skeleton, collapsible, alert-dialog, sonner, save-status, theme-toggle,
input-dialog, confirm-dialog) using ComponentProps<'el'> / ComponentProps<typeof
Radix.X> + VariantProps for cva components — refs now optional, event params
typed. Also converted ThemeContext.tsx (createContext(null) made useContext's
post-guard return `never`, so setTheme was uncallable — fixed with a typed
context value) and three tracker cells (EditableCell/NotesCell/
LowerThisMonthButton) that consume the branded TrackerRow.

Added a shared errMessage(unknown) helper to utils (strict catch → unknown);
usePaymentActions reuses it. TrackerRow gains monthly_notes + amount_suggestion
(real server fields).

typecheck 0, lint 0 errors (42 warns), build green, 48 client tests. NOTE: the
e2e auth-setup probe is currently red on an unrelated, pre-existing
release-notes-modal dismissal race (reproduces on the last known-good commit;
the a11y snapshot shows these converted primitives rendering correctly) —
investigating separately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:57:22 -05:00
null 3c7a55c3a1 feat(ts): type the Tracker API response with branded Dollars (A1)
New client/types.ts domain-types module. Types the full /tracker response —
TrackerResponse { summary, bank_tracking, cashflow, rows } — with every money
field branded `Dollars` (the server serializes cents→dollars, verified against
statusService.buildTrackerRow + trackerService.getTracker + buildBankTracking +
buildSafeToSpend). api.tracker() now returns Promise<TrackerResponse>.

TrackerRow + TrackerStatus move to @/types (canonical) and are re-exported from
@/lib/trackerUtils for compat; trackerUtils' row money fields upgrade from plain
`number` to branded `Dollars` (rowThreshold -> Dollars, paymentSummary re-brands
its computed amounts via asDollars so callers can fmt() them directly). This is
the first endpoint where the money brand flows end-to-end: fetch layer -> row
helpers, and (in phase B) into the components that format them.

Nested payloads no typed consumer reads yet (summary.trend, autopay_suggestion/
_stats, sparkline) are left `unknown`, to refine when consumed. asDollars is an
identity at runtime, so behavior is unchanged: typecheck 0, lint 0 errors, build
green, 48 client tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:21:45 -05:00
null 8c30a7ab09 refactor(ts): convert small hooks + version to TypeScript (TS8)
- useAutoSave.ts — generic useAutoSave<T>; AutoSaveStatus union; timer ref
  typed ReturnType<typeof setTimeout>; pending ref T|null narrows on != null.
- useSearchPanelPreference.ts — typed [boolean, setter] tuple return.
- version.ts — ReleaseNotes/ReleaseHighlight shapes; the Vite-injected
  __APP_VERSION__ global declared inline (declare const).

No behavior change. useAutoSave.test.jsx imports the .ts transparently and
passes. typecheck 0, lint 0 errors (47 warns), build green, 48 client tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:01:30 -05:00
null 3c51464bec refactor(ts): convert pure lib utilities to TypeScript (TS7)
Five self-contained leaf modules, no behavior change:
- reorder.ts — generic moveInArray<T>, reorderPayload, movedItemId
- billingSchedule.ts — a Schedule union + typed normalize/label helpers
- billDrafts.ts — SourceBill/Template/Category shapes for makeBillDraft
- trackerTableColumns.ts — typed column parse/normalize
- cashflowUtils.ts — typed SVG step-path timeline geometry (Safe-to-Spend)

noUncheckedIndexedAccess handled via in-range `!` (guarded lengths) and
destructuring defaults. Their .test.js suites import the .ts transparently via
Vite/vitest and still pass. typecheck 0, lint 0 errors (47 warns), build green,
48 client tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:59:16 -05:00
null 7c6be66794 refactor(ts): convert client/lib/trackerUtils to TypeScript (TS6)
The tracker row/status/sort helpers are now typed. Introduces a TrackerRow
domain interface (the fields these helpers read) and a TrackerStatus union
(paid|autodraft|upcoming|due_soon|late|missed|skipped). Row money fields stay
plain `number` for now — branding them Dollars is a later increment, done when
the tracker API response itself is typed so the brand flows in from the source.

Strict-mode handling: STATUS_SORT_ORDER is Record<TrackerStatus,number> (no
undefined on lookup); moveInArray guards the noUncheckedIndexedAccess `T |
undefined` from splice; the sort comparator relies on aliased-condition
narrowing to reach string|number in each branch. typecheck 0 errors, lint 0
errors (47 warnings unchanged), build green, 48 client tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:53:45 -05:00
null b221e02d85 refactor(ts): convert client/lib/utils to TypeScript (TS4)
The highest-traffic leaf module (cn, fmt, date/byte/uptime formatters,
categoryColor) is now strict .ts. fmt inherits formatUSD's branded-dollars input
via Parameters<typeof formatUSD>; noUncheckedIndexedAccess handled (destructuring
defaults, in-range assertion). .jsx callers unaffected (Vite resolves the .ts).
typecheck + build + 48 client tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:42:46 -05:00
null 255036afc2 feat(ts): branded Cents/Dollars money types — first TS conversion (T2)
Converted client/lib/money.js -> money.ts with branded types:
  type Cents = number & { __unit: 'cents' }
  type Dollars = number & { __unit: 'dollars' }
plus asCents/asDollars/centsToDollars/dollarsToCents. The formatters now require
the correct branded unit (a bare number won't do), so a typed caller physically
cannot format cents as dollars (the 100×-too-big bug) or vice-versa. Existing
.jsx callers are unaffected (checkJs off) — gradual adoption.

money.type-test.ts is a compile-time guard (never imported/bundled): its
@ts-expect-error lines assert each unit mixup is a real error, so typecheck
fails loudly if the branding ever regresses. Verified: removing a guard makes
tsc error 'Argument of type 1234 is not assignable to DollarsInput'.

typecheck + build (with React Compiler) + 48 client tests all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 21:36:24 -05:00
null 995f635d35 refactor(tracker): consolidate isPaidStatus + rowOutstanding + toast gap (T5)
Added a single isPaidStatus(status) (+ PAID_STATUSES) to statusService and a
matching client helper in trackerUtils, routing the unambiguous settled-status
checks through it (trackerService, StatusBadge, CalendarPage, rowIsPaid). The
intentionally paid-only counts stay distinct. Replaced two inline
Math.max(r.balance||0,0) with rowOutstanding, and gave the Tracker settings
load a quiet toast instead of a silent swallow. Behavior-preserving.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 18:36:30 -05:00
null d92cc38116 fix(bill-modal): correctness + toast fallbacks + validator consolidation (BM1)
- handleBlur now takes the field value explicitly (was positional guessing that
  fell through to interestRate for unmapped fields).
- Three copy-pasted money validators -> one shared validateNonNegativeMoney in
  client/lib/money.js; expected-amount copy 'positive' -> 'non-negative' (0 ok).
- Removed the save action's duplicate due-day/interest-rate re-validation
  (validateForm already covers it); kept the parses.
- Added err.message fallbacks to save/deactivate/verify-autopay toasts.
- Save toasts now name the bill.

Test: client/lib/money.test.js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 18:32:25 -05:00
null a15f00c568 refactor(client): single source of truth for money formatting (IMP-CODE-01)
The client had ~15 hand-rolled currency formatters (local `fmt`/`money`/
`fmtFull`/`fmtDollars`/…) plus a canonical `fmt` in lib/utils used at ~190
sites — same rules copy-pasted, with inconsistent handling of negatives,
whole-dollar, cents vs dollars, and blank input.

Add client/lib/money.js as the one implementation:
  - formatUSD(dollars)      — "$1,234.56" (whole/dash options)
  - formatUSDWhole(dollars) — "$1,235"
  - formatCentsUSD(cents)   — from integer cents; signed "+/-" and dash options
Inputs are coerced so null/''/NaN never render as "$NaN", and -0 is
normalized so it never shows as "-$0.00".

lib/utils.fmt now delegates to formatUSD (byte-identical — the existing
utils.test.js fmt suite is the regression guard), and the 15 local
formatters delegate to money.js. No display currency formatting remains
outside money.js; the /100 conversions left behind are calculations
(form prefill), not display.

Tests: client/lib/money.test.js (13). Full client suite 46 pass; build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 12:46:22 -05:00
null 029c227685 fix(qa): seed demo data amounts, bill amount validation, negative USD format, a11y aria-labels, Playwright E2E setup (batch 0.41.0 QA) 2026-07-02 20:36:09 -05:00
null 81ddcb5fc1 feat(banking): bank transactions page with merchant/store matching, transaction matching refactor, bank sync improvements (batch 0.40.0) 2026-06-14 15:15:31 -05:00
null dc49eb9633 feat(cashflow): safe-to-spend projection with timeline, vitest setup, package upgrades 2026-06-12 01:32:28 -05:00
null c6708982a9 fix(utils): extract localDateString to shared lib, replace .toISOString().slice() pattern across client 2026-06-11 23:40:22 -05:00
null f7ad1c1ebb fix(tracker): table columns and settings improvements 2026-06-07 17:23:14 -05:00
null 1ebb2da50a feat: tracker bucket rollover logic, utils cleanup, HISTORY update 2026-06-07 00:41:07 -05:00
null 557378dab9 chore: bump to v0.35.0 2026-05-31 16:09:40 -05:00
null ab93c53c82 chore: bump to v0.36.0 2026-05-31 15:57:03 -05:00
null 31bafb0e55 0.34.3 2026-05-31 15:06:10 -05:00
null 90cfed035b feat: Payoff Custom mode, Summary reordering, unifed billing schedule, SimpleFIN + backup fixes (batch v0.34.1.3) 2026-05-30 21:20:51 -05:00
null c23cae1107 feat: reordering across management pages (Bills, Subscriptions, Categories, Snowball) — batch v0.34.1.2 2026-05-30 20:04:50 -05:00
null 3978507572 feat(tracker): overdue command center with snooze/skip/pay + sidebar badge
- migration v0.70 adds snoozed_until TEXT to monthly_bill_state
- trackerService: snoozed_until in monthly state fetch + getOverdueCount()
- GET /api/tracker/overdue-count endpoint
- PUT /bills/:id/monthly-state validates snoozed_until
- OverdueCommandCenter component: collapsible, per-bill actions, hides snoozed
- useOverdueCount hook (2-min stale, 5-min poll, tab-only)
- Sidebar/nav uses overdue count badge on Tracker menu item
- Bump v0.33.8.7 → v0.34.0
2026-05-30 13:19:09 -05:00
null b34e21d1ba feat: advisory non-bill transaction filter system (batch 0.33.8.0)
- Migration v0.68: seeds advisory_non_bill_filters (5k+ patterns) and
  advisory_bill_like_overrides (83 override terms) on first startup.
  Idempotent — skips if already seeded.
- advisoryFilterService.js: lazy in-memory cache checks override terms
  first, then scans patterns. Returns null | {confidence, category, rationale}.
- Transaction list: each row gets advisory_filter from the server.
- High-confidence unmatched transactions: show 'Probably not a bill'
  italic text instead of 'No bill linked'.
- MatchBillDialog high confidence: 'Create Bill' replaced with
  'Probably not a bill · create anyway' text link for manual override.
- MatchBillDialog medium confidence: Create Bill button renders muted.
- Same logic in empty-state CTA when search returns no results.
- BillModal onSave now returns the saved bill so callers can auto-match.
- Bump v0.33.7.3 -> v0.33.8.0
2026-05-29 18:06:12 -05:00
null 392de3264f fix(ui): SimpleFIN transaction table fixed column sizing (batch 0.33.7.3)
- Table now uses table-fixed + colgroup for fixed column widths
- Long transaction text can no longer push action buttons off-screen
- Action buttons are compact icon-only with aria-label/title
- Long matched bill names are truncated with truncate class
- Bump v0.33.7.2 -> v0.33.7.3
2026-05-29 16:51:31 -05:00
null 32f1568515 feat: SimpleFIN payment backfill button on subscription bills (v0.33.7.2) 2026-05-29 04:19:20 -05:00
null b124e48ebc v0.28.0 2026-05-16 15:38:28 -05:00
null 59d9d21d4c v0.28.0 2026-05-16 10:34:32 -05:00
null 9174ec3290 v0.28. 2026-05-16 10:17:24 -05:00
null 0ba315bd32 v0.28.0 2026-05-15 22:45:38 -05:00
null 263f1c5e6e v0.27.04 2026-05-15 01:36:56 -05:00
null d720931894 v0.27.02 push 2026-05-14 21:00:07 -05:00
null ce22139bb3 chore: bump version to 0.27.01 2026-05-14 03:01:47 -05:00
null 7d2d0bf45e 0.28.0 snowball release 2026-05-14 02:11:54 -05:00