SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
3.8 KB · 71 lines tsx
Raw Blame History
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