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 { EventTypeBadge } from '@/components/ui/badges';4import { Container, PageHeader, Unavailable } from '@/components/ui/section';5import { api, safe } from '@/lib/api';6import { cn } from '@/lib/cn';7import { fmtInt, fmtPctSigned, fmtScore, num } from '@/lib/format';8import { routes } from '@/lib/site';910export const metadata: Metadata = { title: 'Industry atlas', description: 'A living index per industry: monitored companies, events, hiring momentum, activity and AI adoption.' };11export const revalidate = 300;1213export default async function IndustriesPage() {14 const data = await safe(api.industries());15 return (16 <Container wide>17 <PageHeader eyebrow="Industry atlas" title="Industries" lede="Each industry is a living index over its monitored companies: activity, structured events, hiring momentum and observable AI adoption. Values are averages over the monitored population, not market statistics." />18 {!data ? (19 <Unavailable what="Industries" />20 ) : (21 <>22 <div className="table-scroll hidden md:block">23 <table className="data-table">24 <thead>25 <tr>26 <th>Industry</th>27 <th className="num">Companies</th>28 <th className="num">Events 7 d</th>29 <th className="num">Events 30 d</th>30 <th className="num">Activity</th>31 <th className="num">Hiring 30 d</th>32 <th className="num">AI adoption</th>33 <th>Top event types</th>34 </tr>35 </thead>36 <tbody>37 {data.items.map((i) => {38 const h = num(i.hiring_momentum_30d);39 return (40 <tr key={i.slug}>41 <td className="primary">42 <Link href={routes.industry(i.slug)} className="row-link">43 {i.name}44 </Link>45 {i.parent_slug && <span className="block text-[11px] font-normal text-ink-3">in {i.parent_slug.replace(/-/g, ' ')}</span>}46 </td>47 <td className="num tnum">{fmtInt(i.companies)}</td>48 <td className="num tnum">{fmtInt(i.events_7d)}</td>49 <td className="num tnum">{fmtInt(i.events_30d)}</td>50 <td className="num tnum">{fmtScore(i.activity_score)}</td>51 <td className={cn('num tnum', h !== null && (h > 0 ? 'text-positive' : h < 0 ? 'text-danger' : ''))}>{h === null ? '—' : fmtPctSigned(h)}</td>52 <td className="num tnum">{fmtScore(i.ai_adoption)}</td>53 <td>54 <span className="flex flex-wrap gap-1">55 {i.top_event_types.slice(0, 3).map((t) => (56 <EventTypeBadge key={t} type={t} small />57 ))}58 </span>59 </td>60 </tr>61 );62 })}63 </tbody>64 </table>65 </div>66 <ul className="divide-y divide-rule border-y border-rule md:hidden">67 {data.items.map((i) => (68 <li key={i.slug}>69 <Link href={routes.industry(i.slug)} className="block py-3">70 <p className="text-[15px] font-medium text-ink">{i.name}</p>71 <p className="tnum mt-0.5 text-xs text-ink-3">72 {fmtInt(i.companies)} companies · {fmtInt(i.events_30d)} events 30 d · activity {fmtScore(i.activity_score)} · hiring {fmtPctSigned(i.hiring_momentum_30d, 0)}73 </p>74 <p className="mt-1 flex flex-wrap gap-1">75 {i.top_event_types.slice(0, 3).map((t) => (76 <EventTypeBadge key={t} type={t} small />77 ))}78 </p>79 </Link>80 </li>81 ))}82 </ul>83 </>84 )}85 </Container>86 );87}88