Skip to content

Add shared budgets#67

Merged
andrewzolotukhin merged 16 commits into
mainfrom
feat/budgets
Jul 8, 2026
Merged

Add shared budgets#67
andrewzolotukhin merged 16 commits into
mainfrom
feat/budgets

Conversation

@andrewzolotukhin

@andrewzolotukhin andrewzolotukhin commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Original request

Plan and implement the new Budgets feature for xpenser:

  • every transaction belongs to a budget, with existing transactions migrated into a user's Main budget
  • budgets can be shared with other users through magic-link invitations
  • budget members can have admin/member access, with admins able to manage access
  • vendors, categories, tags, reports, Telegram, MCP, and the main app workflows should respect budget scope
  • follow-up refinements: per-user budget names, archive/delete flows, budget-level favorite currencies, a dedicated budget management page, member management, user avatars, creator avatars on shared transactions, avatar upload fixes, and production-safe PR database restore handling

Latest follow-up implemented in this update:

  • clarify whether API batching was disabled
  • expand the shared budgets blog post with screenshots and stronger SEO-oriented copy

What changed

  • Added budget-backed transaction ownership and sharing flows across the app, including invite acceptance, role-aware budget access, budget selector UX, budget archive/delete, and per-user budget naming.
  • Moved budget currency preferences to the budget management surface and kept budget detail management on dedicated /:id routes.
  • Added member administration for budget admins, including role/status visibility and member removal/update controls.
  • Added reusable user avatar support and transaction creator avatars where shared-budget rows were created by another user.
  • Added/updated migration and PR database restore handling so the PR environment can rebuild from a production copy without touching production data.
  • Expanded /blog/shared-budgets into a full SEO-friendly article targeting shared budget expense tracker, with internal links, richer copy, a hero image, and inline screenshots.
  • Added MDX figure/image rendering support through next/image, plus a focused MDX component test.

API batching note

API batching was not disabled globally. The only intentional direct/no-batch usage is on the budget detail management flow, where route-specific budget detail, member, invite, and currency reads/mutations need independent invalidation and clearer request behavior. The rest of the app keeps the existing API client behavior.

Blog update

  • Page: /blog/shared-budgets
  • Target keyword: shared budget expense tracker
  • Content length: approximately 1,500+ words
  • Added screenshots:
    • apps/web/public/blog/shared-budgets-overview.png
    • apps/web/public/blog/shared-budget-detail.png
    • apps/web/public/blog/shared-budget-transactions.png
  • Screenshots were captured from the PR preview and sanitized before committing so production-derived transaction/vendor/category/amount data is not published.

Validation

Local validation on feat/budgets:

  • npx vitest --run apps/web/components/mdx-components.test.tsx apps/web/lib/blog.test.ts apps/web/components/blog-pages.test.tsx
  • npm run lint
  • npm run typecheck
  • npm test
  • git diff --check

Preview validation:

  • Preview URL: https://xpenser-pr-067.cleverbrush.com
  • Checked /blog and /blog/shared-budgets on desktop and mobile.
  • Confirmed the blog listing, article H1, target keyword, hero image, inline screenshots, and internal links load correctly.

GitHub validation:

  • Commit: 92291d7
  • Workflow run: PR Environments #271, run id 28943291046, completed successfully.

Telemetry check:

  • Checked SigNoz logs/metrics/traces for PR 67 after preview QA.
  • No app 5xx metrics or ERROR/FATAL logs were found for the blog QA path.
  • Error traces seen were explainable external/client cases: Brandfetch 429 from enrichment traffic and failed preview login attempts before using existing auth state.

Notification:

  • PR-ready notification was attempted, but the configured local notifier endpoint at 127.0.0.1:8787 was not reachable (curl connection failure).

@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 11:02 — with GitHub Actions Inactive
@andrewzolotukhin

Copy link
Copy Markdown
Contributor Author

PR Workflow Update

Preview URL: https://xpenser-pr-067.cleverbrush.com

Workflow run / artifacts: https://github.com/cleverbrush/xpenser/actions/runs/28860710673
Playwright artifact: playwright-report (8135816672).

Manual preview QA completed in the deployed preview:

  • Logged in as the seeded preview user.
  • Confirmed the header budget selector appears next to the xpenser logo and starts on Main.
  • Created Codex QA Budget 67 retry from Preferences > Budgets.
  • Switched the active selector to the new budget.
  • Confirmed the new budget started with no categories, added QA Meals 67, and returned to Add.
  • Saved a 12.34 USD expense under QA Meals 67 in the selected budget.
  • Confirmed the transaction appears in the new budget's Transactions list.
  • Switched back to Main and confirmed the QA transaction is not mixed into Main.

Validation:

  • Local npm run lint: passed.
  • Local npm run typecheck: passed.
  • Local npm test: passed, 88 files / 492 tests.
  • git diff --check: passed before commit.
  • PR Lint and test: passed.
  • PR Deploy PR environment: passed.
  • PR Playwright e2e: passed.

SigNoz:

  • Logs query for service.name IN ('xpenser-web-pr-67', 'xpenser-api-pr-67') with severity_text IN ('ERROR', 'FATAL') over the last hour returned no rows.
  • Trace query for the manual QA window over xpenser-web-pr-67 and xpenser-api-pr-67 returned no hasError = true spans.
  • Earlier e2e-window trace errors were Brandfetch 404s from external vendor enrichment (api.brandfetch.io/v2/brands/transaction) while the app vendor request returned 201; not a budget-flow failure.

Note: I attempted to update the PR description with this status, but the GitHub integration returned 403 Resource not accessible by integration for both PR and issue update endpoints.

@andrewzolotukhin

Copy link
Copy Markdown
Contributor Author

Telegram PR-ready notification was skipped: PR_NOTIFICATION_API_TOKEN is set, but the default notifier endpoint http://127.0.0.1:8787/v1/pr-ready is not reachable from this environment.

@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 12:43 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 14:01 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 16:46 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 17:10 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 17:21 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 17:32 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 17:43 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 7, 2026 17:51 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 8, 2026 05:58 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 8, 2026 06:05 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 8, 2026 06:22 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 8, 2026 06:31 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 8, 2026 10:45 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin temporarily deployed to pr-67 July 8, 2026 12:42 — with GitHub Actions Inactive
@andrewzolotukhin andrewzolotukhin merged commit 71c9d09 into main Jul 8, 2026
4 checks passed
@andrewzolotukhin andrewzolotukhin deleted the feat/budgets branch July 8, 2026 13:09
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