# Bill Tracker — Future Improvements

**This document tracks potential future enhancements for Bill Tracker.**

**Last Updated:** 2026-05-11  
**Current Version:** v0.24.3

## How to Use This Document

This file is a living document. Agents should:
1. Read this file before proposing changes
2. Add new recommendations with priority levels
3. Never add completed items — move those to HISTORY.md instead
4. Reference this file when dispatching improvement tasks
5. Only Ripley can remove items from this list. Notify Ripley if something neds to be removed.

### Priority Format

All items must include the priority emoji in their heading, matching the section they belong to:

| Priority | Emoji | Heading Format |
|----------|-------|---------------|
| CRITICAL | 🔴 | `### 🔴 Title — CRITICAL` |
| HIGH | 🟠 | `### 🟠 Title — HIGH` |
| MEDIUM | 🟡 | `### 🟡 Title — MEDIUM` |
| LOW | 🔵 | `### 🔵 Title — LOW` |
| NICE TO HAVE | 💭 | `### 💭 Title — NICE TO HAVE` |

Items are grouped under their priority section heading (`## 🔴 CRITICAL`, `## 🟠 HIGH`, etc.) and sorted most-impactful-first within each tier.


## Pending Recommendations

### 🔴 Import XLSX Dual-Column Layout Not Parsed — CRITICAL
**Priority:** CRITICAL  
**Added:** 2026-05-11 by Ripley

**Description:**
The real-world spreadsheet (`backups/monthly bills.xlsx`) uses a **dual-column layout** — each monthly sheet is split into two halves representing two payment periods:
- **Left half (columns A-E):** Bills due around the **1st** of the month
- **Right half (columns G-K):** Bills due around the **15th** of the month

Each half has its own `Due Date | Bill | Amount | Paid Date | Date Cleared` headers. The current parser only detects headers in the first row and processes columns linearly, so it captures the 1st-of-month bills but completely misses all 15th-of-month bills (roughly half the data).

Example from Apr 2026 sheet:
```
Left (1st):  auto | Roadrunner ATV | $225.64 | paid 2026-03-01  (due ~1st)
Right (15th):      | Amazon chase card | $366    | paid 2026-04-20  (due ~15th)
```

**Rationale:**
- This is the actual production spreadsheet the app needs to import
- ~100 monthly sheets spanning 2017–2026, each with two payment periods
- The 15th-of-month bills (credit cards, loans, subscriptions) are completely lost during import
- Without dual-column support, the import feature is broken for real data
- The "1st vs 15th" split is semantically meaningful — it maps to `due_day` in the bill model

**Additional Issues in This Spreadsheet:**
- Rows 1–2 contain paycheck/leftover summary data, not bills — parser must skip these
- Non-numeric amount values: "double pay", blank amounts, "past due" — need graceful handling
- Due date column contains non-date values: "auto" (autopay indicator), "24th" (day-of-month shorthand), account numbers like "9522104"
- Some sheets have slight column layout variations (extra column, merged cells)
- Sheet names have typos: "Januaru 2021", "Novevmber 2019", "Febuary 2023" — parser already handles these
- 3 non-month sheets ("2018 taxes", "debt totoals", "home ownership expenses") should be skipped — already handled by `NON_MONTH_SHEET_RE`
- "auto" in the Due Date column is an autopay flag, not a date — should be detected as a label, not parsed as a date

**Implementation Notes:**
- Modify `spreadsheetImportService.js` to detect dual-column headers in a single sheet row
- When two sets of bill headers are found (A-E and G-K), process each half independently
- Left half rows should default `due_day` to ~1, right half rows should default `due_day` to ~15
- Each half produces its own set of rows with the same sheet name/month context
- Handle non-numeric amount cells gracefully (null amount, "double pay" as a note/label)
- "auto" in Due Date column → set `autopay` label/detected label, don't try to parse as date
- "24th" in Due Date column → parse as day-of-month (24)
- Skip rows where the bill name cell is blank AND the amount cell is blank or non-numeric
- Filter out paycheck/summary rows (Row 1: Paycheck amounts, Row 2: Left Over calculations)
- The `backups/monthly bills.xlsx` file is in `backups/` (gitignored) for testing
- Files to modify: `services/spreadsheetImportService.js`, possibly `routes/import.js`
- Estimated effort: 4-6 hours

