SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
3.3 KB · 70 lines tsx
Raw Blame History
1"use client";2import * as React from "react";3import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from "recharts";4import { formatNumber, formatPercent, titleCase } from "@/lib/format";5import { CHART_COLORS, NETWORK_COLORS } from "./chart-theme";6import { ChartTooltip } from "./chart-tooltip";78export interface NetworkSlice {9  network: string;10  requests: number;11  percent: number;12}1314const LIVE_NETWORKS = new Set(["residential"]);1516/**17 * Part-to-whole of resolved network classes. All four classes are listed (0 % rows included);18 * networks that are not live yet say so instead of pretending to have traffic.19 */20export function NetworkDonut({ data, size = 156 }: { data: NetworkSlice[]; size?: number }) {21  const nonZero = data.filter((d) => d.requests > 0);22  const total = data.reduce((a, d) => a + d.requests, 0);23  return (24    <div className="flex flex-col items-center gap-5 sm:flex-row sm:items-center">25      <div className="relative shrink-0" style={{ width: size, height: size }}>26        <ResponsiveContainer width="100%" height="100%">27          <PieChart margin={{ top: 0, right: 0, bottom: 0, left: 0 }}>28            <Tooltip content={<ChartTooltip labelFormatter={(_l, p) => titleCase(String(p[0]?.name ?? ""))} valueFormatter={(v) => `${formatNumber(v)} req`} />} />29            <Pie30              data={nonZero.length ? nonZero : [{ network: "none", requests: 1, percent: 0 }]}31              dataKey="requests"32              nameKey="network"33              innerRadius={size / 2 - 22}34              outerRadius={size / 2 - 6}35              paddingAngle={nonZero.length > 1 ? 2 : 0}36              stroke={CHART_COLORS.surface}37              strokeWidth={2}38              isAnimationActive={false}39              cornerRadius={3}40            >41              {(nonZero.length ? nonZero : [{ network: "none" }]).map((d) => (42                <Cell key={d.network} fill={d.network === "none" ? "var(--bg-muted)" : NETWORK_COLORS[d.network] ?? CHART_COLORS.muted} />43              ))}44            </Pie>45          </PieChart>46        </ResponsiveContainer>47        <div className="pointer-events-none absolute inset-0 flex flex-col items-center justify-center">48          <span className="text-[18px] font-semibold leading-none tracking-tight tabular">{formatNumber(total)}</span>49          <span className="mt-1 text-[11px] text-fg-subtle">resolved</span>50        </div>51      </div>52      <ul className="w-full min-w-0 flex-1 divide-y divide-border text-[13px]">53        {data.map((d) => (54          <li key={d.network} className="flex items-center justify-between gap-3 py-2">55            <span className="flex items-center gap-2 text-fg-muted">56              <span aria-hidden className="inline-block size-2 rounded-[2px]" style={{ backgroundColor: NETWORK_COLORS[d.network] ?? CHART_COLORS.muted }} />57              <span className="text-fg">{d.network === "isp" ? "ISP" : titleCase(d.network)}</span>58              {!LIVE_NETWORKS.has(d.network) ? <span className="text-[11px] text-fg-subtle">coming soon</span> : null}59            </span>60            <span className="flex items-center gap-3 font-mono tabular">61              <span className="text-fg-subtle">{formatNumber(d.requests)}</span>62              <span className="w-12 text-right">{formatPercent(d.percent, d.percent > 0 && d.percent < 1 ? 1 : 0)}</span>63            </span>64          </li>65        ))}66      </ul>67    </div>68  );69}70