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.1 KB · 58 lines tsx
Raw Blame History
1import Link from 'next/link';2import type { VariantRow } from '@/lib/queries/assets';3import { fmtMoney, fmtNum, fmtRelative, cn, confidenceLabel } from '@/lib/format';4import { Table, th, td, tdNum, Badge } from '@/components/ui/primitives';56/**7 * Dense variant/grade matrix for md+ screens (phones use the chip selector): RIV with its band and8 * confidence, latest sale, sales and listings per variant. Rows link to the variant view.9 */10export function VariantTable({ variants, href, activeId, className }: { variants: VariantRow[]; href: (id: string | null) => string; activeId: string | null; className?: string }) {11  const rows = variants.slice(0, 8);12  if (rows.length < 2) return null;13  return (14    <div className={cn('card overflow-hidden', className)}>15      <div className="flex items-baseline justify-between border-b border-border px-3 py-2">16        <h2 className="text-[12px] font-semibold text-fg">By variant / grade</h2>17        <span className="text-[10px] text-subtle">{variants.length} tracked · click a row to focus</span>18      </div>19      <Table>20        <thead>21          <tr>22            <th className={th}>Variant</th>23            <th className={cn(th, 'text-right')}>RIV</th>24            <th className={cn(th, 'text-right')}>Range</th>25            <th className={th}>Confidence</th>26            <th className={cn(th, 'text-right')}>Latest sale</th>27            <th className={cn(th, 'text-right')}>Sales</th>28            <th className={cn(th, 'text-right')}>Listings</th>29          </tr>30        </thead>31        <tbody>32          {rows.map((v) => {33            const label = confidenceLabel(v.rivConfidence);34            return (35              <tr key={v.id} className={cn('hover:bg-sunken', activeId === v.id && 'bg-inset')}>36                <td className={cn(td, 'font-medium')}>37                  <Link href={href(v.id)} scroll={false} className="hover:underline">38                    {v.label}39                  </Link>40                </td>41                <td className={cn(tdNum, 'font-semibold')}>{v.rivUsd === null ? <span className="text-subtle">—</span> : fmtMoney(v.rivUsd)}</td>42                <td className={cn(tdNum, 'text-muted')}>{v.rivLowUsd !== null && v.rivHighUsd !== null ? `${fmtMoney(v.rivLowUsd)} – ${fmtMoney(v.rivHighUsd)}` : <span className="text-subtle">—</span>}</td>43                <td className={td}>{v.rivUsd === null ? <span className="text-subtle">—</span> : <Badge tone={label === 'High' ? 'gain' : label === 'Medium' ? 'index' : label === 'Low' ? 'alert' : 'neutral'}>{label} · n={v.rivSampleSize}</Badge>}</td>44                <td className={tdNum}>45                  {v.latestSaleUsd === null ? <span className="text-subtle">—</span> : fmtMoney(v.latestSaleUsd)}46                  {v.latestSaleAt ? <span className="block text-[10px] text-subtle">{fmtRelative(v.latestSaleAt)}</span> : null}47                </td>48                <td className={tdNum}>{fmtNum(v.salesCount)}</td>49                <td className={tdNum}>{fmtNum(v.activeListings)}</td>50              </tr>51            );52          })}53        </tbody>54      </Table>55    </div>56  );57}58