"use client"; import { ErrorBanner } from "../ui/ErrorBanner"; import { LoadingPulse } from "../ui/LoadingPulse"; import { StatCard } from "../ui/StatCard"; import { flags } from "../../lib/flags"; import { useApi } from "../../lib/use-api"; interface HolderRow { name: string; shares: number | null; pct: number | null; change: number | null; value: number | null; } interface OwnershipResponse { ticker: string; available: boolean; source: string | null; institutional_pct: number | null; insider_pct: number | null; float_pct: number | null; institutions: HolderRow[]; insiders: HolderRow[]; } function formatPct(value: number | null): string { return value == null ? "—" : `${value.toFixed(1)}%`; } function formatShares(value: number | null): string { if (value == null) return "—"; const abs = Math.abs(value); if (abs >= 1e9) return `${(value / 1e9).toFixed(2)}B`; if (abs >= 1e6) return `${(value / 1e6).toFixed(1)}M`; if (abs >= 1e3) return `${(value / 1e3).toFixed(1)}K`; return value.toFixed(0); } function formatChange(value: number | null): string { if (value == null) return "—"; const sign = value > 0 ? "+" : ""; return `${sign}${formatShares(value)}`; } function formatValue(value: number | null): string { if (value == null) return "—"; const abs = Math.abs(value); if (abs >= 1e12) return `$${(value / 1e12).toFixed(2)}T`; if (abs >= 1e9) return `$${(value / 1e9).toFixed(1)}B`; if (abs >= 1e6) return `$${(value / 1e6).toFixed(1)}M`; return `$${value.toFixed(0)}`; } function HolderTable({ title, rows }: { title: string; rows: HolderRow[] }) { return (
{title}
{rows.length > 0 ? rows.map((row) => ( )) : ( )}
Holder Shares % Change Value
{row.name} {formatShares(row.shares)} {formatPct(row.pct)} = 0 ? "text-fin-positive" : "text-fin-negative"}`}> {formatChange(row.change)} {formatValue(row.value)}
No holder rows available.
); } function OwnershipBar({ data }: { data: OwnershipResponse }) { const institutional = data.institutional_pct ?? 0; const insider = data.insider_pct ?? 0; const float = data.float_pct ?? Math.max(0, 100 - institutional - insider); const total = institutional + insider + float || 100; const instWidth = (institutional / total) * 100; const insiderWidth = (insider / total) * 100; const floatWidth = Math.max(0, 100 - instWidth - insiderWidth); return (
Institutional Insider Float / Retail
); } export function Ownership({ ticker }: { ticker: string }) { const url = flags.ownership ? `/api/market/ownership/${encodeURIComponent(ticker)}` : null; const { data, loading, error } = useApi(url, { cacheTtlMs: 300_000 }); if (!flags.ownership) return null; return (

Ownership

Institutional and insider holder snapshot{data?.source ? ` via ${data.source}` : ""}.

{loading ? : ( <> {!data?.available && ( )}
{data && }
)}
); }