import type { Metadata } from 'next'; import Link from 'next/link'; import { PageHeader } from '@/components/ui/page-header'; import { Card, CardHeader, EmptyState, Stat } from '@/components/ui/primitives'; import { Segmented } from '@/components/ui/tabs'; import { LineChart } from '@/components/charts/line-chart'; import { HistoryTab } from '@/components/asset/asset-tabs'; import { getAssetBySlug, getAssetSalePoints, getAssetVariants, rankedAssets } from '@/lib/queries/assets'; import { searchAssets } from '@rareindex/search'; import { fmtMoney, fmtNum, confidenceLabel } from '@/lib/format'; import { catName, gradeLabel } from '@/lib/taxonomy'; import { sp1, spEnum, type SP } from '@/lib/search-params'; import { AssetList } from '@/components/market/asset-list'; export const metadata: Metadata = { title: 'Price history', description: 'Search any tracked collectible and view its full price history: sales, RareIndex Valuation, lowest ask and volume.' }; const PERIODS = ['1y', '2y', '5y', 'all'] as const; /** ISO day string N days ago ('' for no cutoff); kept outside components so render stays pure. */ function windowCutoffIso(days: number | null): string { if (!days) return ''; return new Date(Date.now() - days * 86_400_000).toISOString().slice(0, 10); } export default async function PriceHistoryPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; const slug = sp1(sp, 'asset') ?? null; const q = sp1(sp, 'q') ?? ''; const period = spEnum(sp, 'p', PERIODS, '2y'); const vId = sp1(sp, 'v') ?? null; const asset = slug ? await getAssetBySlug(slug) : null; return (
{asset ? ( ) : q ? ( ) : ( )}
); } async function Selected({ asset, period, vId }: { asset: NonNullable>>; period: (typeof PERIODS)[number]; vId: string | null }) { const variants = await getAssetVariants(asset.id); const variant = vId ? variants.find((v) => v.id === vId) ?? null : null; const points = await getAssetSalePoints(asset.id, 5000); const days = period === '1y' ? 365 : period === '2y' ? 730 : period === '5y' ? 1826 : null; const cutoff = windowCutoffIso(days); const scatter = points.filter((p) => (!variant || p.variantId === variant.id) && p.date >= cutoff); const href = (p: string, v: string | null) => `/price-history?asset=${asset.slug}&p=${p}${v ? `&v=${v}` : ''}`; return (
{asset.title}

{catName(asset.categorySlug)} {variant ? ` · ${variant.label}` : ' · all variants'}

({ id: p, label: p.toUpperCase(), href: href(p, variant?.id ?? null) }))} /> {variants.length ? ({ id: v.id, label: v.label, href: href(period, v.id) }))]} /> : null}
p.usd))) : '—'} /> p.usd))) : '—'} />
({ x: p.date, y: p.usd, meta: `${gradeLabel(p.grader, p.grade) ?? 'raw'} · ${p.sourceName}` })) }]} emptyLabel="No verified sales in this window" />
); } async function Results({ q }: { q: string }) { const res = await searchAssets(q, { limit: 20 }); return ( {res.hits.length ? (
    {res.hits.map((h) => (
  • {h.title} {catName(h.categorySlug)} · {h.salesCount} sales {h.rivUsd === null ? '—' : fmtMoney(h.rivUsd)}
  • ))}
) : ( )}
); } async function Popular() { const items = await rankedAssets('volume', { limit: 20 }); return ( ); }