TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import Link from 'next/link';2import type { MarketRow } from '@/lib/queries/markets';3import { fmtMoney, fmtNum, fmtRelative, cn } from '@/lib/format';4import { Table, th, td, tdNum, Delta, Badge } from '@/components/ui/primitives';5import { heatStyle } from '@/components/charts/heat';67/** Heat table of categories (markets overview and child categories). */8export function MarketTable({ rows, showPhase = true, className }: { rows: MarketRow[]; showPhase?: boolean; className?: string }) {9 const sorted = [...rows].sort((a, b) => b.counts.sales - a.counts.sales || b.counts.assets - a.counts.assets || a.node.sortOrder - b.node.sortOrder);10 return (11 <Table className={className}>12 <thead>13 <tr>14 <th className={th}>Category</th>15 <th className={cn(th, 'text-right')}>Index</th>16 <th className={cn(th, 'text-right')}>1D</th>17 <th className={cn(th, 'text-right')}>7D</th>18 <th className={cn(th, 'text-right')}>30D</th>19 <th className={cn(th, 'text-right')}>1Y</th>20 <th className={cn(th, 'text-right')}>Assets</th>21 <th className={cn(th, 'text-right')}>Priced</th>22 <th className={cn(th, 'text-right')}>Sales</th>23 <th className={cn(th, 'text-right')}>Sales 30D</th>24 <th className={cn(th, 'text-right')}>Volume 30D</th>25 <th className={cn(th, 'text-right')}>Listings</th>26 <th className={cn(th, 'text-right')}>Liquidity</th>27 <th className={cn(th, 'text-right')} title="Estimated population × representative value; shown only with a methodology confidence label">28 Market cap est.29 </th>30 <th className={th}>Last sale</th>31 </tr>32 </thead>33 <tbody>34 {sorted.map(({ node, snapshot, counts }) => {35 const inactive = counts.assets === 0;36 return (37 <tr key={node.slug} className={cn('hover:bg-sunken', inactive && 'text-subtle')}>38 <td className={td}>39 <Link href={`/markets/${node.slug}`} className={cn('font-medium hover:underline', inactive ? 'text-muted' : 'text-fg')}>40 {node.name}41 </Link>42 {showPhase ? (43 <Badge tone={node.phase === 1 ? 'index' : 'neutral'} className="ml-2">44 Phase {node.phase}45 </Badge>46 ) : null}47 {node.children.length ? <span className="ml-2 text-[10px] text-subtle">{node.children.length} sub</span> : null}48 </td>49 <td className={tdNum}>{snapshot?.indexValue != null ? snapshot.indexValue.toFixed(1) : <span className="text-subtle">—</span>}</td>50 {(['change1d', 'change7d', 'change30d', 'change1y'] as const).map((k) => (51 <td key={k} className={tdNum} style={heatStyle(snapshot?.[k])}>52 <Delta value={snapshot?.[k]} />53 </td>54 ))}55 <td className={tdNum}>{fmtNum(counts.assets)}</td>56 <td className={tdNum}>{fmtNum(counts.priced)}</td>57 <td className={tdNum}>{fmtNum(counts.sales)}</td>58 <td className={tdNum}>{fmtNum(counts.sales30d)}</td>59 <td className={tdNum}>{fmtMoney(counts.volume30dUsd, 'USD', { compact: true })}</td>60 <td className={tdNum}>{fmtNum(counts.listings)}</td>61 <td className={tdNum}>{snapshot?.liquidityScore != null ? Math.round(snapshot.liquidityScore) : <span className="text-subtle">—</span>}</td>62 <td className={tdNum}>{snapshot?.marketCapEstUsd != null ? fmtMoney(snapshot.marketCapEstUsd, 'USD', { compact: true }) : <span className="text-subtle">—</span>}</td>63 <td className={cn(td, 'text-muted')}>{counts.lastSaleAt ? fmtRelative(counts.lastSaleAt) : '—'}</td>64 </tr>65 );66 })}67 </tbody>68 </Table>69 );70}71