# FastAPI + Vue Dashboard Transition Pattern

Use when moving the FinanceManager UI beyond Streamlit while preserving the existing ledger/runtime safety model.

## Architecture decision

- Keep the Python finance core and SQLite runtime DB as the source of truth.
- Add FastAPI as a thin, read-first API boundary over existing dashboard read models/services.
- Add Vue 3 + TypeScript + Vite as the future premium User Mode frontend.
- Keep Streamlit as Admin/Operations/Fallback until the Vue slice is accepted.
- Do not rewrite Streamlit during the transition unless explicitly required.

## Backend v0 slice

Start with read-only endpoints and DTO contracts before any frontend write flows:

- `/api/health`
- `/api/runtime/status`
- `/api/provider/status` with sanitized provider state only
- `/api/overview`
- `/api/crypto/positions`
- `/api/crypto/positions/{asset_id}` for read-only detail drawers when useful
- `/api/equity/positions`
- `/api/equity/positions/{position_id}` for read-only detail drawers when useful
- `/api/cash/summary`
- `/api/wallets` when crypto wallet allocation is a visible User Mode page
- `/api/reports` for listing existing runtime-generated reports only; Vue v0 must not create reports or expose raw paths unnecessarily in summaries

Recommended package shape:

- `src/jarvis_finance/api/main.py`
- `src/jarvis_finance/api/dependencies.py`
- `src/jarvis_finance/api/routers/`
- `src/jarvis_finance/api/schemas/`
- `src/jarvis_finance/services/`

DTOs should use explicit string amounts for `Decimal` values. For the first Vue User Mode slice, prefer flat, browser-friendly DTOs over nested finance-core objects:

- Overview fields: `total_value_chf`, `crypto_value_chf`, `equity_value_chf`, `cash_value_chf`, `unpriced_positions_count`, `critical_alerts_count`, `last_price_update`, `data_quality_status`.
- Crypto rows: `asset_id`, `name`, `symbol`, `quantity_total`, `price_chf`, `market_value_chf`, `portfolio_share_pct`, `wallet_count`, `price_status`, `last_price_update`, `coingecko_url`.
- Equity rows: `position_id`, `name`, `ticker`, `isin`, `account`, `asset_class`, `quantity`, `currency`, `price`, `market_value_chf`, `status`.
- Cash rows and summaries should also transmit Decimal-like values as strings.
- Keep nested `Money { amount, currency, display }` / `Quantity { amount, unit, display }` useful for backend/admin contracts, but do not force the v0 Vue table layer to unwrap nested objects for every cell.
- Read DTOs may later add audit events, report metadata, and transaction preview/confirm contracts, but write/confirm contracts are out of scope for the first read-only slice.

## Frontend v0 slice

Build only the minimum useful User Mode first. For accepted User Mode vertical slices, local FastAPI/runtime data may auto-load on page mount, but this means **local read-only endpoints only**. Provider refreshes, price lookups, report generation, imports, or writes remain explicit-click/CLI/scheduled actions and must not run during normal render.

1. Vite + Vue 3 + TypeScript scaffold under `frontend/`.
2. API client against FastAPI read-only endpoints.
3. Command Center cards.
4. Crypto positions table with row-click detail panel and wallet allocation detail.
5. Portfolio, Equity, Cash/Wallets read-only tables or summary pages.
6. Reports placeholder page that says reports remain CLI/Streamlit/runtime-generated; do not generate reports from Vue v0.
7. No writes, no auth, no deployment in this first vertical slice unless explicitly authorized.

Recommended v0 file shape:

- `frontend/package.json`, `vite.config.ts`, `tsconfig.json`, `index.html`, `postcss.config.js`, `tailwind.config.ts`
- `frontend/src/main.ts`, `App.vue`, `router/index.ts`, `style.css`, `env.d.ts`
- `frontend/src/api/{client,types,portfolio,crypto,equity,cash}.ts`
- `frontend/src/components/{AppLayout,SidebarNav,SummaryCard,DataStatusBadge,MoneyValue,QuantityValue,PositionTable,DetailDrawer}.vue`
- `frontend/src/pages/{CommandCenter,CryptoPage,PortfolioPage,EquityPage,WalletsPage,ReportsPage}.vue`

## Security and Git-safety additions

Finance repos that add a frontend must block generated and secret-bearing frontend artifacts:

