Skip to content
Ritesh FirodiyaGet in touch

Work / Chitragupt / Wiki / Decisions

2026-08-07-expense-portfolio-wireframe-alignment

Decisioncanonicalverified 2026-08-07

DECISION.2026-08-07.EXPENSE-PORTFOLIO-WIREFRAME-ALIGNMENT

Expense + Portfolio React pages rebuilt to match every wireframe variant, including Family-tier

Decision

Audited all wireframe variants under .context/designs/web/{expense,portfolio}/ (7 for Expense: review, transactions, categorise-modal, empty, locked, family, family-locked; 9 for Portfolio: review, review-cg-booked, holdings, map-goal-modal, empty, locked, family, family-locked, view-only) against the live React implementation and closed every gap found, component-for-component, with real derived data — no hardcoded numbers, no fabricated AI copy, no placeholder ML confidence scores.

Scope, per user direction: initially asked to scope self-tier-only vs. full family-tier; user chose "everything including Family-tier." When the CG-booked lot-classification wireframe turned out to need per-trade data the broker-cg parser doesn't extract (only aggregate STCG/LTCG totals — no per-trade line items to classify into FIFO/specific lots), user chose to skip it rather than fabricate or silently drop it.

Expense pillar — categorise-modal rebuilt with real merchant-history-frequency suggestions (computeSuggestions) instead of fake ML; transactions page rebuilt with pagination, real per-merchant quick-tags, a working merchant-rule apply path (found and fixed a real bug: expense_category_overrides were written but never consulted at parse time — wired through MapperContext → bank-statement.ts); cashflow chart got real ▲/▼ markers per the wiki's drew-from-balance/deficit definitions; a NaN-poisoned donut bug fixed (Zod's z.record(enum, optional()) fills every enum key as undefined for absent categories — paise <= 0 silently let undefined through).

Portfolio pillar — holdings page deduped to latest-position-per-holding (positions intentionally keep one row per (holding, AY) for history; the holdings table wasn't collapsing that); fixed a +239759.7% YTD bug (EPF/PPF zero-cost-basis positions poisoning a (nav-cost)/cost ratio, same root-cause pattern as an earlier fix elsewhere that hadn't been applied to this call site); Realised CG section fixed to always render instead of returning null for zero LTCG; goal funding-pace made observation-only (no directive verbs, per the read-only-review invariant).

Family-tier backend + frontend built from scratch for both pillars — getFamilyExpenseOverview (category/monthly-series/OTT-overlap/top-merchant aggregation, all scope-gated per member) and getFamilyPortfolioOverview (per-member NAV/asset-class/realised-CG/§112A-headroom, reading each member's own already-deduped portfolio_reviews/{ay} doc rather than re-deriving from raw positions) — with matching React components (household donuts, stacked-by-member charts, per-member × asset-class tables, cross-PAN CG headroom). The household §112A headroom is a real sum of each granted member's own exemption row, never a hardcoded N × ₹1.25L.

This build surfaced a follow-up bug, logged separately: 2026-08-07-family-member-gating-bug-fix — the family overview callables and the client unlock check both assumed the caller was always the head, so a non-head "member" could never actually reach this view. Fixed same day.

Why

User-reported: "UX still does not match our html wireframes for expense and portfolio" — a systematic gap audit rather than one-off bug fixes, because prior single-bug fixes (double-counted foreign income, family-member bank doc leak, mismapped family transfer — see 2026-08-06-expense-portfolio-double-counting-round-3) kept surfacing more variants that had never been built at all (family-tier was entirely missing on both pillars before this session).

Impact

  • apps/website/src/app/(app)/expense/components/ExpensePage.tsx, .../expense/transactions/page.tsx, .../expense/components/CategoriseModal.tsx and related components — full rewrite to match all 7 Expense variants.
  • apps/website/src/app/(app)/portfolio/components/PortfolioPage.tsx, .../portfolio/holdings/page.tsx, .../portfolio/components/MapGoalModal.tsx and related components — full rewrite to match 8 of 9 Portfolio variants (CG-booked lot-classification explicitly scoped out).
  • apps/functions/src/family/overview.ts, packages/shared/src/contracts/family.ts — new getFamilyExpenseOverview / getFamilyPortfolioOverview callables and contracts.
  • pillar-expense, pillar-portfolio — NOT yet re-synced against the new family-tier widgets and component names introduced this session; a /wiki-lint or /sync-check pass is recommended as a follow-up before treating those entity pages as current.
  • CG-booked lot-classification (flagged lots needing FIFO/specific-lot classification) remains unbuilt — needs apps/functions/src/.../broker-cg.ts mapper work to extract per-trade line items before any UI can be built on top of it. Not a silent gap: the Portfolio review page's CG section renders its real aggregate STCG/LTCG data; only the lot-classification drill-down is missing.

Status

Active

Sources

Every project of mine is written down like this.

Read the résumé