g1t/apps/web/app/components/add-ci.tsx

54 lines2,198 bytesCodeBlame
1/**
2 * What a repository with no checks shows where checks would be: that
3 * nothing proves a change works, and a button that opens a pull request
4 * adding a starter workflow for the stack g1t finds (routes/repo/add-ci.ts).
5 */
6import { FlaskConical, LoaderCircle, Plus } from "lucide-react";
7import { useFetcher } from "react-router";
8
9import { Button, ErrorText } from "./ui";
10
11export function AddCiPrompt({
12 owner,
13 repo,
14 canAdd,
15 compact = false,
16}: {
17 owner: string;
18 repo: string;
19 /** Whether the viewer may push: who may open the pull request. */
20 canAdd: boolean;
21 /** Fits a row of the merge box rather than a page. */
22 compact?: boolean;
23}) {
24 const fetcher = useFetcher<{ error?: string }>();
25 const adding = fetcher.state !== "idle";
26 return (
27 <div className={compact ? "flex gap-3 px-4 py-3" : "flex gap-3 rounded-xl border border-dashed border-line-strong bg-surface p-4"}>
28 <FlaskConical size={16} className="mt-0.5 shrink-0 text-warn" />
29 <div className="min-w-0 grow">
30 <p className="text-sm font-medium">This repository has no checks</p>
31 <p className="mt-0.5 text-sm text-muted">
32 Nothing runs on its pull requests, so neither people nor agents can prove a change works before it merges.
33 Add a workflow in <code className="text-fg">.g1t/workflows</code>, then require it on the default branch.
34 </p>
35 {canAdd && (
36 <fetcher.Form method="post" action={`/${owner}/${repo}/add-ci`} className="mt-3 flex flex-wrap items-center gap-3">
37 <Button variant="primary" type="submit" disabled={adding}>
38 {adding ? <LoaderCircle size={14} className="animate-spin" /> : <Plus size={14} />}
39 {adding ? "Opening a pull request…" : "Add CI"}
40 </Button>
41 <span className="text-xs text-faint">
42 Opens a pull request with a starter workflow for the stack g1t finds. You can change it before merging.
43 </span>
44 </fetcher.Form>
45 )}
46 {fetcher.data?.error && (
47 <div className="mt-2">
48 <ErrorText>{fetcher.data.error}</ErrorText>
49 </div>
50 )}
51 </div>
52 </div>
53 );
54}