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

63 lines2,327 bytesCodeBlame
1import { env } from "cloudflare:workers";
2import { Link } from "react-router";
3
4import type { Route } from "./+types/intents";
5import { Status, TimeAgo } from "../../components/ui";
6import { getViewer, unwrap } from "../../lib/session.server";
7
8export async function loader({ params, context }: Route.LoaderArgs) {
9 const path = { namespace: params.owner, name: params.repo };
10 return {
11 intents: unwrap(await env.WORK.listIntents(path, getViewer(context))),
12 };
13}
14
15export default function Intents({ loaderData, params }: Route.ComponentProps) {
16 const { intents } = loaderData;
17 const base = `/${params.owner}/${params.repo}/intents`;
18 return (
19 <div className="mt-6">
20 <div className="flex items-center justify-between">
21 <p className="text-sm text-muted">
22 An intent is a goal for this repo. Agents attempt it in parallel and
23 the best attempt ships.
24 </p>
25 <Link
26 to={`${base}/new`}
27 className="shrink-0 rounded-md bg-fg px-3 py-2 text-sm font-medium text-bg hover:bg-white"
28 >
29 New intent
30 </Link>
31 </div>
32 {intents.length === 0 ? (
33 <p className="mt-10 text-center text-muted">No intents yet.</p>
34 ) : (
35 <ul className="mt-4 divide-y divide-line rounded-md border border-line">
36 {intents.map((intent) => (
37 <li key={intent.id}>
38 <Link
39 to={`${base}/${intent.number}`}
40 className="flex items-center gap-4 px-4 py-3 hover:bg-surface"
41 >
42 <span className="w-10 font-mono text-sm text-muted">
43 #{intent.number}
44 </span>
45 <span className="min-w-0 grow">
46 <span className="block truncate">{intent.title}</span>
47 <span className="text-xs text-muted">
48 {intent.author.username} · <TimeAgo at={intent.createdAt} />
49 </span>
50 </span>
51 <span className="shrink-0 text-xs text-muted">
52 {intent.attemptCount}{" "}
53 {intent.attemptCount === 1 ? "attempt" : "attempts"}
54 </span>
55 <Status value={intent.status} />
56 </Link>
57 </li>
58 ))}
59 </ul>
60 )}
61 </div>
62 );
63}