import type { Metadata } from 'next'; import { PageHeader } from '@/components/ui/page-header'; import { Segmented } from '@/components/ui/tabs'; import { Card, EmptyState } from '@/components/ui/primitives'; import { Treemap } from '@/components/charts/treemap'; import { getMarketRows } from '@/lib/queries/markets'; import { spEnum, sp1, type SP } from '@/lib/search-params'; import { getCategory } from '@rareindex/taxonomy'; export const metadata: Metadata = { title: 'Market map', description: 'Heatmap of collectible category performance: area by 30-day volume or tracked assets, colour by period change.' }; export const revalidate = 300; const PERIODS = ['1d', '7d', '30d', '1y'] as const; const SIZES = ['volume', 'assets', 'sales'] as const; export default async function MarketMapPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; const period = spEnum(sp, 'p', PERIODS, '30d'); const size = spEnum(sp, 'size', SIZES, 'volume'); const parent = sp1(sp, 'family') ?? null; const rows = await getMarketRows(parent); const key = period === '1d' ? 'change1d' : period === '7d' ? 'change7d' : period === '1y' ? 'change1y' : 'change30d'; const nodes = rows .filter((r) => r.counts.assets > 0) .map((r) => { const vol = r.counts.volume30dUsd ?? 0; const s = size === 'volume' ? vol : size === 'sales' ? r.counts.sales : r.counts.assets; return { id: r.node.slug, label: r.node.name, size: s, change: r.snapshot?.[key] ?? null, href: r.node.children.length && !parent ? `/market-map?p=${period}&size=${size}&family=${r.node.slug}` : `/markets/${r.node.slug}`, sublabel: `${r.counts.assets.toLocaleString('en-US')} assets · ${r.counts.sales.toLocaleString('en-US')} sales`, sizeLabel: size === 'volume' ? '30D volume ($)' : size === 'sales' ? 'sales' : 'tracked assets' }; }) .filter((n) => n.size > 0); const withChange = nodes.filter((n) => n.change !== null).length; const q = (p: string, s: string) => `/market-map?p=${p}&size=${s}${parent ? `&family=${parent}` : ''}`; return (
({ id: p, label: p.toUpperCase(), href: q(p, size) }))} /> ({ id: s, label: s === 'volume' ? 'Volume' : s === 'sales' ? 'Sales' : 'Assets', href: q(period, s) }))} /> } /> {nodes.length ? ( <>

{nodes.length} categories with data · {withChange} with a computed {period.toUpperCase()} change{size === 'volume' && nodes.every((n) => n.size === 0) ? ' · no volume yet, showing assets' : ''}.

) : ( )}
); }