'use client'; import { format } from 'date-fns'; import { Panel } from '@/components/ui/panel'; import { formatCurrency } from '@/lib/format'; import { InteractivePriceChart } from '@/components/charts/interactive-price-chart'; import type { DataSeries, Holding, PriceData } from '@/lib/types'; type PriceHistoryCardProps = { loading: boolean; priceHistory: PriceData | null>; benchmarkHistory: PriceData | null>; quote: PriceData; position: Holding | null; }; function formatLongDate(value: string) { const parsed = new Date(value); return Number.isNaN(parsed.getTime()) ? value : format(parsed, 'MMM dd, yyyy'); } function asFiniteNumber(value: string | number | null | undefined) { if (value === null || value === undefined) { return null; } const parsed = typeof value === 'number' ? value : Number(value); return Number.isFinite(parsed) ? parsed : null; } export function PriceHistoryCard(props: PriceHistoryCardProps) { const priceHistoryValue = props.priceHistory.value; const benchmarkHistoryValue = props.benchmarkHistory.value; const quoteValue = props.quote.value; const firstPoint = priceHistoryValue?.[0]; const lastPoint = priceHistoryValue?.[priceHistoryValue.length - 1]; const inPortfolio = props.position !== null; const hasPriceData = priceHistoryValue !== null && priceHistoryValue.length > 0; const defaultChange = firstPoint && lastPoint ? lastPoint.close - firstPoint.close : null; const defaultChangePct = firstPoint && firstPoint.close > 0 && defaultChange !== null ? (defaultChange / firstPoint.close) * 100 : null; const holdingChange = asFiniteNumber(props.position?.gain_loss); const holdingChangePct = asFiniteNumber(props.position?.gain_loss_pct); const change = inPortfolio ? holdingChange : defaultChange; const changePct = inPortfolio ? holdingChangePct : defaultChangePct; const rangeLabel = inPortfolio ? 'Holding return' : '1Y return'; const changeLabel = inPortfolio ? 'Holding P/L' : '1Y change'; const dateRange = inPortfolio ? props.position?.created_at && (props.position.last_price_at ?? lastPoint?.date) ? `${formatLongDate(props.position.created_at)} to ${formatLongDate(props.position.last_price_at ?? lastPoint?.date ?? '')}` : 'No holding period available' : firstPoint && lastPoint ? `${formatLongDate(firstPoint.date)} to ${formatLongDate(lastPoint.date)}` : 'No comparison range'; const statusToneClass = inPortfolio ? 'text-[#96f5bf]' : 'text-[color:var(--terminal-bright)]'; const performanceToneClass = change !== null && change < 0 ? 'text-[#ff9f9f]' : 'text-[#96f5bf]'; const chartData = priceHistoryValue?.map(point => ({ date: point.date, price: point.close })) ?? []; const benchmarkData = benchmarkHistoryValue?.map((point) => ({ date: point.date, price: point.close })) ?? []; const comparisonSeries: DataSeries[] = [ { id: 'stock', label: 'Stock', data: chartData, color: '#96f5bf', type: 'line' }, { id: 'sp500', label: 'S&P 500', data: benchmarkData, color: '#8ba0b8', type: 'line' } ]; const quoteAvailable = quoteValue !== null && Number.isFinite(quoteValue); const staleIndicator = props.quote.stale ? ' (stale)' : ''; const helperText = quoteAvailable ? `Spot price ${formatCurrency(quoteValue)}${staleIndicator}` : 'Spot price unavailable'; return (
{!hasPriceData && (

Price history data is currently unavailable. This may be due to a temporary issue with the market data provider.

)}

Portfolio status

{inPortfolio ? 'In portfolio' : 'Not in portfolio'}

{helperText}

{changeLabel}

{change === null ? 'n/a' : formatCurrency(change)}

{rangeLabel}

{changePct === null ? 'n/a' : `${changePct.toFixed(2)}%`}

{dateRange}

{hasPriceData && ( format(new Date(date), 'MMM dd, yyyy') }} /> )}
); }