spb/company-atlas
Public
Python 66.3%
TypeScript 22.7%
JavaScript 8.6%
HTML 1.4%
CSS 0.7%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { WorldMap } from '@/components/charts/world-map';4import { CountryChip } from '@/components/ui/badges';5import { Container, PageHeader, Unavailable } from '@/components/ui/section';6import { api, safe } from '@/lib/api';7import { cn } from '@/lib/cn';8import { fmtInt, fmtPctSigned, fmtScore, num } from '@/lib/format';9import { routes } from '@/lib/site';1011export const metadata: Metadata = { title: 'Country atlas', description: 'Activity, hiring, expansion and industry mix of monitored companies by country.' };12export const revalidate = 300;1314export default async function CountriesPage() {15 const [data, map] = await Promise.all([safe(api.countries()), safe(api.map('events_30d'))]);16 return (17 <Container wide>18 <PageHeader eyebrow="Country atlas" title="Countries" lede="Monitored companies by headquarters country: activity, structured events, hiring momentum and industry mix. Coverage differs by country — compare trends, not absolute counts." />19 {map?.buckets.length ? <WorldMap buckets={map.buckets} className="mb-8" /> : null}20 {!data ? (21 <Unavailable what="Countries" />22 ) : (23 <>24 <div className="table-scroll hidden md:block">25 <table className="data-table">26 <thead>27 <tr>28 <th>Country</th>29 <th>Region</th>30 <th className="num">Companies</th>31 <th className="num">Events 7 d</th>32 <th className="num">Events 30 d</th>33 <th className="num">Activity</th>34 <th className="num">Hiring 30 d</th>35 <th>Industry mix</th>36 </tr>37 </thead>38 <tbody>39 {data.items.map((c) => {40 const h = num(c.hiring_momentum_30d);41 return (42 <tr key={c.code}>43 <td className="primary">44 <Link href={routes.country(c.code)} className="row-link inline-flex items-center gap-2">45 <CountryChip code={c.code} link={false} /> {c.name}46 </Link>47 </td>48 <td className="text-ink-2">{c.region ?? '—'}</td>49 <td className="num tnum">{fmtInt(c.companies)}</td>50 <td className="num tnum">{fmtInt(c.events_7d)}</td>51 <td className="num tnum">{fmtInt(c.events_30d)}</td>52 <td className="num tnum">{fmtScore(c.activity_score)}</td>53 <td className={cn('num tnum', h !== null && (h > 0 ? 'text-positive' : h < 0 ? 'text-danger' : ''))}>{h === null ? '—' : fmtPctSigned(h)}</td>54 <td className="text-xs text-ink-2">{c.industry_mix.slice(0, 3).map((m) => `${m.industry.replace(/-/g, ' ')} ${m.companies}`).join(' · ')}</td>55 </tr>56 );57 })}58 </tbody>59 </table>60 </div>61 <ul className="divide-y divide-rule border-y border-rule md:hidden">62 {data.items.map((c) => (63 <li key={c.code}>64 <Link href={routes.country(c.code)} className="block py-3">65 <p className="flex items-center gap-2 text-[15px] font-medium text-ink">66 <CountryChip code={c.code} link={false} /> {c.name}67 </p>68 <p className="tnum mt-0.5 text-xs text-ink-3">69 {fmtInt(c.companies)} companies · {fmtInt(c.events_30d)} events 30 d · activity {fmtScore(c.activity_score)} · hiring {fmtPctSigned(c.hiring_momentum_30d, 0)}70 </p>71 </Link>72 </li>73 ))}74 </ul>75 </>76 )}77 </Container>78 );79}80