spb/internetpressure
Public
TypeScript 36.3%
Python 31.8%
Go 18%
JavaScript 9.8%
Shell 1.9%
SQL 1.4%
CSS 0.5%
1'use client';23import { useState } from 'react';4import { SeriesChart } from '@/components/charts/SeriesChart';5import { fmt, fmtInt } from '@/lib/format';6import type { AdminBaselines } from '@/lib/types';7import { AdminPage, ErrorNote, Field, Panel, inputCls, useAdmin } from './shared';89const SIGNALS = ['ttfb_z', 'tcp_z', 'rtt_z', 'loss', 'dns_fail_rate', 'dns_latency_z', 'resolver_disagreement', 'target_down_corroborated', 'fail_rate_z', 'http_5xx_rate', 'tls_fail_rate', 'reset_timeout_rate', 'route_change_rate', 'hop_count_z', 'path_latency_shift', 'bgp_withdrawals_z', 'bgp_announcements_z', 'bgp_origin_changes_z', 'bgp_collector_disagreement', 'vendor_incidents'];1011export function Baselines() {12 const [signal, setSignal] = useState('ttfb_z');13 const [scopeType, setScopeType] = useState('global');14 const [scopeId, setScopeId] = useState('');15 const { data, err, loading } = useAdmin<AdminBaselines>('/baselines', { signal_id: signal, scope_type: scopeType, scope_id: scopeId || undefined });16 const pts = data?.points ?? [];17 return (18 <AdminPage title="Baseline diagnostics" desc="Value vs rolling median ± MAD for one signal and scope. If the median drifts with the value, the baseline window is too short or the exclusion too small.">19 <div className="grid grid-cols-2 gap-3 md:grid-cols-4">20 <Field label="signal_id">21 <select value={signal} onChange={(e) => setSignal(e.target.value)} className={`${inputCls} num w-full`}>22 {SIGNALS.map((s) => (23 <option key={s}>{s}</option>24 ))}25 </select>26 </Field>27 <Field label="scope_type">28 <select value={scopeType} onChange={(e) => setScopeType(e.target.value)} className={`${inputCls} w-full`}>29 {['global', 'region', 'country', 'asn', 'service', 'probe', 'target'].map((s) => (30 <option key={s}>{s}</option>31 ))}32 </select>33 </Field>34 <Field label="scope_id">35 <input value={scopeId} onChange={(e) => setScopeId(e.target.value)} placeholder="na-east · CA · 13335 · …" className={`${inputCls} num w-full`} />36 </Field>37 <div className="num flex items-end text-[11px] text-ink-3">{data ? `${fmtInt(data.samples)} samples · ${data.baseline_days} d baseline` : loading ? 'loading…' : ''}</div>38 </div>39 <ErrorNote err={err} />40 {pts.length > 0 && (41 <Panel className="mt-3">42 <SeriesChart43 lines={[44 { name: 'value', color: '#E6EDF3', points: pts.map((p) => ({ ts: p.ts, value: p.value })), width: 1.25 },45 { name: 'median', color: '#5B8DEF', points: pts.map((p) => ({ ts: p.ts, value: p.median })), dashed: true },46 { name: 'median + MAD', color: '#8B98A5', points: pts.map((p) => ({ ts: p.ts, value: p.median + p.mad })), dashed: true, width: 0.8 },47 { name: 'median − MAD', color: '#8B98A5', points: pts.map((p) => ({ ts: p.ts, value: p.median - p.mad })), dashed: true, width: 0.8 },48 { name: 'robust z', color: '#E76F51', points: pts.map((p) => ({ ts: p.ts, value: p.z })), yAxisIndex: 1 },49 ]}50 yMax="auto"51 bands={false}52 height={300}53 y2={{ max: 'auto', name: 'z' }}54 />55 <p className="num mt-2 text-[11px] text-ink-3">56 latest: value {fmt(pts.at(-1)!.value)} · median {fmt(pts.at(-1)!.median)} · MAD {fmt(pts.at(-1)!.mad, 2)} · z {fmt(pts.at(-1)!.z)}57 </p>58 </Panel>59 )}60 </AdminPage>61 );62}63