Skip to content
44 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";
10
11export function meta({ params, ...args }: Route.MetaArgs) {
12 return page(args, { title: `New space · Artifacts · ${params.owner} · g1t` });
13}
14
15export async function loader({ params, context, request }: Route.LoaderArgs) {
16 const viewer = requireUser(context, request);
17 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
18 const teams = await identity.listTeams(viewer, params.owner.toLowerCase()).catch(() => null);
19 return { teams: teams?.ok ? teams.value.map((t) => ({ slug: t.slug, name: t.name })) : [] };
20}
21
22/** A new space: for everyone, a team, or only its members. Whoever makes it has full access. */
23export default function NewSpace({ loaderData, params }: Route.ComponentProps) {
24 const slug = params.owner.toLowerCase();
25 const navigate = useNavigate();
26 const { send, busy, error } = useFoliosAction(slug);
27 return (
28 <div className="mx-auto max-w-2xl">
29 <h1 className="text-xl font-semibold tracking-tight">New space</h1>
30 <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>
31 <SpaceForm
32 initial={initialSpace()}
33 teams={loaderData.teams}
34 submitLabel="Create space"
35 busy={busy}
36 error={error}
37 onSubmit={async (value) => {
38 const made = await send<{ slug: string }>("create_space", { space: spaceInput(value) });
39 if (made.ok) navigate(spacePath(slug, made.value.slug));
40 }}
41 />
42 </div>
43 );
44}