From f024c46e874b3cacb7af5bf96aec376b88b86156 Mon Sep 17 00:00:00 2001 From: Tyler Hoang Date: Tue, 19 May 2026 00:35:20 -0700 Subject: feat: replace StatsCard with QualityCard (margins, returns, leverage) Co-Authored-By: Claude Sonnet 4.6 --- frontend/app/page.tsx | 62 ++----------------------------- frontend/components/prism/QualityCard.tsx | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 59 deletions(-) create mode 100644 frontend/components/prism/QualityCard.tsx (limited to 'frontend') diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index d6bde2a..6253221 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -11,9 +11,10 @@ import { KPIStrip } from "@/components/prism/KPIStrip"; import { Sidebar } from "@/components/prism/Sidebar"; import { TickerHeader } from "@/components/prism/TickerHeader"; import { TopBar } from "@/components/prism/TopBar"; +import { QualityCard } from "@/components/prism/QualityCard"; import { VolumeCard } from "@/components/prism/VolumeCard"; import { ApiError, api } from "@/lib/api"; -import { deltaClass, fmtCurrency, fmtLarge, fmtNumber, fmtPct } from "@/lib/format"; +import { deltaClass, fmtNumber, fmtPct } from "@/lib/format"; import { buildKpis, limitIndices, marketClock, OVERVIEW_NAV_ITEMS, signalTone } from "@/lib/overview"; import type { HistoryPoint, MarketIndex, SearchResult, TickerOverview, WatchlistResponse } from "@/types/api"; @@ -316,7 +317,7 @@ function OverviewClient() {
- +
@@ -496,54 +497,6 @@ function ShortInterestCard({ overview }: { overview: TickerOverview }) { ); } -function StatsCard({ overview }: { overview: TickerOverview }) { - const referenceRows = [ - { label: "Market Cap", value: fmtLarge(overview.stats.market_cap), missing: overview.stats.market_cap == null }, - { label: "P/E TTM", value: overview.stats.trailing_pe == null ? "-" : `${fmtNumber(overview.stats.trailing_pe)}x`, missing: overview.stats.trailing_pe == null }, - { label: "EPS TTM", value: fmtCurrency(overview.stats.trailing_eps), missing: overview.stats.trailing_eps == null }, - { label: "P/B", value: overview.ratios.price_to_book == null ? "-" : `${fmtNumber(overview.ratios.price_to_book)}x`, missing: overview.ratios.price_to_book == null }, - { label: "P/S", value: overview.ratios.price_to_sales == null ? "-" : `${fmtNumber(overview.ratios.price_to_sales)}x`, missing: overview.ratios.price_to_sales == null }, - { label: "EV/Sales", value: overview.ratios.ev_to_sales == null ? "-" : `${fmtNumber(overview.ratios.ev_to_sales)}x`, missing: overview.ratios.ev_to_sales == null }, - { label: "EV/EBITDA", value: overview.ratios.ev_to_ebitda == null ? "-" : `${fmtNumber(overview.ratios.ev_to_ebitda)}x`, missing: overview.ratios.ev_to_ebitda == null }, - { label: "Gross Margin", value: fmtPct(overview.ratios.gross_margin_ttm), missing: overview.ratios.gross_margin_ttm == null }, - { label: "Op Margin", value: fmtPct(overview.ratios.operating_margin_ttm), missing: overview.ratios.operating_margin_ttm == null }, - { label: "Net Margin", value: fmtPct(overview.ratios.net_margin_ttm), missing: overview.ratios.net_margin_ttm == null }, - { label: "ROE", value: fmtPct(overview.ratios.roe_ttm), missing: overview.ratios.roe_ttm == null }, - { label: "ROA", value: fmtPct(overview.ratios.roa_ttm), missing: overview.ratios.roa_ttm == null }, - { label: "ROIC", value: fmtPct(overview.ratios.roic_ttm), missing: overview.ratios.roic_ttm == null }, - { label: "D/E", value: overview.ratios.debt_to_equity == null ? "-" : `${fmtNumber(overview.ratios.debt_to_equity)}x`, missing: overview.ratios.debt_to_equity == null }, - { label: "Current Ratio", value: overview.ratios.current_ratio == null ? "-" : `${fmtNumber(overview.ratios.current_ratio)}x`, missing: overview.ratios.current_ratio == null }, - { label: "Dividend Yield", value: fmtPct(overview.ratios.dividend_yield_ttm), missing: overview.ratios.dividend_yield_ttm == null }, - { label: "Payout Ratio", value: fmtPct(overview.ratios.dividend_payout_ratio_ttm), missing: overview.ratios.dividend_payout_ratio_ttm == null } - ]; - - const visibleRows = referenceRows.filter((r) => !r.missing); - const suppressedCount = referenceRows.length - visibleRows.length; - - return ( -
-
-
-
Reference
-

Reference

-
-
- {visibleRows.length > 0 && ( -
- {visibleRows.map((row) => ( - - ))} -
- )} - {suppressedCount > 0 && ( -

0 ? "var(--sp-4)" : 0 }}> - ยท Statement data incomplete -

- )} -
- ); -} - function DetailItem({ label, value, missing = false }: { label: string; value: string; missing?: boolean }) { return (
@@ -553,15 +506,6 @@ function DetailItem({ label, value, missing = false }: { label: string; value: s ); } -function StatRow({ label, value, missing = false }: { label: string; value: string; missing?: boolean }) { - return ( -
- {label} - {missing ? "Unavailable" : value} -
- ); -} - function LoadingShell() { return ( !r.missing); + if (!visible.length) return null; + + return ( +
+
+
+
Quality
+

Quality

+
+
+
+ {visible.map((row) => ( +
+ {row.label} + {row.value} +
+ ))} +
+
+ ); +} -- cgit v1.3-2-g0d8e