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%
4.3 KB · 93 lines tsx
Raw Blame History
1import Link from "next/link";2import { Table, TableBody, TableCell, TableEmpty, TableHead, TableHeader, TableRow } from "@/components/ui/table";3import { Badge, StatusBadge } from "@/components/ui/badge";4import { formatBytes, formatDate, formatMs, formatUsd, timeAgo } from "@/lib/format";5import type { RequestListRow } from "@/lib/queries/dashboard";6import { cn } from "@/lib/utils";78export function NetworkLabel({ network, requested }: { network: string | null; requested?: string }) {9  if (!network) return <span className="text-fg-subtle">—</span>;10  return (11    <span className="inline-flex items-center gap-1">12      <span>{network === "isp" ? "ISP" : network}</span>13      {requested && requested !== "auto" && requested !== network ? (14        <span className="text-[11px] text-fg-subtle" title={`Requested ${requested}`}>15          (asked {requested})16        </span>17      ) : null}18    </span>19  );20}2122export function HttpCode({ code }: { code: number | null }) {23  if (code === null) return <span className="text-fg-subtle">—</span>;24  const tone = code < 300 ? "text-success" : code < 400 ? "text-info" : code < 500 ? "text-warning" : "text-danger";25  return <span className={cn("font-mono tabular", tone)}>{code}</span>;26}2728/** Request log table. `compact` hides secondary columns (used on the Overview). */29export function RequestsTable({ rows, compact, emptyMessage = "No requests match these filters." }: { rows: RequestListRow[]; compact?: boolean; emptyMessage?: React.ReactNode }) {30  const cols = compact ? 7 : 11;31  return (32    <Table>33      <TableHeader>34        <TableRow className="hover:bg-transparent">35          <TableHead>Request</TableHead>36          <TableHead>Time</TableHead>37          <TableHead>Domain</TableHead>38          <TableHead>Status</TableHead>39          <TableHead className="text-right">HTTP</TableHead>40          {!compact ? <TableHead>Network</TableHead> : null}41          {!compact ? <TableHead>Country</TableHead> : null}42          <TableHead className="text-right">Latency</TableHead>43          {!compact ? <TableHead className="text-right">Attempts</TableHead> : null}44          {!compact ? <TableHead className="text-right">Bandwidth</TableHead> : null}45          <TableHead className="text-right">Cost</TableHead>46        </TableRow>47      </TableHeader>48      <TableBody>49        {rows.length === 0 ? (50          <TableEmpty colSpan={cols}>{emptyMessage}</TableEmpty>51        ) : (52          rows.map((r) => (53            <TableRow key={r.id}>54              <TableCell>55                <Link href={`/dashboard/requests/${r.id}`} className="font-mono text-[12.5px] text-accent underline-offset-4 hover:underline" prefetch={false}>56                  {r.id}57                </Link>58                {r.source !== "api" ? (59                  <Badge variant="outline" className="ml-2 align-middle text-[10.5px]">60                    {r.source}61                  </Badge>62                ) : null}63              </TableCell>64              <TableCell className="whitespace-nowrap text-fg-muted" title={formatDate(r.createdAt, { timeStyle: "medium" })}>65                {timeAgo(r.createdAt)}66              </TableCell>67              <TableCell className="max-w-[220px] truncate" title={r.url}>68                {r.domain}69              </TableCell>70              <TableCell>71                <StatusBadge status={r.status} />72              </TableCell>73              <TableCell className="text-right">74                <HttpCode code={r.httpStatus} />75              </TableCell>76              {!compact ? (77                <TableCell className="text-fg-muted">78                  <NetworkLabel network={r.network} requested={r.requestedNetwork} />79                </TableCell>80              ) : null}81              {!compact ? <TableCell className="font-mono tabular text-fg-muted">{r.country ?? "—"}</TableCell> : null}82              <TableCell className="text-right font-mono tabular">{formatMs(r.latencyMs)}</TableCell>83              {!compact ? <TableCell className="text-right font-mono tabular">{r.attempts || "—"}</TableCell> : null}84              {!compact ? <TableCell className="text-right font-mono tabular text-fg-muted">{formatBytes(r.bytesIn + r.bytesOut)}</TableCell> : null}85              <TableCell className="text-right font-mono tabular">{formatUsd(r.priceUsd, true)}</TableCell>86            </TableRow>87          ))88        )}89      </TableBody>90    </Table>91  );92}93