Work / Chitragupt / Wiki / Decisions
2026-07-02-dashboard-history-callables
Decisioncanonicalverified 2026-07-02
DECISION.2026-07-02.DASHBOARD-HISTORY-CALLABLESDecision
Three new callables + one reference collection power the dashboard chart row:
getTaxHistorySeries(from_ay, to_ay)— returns{ ay, income_paise, tax_paise, position_kind, position_paise }[]for the caller'susers/{uid}/tax_reviews/{ay}subcollection. AYs without a stored review are omitted.getPortfolioHistorySeries(from_fy, to_fy)— returns{ fy, invested_paise, realised_paise, value_paise }[]fromusers/{uid}/portfolio_reviews/{ay}(portfolio-reviews are keyed by AY == FY for this range). Missing FYs omitted;realised_paise= short-term + long-term sum.getNiftyBenchmark(from_fy, to_fy)— readsnifty_benchmarks/{fy}(public read, admin write) and returns end-of-FY Nifty 50 closes for the range.
All three follow the existing onZodCall pattern. Frontend hooks in apps/website/src/store/tax-history.ts and apps/website/src/store/portfolio-history.ts wrap useCallableQuery and expose discriminated-union state consistent with useMarketplaceCas.
The nifty_benchmarks/{fy} collection is seeded via apps/functions/scripts/seed-prod.mjs alongside ay_registry and admin_kpis. Initial seed covers FY 2020-21 through FY 2024-25 with published historical closing values on the last trading day of each FY.
Why
The wireframes for dashboard-populated.md show three charts (Tax journey · Cashflow · Portfolio vs Nifty) that need historical data:
- Tax journey wants five AYs of income + tax paid.
- Portfolio vs Nifty wants per-FY invested / realised / value plus a broad-market benchmark to compare against.
Neither exists at the store level. useTaxReview(ay) and usePortfolioReview(ay) each subscribe to a single AY, and Nifty prices weren't stored anywhere.
Three options considered:
- Ship the charts with placeholder/empty state and defer the callables.
- Ship the callables now; seed Nifty manually.
- Skip the two AY-history charts on the dashboard and keep only the KPI row + cashflow.
We picked (2) — ship the callables now with a manually seeded Nifty table. The alternative left the wireframe's most visible dashboard surface stranded and the chart shells never fill; option (3) is a regression against the wireframe.
The Nifty table is manually maintained until a scheduled Cloud Function ships to refresh it from a market-data feed. Follow-up ticket: automate the refresh.
Impact
- New callables live under
apps/functions/src/tax/history.ts,apps/functions/src/portfolio/history.ts,apps/functions/src/benchmarks/nifty.ts. - Contracts in
packages/shared/src/contracts/{tax,portfolio,benchmarks}.ts. - Schema in
packages/shared/src/schemas/nifty-benchmark.ts. - Rules:
nifty_benchmarks/{fy}public-read, server-write. Update infirebase/firestore.rules. seed-prod.mjsseeds the Nifty rows idempotently.- Every dashboard chart accepts a discriminated-union state and renders placeholder / skeleton / error / chart per the frontend three-states rule.
Related surfaces:
Related concepts:
- concept-three-states
Status
Active.
Sources
- .context/designs/web/home/home.html (chart section reference)
- apps/functions/src/tax/history.ts
- apps/functions/src/portfolio/history.ts
- apps/functions/src/benchmarks/nifty.ts
- apps/functions/scripts/seed-prod.mjs
Every project of mine is written down like this.
Read the résumé