Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
103 linesCodeBlameRaw
1import { ChevronLeft, GitCommitHorizontal, Waypoints } from "lucide-react";
2import { Link, data } from "react-router";
3
4import type { Route } from "./+types/observability-request";
5import { LogLines } from "../../components/observability-logs";
6import { StatusBadge, SourceBadge } from "../../components/observability";
7import { ButtonLink } from "../../components/ui";
8import { Alert, AlertDescription, AlertTitle } from "../../components/ui/alert";
9import { Card } from "../../components/ui/card";
10import { page } from "../../lib/meta";
11import { clock, duration, shortId } from "../../lib/observability";
12import { observed } from "../../lib/observability.server";
13import { outcomeWords } from "../../lib/observability-words";
14
15export function meta({ params, ...args }: Route.MetaArgs) {
16 return page(args, { title: `Request ${params.id} · ${params.owner}/${params.repo} · g1t` });
17}
18
19export async function loader({ params, context, request }: Route.LoaderArgs) {
20 const { projectId, service } = await observed(context, params, request);
21 const found = await service.request({ project_id: projectId, id: params.id });
22 if (!found) throw data(null, { status: 404 });
23 return found;
24}
25
26export default function RequestPage({ loaderData, params }: Route.ComponentProps) {
27 const { request: r, logs } = loaderData;
28 const base = `/${params.owner}/${params.repo}`;
29 const facts: [string, React.ReactNode][] = [
30 ["Time", <span className="font-mono tabular-nums">{clock(r.time_us, true)} UTC</span>],
31 ["Duration", duration(r.duration_us / 1000)],
32 ["CPU time", r.cpu_ms === null ? "—" : duration(r.cpu_ms)],
33 ["Outcome", outcomeWords(r.outcome)],
34 ["Served from", [r.region, r.country].filter(Boolean).join(" · ") || "—"],
35 ["Trigger", r.trigger],
36 ["Service", <span className="font-mono">{r.service || "—"}</span>],
37 ["Environment", r.environment ?? "—"],
38 ];
39 return (
40 <div className="mx-auto max-w-5xl">
41 <Link to={`${base}/observability/requests`} className="inline-flex items-center gap-1 text-sm text-muted hover:text-fg">
42 <ChevronLeft size={14} />
43 Requests
44 </Link>
45 <header className="mt-3 flex flex-wrap items-start justify-between gap-4">
46 <div className="min-w-0">
47 <h1 className="flex min-w-0 items-center gap-3 font-mono text-lg">
48 <StatusBadge status={r.status} outcome={r.outcome} />
49 <span className="shrink-0 text-faint">{r.method ?? r.trigger}</span>
50 <span className="truncate">{r.url ?? r.route}</span>
51 </h1>
52 <p className="mt-1 flex flex-wrap items-center gap-2 text-xs text-faint">
53 <span className="font-mono">{r.id}</span>
54 <SourceBadge source={r.source} />
55 </p>
56 </div>
57 <div className="flex flex-wrap gap-2">
58 {r.trace_id && (
59 <ButtonLink to={`${base}/observability/traces/${r.trace_id}`} variant="outline" size="sm">
60 <Waypoints size={14} />
61 Trace
62 </ButtonLink>
63 )}
64 {r.deployment_id && (
65 <ButtonLink to={`${base}/deployments/${r.deployment_id}`} variant="outline" size="sm">
66 <GitCommitHorizontal size={14} />
67 {r.commit_sha ? shortId(r.commit_sha, 7) : "Deployment"}
68 </ButtonLink>
69 )}
70 </div>
71 </header>
72
73 {r.error && (
74 <Alert className="mt-5">
75 <AlertTitle className="font-mono">{r.error.name}</AlertTitle>
76 <AlertDescription>{r.error.message}</AlertDescription>
77 </Alert>
78 )}
79
80 <Card className="mt-5 grid grid-cols-2 gap-x-6 gap-y-3 p-4 sm:grid-cols-4">
81 {facts.map(([label, value]) => (
82 <div key={label} className="min-w-0">
83 <p className="text-xs text-muted">{label}</p>
84 <p className="mt-0.5 truncate text-sm">{value}</p>
85 </div>
86 ))}
87 </Card>
88
89 <section className="mt-6" aria-labelledby="logs-title">
90 <h2 id="logs-title" className="text-sm font-semibold">
91 Logs <span className="font-normal text-faint">{logs.length}</span>
92 </h2>
93 {logs.length === 0 ? (
94 <p className="mt-2 text-sm text-muted">It wrote nothing.</p>
95 ) : (
96 <Card className="mt-2 overflow-hidden">
97 <LogLines logs={logs} base={base} showRequest={false} />
98 </Card>
99 )}
100 </section>
101 </div>
102 );
103}