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.

53 linesCodeBlameRaw
1import { data, useNavigate } from "react-router";
2
3import type { Route } from "./+types/space-new";
4import { useFoliosAction } from "../../../components/folios/actions";
5import { SpaceForm, initialSpace, spaceInput } from "../../../components/folios/space-form";
6import { spacePath } from "../../../lib/folios";
7import { page } from "../../../lib/meta";
8import { identity } from "../../../lib/services.server";
9import { requireUser, roleIn } from "../../../lib/session.server";
10import { Unavailable } from "../../../components/boundary";
11import { isFailed, orElse, part } from "../../../lib/partial";
12
13export function meta({ params, ...args }: Route.MetaArgs) {
14 return page(args, { title: `New space · Artifacts · ${params.owner} · g1t` });
15}
16
17export async function loader({ params, context, request }: Route.LoaderArgs) {
18 const viewer = requireUser(context, request);
19 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
20 // The teams a space can be for: without them, a space for everyone or
21 // for its members can still be made, and the page says why no team is offered.
22 const teams = await part(identity.listTeams(viewer, params.owner.toLowerCase()), "Teams");
23 return { teams: isFailed(teams) ? teams : teams.ok ? teams.value.map((t) => ({ slug: t.slug, name: t.name })) : [] };
24}
25
26/** A new space: for everyone, a team, or only its members. Whoever makes it has full access. */
27export default function NewSpace({ loaderData, params }: Route.ComponentProps) {
28 const slug = params.owner.toLowerCase();
29 const navigate = useNavigate();
30 const { send, busy, error } = useFoliosAction(slug);
31 return (
32 <div className="mx-auto max-w-2xl">
33 <h1 className="text-xl font-semibold tracking-tight">New space</h1>
34 <p className="mt-1 mb-8 text-sm text-muted">A space holds artifacts for a team, a project or a topic. What goes in it follows its access.</p>
35 {isFailed(loaderData.teams) && (
36 <div className="mb-4">
37 <Unavailable what={loaderData.teams.what} compact />
38 </div>
39 )}
40 <SpaceForm
41 initial={initialSpace()}
42 teams={orElse(loaderData.teams, [])}
43 submitLabel="Create space"
44 busy={busy}
45 error={error}
46 onSubmit={async (value) => {
47 const made = await send<{ slug: string }>("create_space", { space: spaceInput(value) });
48 if (made.ok) navigate(spacePath(slug, made.value.slug));
49 }}
50 />
51 </div>
52 );
53}