### 🔴 No Confirmation Before Destructive Actions — CRITICAL
**Priority:** CRITICAL  
**Added:** 2026-05-11 by Ripley

**Description:**
Deleting a bill or payment is one click with no confirmation dialog and no undo. For a financial app, accidental data loss destroys user trust.

**Rationale:**
- Bills and payments represent real financial commitments and history
- No "are you sure?" prompt before delete
- No undo mechanism, no soft delete, no recovery
- One misclick = gone forever — unacceptable for financial data
- This is a trust and data integrity issue, not a UX nicety

**Implementation Notes:**
- Add confirmation dialog to all delete actions (bills, payments, categories)
- Consider soft delete (`deleted_at` column) with a grace period before permanent removal
- Add undo toast after delete that allows restoration within a short window
- Files to modify: `BillModal.jsx`, `BillsTableInner.jsx`, `TrackerPage.jsx`, `CategoriesPage.jsx`
- Estimated effort: 3-4 hours


### 🟠 HIGH

### 🟠 No Search or Filter Across Bills — HIGH
**Priority:** HIGH  
**Added:** 2026-05-11 by Ripley

**Description:**
No way to find a bill by name, category, or amount. Users must scroll through the entire list to find anything. Every bill tracker on the market has instant search.

**Rationale:**
- With dozens of bills, scrolling is slow and error-prone
- No search bar on BillsPage, TrackerPage, or CalendarPage
- Can't filter by category, amount range, autopay status, or billing cycle
- Can't quickly find "where's that electric bill?" without visually scanning
- Table stakes for any list-based app

**Implementation Notes:**
- Add search input to BillsPage (filter by name, category, notes)
- Add filter chips/dropdowns for category, billing cycle, autopay, active/inactive
- Add search to TrackerPage (filter visible rows by bill name)
- Consider a global `Cmd+K` / `Ctrl+K` command palette for instant bill lookup
- Files to modify: `BillsPage.jsx`, `TrackerPage.jsx`, `client/api.js`
- Estimated effort: 6-8 hours

### 🟠 No Visible Overdue Indicators — HIGH
**Priority:** HIGH  
**Added:** 2026-05-11 by Ripley

**Description:**
Overdue bills aren't visually flagged on the tracker. An unpaid past-due bill looks the same as one not due yet. The `notify_overdue` setting exists but there's no visual distinction in the UI.

**Rationale:**
- The whole point of a bill tracker is to not miss payments
- Overdue bills should be impossible to overlook — red highlight, badge count, sticky alert
- Data model supports overdue notifications but tracker grid shows no overdue state
- Users scanning the tracker won't notice a missed bill buried in the list

**Implementation Notes:**
- Add overdue detection: if today > due date for current month and no payment logged, mark overdue
- Red/amber background on overdue tracker rows
- Overdue count badge in Sidebar next to Tracker nav link
- Optional: overdue summary banner at top of TrackerPage
- Files to modify: `TrackerPage.jsx`, `Sidebar.jsx`, `routes/tracker.js` (add overdue count to API response)
- Estimated effort: 4-6 hours

### 🟠 Filtered Export for Reports — HIGH
**Priority:** HIGH  
**Added:** 2026-05-11 by Ripley (upgraded from LOW)

**Description:**
No way to export filtered data (e.g., "all bills in category X for last 6 months", "everything overdue in 2026"). Export dumps everything or nothing.

**Rationale:**
- Exporting filtered reports is core functionality for a bill tracker, not a nice-to-have
- Users need "all Q1 utility bills" or "overdue payments this year" for reconciliation and tax prep
- `/api/export/user-excel` exports everything — no query params for date range, category, or status
- This is how people actually use financial data outside the app

**Implementation Notes:**
- Add query params to export endpoints: `category_id`, `start`, `end`, `status` (paid/unpaid/overdue)
- Files to modify: `routes/export.js`, `client/pages/DataPage.jsx`
- Estimated effort: 6 hours


### 🟡 MEDIUM

### 🟡 No Bill Template / Duplicate Bill — MEDIUM
**Priority:** MEDIUM  
**Added:** 2026-05-11 by Ripley

**Description:**
Creating a new bill means filling 10+ fields every time. No way to duplicate an existing bill or use a template. If you have 3 utilities from the same provider, you're retyping everything.

