SPB Git

spb/rareindex

Public
3.9 KB · 73 lines tsx
1import type { Metadata } from 'next';2import Link from 'next/link';3import { PageHeader } from '@/components/ui/page-header';4import { Badge } from '@/components/ui/primitives';5import { getCategoryCounts } from '@/lib/queries/markets';6import { FAMILIES, getCategory } from '@rareindex/taxonomy';7import { fmtNum } from '@/lib/format';8import { INDICES } from '@rareindex/taxonomy';910export const metadata: Metadata = { title: 'Categories', description: 'The RareIndex collectible taxonomy: every family and subcategory tracked, with launch phase and live coverage.' };11export const revalidate = 300;1213export default async function CategoriesPage() {14  const counts = await getCategoryCounts();15  const total = (slugs: string[]) => slugs.reduce((a, s) => a + (counts.get(s)?.assets ?? 0), 0);16  const groups = [1, 2, 3].map((phase) => ({ phase, families: FAMILIES.filter((f) => f.phase === phase) }));17  return (18    <div>19      <PageHeader title="Categories" description={`An extensible taxonomy of ${FAMILIES.length} collectible families. Phase 1 is the launch wedge; every family is architected for data from day one. New categories are proposed automatically from market data and approved by an editor.`} />20      {groups.map((g) => (21        <section key={g.phase} className="mb-8">22          <h2 className="mb-3 flex items-center gap-2 text-sm font-semibold">23            Phase {g.phase}24            <span className="text-xs font-normal text-muted">{g.phase === 1 ? 'Launch wedge' : g.phase === 2 ? 'Next expansion' : 'Full universe'}</span>25            <span className="num text-xs font-normal text-subtle">{g.families.length} families</span>26          </h2>27          <div className="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">28            {g.families.map((f) => {29              const assets = total([f.slug, ...f.children.flatMap((c) => [c, ...(getCategory(c)?.children ?? [])])]);30              const idx = INDICES.find((i) => i.ticker === f.index);31              return (32                <div key={f.slug} className="card flex flex-col p-3">33                  <div className="flex items-start justify-between gap-2">34                    <Link href={`/markets/${f.slug}`} className="text-[13px] font-semibold text-fg hover:underline">35                      {f.name}36                    </Link>37                    {idx ? (38                      <Link href={`/rareindex/${idx.ticker}`} className="shrink-0">39                        <Badge tone="index">{idx.ticker}</Badge>40                      </Link>41                    ) : null}42                  </div>43                  {f.description ? <p className="mt-1 line-clamp-2 text-[11px] text-muted">{f.description}</p> : null}44                  {f.children.length ? (45                    <ul className="mt-2 flex flex-wrap gap-1">46                      {f.children.map((c) => {47                        const n = getCategory(c)!;48                        const cnt = counts.get(c)?.assets ?? 0;49                        return (50                          <li key={c}>51                            <Link href={`/markets/${c}`} className="rounded-sm bg-inset px-1.5 py-0.5 text-[11px] text-muted hover:text-fg">52                              {n.name}53                              {cnt ? <span className="num ml-1 text-subtle">{fmtNum(cnt, { compact: true })}</span> : null}54                            </Link>55                          </li>56                        );57                      })}58                    </ul>59                  ) : null}60                  <div className="mt-auto flex items-center justify-between pt-2 text-[11px] text-subtle">61                    <span className="num">{assets ? `${fmtNum(assets)} assets` : 'no data yet'}</span>62                    <span>{f.conditionScale ? `scale: ${f.conditionScale}` : ''}</span>63                  </div>64                </div>65              );66            })}67          </div>68        </section>69      ))}70    </div>71  );72}73
Files · f9a634ec80792524177fc3e5fba5c7953d672423
Menu

Theme