From 927a77d68b138778690d380fe7931cc37ce06c9e Mon Sep 17 00:00:00 2001
From: Tyler Hoang <tyler@tylerhoang.xyz>
Date: Tue, 19 May 2026 00:41:35 -0700
Subject: feat: fetch valuation on overview tab, add DCF block to
 ValuationOverviewCard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 .../components/prism/ValuationOverviewCard.tsx     | 44 ++++++++++++++++++++--
 1 file changed, 41 insertions(+), 3 deletions(-)

(limited to 'frontend/components/prism/ValuationOverviewCard.tsx')

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>
   );
 }
-- 
cgit v1.3-2-g0d8e

