spb/internetpressure
Public
TypeScript 36.3%
Python 31.8%
Go 18%
JavaScript 9.8%
Shell 1.9%
SQL 1.4%
CSS 0.5%
1import Link from 'next/link';2import { IncidentRow, TYPE_LABEL } from '@/components/incidents/IncidentRow';3import { Empty, PNum, Section } from '@/components/ui/primitives';4import { MONTH_NAMES, fmt, fmtInt } from '@/lib/format';5import { pressureColor } from '@/lib/pressure';6import type { HistoryDay, HistoryMonthRow, HistorySummary, Incident } from '@/lib/types';78/** Calendar heat strip: one cell per day coloured by max pressure. */9export function DayStrip({ days, year, month }: { days: HistoryDay[]; year: number; month: number }) {10 const dim = new Date(Date.UTC(year, month, 0)).getUTCDate();11 const byDate = new Map(days.map((d) => [d.date, d]));12 const firstDow = (new Date(Date.UTC(year, month - 1, 1)).getUTCDay() + 6) % 7; // Monday first13 return (14 <div>15 <div className="grid grid-cols-7 gap-1 text-center text-[10px] text-ink-3">16 {['M', 'T', 'W', 'T', 'F', 'S', 'S'].map((d, i) => (17 <span key={i}>{d}</span>18 ))}19 </div>20 <div className="mt-1 grid grid-cols-7 gap-0.5 sm:gap-1">21 {Array.from({ length: firstDow }).map((_, i) => (22 <span key={`e${i}`} />23 ))}24 {Array.from({ length: dim }, (_, i) => i + 1).map((n) => {25 const date = `${year}-${String(month).padStart(2, '0')}-${String(n).padStart(2, '0')}`;26 const d = byDate.get(date);27 return (28 <div key={date} className="min-h-[44px] min-w-0 overflow-hidden rounded-[3px] border border-line p-1 sm:p-1.5" style={{ background: d ? pressureColor(d.max) + '33' : 'var(--neutral)' }} title={d ? `${date}: min ${fmt(d.min)} · avg ${fmt(d.avg)} · max ${fmt(d.max)} · ${d.events} events` : `${date}: not observed`}>29 <div className="flex items-baseline justify-between">30 <span className="num text-[10.5px] text-ink-2">{n}</span>31 {d && d.events > 0 && <span className="num text-[9.5px] text-high">{d.events}</span>}32 </div>33 {d && (34 <div className="num text-[13px] leading-tight" style={{ color: pressureColor(d.max) }}>35 {fmt(d.max, 0)}36 </div>37 )}38 </div>39 );40 })}41 </div>42 <p className="mt-2 text-[10.5px] text-ink-3">Cell number = daily max pressure · red count = events opened that day · grey = not observed (observatory started 2026-09)</p>43 </div>44 );45}4647export function MonthStrip({ months }: { months: HistoryMonthRow[] }) {48 if (!months.length) return <Empty>No month observed yet.</Empty>;49 return (50 <ul className="grid grid-cols-2 gap-2 sm:grid-cols-4 lg:grid-cols-6">51 {months.map((m) => {52 const [y, mo] = m.month.split('-');53 return (54 <li key={m.month}>55 <Link href={`/history/${y}/${Number(mo)}`} className="block rounded-[3px] border border-line p-3 hover:border-line-2" style={{ background: pressureColor(m.max) + '22' }}>56 <div className="text-[12px] text-ink">57 {MONTH_NAMES[Number(mo) - 1]} {y}58 </div>59 <div className="num mt-1 text-[22px] leading-none" style={{ color: pressureColor(m.max) }}>60 {fmt(m.max)}61 </div>62 <div className="num mt-1 text-[10.5px] text-ink-2">63 avg {fmt(m.avg)} · min {fmt(m.min)} · {fmtInt(m.events)} events64 </div>65 </Link>66 </li>67 );68 })}69 </ul>70 );71}7273export function LargestGrid({ largest }: { largest: HistorySummary['largest'] }) {74 const cats: [keyof HistorySummary['largest'], string][] = [75 ['pressure', 'Largest by pressure'],76 ['routing', 'Largest routing event'],77 ['dns', 'Largest DNS event'],78 ['latency', 'Largest latency event'],79 ];80 return (81 <ul className="grid grid-cols-[minmax(0,1fr)] gap-px overflow-hidden rounded-[4px] border border-line bg-line sm:grid-cols-2 lg:grid-cols-4">82 {cats.map(([k, label]) => {83 const inc = largest[k];84 return (85 <li key={k} className="bg-panel p-3">86 <div className="label">{label}</div>87 {inc ? (88 <>89 <Link href={`/event/${inc.slug}`} className="mt-1 block text-[13px] text-ink hover:text-accent">90 {inc.title}91 </Link>92 <div className="num mt-1 flex items-baseline gap-2 text-[11px] text-ink-2">93 <PNum value={inc.peak_pressure} className="text-[18px]" />94 <span>{TYPE_LABEL[inc.type]}</span>95 </div>96 </>97 ) : (98 <p className="mt-1 text-[12px] text-ink-3">none in range</p>99 )}100 </li>101 );102 })}103 </ul>104 );105}106107export function TopLists({ s }: { s: HistorySummary }) {108 return (109 <div className="grid grid-cols-[minmax(0,1fr)] gap-x-10 lg:grid-cols-2">110 <Section label="Most affected ASNs" className="min-w-0">111 {s.top_asns.length ? (112 <table className="tbl">113 <thead>114 <tr>115 <th>ASN</th>116 <th className="r">Events</th>117 <th className="r">Max pressure</th>118 </tr>119 </thead>120 <tbody>121 {s.top_asns.map((a) => (122 <tr key={a.asn}>123 <td>124 <Link href={`/asn/${a.asn}`} className="text-ink hover:text-accent">125 <span className="num">AS{a.asn}</span> {a.name}126 </Link>127 </td>128 <td className="num r">{fmtInt(a.events)}</td>129 <td className="r">130 <PNum value={a.max_pressure} />131 </td>132 </tr>133 ))}134 </tbody>135 </table>136 ) : (137 <Empty />138 )}139 </Section>140 <Section label="Most affected regions" className="min-w-0">141 {s.top_regions.length ? (142 <table className="tbl">143 <thead>144 <tr>145 <th>Region</th>146 <th className="r">Events</th>147 <th className="r">Max</th>148 <th className="r">Hours elevated</th>149 </tr>150 </thead>151 <tbody>152 {s.top_regions.map((r) => (153 <tr key={r.id}>154 <td>155 <Link href={`/internet/${r.id}`} className="text-ink hover:text-accent">156 {r.name}157 </Link>158 </td>159 <td className="num r">{fmtInt(r.events)}</td>160 <td className="r">161 <PNum value={r.max_pressure} />162 </td>163 <td className="num r text-ink-2">{fmt(r.hours_elevated)}</td>164 </tr>165 ))}166 </tbody>167 </table>168 ) : (169 <Empty />170 )}171 </Section>172 </div>173 );174}175176export function TopEvents({ events }: { events: Incident[] }) {177 if (!events.length) return <Empty>No event in this range.</Empty>;178 return (179 <ul className="divide-y divide-line">180 {events.map((e) => (181 <IncidentRow key={e.event_id} inc={e} compact />182 ))}183 </ul>184 );185}186