import type { Metadata } from 'next'; import Link from 'next/link'; import { PageHeader } from '@/components/ui/page-header'; import { MarketTable } from '@/components/market/market-table'; import { CategoryGrid } from '@/components/market/category-grid'; import { Card, CardHeader } from '@/components/ui/primitives'; import { getCategorySparklines, getMarketRows } from '@/lib/queries/markets'; import { getCategoryThumbnails } from '@/lib/queries/assets'; import { listIndices } from '@/lib/queries/indices'; import { fmtMoney, fmtNum } from '@/lib/format'; export const metadata: Metadata = { title: 'Markets', description: 'Collectible category markets: index performance, sales, volume, listings, liquidity and market capitalization estimates.' }; export const revalidate = 300; export default async function MarketsPage() { const [rows, indices] = await Promise.all([getMarketRows(), listIndices()]); const active = rows.filter((r) => r.counts.assets > 0); const [thumbs, sparklines] = await Promise.all([getCategoryThumbnails(active.map((r) => r.node.slug)), getCategorySparklines(active.map((r) => r.node.slug), 90)]); const totals = active.reduce((a, r) => ({ assets: a.assets + r.counts.assets, priced: a.priced + r.counts.priced, sales: a.sales + r.counts.sales, sales30d: a.sales30d + r.counts.sales30d, volume: a.volume + (r.counts.volume30dUsd ?? 0), listings: a.listings + r.counts.listings }), { assets: 0, priced: 0, sales: 0, sales30d: 0, volume: 0, listings: 0 }); const published = indices.filter((i) => i.latest).length; return (
Market capitalization estimates multiply an estimated population by a representative value and are shown only with their confidence label; they are indicative, never precise (§124). Listing prices are not confirmed transactions.