summaryrefslogtreecommitdiff
path: root/frontend/components
diff options
context:
space:
mode:
authorTyler Hoang <tyler@tylerhoang.xyz>2026-05-19 00:41:35 -0700
committerTyler Hoang <tyler@tylerhoang.xyz>2026-05-19 00:41:35 -0700
commit927a77d68b138778690d380fe7931cc37ce06c9e (patch)
tree050e8bec85837dd6db98188c88349959a1925040 /frontend/components
parent0a6fa2a6566a6d20b9cd587f1d188869971871c5 (diff)
feat: fetch valuation on overview tab, add DCF block to ValuationOverviewCard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'frontend/components')
-rw-r--r--frontend/components/prism/ValuationOverviewCard.tsx44
1 files changed, 41 insertions, 3 deletions
diff --git a/frontend/components/prism/ValuationOverviewCard.tsx b/frontend/components/prism/ValuationOverviewCard.tsx
index f85e443..9e59787 100644
--- a/frontend/components/prism/ValuationOverviewCard.tsx
+++ b/frontend/components/prism/ValuationOverviewCard.tsx
@@ -1,11 +1,15 @@
-import type { TickerOverview } from "@/types/api";
-import { fmtCurrency, fmtLarge, fmtNumber } from "@/lib/format";
+import type { TickerOverview, ValuationResponse } from "@/types/api";
+import { fmtCurrency, fmtLarge, fmtNumber, fmtPct } from "@/lib/format";
+
+type ValState = "idle" | "loading" | "ready" | "error";
type Props = {
overview: TickerOverview;
+ valuation: ValuationResponse | null;
+ valState: ValState;
};
-export function ValuationOverviewCard({ overview }: Props) {
+export function ValuationOverviewCard({ overview, valuation, valState }: Props) {
const rows = [
{ 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 },
@@ -17,6 +21,8 @@ export function ValuationOverviewCard({ overview }: Props) {
];
const visible = rows.filter((r) => !r.missing);
+ const dcf = valuation?.dcf;
+ const showDcf = dcf?.available && dcf.intrinsic_value_per_share != null;
return (
<section className="psm-card">
@@ -36,6 +42,38 @@ export function ValuationOverviewCard({ overview }: Props) {
))}
</div>
)}
+ {valState === "loading" && (
+ <>
+ <hr className="psm-divider" />
+ <div className="psm-stat-list">
+ <div className="psm-stat-row">
+ <span className="psm-stat-label psm-skeleton" style={{ width: "120px", height: "14px", display: "inline-block" }} />
+ <span className="psm-stat-value psm-skeleton" style={{ width: "60px", height: "14px", display: "inline-block" }} />
+ </div>
+ <div className="psm-stat-row">
+ <span className="psm-stat-label psm-skeleton" style={{ width: "100px", height: "14px", display: "inline-block" }} />
+ <span className="psm-stat-value psm-skeleton" style={{ width: "48px", height: "14px", display: "inline-block" }} />
+ </div>
+ </div>
+ </>
+ )}
+ {valState === "ready" && showDcf && dcf && (
+ <>
+ <hr className="psm-divider" />
+ <div className="psm-stat-list">
+ <div className="psm-stat-row">
+ <span className="psm-stat-label">DCF Intrinsic Value</span>
+ <span className="psm-stat-value">{fmtCurrency(dcf.intrinsic_value_per_share)}</span>
+ </div>
+ {dcf.growth_rate_used != null && (
+ <div className="psm-stat-row">
+ <span className="psm-stat-label">FCF Growth Used</span>
+ <span className="psm-stat-value">{fmtPct(dcf.growth_rate_used)}</span>
+ </div>
+ )}
+ </div>
+ </>
+ )}
</section>
);
}