diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 01:35:43 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 01:35:43 -0700 |
| commit | 3ca117ed7f811e695861a03f25afad5aea790829 (patch) | |
| tree | b62d1a4545d2f4135eaa53028b6ffe7b6235ef72 /frontend/components/prism | |
| parent | 49907ef9991d2f13d53bb02fa648c8a06fbfe80a (diff) | |
feat: add ValuationPage data-fetch wrapper
Diffstat (limited to 'frontend/components/prism')
| -rw-r--r-- | frontend/components/prism/ValuationPage.tsx | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/frontend/components/prism/ValuationPage.tsx b/frontend/components/prism/ValuationPage.tsx new file mode 100644 index 0000000..3a3ba27 --- /dev/null +++ b/frontend/components/prism/ValuationPage.tsx @@ -0,0 +1,61 @@ +"use client"; +import { useEffect, useState } from "react"; +import { api } from "@/lib/api"; +import { buildKpis } from "@/lib/overview"; +import { ValuationCard } from "@/components/prism/ValuationCard"; +import { KPIStrip } from "@/components/prism/KPIStrip"; +import { TickerHeader } from "@/components/prism/TickerHeader"; +import type { TickerOverview, ValuationResponse } from "@/types/api"; + +type ValState = "loading" | "ready" | "error"; + +type Props = { + ticker: string; + overview: TickerOverview; + isSaved: boolean; + onToggleWatchlist: () => void; +}; + +export function ValuationPage({ ticker, overview, isSaved, onToggleWatchlist }: Props) { + const [data, setData] = useState<ValuationResponse | null>(null); + const [valState, setValState] = useState<ValState>("loading"); + const kpis = buildKpis(overview); + + useEffect(() => { + let cancelled = false; + setValState("loading"); + setData(null); + + api + .valuation(ticker) + .then((res) => { + if (!cancelled) { + setData(res); + setValState("ready"); + } + }) + .catch(() => { + if (!cancelled) setValState("error"); + }); + + return () => { + cancelled = true; + }; + }, [ticker]); + + return ( + <> + <TickerHeader overview={overview} isSaved={isSaved} onToggleWatchlist={onToggleWatchlist} /> + <KPIStrip items={kpis} /> + {valState === "loading" && ( + <section className="psm-card psm-skeleton" style={{ minHeight: 320 }} /> + )} + {valState === "error" && ( + <section className="psm-card"> + <p className="psm-muted-copy">Valuation data unavailable for {ticker}.</p> + </section> + )} + {valState === "ready" && data && <ValuationCard data={data} />} + </> + ); +} |
