useSubscriptions + useSubscriptionRecommendations (+ shared useBills/
useCategories). Optimistic updates (toggle, reorder, dismiss recommendation)
and their await-load() reconciliation preserved by routing setData/setBills/
setRecommendations through queryClient.setQueryData and load()/loadRecommendations
through invalidateQueries. The redundant mount-load effect was removed (hooks
fetch on mount). useOptimistic layer unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reuses the shared useBills/useCategories caches (+ new useBillTemplates/
useDeletedBills), so bill mutations here now also refresh the Tracker/badge
live via the shared ['bills'] key. Optimistic list edits (delete, reorder)
write through queryClient.setQueryData; post-mutation load() calls became a
refresh() that invalidates the 4 page queries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaced the manual useState(data/loading/error) + load useCallback + useEffect
(and the R3 request-seq guard) with a useAnalyticsSummary(params) query hook.
React Query now handles caching, dedup, cancellation, and out-of-order responses
via the params-encoded key; keepPreviousData keeps the last result visible while
a new month/filter loads. Refresh -> refetch; the redundant page-load error toast
is dropped in favor of the existing inline error state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The app never called invalidateQueries; a Tracker mutation only refetch()'d
the one tracker query. So the sidebar overdue badge (['overdue-count'],
2-min staleTime), drift report, and bills list stayed stale after pay/skip/
edit — you could clear your last overdue bill and still see '3' for minutes.
Added useInvalidateTrackerData() (tracker + overdue-count + drift-report +
bills) and wired it into rows, BillModal.onSave, bank-sync, reorder, and the
payment/late-attribution handlers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Detects when a bill's recent payments have diverged from its configured
expected amount for 2+ consecutive months and surfaces it in a new
collapsible amber panel on the Tracker page.
- Migration v0.71: adds `drift_snoozed_until` to bills and
`notify_amount_change` to users
- New `driftService.getDriftReport()`: computes per-bill payment median
over last 3 months, flags drift above a user-configurable threshold
(default 5%, minimum $1 delta)
- New `GET /api/bills/drift-report` and `POST /api/bills/:id/snooze-drift`
routes (registered before `/:id` to avoid routing conflict)
- `runDriftNotifications()` added to daily worker — sends amber digest
email per user listing all changed bills with old → new amounts
- `notify_amount_change` wired through profile and notifications routes
- `DriftInsightPanel`: collapsible amber panel with per-bill
strikethrough old → new amount, ±% badge, TrendingUp/TrendingDown
icons, "Update to $X.XX" (with undo toast) and "Dismiss" (30 days)
actions; teal palette for price decreases
- `drift_threshold_pct` setting added to SettingsPage Billing Behavior
- "Notify on price changes" toggle added to ProfilePage notifications
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>