# Budget UX Foundation v3 — URL Filters, Stores & Analytics Envelope

Use this reference for FinanceManager technical UX-foundation sprints that should improve maintainability/bookmarkability without adding budget domain features.

## Scope boundaries

- Do not add import sources, Grocery, OCR/camera, reports/export, Merchant Engine expansion, Fixkosten/subscription expansion, saved-view UI, tag-group UI, or Transaction Template UI unless separately authorized.
- Treat Transaction Templates, saved analysis views, tag groups, and Data Explorer view presets as roadmap/docs only in this class of sprint.
- Keep provider keys backend/runtime-only; no `VITE_*` secrets and no API keys in frontend or chat.
- Preserve Preview → Confirm → Audit for any write actions; store/fetch real values only through runtime DB and report aggregate/sanitized status in chat.

## URL-synchronous filters pattern

- Build one central composable, e.g. `frontend/src/composables/useQueryFilters.ts`, rather than per-page ad-hoc route/query code.
- Supported canonical filters: `year`, `month`, `date_from`, `date_to`, `type`, `account`, `category`, `merchant`, `source`, `tag`, `status`, `search`, `amount_min`, `amount_max`.
- Sanitize query values before applying them:
  - invalid years fall back to the active default, currently 2026;
  - invalid months/date formats become empty/default;
  - amount filters must parse as numbers or be removed;
  - unsupported enum-like types fall back to safe defaults.
- Keep `year=2026` in the URL for bookmark clarity. Avoid leaking internal aliases such as `budget_year` into normal analysis URLs; translate aliases only at API/store boundary where needed.
- Use router `replace` on initial sync/reload and `push` for user-driven apply actions so browser Back/Forward remains meaningful.
- Pilot pages: Daten-Explorer, Effektive Ausgaben, Buchungen prüfen, Kategorieanalyse.

## Pinia store cut

Introduce domain stores incrementally, not a whole-frontend rewrite:

- `budgetOverviewStore`: cockpit/overview DTOs, selected month, loading/error, refresh.
- `budgetTransactionsStore`: confirmed transactions/effective expenses, update transaction, refresh after edits.
- `budgetReviewStore`: import/review candidates and summary, confirm candidate, refresh after confirm.
- `budgetAnalyticsStore`: data explorer/category/monthly/budget-vs-actual analytics, envelope-aware data loading.

Store rules:

- Centralize loading/error state.
- Cache only the last loaded filter set unless a broader cache is explicitly designed.
- After Confirm/Update, refresh the affected Review/Transactions/Analytics surfaces.
- Keep page tests mounted with Pinia in the global Vue test setup.

## Analytics response envelope

For budget analytics endpoints, add a backwards-compatible envelope while preserving legacy keys during transition:

```json
{
  "meta": {"view": "data_explorer", "generated_at": "...", "currency": "CHF"},
  "filters_applied": {"year": 2026, "month": null, "category_id": null},
  "totals": {},
  "series": [],
  "rows": [],
  "warnings": [],
  "errors": []
}
```

Endpoints to normalize first:

- `/api/budget/analysis/data-explorer`
- `/api/budget/analysis/categories`
- `/api/budget/analysis/monthly` and legacy `/monthly-comparison`
- `/api/budget/analysis/budget-vs-actual`
- optionally `/api/budget/dashboard/cockpit`

Implementation guidance:

- Add a small backend helper such as `analytics_envelope(data, view, filters, totals, series, rows)`.
- Keep existing response keys (`kpis`, `transactions`, `categories`, `months`, etc.) until all frontend consumers are migrated.
- Frontend stores/components should prefer `totals`, `series`, `rows` where available and fall back to old keys.
- Empty analytics data must render empty states, not silent chart errors.

## Command Center system status tile

Add only a small/de-emphasized status tile, not an admin section:

- app/API version and git commit if available;
- schema version;
- last import session;
- last backup time;
- Git-safety status or `nicht geprüft`;
- shortened runtime path only.

If the backend does not expose a field yet, show controlled `nicht verfügbar`/`nicht geprüft`; do not invent values.

## Verification gate

Run and report:

- Python compile;
- full backend pytest;
- full frontend Vitest;
- frontend build;
- source secret scan;
- frontend build secret scan using real token regexes, not broad substrings like `sk-` that match CSS (`mask-background`);
- Git-safety focused on changed files/diff as well as repo policy, because legacy synthetic CSV fixtures may already exist;
- browser/Tailscale sanity for all pilot URL-filter pages plus Command Center;
- `git diff --check`;
- commit, push, and remote-hash equality.

## Browser sanity checklist

For each pilot page, open a URL with query params and verify:

- page content renders;
- input/select state reflects query values;
- URL remains canonical after initial sync;
- reload preserves filters;
- no console errors.

Use the browser-visible Tailscale origin for Vue/FastAPI checks, not phone-local or agent-local `127.0.0.1`.