SPB Git forge
28commits 1branches 0releases
7.7 MBsize
maindefault branch
10 days agolast push
Python 66.3% TypeScript 22.7% JavaScript 8.6% HTML 1.4% CSS 0.7%
3.8 KB · 80 lines tsx
Raw Blame History
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