g1t/apps/web/app/routes/workspace-new.tsx

70 lines2,463 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Workspaces own repositories1import { Form, redirect } from "react-router";
2
3import type { Route } from "./+types/workspace-new";
4import { Button, ErrorText, Field, Input } from "../components/ui";
5import { identity } from "../lib/services.server";
6import { assertSameOrigin, requireUser } from "../lib/session.server";
7
8export function meta({}: Route.MetaArgs) {
9 return [{ title: "New workspace · g1t" }];
10}
11
12export function loader({ request, context }: Route.LoaderArgs) {
13 const user = requireUser(context, request);
14 return { user, first: (user.workspaces ?? []).length === 0 };
15}
16
17export async function action({ request, context }: Route.ActionArgs) {
18 assertSameOrigin(request);
19 const user = requireUser(context, request);
20 const form = await request.formData();
21 const result = await identity.createWorkspace(
22 user,
23 String(form.get("slug") ?? ""),
24 String(form.get("name") ?? ""),
25 );
26 if (!result.ok) return { error: result.error.message };
27 throw redirect(`/${result.value.slug}`);
28}
29
30export default function NewWorkspace({
31 loaderData,
32 actionData,
33}: Route.ComponentProps) {
34 const { user, first } = loaderData;
35 return (
36 <main className="mx-auto max-w-lg px-4 py-12">
37 <h1 className="text-xl font-semibold">
38 {first ? "Create your first workspace" : "New workspace"}
39 </h1>
40 <p className="mt-2 text-sm text-muted">
41 A workspace owns repositories and is the first part of their address:{" "}
42 <span className="font-mono text-fg">g1t.sh/workspace/repo</span>. Use
43 one for yourself, and one for each team or company you work with.
44 </p>
45 <Form method="post" className="mt-8 space-y-4">
46 <Field
47 label="Name in URLs"
48 hint="Lowercase letters, digits and single hyphens. It cannot be changed later."
49 >
50 <div className="flex items-center gap-2 font-mono text-sm">
51 <span className="text-muted">g1t.sh/</span>
52 <Input
53 name="slug"
54 required
55 autoFocus
56 maxLength={39}
57 defaultValue={first ? user.username : ""}
58 pattern="[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9]))*"
59 />
60 </div>
61 </Field>
62 <Field label="Display name (optional)">
63 <Input name="name" maxLength={80} />
64 </Field>
65 <ErrorText>{actionData?.error}</ErrorText>
66 <Button type="submit">Create workspace</Button>
67 </Form>
68 </main>
69 );
70}