g1t/apps/web/app/routes/repo/pulls.tsx

77 lines3,146 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Issues and pull requests replace intents and attempts1import { Bot } from "lucide-react";
2import { Link } from "react-router";
3
4import type { Route } from "./+types/pulls";
5import { EmptyState, TimeAgo } from "../../components/ui";
6import { PullIcon, StateTabs } from "../../components/work";
7import { work } from "../../lib/services.server";
8import { getViewer, unwrap } from "../../lib/session.server";
9
10export function meta({ params }: Route.MetaArgs) {
11 return [{ title: `Pull requests · ${params.owner}/${params.repo} · g1t` }];
12}
13
14export async function loader({ params, context, request }: Route.LoaderArgs) {
15 const path = { namespace: params.owner, name: params.repo };
16 const state =
17 new URL(request.url).searchParams.get("state") === "closed" ? "closed" : "open";
18 return {
19 pulls: unwrap(await work.listPulls(path, getViewer(context), state)),
20 state,
21 } as const;
22}
23
24export default function Pulls({ loaderData, params }: Route.ComponentProps) {
25 const { pulls, state } = loaderData;
26 const base = `/${params.owner}/${params.repo}`;
27 return (
28 <div>
29 <StateTabs to={`${base}/pulls`} state={state} />
30 <div className="mt-4">
31 {pulls.length === 0 ? (
32 <EmptyState
33 title={state === "open" ? "No open pull requests" : "No closed pull requests"}
34 >
35 A pull request is a change proposed from its own fork. Open one
36 from an issue, or have an agent do it.
37 </EmptyState>
38 ) : (
39 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line">
40 {pulls.map((pull) => (
41 <li key={pull.id}>
42 <Link
43 to={`${base}/pull/${pull.number}`}
44 className="flex items-start gap-3 px-4 py-3 transition-colors hover:bg-surface"
45 >
46 <span className="mt-0.5">
47 <PullIcon status={pull.status} />
48 </span>
49 <span className="min-w-0 grow">
50 <span className="flex items-center gap-2">
51 <span className="truncate font-medium">{pull.title}</span>
52 {pull.status === "draft" && (
53 <span className="shrink-0 rounded-full border border-line px-1.5 py-px text-[0.6875rem] text-faint">
54 draft
55 </span>
56 )}
57 </span>
58 <span className="mt-0.5 block text-xs text-faint">
59 #{pull.number} opened <TimeAgo at={pull.createdAt} /> by{" "}
60 {pull.author.username}
61 {pull.issue != null && <> · for #{pull.issue}</>}
62 {pull.supersededBy != null && <> · superseded by #{pull.supersededBy}</>}
63 </span>
64 </span>
65 <span className="mt-0.5 flex shrink-0 items-center gap-1 font-mono text-xs text-muted">
66 <Bot size={13} />
67 {pull.agent}
68 </span>
69 </Link>
70 </li>
71 ))}
72 </ul>
73 )}
74 </div>
75 </div>
76 );
77}