|
| 1 | +import { createFileRoute, Link } from "@tanstack/react-router"; |
| 2 | +import { ArrowRight, Github, Loader2, ShieldCheck, GitPullRequest } from "lucide-react"; |
| 3 | + |
| 4 | +import { Section, SectionTitle, Card, Callout, Eyebrow } from "@/components/site/primitives"; |
| 5 | +import { useSession } from "@/lib/api/session"; |
| 6 | + |
| 7 | +// Self-serve signup surface (part of #4802). The install flow's first step ("Sign up") previously had |
| 8 | +// no dedicated page -- this is it: it explains the GitHub-backed account model and starts the real |
| 9 | +// GitHub OAuth flow (useSession().signIn -> /v1/auth/github/start), then points to /install to connect |
| 10 | +// a repository. No credential form is collected here; identity is GitHub's, matching how the rest of the |
| 11 | +// app authenticates. Reads no secrets and fabricates no session. |
| 12 | + |
| 13 | +export const Route = createFileRoute("/signup")({ |
| 14 | + head: () => ({ |
| 15 | + meta: [ |
| 16 | + { title: "Sign up — LoopOver self-serve" }, |
| 17 | + { |
| 18 | + name: "description", |
| 19 | + content: |
| 20 | + "Create your LoopOver account with GitHub, then install the App on your own repository — self-serve, no engineering step required.", |
| 21 | + }, |
| 22 | + { property: "og:title", content: "Sign up — LoopOver self-serve" }, |
| 23 | + { |
| 24 | + property: "og:description", |
| 25 | + content: "Sign up with GitHub and connect your repository, self-serve.", |
| 26 | + }, |
| 27 | + { property: "og:url", content: "/signup" }, |
| 28 | + ], |
| 29 | + links: [{ rel: "canonical", href: "/signup" }], |
| 30 | + }), |
| 31 | + component: SignupPage, |
| 32 | +}); |
| 33 | + |
| 34 | +const POINTS = [ |
| 35 | + { |
| 36 | + icon: Github, |
| 37 | + title: "GitHub is your identity", |
| 38 | + description: |
| 39 | + "No separate password to manage. You sign in with GitHub, and your account is tied to the repositories you already own.", |
| 40 | + }, |
| 41 | + { |
| 42 | + icon: ShieldCheck, |
| 43 | + title: "Scoped from the start", |
| 44 | + description: |
| 45 | + "Signing up grants nothing on your repositories. Access is requested only when you install the App, and you confirm the exact scopes then.", |
| 46 | + }, |
| 47 | + { |
| 48 | + icon: GitPullRequest, |
| 49 | + title: "Straight to connecting a repo", |
| 50 | + description: |
| 51 | + "Once you're signed in, connect a repository and LoopOver starts reviewing its pull requests — no engineering handoff.", |
| 52 | + }, |
| 53 | +]; |
| 54 | + |
| 55 | +export function SignupPage() { |
| 56 | + const { auth, signIn } = useSession(); |
| 57 | + const isStarting = auth.status === "starting"; |
| 58 | + |
| 59 | + return ( |
| 60 | + <> |
| 61 | + <Section className="pt-16 pb-12 sm:pt-24"> |
| 62 | + <div className="max-w-3xl"> |
| 63 | + <Eyebrow accent>Step 1 · Sign up</Eyebrow> |
| 64 | + <h1 className="mt-4 text-token-2xl font-medium tracking-tight text-foreground"> |
| 65 | + Create your account with GitHub. |
| 66 | + </h1> |
| 67 | + <p className="mt-4 max-w-2xl text-token-md text-muted-foreground"> |
| 68 | + LoopOver is self-serve: sign up with GitHub, then install the App on your own repository |
| 69 | + and confirm the scoped permissions — no manual or engineering step. |
| 70 | + </p> |
| 71 | + <div className="mt-7 flex flex-wrap items-center gap-2"> |
| 72 | + <button |
| 73 | + type="button" |
| 74 | + onClick={() => void signIn()} |
| 75 | + disabled={isStarting} |
| 76 | + className="inline-flex h-9 items-center justify-center gap-1.5 whitespace-nowrap rounded-token bg-coral px-4 text-token-sm font-medium text-primary-foreground transition-[filter,transform] duration-150 hover:brightness-110 active:scale-[0.98] focus-ring motion-reduce:transition-none motion-reduce:active:scale-100 disabled:cursor-not-allowed disabled:opacity-60" |
| 77 | + > |
| 78 | + {isStarting ? ( |
| 79 | + <Loader2 className="size-3.5 animate-spin motion-reduce:animate-none" /> |
| 80 | + ) : ( |
| 81 | + <Github className="size-3.5" /> |
| 82 | + )} |
| 83 | + {isStarting ? "Starting sign-up…" : "Continue with GitHub"} |
| 84 | + </button> |
| 85 | + <Link |
| 86 | + to="/install" |
| 87 | + className="inline-flex h-9 items-center justify-center gap-1.5 whitespace-nowrap rounded-token border border-border bg-transparent px-4 text-token-sm font-medium text-foreground transition-colors duration-150 hover:bg-accent focus-ring motion-reduce:transition-none" |
| 88 | + > |
| 89 | + See the setup steps |
| 90 | + <ArrowRight className="size-3.5" /> |
| 91 | + </Link> |
| 92 | + </div> |
| 93 | + {auth.status === "error" ? ( |
| 94 | + <p className="mt-3 max-w-md rounded-token border border-danger/40 bg-danger/10 px-3 py-2 text-token-xs text-danger"> |
| 95 | + {auth.message} |
| 96 | + </p> |
| 97 | + ) : null} |
| 98 | + </div> |
| 99 | + </Section> |
| 100 | + |
| 101 | + <Section className="py-0"> |
| 102 | + <SectionTitle |
| 103 | + eyebrow="What signing up means" |
| 104 | + title="An account you already control" |
| 105 | + description="Signup is a GitHub sign-in, not a new credential store. You stay in control of what LoopOver can access, and when." |
| 106 | + /> |
| 107 | + <div className="mt-8 grid gap-4 sm:grid-cols-3"> |
| 108 | + {POINTS.map((point) => { |
| 109 | + const Icon = point.icon; |
| 110 | + return ( |
| 111 | + <Card key={point.title}> |
| 112 | + <div className="flex items-center gap-2 text-muted-foreground"> |
| 113 | + <Icon aria-hidden className="size-4" /> |
| 114 | + <h3 className="text-token-md font-medium text-foreground">{point.title}</h3> |
| 115 | + </div> |
| 116 | + <p className="mt-2 text-token-sm text-muted-foreground">{point.description}</p> |
| 117 | + </Card> |
| 118 | + ); |
| 119 | + })} |
| 120 | + </div> |
| 121 | + </Section> |
| 122 | + |
| 123 | + <Section className="pt-12 pb-24"> |
| 124 | + <div className="max-w-3xl"> |
| 125 | + <Callout variant="safety" title="Nothing is granted by signing up"> |
| 126 | + Creating an account grants LoopOver no access to any repository. That happens only at |
| 127 | + install, where you pick the repositories and confirm the scopes. See{" "} |
| 128 | + <Link to="/install" className="text-foreground underline underline-offset-2"> |
| 129 | + the setup steps |
| 130 | + </Link>{" "} |
| 131 | + for what comes next. |
| 132 | + </Callout> |
| 133 | + </div> |
| 134 | + </Section> |
| 135 | + </> |
| 136 | + ); |
| 137 | +} |
0 commit comments