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%
4.1 KB · 88 lines tsx
Raw Blame History
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