fix: overdue badge excludes due today, tooltip with names, larger column labels

This commit is contained in:
null 2026-06-04 01:12:25 -05:00
parent 46bcf83d22
commit 779ace60dd
2 changed files with 5 additions and 1 deletions

View File

@ -28,6 +28,10 @@
- **Pin Due — urgent bills float to top of tracker** — A "Pin Due" toggle button in the TrackerPage header sorts overdue and due-soon bills to the top of each bucket when enabled. Priority order: `missed``late``due_soon``upcoming` → everything else; ties broken by `due_day`. The sort runs after filtering but before the bucket split, so each half-month bucket is sorted independently. The button uses `variant="default"` (solid) when active and `variant="outline"` when off so the current mode is always visible. Preference persists across sessions via `localStorage` under `tracker_pin_upcoming`. Drag reorder is automatically disabled while the toggle is on (`reorderEnabled` now also requires `!pinUpcoming`) since the two modes conflict. - **Pin Due — urgent bills float to top of tracker** — A "Pin Due" toggle button in the TrackerPage header sorts overdue and due-soon bills to the top of each bucket when enabled. Priority order: `missed``late``due_soon``upcoming` → everything else; ties broken by `due_day`. The sort runs after filtering but before the bucket split, so each half-month bucket is sorted independently. The button uses `variant="default"` (solid) when active and `variant="outline"` when off so the current mode is always visible. Preference persists across sessions via `localStorage` under `tracker_pin_upcoming`. Drag reorder is automatically disabled while the toggle is on (`reorderEnabled` now also requires `!pinUpcoming`) since the two modes conflict.
- **Overdue badge: "due today" no longer counts as overdue + tooltip added**`getOverdueCount` changed `dueDate > todayStr` to `dueDate >= todayStr` so bills due today are not counted as past due — only bills strictly in the past trigger the badge. The function now also returns `names: [...]` alongside the count. Both the sidebar `TrackerMenu` (desktop) and `NavPill` (mobile) wrap the badge in a `<Tooltip>` that shows "2 past due · Discord Nitro · Camry" on hover, so the number is immediately explained without navigating anywhere. Up to 5 names are shown with a "+N more" overflow line.
- **Column labels larger and lighter in tracker table**`TableHead` elements in `TrackerBucket` changed from `text-[10px] font-semibold tracking-widest` to `text-xs font-medium tracking-wider`. Size increased from 10 px to 12 px for readability; weight dropped from semibold to medium so the labels don't visually compete with the bold bill name text in each row.
- **Tracker row keyboard navigation** — Tracker rows (desktop table view) are now keyboard navigable. Each row has `tabIndex={0}`, `data-tracker-row`, `aria-rowindex`, and an `aria-label` announcing the bill name, status, and due day. A `focus-visible:ring-2 ring-primary/60 ring-inset` focus ring appears on keyboard focus only. Key bindings: `↓`/`j` focuses the next row, `↑`/`k` the previous (both cross bucket boundaries via `querySelectorAll('[data-tracker-row]')`), `Enter` opens the edit modal, `P` toggles paid/unpaid (skipped bills ignored, `Ctrl+P`/`Cmd+P` passes through to the browser), `Esc` blurs the row. The `onKeyDown` handler guards against firing on nested interactive elements with `if (e.target !== e.currentTarget) return`. - **Tracker row keyboard navigation** — Tracker rows (desktop table view) are now keyboard navigable. Each row has `tabIndex={0}`, `data-tracker-row`, `aria-rowindex`, and an `aria-label` announcing the bill name, status, and due day. A `focus-visible:ring-2 ring-primary/60 ring-inset` focus ring appears on keyboard focus only. Key bindings: `↓`/`j` focuses the next row, `↑`/`k` the previous (both cross bucket boundaries via `querySelectorAll('[data-tracker-row]')`), `Enter` opens the edit modal, `P` toggles paid/unpaid (skipped bills ignored, `Ctrl+P`/`Cmd+P` passes through to the browser), `Esc` blurs the row. The `onKeyDown` handler guards against firing on nested interactive elements with `if (e.target !== e.currentTarget) return`.
- **Bills list query optimised and merchant rule index added**`GET /api/bills` replaced a correlated `EXISTS(SELECT 1 FROM bill_history_ranges WHERE bill_id = b.id)` per bill row with a single `LEFT JOIN (SELECT DISTINCT bill_id FROM bill_history_ranges) hr`. DB migration `v0.81` adds composite index `idx_bill_merchant_rules_user_bill ON bill_merchant_rules(user_id, bill_id)` — the existing index only covered `user_id`, making the EXISTS check in `GET /api/bills/:id` scan by user then filter by bill; the composite index makes it a direct point lookup. Pagination was not added — the UI depends on all bills being loaded at once and personal-scale data volumes don't warrant it. - **Bills list query optimised and merchant rule index added**`GET /api/bills` replaced a correlated `EXISTS(SELECT 1 FROM bill_history_ranges WHERE bill_id = b.id)` per bill row with a single `LEFT JOIN (SELECT DISTINCT bill_id FROM bill_history_ranges) hr`. DB migration `v0.81` adds composite index `idx_bill_merchant_rules_user_bill ON bill_merchant_rules(user_id, bill_id)` — the existing index only covered `user_id`, making the EXISTS check in `GET /api/bills/:id` scan by user then filter by bill; the composite index makes it a direct point lookup. Pagination was not added — the UI depends on all bills being loaded at once and personal-scale data volumes don't warrant it.

View File

@ -333,7 +333,7 @@ export default function BankSyncSection({ onConnectionChange, cardProps = {} })
const loadBankTracking = useCallback(async () => { const loadBankTracking = useCallback(async () => {
try { try {
const [settings, accounts] = await Promise.all([ const [settings, accounts] = await Promise.all([
api.getSettings(), api.settings(),
api.allFinancialAccounts().catch(() => []), api.allFinancialAccounts().catch(() => []),
]); ]);
setBtEnabled(settings.bank_tracking_enabled === 'true'); setBtEnabled(settings.bank_tracking_enabled === 'true');