TypeScript 61.9%
HTML 37.2%
SQL 0.7%
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