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.5 KB · 79 lines tsx
Raw Blame History
1import { Route } from "lucide-react";2import { Badge, StatusBadge } from "@/components/ui/badge";3import { EmptyState } from "@/components/ui/empty-state";4import { formatBytes, formatMs } from "@/lib/format";5import type { RequestAttemptView } from "@/lib/queries/dashboard";6import { HttpCode } from "./requests-table";78function outcomeStatus(outcome: string): string {9  if (outcome === "success") return "success";10  if (outcome === "blocked") return "blocked";11  if (outcome === "timeout") return "pending";12  return "failed";13}1415/**16 * Vertical timeline of routing attempts. `provider` is only present when the organization has17 * provider visibility enabled; it is then labelled as debug output.18 */19export function AttemptsTimeline({ attempts, providerVisibility }: { attempts: RequestAttemptView[]; providerVisibility: boolean }) {20  if (!attempts.length) {21    return <EmptyState compact icon={Route} title="No attempts recorded" description="Attempts appear once the router has tried at least one route for this request." />;22  }23  return (24    <ol className="relative ml-3 border-l border-border">25      {attempts.map((a) => (26        <li key={a.id} className="relative pb-5 pl-6 last:pb-0">27          <span aria-hidden className={`absolute -left-[5px] top-1.5 size-2.5 rounded-full border-2 border-bg-elevated ${a.outcome === "success" ? "bg-success" : a.outcome === "blocked" ? "bg-danger" : "bg-warning"}`} />28          <div className="flex flex-wrap items-center gap-2 text-[13px]">29            <span className="font-mono text-[12px] text-fg-subtle">#{a.attemptNo}</span>30            <StatusBadge status={outcomeStatus(a.outcome)} />31            <span className="text-fg-muted">32              via <span className="text-fg">{a.network === "isp" ? "ISP" : a.network}</span>33              {a.country ? <span className="font-mono"> · {a.country}</span> : null}34            </span>35            {providerVisibility && a.provider ? (36              <Badge variant="outline" className="font-mono">37                <span className="text-fg-subtle">debug</span> {a.provider}38              </Badge>39            ) : null}40            <span className="ml-auto font-mono tabular text-fg-muted">{formatMs(a.durationMs)}</span>41          </div>42          <dl className="mt-1.5 grid grid-cols-2 gap-x-6 gap-y-1 text-[12.5px] sm:grid-cols-4">43            <div>44              <dt className="text-fg-subtle">Outcome</dt>45              <dd className="font-medium">{a.outcome.replace(/_/g, " ")}</dd>46            </div>47            <div>48              <dt className="text-fg-subtle">HTTP</dt>49              <dd>50                <HttpCode code={a.httpStatus} />51              </dd>52            </div>53            <div>54              <dt className="text-fg-subtle">Bytes</dt>55              <dd className="font-mono tabular">{formatBytes(a.bytesIn + a.bytesOut)}</dd>56            </div>57            <div>58              <dt className="text-fg-subtle">Routing score</dt>59              <dd className="font-mono tabular">{a.routingScore === null ? "—" : a.routingScore.toFixed(2)}</dd>60            </div>61            {a.errorCode ? (62              <div className="col-span-2">63                <dt className="text-fg-subtle">Error</dt>64                <dd className="font-mono text-danger">{a.errorCode}</dd>65              </div>66            ) : null}67            {a.blockReason ? (68              <div className="col-span-2">69                <dt className="text-fg-subtle">Block reason</dt>70                <dd>{a.blockReason}</dd>71              </div>72            ) : null}73          </dl>74        </li>75      ))}76    </ol>77  );78}79