**Rationale:**
- Bill creation has many fields (name, category, due day, amount, autopay, website, account, 2FA, notes)
- Common pattern: similar bills from same provider or same category with slight variations
- "Duplicate bill" is table stakes in every bill tracker
- Reduces friction and errors during bill setup

**Implementation Notes:**
- Add "Duplicate" button/action on each bill row and in BillModal
- Pre-fill all fields from source bill, clear `name` and set "(Copy)" suffix
- Files to modify: `BillModal.jsx`, `BillsPage.jsx`, `routes/bills.js` (POST endpoint can accept `source_bill_id` param)
- Estimated effort: 3-4 hours

### 🟡 No Partial Payment Support — MEDIUM
**Priority:** MEDIUM  
**Added:** 2026-05-11 by Ripley

**Description:**
The UI only supports logging a single payment per bill per month. The `payments` table schema supports multiple entries per bill, but the frontend doesn't surface this. Split payments (half now, half later) can't be tracked.

**Rationale:**
- Many bills get paid in installments (medical, tuition, large utilities)
- Payment plan arrangements require tracking multiple payments against one bill
- The data model already supports it — it's purely a frontend gap
- Without this, users either over-record or under-record partial payments

**Implementation Notes:**
- Show payment history per bill in tracker (expandable row or modal tab)
- Allow "Add partial payment" with amount + date, summing to bill total
- Display remaining balance on partially-paid bills
- Files to modify: `TrackerPage.jsx`, `routes/payments.js`, possibly `BillModal.jsx`
- Estimated effort: 6-8 hours

### 🟡 No Year-Over-Year Comparison in Analytics — MEDIUM
**Priority:** MEDIUM  
**Added:** 2026-05-11 by Ripley

**Description:**
Analytics shows monthly trends within a single year but there's no "this month vs same month last year" view. Users can't evaluate whether spending is improving.

**Rationale:**
- The whole point of analytics is answering "am I doing better or worse?"
- Within-year trends are useful but don't show long-term improvement
- Comparing April 2026 to April 2025 is the natural question people ask
- Available in every competing app (YNAB, Monarch, etc.)

**Implementation Notes:**
- Add YoY comparison toggle or tab to AnalyticsPage
- Query: same month range across current and previous year, diff the totals
- Show percentage change and absolute change per category
- Files to modify: `AnalyticsPage.jsx`, `routes/analytics.js` (add YoY endpoint or params)
- Estimated effort: 6-8 hours

### 🟡 No Bulk Actions — MEDIUM
**Priority:** MEDIUM  
**Added:** 2026-05-11 by Ripley

**Description:**
Every action is one-at-a-time. Can't select multiple bills and mark them paid, skip them for a month, or change their category.

**Rationale:**
- End-of-month reconciliation means marking many bills as paid in a row
- Category reorganization affects multiple bills at once
- Skipping seasonal bills for summer/winter requires individual clicks
- Bulk actions are standard in any list-based management UI

**Implementation Notes:**
- Add checkbox selection to BillsPage rows (with select-all toggle)
- Bulk action toolbar: Mark Paid, Skip This Month, Change Category, Delete
- Backend: batch endpoints or loop with progress indicator
- Files to modify: `BillsPage.jsx`, `BillsTableInner.jsx`, `routes/bills.js`, `routes/payments.js`
- Estimated effort: 8-10 hours

### Architecture: Business Logic Mixed with Route Handlers
**Priority:** MEDIUM  
**Added:** 2026-05-08 by Neo

**Description:**
Many routes contain business logic that should be extracted to service layers.

**Rationale:**
- `bills.js` contains `parseDueDay()`, `parseInterestRate()` — validation logic
- `tracker.js` contains date/range calculations that are reused across routes
- `admin.js` has complex OIDC config building mixed with routing
- `analytics.js` has complex date-building logic (`buildMonths`, `monthKey`, etc.)

**Implementation Notes:**
- Files to modify: Multiple route files + new service files in `/services/`
- Estimated effort: 8 hours
- Proposed structure:
  ```
  /services/billsService.js
  /services/trackerService.js
  /services/analyticsService.js
  /services/authService.js (existing)
  /services/oidcService.js (existing)
  /services/cleanupService.js (existing)
  ```
- Route handlers should call services, not contain business logic


### 🔵 LOW

### 🔵 Payment Method Tracking and Summary — LOW
**Priority:** LOW  
**Added:** 2026-05-11 by Ripley

**Description:**
The `payments` table has a `method` column (free-text) but no way to see "how much did I pay via autopay vs manual vs credit card this month." No standardized method options, no summary.

**Rationale:**
- Useful for reconciling credit card statements vs bank statements
- Autopay vs manual tracking helps identify bills that should be switched to autopay
- Payment method breakdown is a common analytics view in financial apps
- Current `method` field is unvalidated free text — no consistency

**Implementation Notes:**
- Standardize payment methods: enum or controlled list (autopay, bank_transfer, credit_card, check, cash, other)
- Add payment method breakdown to analytics or summary page
- Files to modify: `routes/payments.js`, `AnalyticsPage.jsx` or `SummaryPage.jsx`, schema migration for method validation
- Estimated effort: 4-6 hours

### 🔵 No Keyboard Navigation or Shortcuts — LOW
**Priority:** LOW  
**Added:** 2026-05-11 by Ripley

**Description:**
Only a skip link exists for keyboard accessibility. No `Cmd+K` to find a bill, no `Esc` to close modals, no arrow keys to navigate the tracker grid. Power users and accessibility need keyboard support.

**Rationale:**
- Keyboard accessibility is required for WCAG compliance
- Power users navigate faster with keyboard shortcuts
- Modal dismiss on `Esc` is expected behavior in any modern app
- Command palette (`Cmd+K`) pairs with the search feature (also missing)

**Implementation Notes:**
- `Esc` closes any open modal/dialog
- `Cmd+K` / `Ctrl+K` opens search/command palette
- Arrow keys navigate tracker rows when grid is focused
- Tab order follows logical flow, not DOM order
- Files to modify: `App.jsx`, `BillModal.jsx`, `TrackerPage.jsx`, all dialog components
- Estimated effort: 6-8 hours

### Add comprehensive unit and integration tests
**Priority:** LOW  
**Added:** 2026-05-08 by Scarlett

**Description:**
Currently no unit tests exist for components or hooks. The only testing appears to be functional tests in `test-functional.js`. Component-level testing is missing.

**Rationale:**
Code quality and maintainability. Unit tests catch regressions and document component behavior. Bill Tracker has complex business logic (bill calculations, monthly state, analytics) that should be tested.

**Implementation Notes:**
- Set up Jest + React Testing Library
- Test key components: BillModal, TrackerPage row, BillsTableInner
- Test hooks: useAuth, custom form hooks
- Test utility functions in `client/lib/utils.js`
- Consider vitest for faster test execution
- Add CI integration for test execution
- Files likely to be modified: Add `client/test/` directory, add `jest.config.cjs`
- Estimated effort: 8-12 hours for baseline coverage

### Features: Missing Bill Grouping and Reorganization API
**Priority:** LOW  
**Added:** 2026-05-08 by Neo

**Description:**
No way to reorder bills, drag-and-drop, or group by custom criteria.

**Rationale:**
- `bills` table has `due_day` ordering but no manual sort order
- Frontend likely orders by `due_day` only
- Users cannot create bill groups or categories for bills
- No way to mark bills as "hidden" or "archived" without deactivating

**Implementation Notes:**
- Files to modify: `/home/kaspa/.openclaw/Projects/bill-tracker/db/schema.sql`, `/routes/bills.js`
- Estimated effort: 6 hours
- Add:
  - `sort_order` column to bills table (default NULL, ordered first by sort_order then due_day)
  - `PUT /api/bills/reorder` endpoint accepting `{bill_id: new_index}`
  - `PUT /api/bills/:id/archived` to soft-dearchive (sets `archived` flag)


### 💭 NICE TO HAVE

### Add consistent form state management pattern
**Priority:** MEH  
**Added:** 2026-05-08 by Scarlett

**Description:**
Form state management is inconsistent across components. Some use `useState` for each field, others use form libraries. Validation patterns vary.

**Rationale:**
Consistency and maintainability. A consistent pattern makes it easier to add new forms and reduce bugs.

**Implementation Notes:**
- Consider react-hook-form for complex forms
- Create reusable form field components (InputField, SelectField, etc.)
- Standardize validation approach
- Files likely to be modified: `client/components/*.jsx`
- Estimated effort: 4-6 hours for migration