- `frontend/dist/`
- `frontend/node_modules/`
- `frontend/.vite/` or any nested `.vite/` cache
- `frontend/.env`
- `frontend/.env.*` except `frontend/.env.example`
- `frontend/*.tsbuildinfo`

Allow-list only source/config files that should be committed, e.g. `frontend/package.json`, `frontend/package-lock.json`, `frontend/tsconfig.json`, Vite/Tailwind/PostCSS config, and `frontend/src/**`.

Provider keys must stay backend/runtime-only. Never place OpenFIGI/FMP/Twelve/Finnhub/etc. keys in `VITE_*` variables or browser-visible config.

## Test pattern

- Use `fastapi.testclient.TestClient` for API contract tests.
- If tests inject an in-memory SQLite connection into FastAPI dependencies, create it with `check_same_thread=False` because FastAPI/TestClient executes sync endpoint functions in a worker thread.
- Also configure the normal SQLite runtime connection with `check_same_thread=False` when FastAPI dependencies yield a connection into sync endpoints; otherwise browser/dev-server smoke tests may pass app startup but fail endpoint calls with SQLite thread-affinity errors.
- Assert Decimal-like values are serialized as strings, not floats.
- Assert flat Vue-facing contract names for overview/crypto/equity/cash/wallet payloads; avoid silently regressing to nested backend-only models.
- Assert detail endpoints return enough read-only data for drawers without exposing write controls.
- Assert read-only contract by checking missing/blocked write routes return non-success.
- Assert provider status payloads do not contain `api_key`, `token`, `secret`, raw URLs, or secret values.
- For Vue, add small Vitest/jsdom tests for API base URL handling, shell navigation, Command Center summary rendering, and row-click detail drawer behavior.
- Browser-smoke the dev server after build/tests: visit Command Center, Crypto, Equity, Wallets, and Reports; click at least one Crypto row and one Equity row and verify the detail drawer appears. Also inspect browser resource entries/console to verify normal render made zero external provider calls. Do not paste real financial values into the chat report.

## Verification sequence

Before staging/commit/push:

```bash
rm -rf .pytest_cache frontend/dist frontend/node_modules $(find . -type d -name __pycache__ -print)
python -m compileall -q src tests
pytest -q
cd frontend && npm install && npm run test && npm run build && cd -
# Optional but recommended after build: scan built frontend for obvious secret terms.
if [ -d frontend/dist ]; then
  ! grep -R -E -i "(OPENFIGI|API[_-]?KEY|TOKEN|SECRET|github_pat_|gh[pousr]_)" frontend/dist
fi
rm -rf .pytest_cache frontend/dist frontend/node_modules $(find . -type d -name __pycache__ -print)
python - <<'PY'
from jarvis_finance.quality.git_safety import assert_safe
assert_safe('.')
print('git_safety_ok')
PY
git diff --check
git status --short
```

Then stage only code/docs/tests, commit, push, and verify local and remote hashes match.

## Pitfalls

- Do not call live providers from normal Vue or Streamlit render paths; provider refresh stays explicit-click/CLI/scheduled.
- Do not expose provider secrets or raw provider diagnostics through `/api/provider/status`.
- Do not introduce browser-side API keys via `VITE_*`.
- Do not treat Streamlit replacement as a license to rewrite the working operations UI in the same slice.
- Do not serialize Decimal values as JSON numbers; use strings to avoid precision loss.
- Do not run Git-safety after tests without cleaning caches first; tests recreate `.pytest_cache` and `__pycache__`.
- Do not stage `frontend/dist`, `frontend/node_modules`, `.vite`, or `*.tsbuildinfo`; build/typecheck tools may create them even when tests pass.
- Do not rely on `git status --short frontend` alone to prove ignored generated artifacts are safe; use `--ignored=matching` or the repo Git-safety scanner when changing ignore rules.
- In Vue SFC generic components, prefer assigning `const emit = defineEmits<...>()` and calling `emit(...)` from templates when inline `$emit` does not update state as expected in tests/browser smoke.
- Do not stop at a successful `git push`; verify `git rev-parse HEAD` equals authenticated `git ls-remote origin refs/heads/main`. If the FinanceManager token CSV has a URL line and a token line, extract only the `github_pat_`/`ghp_` line and use a temporary HTTP auth header for both push and remote-hash verification; never print the token or set a token-bearing remote.
