spb/market-atlas
Public
TypeScript 96.7%
SQL 1.6%
CSS 0.8%
JavaScript 0.5%
1import type { Metadata } from "next";2import Link from "next/link";3import { Page, PageHeader, Section } from "@/components/ui/section";4import { StatusBadge } from "@/components/ui/status-badge";5import { api } from "@/lib/api";6import type { Country } from "@/lib/types";78type Row = Country & { exchanges: Array<{ id: string; name: string; status: string }>; instruments_tracked: number };910export const metadata: Metadata = { title: "Countries", description: "Market atlas by country: exchanges, indices, rates and currencies observed by Market Atlas.", alternates: { canonical: "/countries" } };11export const dynamic = "force-dynamic";1213export default async function CountriesPage() {14 const rows = await api<Row[]>("/v1/countries");15 const regions = [...new Set(rows.map((r) => r.region))].sort();16 return (17 <Page wide>18 <PageHeader kicker="World" title="Countries" lead="Every country in the atlas with its venues and what Market Atlas currently tracks there. Coverage is a measure of Market Atlas observation, not of market size." />19 {regions.map((region) => (20 <Section key={region} title={region}>21 <ul className="grid grid-cols-1 [&>*]:min-w-0 gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">22 {rows23 .filter((r) => r.region === region)24 .sort((a, b) => b.instruments_tracked - a.instruments_tracked || a.name.localeCompare(b.name))25 .map((r) => (26 <li key={r.code} className="rounded-md border border-rule bg-surface px-3 py-2.5 hover:border-rule-strong">27 <Link href={`/countries/${r.code}`} className="flex items-baseline justify-between gap-2">28 <span className="font-medium">{r.name}</span>29 <span className="mono text-xs text-ink-3">30 {r.code} · {r.currency ?? "—"}31 </span>32 </Link>33 <div className="mt-1 flex flex-wrap items-center gap-1.5 text-[11px] text-ink-3">34 {r.exchanges.slice(0, 3).map((e) => (35 <span key={e.id} className="inline-flex items-center gap-1">36 <StatusBadge status={e.status} label={e.status.toLowerCase()} /> {e.name}37 </span>38 ))}39 {!r.exchanges.length && <span>no venue in the atlas</span>}40 </div>41 <div className="mono mt-1 text-[11px] text-ink-3">{r.instruments_tracked.toLocaleString("en-US")} instruments tracked</div>42 </li>43 ))}44 </ul>45 </Section>46 ))}47 </Page>48 );49}50