Skip to content

feat: redesign home into a spending insights dashboard#22

Open
alon710 wants to merge 15 commits into
Shaya16:mainfrom
alon710:alon710/spending-insights-redesign
Open

feat: redesign home into a spending insights dashboard#22
alon710 wants to merge 15 commits into
Shaya16:mainfrom
alon710:alon710/spending-insights-redesign

Conversation

@alon710

@alon710 alon710 commented Jun 6, 2026

Copy link
Copy Markdown

Replaces the card-grid home page with an insights-driven layout: a verdict hero, attention strip, top movers, category breakdown, improve feed, and recent activity, backed by reusable chart primitives (donut, sparkline, burndown). Adds a server-side insights engine under src/server/insights (compute, engine, and tests) exposed through a new /api/insights route, replacing the old /api/home route and its eight per-card components. Updates shared types, colors, i18n strings (en/he), and global styles to support the new design. Moves SECURITY.md and the design spec into docs/ and refreshes the README.

🤖 Generated with Claude Code

alon710 and others added 15 commits May 27, 2026 13:56
Adds a streaming /chat page that uses the same AI provider configured
for categorization (Claude or Ollama). The agent answers free-text
questions about the user's finances through tool calls that wrap
existing read-only DB queries (transactions, monthly summary, top
merchants, category breakdown). Page and sidebar entry are disabled
when no AI provider is configured.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Next.js 16 renames the Middleware file convention to Proxy. The build
emits a deprecation warning until the rename happens.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
refactor: rename middleware.ts to proxy.ts for Next.js 16
…endering

Adds persistent chat sessions backed by SQLite (migration 021), a session history
sidebar with rename and delete actions, AI-generated chat titles via a setChatTitle
tool, and markdown rendering for assistant responses using react-markdown.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(chat): AI chat agent with persistent sessions and tool-based data access
Bank-side credit card payments are tagged kind='transfer' by detectKind
but the transactions summary and list kind-filter used the raw
charged_amount sign, so transfers double-counted with individual card
line items in totals, top merchants, and the largest-expense widget.
Switch those queries to filter on kind, and backfill existing rows in a
new migration that reclassifies bank rows matching the credit-card
payment patterns.
…nting

fix(transactions): exclude credit card transfers from expense totals
* feat(ai): add Gemini as a third AI provider

Adds Google Gemini alongside Claude and Ollama for transaction
categorization. Uses @google/genai with gemini-2.5-flash and stores
the API key encrypted under provider-specific setting keys so it can
coexist with a Claude key. Wires the new option through the setup
wizard, settings page, factory, and i18n.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Add Gemini provider support

* refactor(ai): simplify Gemini provider changes

- always require a key on save and drop the leave-blank-to-keep-stored UX,
  removing the hasStoredKey helper, hasClaudeApiKey/hasGeminiApiKey flags,
  and the i18n hint pair this required
- extract shared parseCategorizationResponse used by ClaudeProvider and
  GeminiProvider
- merge ClaudeConfig and GeminiConfig in the setup wizard into a single
  ApiKeyConfig with a children slot for provider-specific extras
- dispatch friendlyAIError on the provider argument instead of cascading
  regex matches over raw error text
- drop redundant Gemini model whitelist checks in the factory and settings
  writer

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: pin PR target to upstream Shaya16/Spent

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: remove upstream PR target note from CLAUDE.md

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Adds .github/workflows/ci.yml that runs `bun run ci` on every PR + push
to main with fail-fast on five strict checks: Biome formatter, TypeScript,
@lingual/i18n-check (next-intl-recommended) for missing + orphan keys,
knip for dead code, react-compiler-healthcheck, and `bun test`.

Switches the toolchain to Bun: package-lock.json removed, bun.lock
committed, all `npm`/`node` shell-outs in scripts/ replaced with `bun`,
service installer cheat-sheets updated. Service templates (.plist,
.service, .vbs, .xml) unchanged since they invoke node + next directly.

Adds tooling configs (biome.jsonc, knip.json), a wrapper script for
i18n-check with documented dynamic-namespace and baseline-orphan
ignores, and one smoke test (src/server/lib/dedup.test.ts) so the test
gate is real. Applies a one-pass Biome formatter sweep across 110 files.

README, CLAUDE.md, and menubar/README.md updated to reflect bun
commands, new Requirements, CI badge, and the `bun run ci` workflow.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sync): show full provider error message in progress dialog

Truncating to 60 chars hid the actual failure reason. Wrap when the row
is in error state; keep truncation for non-error statuses.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(readme): refresh bank coverage, add chat feature, bump default model

Replaces the three-bank list with the full israeli-bank-scrapers roster
shipped enabled today. Documents the new /chat agent. Updates the
Claude default to claude-haiku-4-5-20251001 and refreshes the roadmap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ai): support Gemini in chat and trim provider API keys

Gemini was wired into setup and categorization but never into the chat
model factory, so chat returned 400 "AI provider not configured" for
Gemini users. Add a gemini branch to createChatModel via @ai-sdk/google,
and trim Claude/Gemini keys before validating and saving.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ui): point Star on GitHub link to alon710/Spent

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#13)

- i18n: serve every page under /en or /he via next-intl; the active
  locale comes from the URL plus the NEXT_LOCALE cookie (no database),
  defaulting to English. The proxy adds CSRF same-origin checks on /api.
