Work / Chitragupt / Wiki / Decisions
2026-08-07-expense-portfolio-wireframe-alignment
Decisioncanonicalverified 2026-08-07
DECISION.2026-08-07.EXPENSE-PORTFOLIO-WIREFRAME-ALIGNMENTExpense + 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.tsxand 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.tsxand 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— newgetFamilyExpenseOverview/getFamilyPortfolioOverviewcallables and contracts.- pillar-expense, pillar-portfolio — NOT yet re-synced against the new family-tier widgets and component names introduced this session; a
/wiki-lintor/sync-checkpass 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.tsmapper 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
- .context/designs/web/expense/*.html
- .context/designs/web/portfolio/*.html
- 2026-08-06-expense-portfolio-double-counting-round-3
- 2026-08-07-family-member-gating-bug-fix
Every project of mine is written down like this.
Read the résumé