flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/apps/web/app/routes/workspace/settings.tsx

61 lines2,337 bytesCodeBlame
1import { Form, data, redirect } from "react-router";
2
3import type { Route } from "./+types/settings";
4import { Button, ErrorText, Field, Input } from "../../components/ui";
5import { identity } from "../../lib/services.server";
6import { assertSameOrigin, getViewer, requireUser, roleIn } from "../../lib/session.server";
7
8export function meta({ params }: Route.MetaArgs) {
9 return [{ title: `Settings · ${params.owner} · g1t` }];
10}
11
12export async function loader({ params, context }: Route.LoaderArgs) {
13 // Owners only; to anyone else the page does not exist.
14 if (roleIn(getViewer(context), params.owner) !== "owner") {
15 throw data(null, { status: 404 });
16 }
17 const workspace = await identity.getWorkspace(params.owner);
18 if (!workspace) throw data(null, { status: 404 });
19 return { workspace };
20}
21
22export async function action({ request, params, context }: Route.ActionArgs) {
23 assertSameOrigin(request);
24 const user = requireUser(context, request);
25 const form = await request.formData();
26 const result = await identity.updateWorkspace(user, params.owner, {
27 name: String(form.get("displayName") ?? ""),
28 description: String(form.get("description") ?? ""),
29 });
30 if (!result.ok) return { error: result.error.message };
31 throw redirect(`/${result.value.slug}`);
32}
33
34export default function WorkspaceSettings({ loaderData, actionData }: Route.ComponentProps) {
35 const { workspace } = loaderData;
36 return (
37 <div className="max-w-lg">
38 <h2 className="font-medium">Workspace details</h2>
39 <Form method="post" className="mt-5 space-y-4">
40 <Field label="Display name">
41 <Input name="displayName" maxLength={80} defaultValue={workspace.name} />
42 </Field>
43 <Field label="Description" hint="One line, shown at the top of the workspace's page.">
44 <Input
45 name="description"
46 maxLength={160}
47 defaultValue={workspace.description ?? ""}
48 />
49 </Field>
50 <Field
51 label="Name in URLs"
52 hint="This cannot be changed: repository addresses and clones depend on it."
53 >
54 <p className="font-mono text-sm text-muted">g1t.sh/{workspace.slug}</p>
55 </Field>
56 <ErrorText>{actionData?.error}</ErrorText>
57 <Button type="submit">Save</Button>
58 </Form>
59 </div>
60 );
61}