Skip to content

feat(THI-52): modernize results tab - #22

Merged
tdody merged 1 commit into
mainfrom
thibaultdody/thi-52-results-tab-headline-strip-projection-table-sensitivity
Apr 19, 2026
Merged

feat(THI-52): modernize results tab#22
tdody merged 1 commit into
mainfrom
thibaultdody/thi-52-results-tab-headline-strip-projection-table-sensitivity

Conversation

@tdody

@tdody tdody commented Apr 19, 2026

Copy link
Copy Markdown
Owner

Summary

Rebuilds the Results tab (1400 → 2088 lines, most of the growth from splitting the monolith into named helper components) around the THI-45/46 design system. Introduces two new shared primitives and restyles every section around tokens.

New primitives

  • shared/SensitivityCard.tsx — labeled card wrapping an SVG line chart with accent stroke, token-colored axes, dashed zero line. Takes {label,value}[] plus optional yLabel: "$" | "%".
  • shared/ExpenseBreakdown.tsx — stacked horizontal bar + legend. Sorts breakdown descending, assigns distinct oklch colors per slice, shows each category's $ and % in the legend.

Headline strip (3-col)

Per the ticket: Annual Cashflow (emphasis=large serif), IRR (from irr_with_exit when projections are loaded, else projections' operating IRR), Break-even Occupancy with warn tone above 70%. Followed by two secondary strips covering Cash-on-Cash, Cap Rate, DSCR, NOI, Gross Yield, Year-1 ROI, After-Tax Monthly, Guest Cost per Night.

10-year projection table with inline bars

ProjectionTable helper — each row's Cashflow cell now renders a thin horizontal bar below the value, width proportional to |value| / max, colored accent for positive and negative for negative. All existing columns preserved (Gross / NOI / Cashflow / Cum CF / CoC / Value / Balance / Equity / After-tax).

Sensitivity

  • STR occupancy + rate sweeps → two <SensitivityCard> in a 2-col grid
  • LTR vacancy + rent (in the comparison section) → same primitive
  • Hold-period IRR sweep → SensitivityCard with yLabel="%"

Expense breakdown

STR and LTR expense sections now use <ExpenseBreakdown> for a stacked bar + legend, paired with a compact housing-costs mini-table on the right.

Other modernization

  • Every MetricCard usage → MetricCell / MetricStrip
  • Warnings (DSCR, occupancy, rental delay) → internal WarnBanner / InfoBanner helpers using warn / accent tokens
  • All collapsibles (tax analysis, monthly cashflow, projections + exit, amortization, STR↔LTR comparison) → shared Disclosure helper with caret + serif title
  • MonthlyCashflowChart rewritten to use token colors and JetBrains Mono axis labels
  • Scenario selector uses the same bottom-rule select style as the Financing tab's loan-type picker, plus a caps pill for rental type
  • All indigo / violet / sky / emerald / amber / red / slate classes removed

Preserved behavior

  • Data fetching unchanged (getResults / getResultsForScenario / getLTRResults / getSensitivity / getLTRSensitivity / getAmortization / getProjections / getMonthlyBreakdown)
  • Scenario selector, STR↔LTR comparison, monthly breakdown, exit analysis, hold-period sweep, amortization, depreciation, tax analysis all still present and fetched on-demand via collapsibles
  • ResultsTab prop interface unchanged

Test plan

  • npm run lint — clean
  • npm run build — passes, CSS 52.09 kB, JS 454.97 kB (-9 kB), 118 modules
  • Dev run, STR: headline strip populates from active scenario, secondary strips match prior values; sensitivity cards render in 2-col grid
  • Dev run, STR: expand Projections → see the 10-year table with inline bars; expand Exit Analysis nested inside
  • Dev run, LTR: headline strip shows Annual CF / Cap / DSCR; expense breakdown renders; amortization expands
  • Dev run, STR: expand Compare STR vs LTR → table highlights the winner per row; LTR sensitivity cards render
  • Dark mode: all strips, tables, sensitivity charts, stacked bar, warnings swap tokens correctly

🤖 Generated with Claude Code

Rebuild the Results tab around the THI-45/46 design system, introducing
two new shared primitives and restyling every section around tokens.

New primitives
- shared/SensitivityCard.tsx: labeled card wrapping an SVG line chart with
  accent stroke, token-colored axes, and zero line. Takes an array of
  {label,value} points plus an optional $ | % yLabel.
- shared/ExpenseBreakdown.tsx: stacked horizontal bar + legend. Sorts the
  breakdown descending, assigns distinct oklch colors per slice, and shows
  each category's $ amount and percentage in the legend.

Headline
- 3-cell MetricStrip at top: Annual Cashflow (emphasis=large serif),
  IRR (from irr_with_exit when available, else projections' operating IRR),
  Break-even Occupancy with warn tone above 70%
- Secondary 4-cell strip: Cash-on-Cash, Cap Rate, DSCR, NOI
- Tertiary strip: Gross Yield, Year-1 ROI, optional ROI-w-appreciation or
  After-Tax Monthly, Guest Cost per Night

10-year projection table with inline bars
- ProjectionTable component: each row's Cashflow cell now includes a thin
  horizontal bar below the value, width proportional to |value| / max,
  colored accent (positive) or negative. Table preserves every existing
  column (Gross / NOI / Cashflow / Cum CF / CoC / Value / Balance / Equity
  / After-tax).

Sensitivity
- STR occupancy + rate sweeps now render as two <SensitivityCard> side by
  side. LTR sensitivity (vacancy + rent) in the comparison section uses
  the same primitive. Hold-period IRR sweep uses SensitivityCard with
  yLabel="%".

Expense breakdown
- STR and LTR expense sections use <ExpenseBreakdown> for a stacked bar +
  legend view, paired with a compact housing-costs mini-table on the right.

Other modernization
- Replaced every MetricCard usage with MetricCell / MetricStrip
- Warnings (DSCR, occupancy, rental delay) become token-based
  WarnBanner / InfoBanner helpers (internal to the file)
- Collapsible sections (tax analysis, monthly cashflow, projections + exit,
  amortization, STR/LTR comparison) consolidated into a shared Disclosure
  helper with caret + serif title
- MonthlyCashflowChart rewritten to use accent / negative / rule-strong
  tokens and JetBrains Mono axis labels
- Scenario selector now uses the same bottom-rule select style as the
  Financing tab's loan-type picker, plus a RentalBadge-style caps pill
- All indigo / violet / sky / emerald / amber / red / slate classes removed

Preserved behavior
- All data-fetching logic unchanged (getResults / getResultsForScenario /
  getLTRResults / getSensitivity / getLTRSensitivity / getAmortization /
  getProjections / getMonthlyBreakdown) with identical loading flow
- Scenario selector, STR ↔ LTR comparison, monthly breakdown, exit analysis,
  hold-period sweep, amortization, depreciation, tax analysis all still
  present and fetched on-demand via collapsibles
- Prop interface for ResultsTab unchanged

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tdody
tdody merged commit 2619bd7 into main Apr 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant