feat: redesign home into a spending insights dashboard#22
Open
alon710 wants to merge 15 commits into
Open
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/insightsroute, replacing the old/api/homeroute and its eight per-card components. Updates shared types, colors, i18n strings (en/he), and global styles to support the new design. MovesSECURITY.mdand the design spec intodocs/and refreshes the README.🤖 Generated with Claude Code