- transactions: detect bank-side credit-card settlements, internal
  account-to-account transfers, and ATM withdrawals so they stop
  double-counting spend; add a "treat ATM withdrawals as transfers"
  setting.
- ui: add vertical spacing between sidebar items, plus broad pre-launch
  polish across components, docs, website, menubar, scripts, copy, and
  PWA icons/manifest.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(bank): correct Discount field, broaden transfer detection, honor 2FA flag on test

- src/lib/types.ts: rename Discount/Mercantile `num` field from "Account
  Number" to "Identifier Code" and drop `numeric: true`. The underlying
  scraper fills selector #aidnum, which is the alphanumeric קוד מזהה
  (user-chosen identifier) on Discount's login form, not the account
  number. Mercantile extends Discount so it inherits the same field.
- src/server/lib/transfers.ts: BANK_PROVIDERS_SET was missing every
  bank-kind provider except hapoalim and leumi, so credit-card payment
  lines on Discount and other banks were classified as expenses instead
  of transfers. Add the remaining bank providers, plus loosen the כאל
  regex to also match the dotted abbreviation כ.א.ל and add a pattern
  for "חיוב לכרטיס ..." prefix.
- src/app/api/setup/bank/test/route.ts: the "Test connection" button
  ignored the per-credential manual-2FA flag, so users who enabled it
  to see the bank browser still got headless attempts. Plumb the flag
  through to scrapeBank.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(transactions): per-transaction exclusion with always-exclude-merchant rule

Finishes wiring up the half-built excluded_merchants feature (the table,
queries, and TransactionWithCategory.isExcluded already existed but were
unreachable from the UI and not honored by summing queries).

Behavior:
- Each row in the transactions table gets two new dropdown actions:
  "Exclude this transaction" (per-row toggle) and "Always exclude this
  merchant" (per-row toggle + persistent rule keyed on provider +
  description). Excluded rows stay visible at 50% opacity so they can
  be un-excluded later.
- After each sync, the orchestrator calls applyMerchantRulesToSyncRun so
  newly inserted rows matching an existing rule are excluded automatically.
- Settings → Data has a new "Excluded merchants" card listing every rule
  with a Remove button.
- All spending and income summaries filter out is_excluded = 1: cash
  flow, monthly trend, top merchants, category breakdown, period totals,
  daily series, category snapshot, transactions summary, "needs
  attention" counts, and the AI categorizer's pending queue.

API:
- POST /api/transactions/[id]/exclude { excluded, alwaysForMerchant? }
- GET /api/excluded-merchants
- DELETE /api/excluded-merchants/[id]

Implementation notes:
- excluded-merchants.ts gains deleteExcludedMerchantByKey so the per-tx
  endpoint can also unwind the rule when alwaysForMerchant=true and the
  caller is toggling exclusion off.
- getTransactionContext now also returns the provider, needed to key
  merchant rules. The one existing caller (PATCH /api/transactions/[id])
  is unaffected by the extra field.

Hebrew translations are best-effort; native speaker review welcome.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat: add financial-event transaction deduplication engine

Group duplicate cross-account representations of one real-world money
movement (internal transfers, bank-side credit card bill payments, ATM
withdrawals) into auditable Financial Events with a canonical row,
confidence scoring, human-readable match reasons, and lossless undo.
This generalizes the previous keyword-based kind-flipping into a tunable,
reversible matching layer while keeping the existing exact dedup, the
kind column, and every reporting query backward compatible.

Adds migration 022 (financial_events, event_members, match_settings,
match_rules, additive transactions columns, blocking index, and a
spendable_transactions view), a pure unit-tested matching engine, the
DB apply/list/confirm/reject layer, sync-flow wiring, an events API, a
transaction-list event badge, and a full design document.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(db): introduce Drizzle ORM as the typed query layer

Add drizzle-orm + drizzle-kit over the existing better-sqlite3 connection
(getOrm() wraps the same getDb() handle). schema.ts mirrors the live DB,
generated by introspecting a fully-migrated database; the .sql migration
runner remains the source of truth for DDL. Migrate the financial-events
query module to Drizzle as the canonical pattern, preserving signatures
and behavior (verified end to end).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(db): migrate query layer to Drizzle ORM (hybrid)

Convert the remaining query modules to the Drizzle query builder for
simple single-table CRUD and upserts, while keeping complex analytics
(aggregates, multi-table joins, window functions, recursive CTEs, and
the dedup ON CONFLICT batch upsert) as raw SQL on the same connection.
Signatures and behavior are preserved exactly; verified end to end
across settings, workspaces, sync runs, budgets, excluded merchants,
categories, and the financial-event pipeline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: remove menubar apps and service scripts, slim tooling

Delete the macOS/Windows menubar apps, the service install scripts, the
docs-seed/setup/uninstall/screenshot scripts, and Spent.sln; refresh CI,
eslint, knip, biome, README, and SECURITY config; add a lint-changed
helper script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Ori Kabeli <ori.kabeli@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Replace the card-grid home page with an insights-driven layout: verdict
hero, attention strip, top movers, category breakdown, improve feed, and
recent activity. Add a server-side insights engine (src/server/insights)
with computed metrics and tests, a new /api/insights route, and reusable
chart primitives (donut, sparkline, burndown). Move SECURITY.md and
design docs into docs/ and refresh i18n strings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant