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

107 lines4,930 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.

status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1import { identity } from "../../lib/services.server";
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step2import { Form, redirect, useSearchParams } from "react-router";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas3
4import type { Route } from "./+types/applications";
5import { page } from "../../lib/meta";
Merge branch 'worktree-agent-a985c01f43538af01'6import { ButtonLink, ErrorText, SubmitButton, TimeAgo } from "../../components/ui";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas7import { DeleteButton } from "../../components/account-settings";
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step8import { AccessSummary, ScopeChecklist } from "../../components/token-scopes";
9import { grantFromForm } from "../../lib/token-scopes";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas10import { assertSameOrigin, requireUser } from "../../lib/session.server";
11
12export function meta(args: Route.MetaArgs) {
13 return page(args, { title: "Connected applications · Settings · g1t" });
14}
15
16export async function loader({ request, context }: Route.LoaderArgs) {
17 const user = requireUser(context, request);
18 return { applications: await identity.listOAuthGrants(user) };
19}
20
21export async function action({ request, context }: Route.ActionArgs) {
22 assertSameOrigin(request);
23 const user = requireUser(context, request);
24 const form = await request.formData();
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step25 const id = String(form.get("id") ?? "");
26 switch (form.get("intent")) {
27 case "sign-out-application":
28 await identity.revokeOAuthGrant(user, id);
29 return null;
30 case "update-application": {
31 const grant = grantFromForm(form);
32 if (!grant.ok) return { editing: id, error: grant.error };
33 const updated = await identity.updateOAuthGrant(user, id, grant.value);
34 if (!updated.ok) return { editing: id, error: updated.error.message };
35 throw redirect("/settings/applications");
36 }
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas37 }
38 return null;
39}
40
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step41export default function ApplicationSettings({ loaderData, actionData }: Route.ComponentProps) {
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas42 const { applications } = loaderData;
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step43 const [params] = useSearchParams();
44 const editing = actionData?.editing ?? params.get("edit");
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas45 return (
46 <section id="applications" className="scroll-mt-20">
47 {applications.length === 0 ? (
48 <p className="text-sm text-faint">None yet.</p>
49 ) : (
50 <ul className="divide-y divide-line rounded-md border border-line">
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step51 {applications.map((application) => {
52 const legacy = application.legacy && application.scopes === null;
53 const open = editing === application.id;
54 return (
55 <li key={application.id} className="px-4 py-3">
56 <div className="flex flex-wrap items-start gap-x-4 gap-y-2">
57 <div className="min-w-0 grow basis-60">
58 <p className="truncate text-sm font-medium">{application.clientName}</p>
59 <p className="text-xs text-faint">
60 Connected <TimeAgo at={application.createdAt} /> · last used{" "}
61 <TimeAgo at={application.lastUsedAt} />
62 </p>
63 <AccessSummary holder={application} />
64 {legacy && (
65 <p className="mt-1.5 text-xs text-warn">
66 Signed in before applications asked for scopes, so it can do everything you can.
67 Narrow it to what it needs.
68 </p>
69 )}
70 </div>
71 <div className="ml-auto flex shrink-0 items-center gap-2">
72 {!open && (
73 <ButtonLink variant="quiet" to={`?edit=${application.id}`} preventScrollReset>
74 Change access
75 </ButtonLink>
76 )}
Merge branch 'worktree-agent-a985c01f43538af01'77 <DeleteButton intent="sign-out-application" id={application.id} label="Sign out" pending="Signing out…" />
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step78 </div>
79 </div>
80 {open && (
81 <Form method="post" className="mt-4 space-y-5 border-t border-line pt-4">
82 <input type="hidden" name="intent" value="update-application" />
83 <input type="hidden" name="id" value={application.id} />
84 <p className="text-sm text-muted">
85 {application.clientName} stays signed in. What it may do changes at once, and its
86 next refresh keeps the change.
87 </p>
88 <ScopeChecklist initial={application.scopes} />
89 {actionData?.editing === application.id && <ErrorText>{actionData.error}</ErrorText>}
90 <div className="flex gap-2">
Merge branch 'worktree-agent-a985c01f43538af01'91 <SubmitButton pending="Saving…" match={{ intent: "update-application", id: application.id }}>
92 Save access
93 </SubmitButton>
Thirteen MCP tools and classic token scopes; agents rate their confidence and can be put on an issue in one step94 <ButtonLink variant="quiet" to="." preventScrollReset>
95 Cancel
96 </ButtonLink>
97 </div>
98 </Form>
99 )}
100 </li>
101 );
102 })}
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas103 </ul>
104 )}
105 </section>
106 );
107}

This file's history is long; its oldest lines are credited to the oldest commit read.