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.

83 linesCodeBlameRaw
1import { Code2, MessagesSquare } from "lucide-react";
2import { Link, data } from "react-router";
3
4import type { Route } from "./+types/code-access";
5import { page } from "../../lib/meta";
6import { identity } from "../../lib/services.server";
7import { requireUser, roleIn } from "../../lib/session.server";
8import { Unavailable } from "../../components/boundary";
9import { isFailed, part } from "../../lib/partial";
10
11export function meta({ params, ...args }: Route.MetaArgs) {
12 return page(args, { title: `Code access · ${params.owner} · g1t` });
13}
14
15/**
16 * What a member without Code access sees in place of a repository, an
17 * issue, a pull request or a project list: what it was, and who to ask.
18 */
19export async function loader({ params, context, request }: Route.LoaderArgs) {
20 const viewer = requireUser(context, request);
21 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
22 // Who to ask is beside what the page says: without it, the page says that much.
23 const members = await part(identity.listMembers(params.owner, viewer), "The workspace's owners");
24 const owners = isFailed(members) ? members : members.ok ? members.value.filter((m) => m.role === "owner").slice(0, 4).map((m) => m.username) : [];
25 const from = new URL(request.url).searchParams.get("from");
26 // Only a path on this site, shown as text.
27 return { owners, from: from && /^\/[^/]/.test(from) ? from.split("?")[0] : null };
28}
29
30export default function CodeAccess({ loaderData, params }: Route.ComponentProps) {
31 const { owners, from } = loaderData;
32 const what = from ? from.split("/").filter(Boolean).slice(1).join("/") : null;
33 return (
34 <div className="mx-auto max-w-lg py-10 text-center">
35 <span className="mx-auto flex size-12 items-center justify-center rounded-xl border border-line bg-surface text-muted">
36 <Code2 size={22} />
37 </span>
38 <h1 className="mt-5 text-2xl font-semibold tracking-tight">Ask an owner for Code access</h1>
39 <p className="mt-2 text-[0.9375rem] leading-relaxed text-muted">
40 {what ? (
41 <>
42 <span className="font-mono text-fg-soft">{what}</span> is part of Code in {params.owner}.
43 </>
44 ) : (
45 <>This is part of Code in {params.owner}.</>
46 )}{" "}
47 Your membership includes Chat, Artifacts and Notifications. An owner can turn on Code access for you.
48 </p>
49 {isFailed(owners) ? (
50 <div className="mt-4 flex justify-center">
51 <Unavailable what={owners.what} compact />
52 </div>
53 ) : owners.length > 0 && (
54 <p className="mt-4 text-sm text-muted">
55 Owners:{" "}
56 {owners.map((owner, index) => (
57 <span key={owner}>
58 {index > 0 && ", "}
59 <Link to={`/u/${owner}`} className="font-medium text-fg hover:text-accent">
60 @{owner}
61 </Link>
62 </span>
63 ))}
64 </p>
65 )}
66 <div className="mt-8 flex justify-center gap-2">
67 <Link
68 to={`/${params.owner}/-/chat`}
69 className="inline-flex h-9 items-center gap-2 rounded-md bg-accent px-3.5 text-sm font-medium text-bg transition-colors hover:bg-accent-hover"
70 >
71 <MessagesSquare size={15} />
72 Back to Chat
73 </Link>
74 <Link
75 to={`/${params.owner}/-/home`}
76 className="inline-flex h-9 items-center rounded-md border border-line px-3.5 text-sm font-medium text-fg/90 transition-colors hover:border-line-strong hover:bg-surface"
77 >
78 Home
79 </Link>
80 </div>
81 </div>
82 );
83}