g1t/apps/web/app/components/not-found.tsx

84 lines3,611 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.

Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1import { Form, Link, useLocation, useRouteLoaderData } from "react-router";
2
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas3import type { Membership, User } from "@g1t/contracts";
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put4
5import { withNext } from "../lib/next";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look6import { useSignUpCopy } from "../lib/registration";
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put7import { missingKind, notFoundCopy } from "../lib/not-found";
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas8import { Pixel404 } from "./logo";
9import { ButtonLink } from "./ui";
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put10
11/**
12 * The page for anything someone cannot see. It reads only the kind of thing
13 * the address names and who is signed in, so a private project and a
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas14 * missing one give the same page, word for word, and the same 404.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put15 */
16export function NotFound({ data }: { data?: unknown }) {
17 const { pathname, search } = useLocation();
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas18 const root = useRouteLoaderData("root") as
19 | { user?: User | null; shell?: { workspace?: Membership | null; repo?: { namespace: string; name: string } | null } }
20 | undefined;
21 const user = root?.user ?? null;
22 const workspace = user ? (root?.shell?.workspace ?? null) : null;
23 // Something missing inside a project the viewer can see: back to it.
24 const project = root?.shell?.repo ?? null;
25 const inProject =
26 project != null && pathname.toLowerCase().startsWith(`/${project.namespace}/${project.name}/`.toLowerCase());
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put27 const kind = missingKind(data, pathname);
28 const copy = notFoundCopy(kind, user?.username);
29 const here = pathname + search;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look30 const signUp = useSignUpCopy();
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put31 return (
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas32 <main className="mx-auto flex max-w-lg flex-col items-center px-4 py-20 text-center sm:py-28">
33 <Pixel404 className="text-[3.5rem] sm:text-[4.5rem]" />
34 <h1 className="mt-10 text-balance text-2xl font-semibold tracking-tight">{copy.title}</h1>
35 <p className="mt-2 text-balance text-muted">{copy.body}</p>
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put36
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas37 <div className="mt-8 flex flex-wrap justify-center gap-2">
38 {copy.signIn ? (
39 <ButtonLink to={withNext("/login", here)}>Sign in</ButtonLink>
40 ) : kind === "person" ? (
41 <ButtonLink to="/search?type=people">Search people</ButtonLink>
42 ) : inProject ? (
43 <ButtonLink to={`/${project.namespace}/${project.name}`}>Go to {project.name}</ButtonLink>
44 ) : workspace ? (
45 <ButtonLink to={`/${workspace.slug}`}>Go to {workspace.name?.trim() || workspace.slug}</ButtonLink>
46 ) : user ? (
47 <ButtonLink to="/">Go to Mission control</ButtonLink>
48 ) : null}
49 <ButtonLink to="/explore" variant="quiet">
50 {user ? "Explore" : "Explore public projects"}
51 </ButtonLink>
52 </div>
53
54 <p className="mt-6 hidden text-sm text-faint sm:block">
55 Looking for something? Press{" "}
56 <kbd className="rounded bg-raised px-1.5 py-0.5 font-mono text-[0.6875rem] text-muted ring-1 ring-line">⌘K</kbd>{" "}
57 to search.
58 </p>
59
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put60 {copy.signIn && (
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas61 <p className="mt-3 text-xs text-faint">
62 New to g1t?{" "}
63 <Link to={withNext("/register", here)} className="text-muted underline-offset-4 hover:text-fg hover:underline">
64 {signUp.primary}
65 </Link>
66 </p>
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put67 )}
68
69 {copy.signedInAs && (
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas70 // Signing out, then in again, comes back here.
71 <Form
72 method="post"
73 action={`/logout?next=${encodeURIComponent(withNext("/login", here))}`}
74 className="mt-3 text-xs text-faint"
75 >
76 Signed in as <span className="font-mono text-muted">@{copy.signedInAs}</span> ·{" "}
77 <button type="submit" className="text-muted underline-offset-4 hover:text-fg hover:underline">
78 Switch account
79 </button>
80 </Form>
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put81 )}
82 </main>
83 );
84}