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%
5.1 KB · 95 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { PageHeader } from '@/components/ui/page-header';4import { Card, CardHeader, EmptyState, Table, th, td, tdNum } from '@/components/ui/primitives';5import { BarChart } from '@/components/charts/bar-chart';6import { getDbGraders, getGradePremiums, getGraderActivity } from '@/lib/queries/market-lists';7import { fmtMoney, fmtNum, fmtRelative, cn } from '@/lib/format';8import { catName } from '@/lib/taxonomy';910export const metadata: Metadata = { title: 'Grading', description: 'Grading companies, scales, empirical grade premiums and population data. PSA 10, BGS 10 and CGC 10 are never assumed equivalent.' };11export const revalidate = 600;1213export default async function GradingPage() {14  const [graders, premiums, activity] = await Promise.all([getDbGraders(), getGradePremiums(), getGraderActivity()]);15  const act = new Map(activity.map((a) => [a.grader, a]));16  const byCat = new Map<string, typeof premiums>();17  for (const p of premiums) (byCat.get(p.categorySlug) ?? byCat.set(p.categorySlug, []).get(p.categorySlug)!).push(p);18  return (19    <div>20      <PageHeader kicker="Reference" title="Grading" description="Grades are tracked per grading company. A market multiplier is measured empirically from sales of the same asset in different grades (§118): PSA 10 ≠ BGS 10 ≠ CGC 10 until the data says otherwise." />21      <Card className="overflow-hidden">22        <CardHeader title="Grading companies" subtitle="Scale, categories and observed activity in RareIndex" />23        <Table>24          <thead>25            <tr>26              <th className={th}>Grader</th>27              <th className={th}>Scale</th>28              <th className={th}>Categories</th>29              <th className={cn(th, 'text-right')}>Graded sales</th>30              <th className={cn(th, 'text-right')}>Assets</th>31              <th className={cn(th, 'text-right')}>Median sale</th>32              <th className={cn(th, 'text-right')}>Pop reports</th>33              <th className={th}>Links</th>34            </tr>35          </thead>36          <tbody>37            {graders.map((g) => {38              const a = act.get(g.slug);39              return (40                <tr key={g.slug} className="hover:bg-sunken">41                  <td className={cn(td, 'font-medium')}>{g.name}</td>42                  <td className={cn(td, 'text-muted')}>43                    {g.scale.type}44                    {g.scale.top ? ` · top ${g.scale.top}` : ''}45                    {g.scale.values.length ? ` · ${g.scale.values.length} steps` : ''}46                  </td>47                  <td className={cn(td, 'max-w-[260px] truncate text-muted')}>{g.categorySlugs.map(catName).join(', ') || '—'}</td>48                  <td className={tdNum}>{fmtNum(a?.sales ?? 0)}</td>49                  <td className={tdNum}>{fmtNum(a?.assets ?? 0)}</td>50                  <td className={tdNum}>{a?.medianUsd != null ? fmtMoney(a.medianUsd) : '—'}</td>51                  <td className={tdNum}>{fmtNum(a?.populationReports ?? 0)}</td>52                  <td className={cn(td, 'text-[11px]')}>53                    {g.populationUrl ? (54                      <a href={g.populationUrl} target="_blank" rel="noopener nofollow" className="mr-2 text-muted hover:text-fg">55                        Population ↗56                      </a>57                    ) : null}58                    {g.verifyUrl ? (59                      <a href={g.verifyUrl.replace('{cert}', '')} target="_blank" rel="noopener nofollow" className="text-muted hover:text-fg">60                        Verify ↗61                      </a>62                    ) : null}63                  </td>64                </tr>65              );66            })}67          </tbody>68        </Table>69      </Card>7071      <Card className="mt-4">72        <CardHeader title="Empirical grade premiums" subtitle="Market multiplier vs the category's raw/base price, with sample size; recomputed by the valuation worker" />73        {byCat.size ? (74          <div className="grid gap-6 p-4 lg:grid-cols-2">75            {[...byCat.entries()].map(([cat, rows]) => (76              <div key={cat}>77                <h3 className="mb-2 text-xs font-semibold">78                  <Link href={`/markets/${cat}`} className="hover:underline">79                    {catName(cat)}80                  </Link>81                  <span className="ml-2 text-[11px] font-normal text-subtle">updated {fmtRelative(rows[0]!.computedAt)}</span>82                </h3>83                <BarChart ariaLabel={`Grade premiums for ${catName(cat)}`} data={rows.slice(0, 14).map((r) => ({ label: `${r.grader.toUpperCase()} ${r.grade}`, value: r.marketMultiplier, sublabel: `n=${r.sampleSize}` }))} maxBars={14} />84              </div>85            ))}86          </div>87        ) : (88          <EmptyState title="No premiums computed yet" description="Premiums require the same asset to have sold in several grades; they appear as sales accumulate." />89        )}90      </Card>91      <p className="mt-3 text-[11px] leading-relaxed text-subtle">RareIndex does not grade or authenticate items. Certification numbers shown on sales and listings are as published by the source; verify them with the grading company.</p>92    </div>93  );94}95