import { type FormEvent, useEffect, useRef, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; import { authClient } from "./auth-client.ts"; // Two-step auth form. Step 1: enter email (or click a social provider). Step 2: // enter password (sign-in) or name + password (sign-up). The previously-used // provider gets a "Last used" pill so returning users land on the right // button. Replaces Clerk's prebuilt / . const API_URL = import.meta.env.VITE_API_URL ?? "http://localhost:4100"; const LAST_PROVIDER_KEY = "superlog.auth.last_provider"; type Mode = "sign-in" | "sign-up"; type Step = "email" | "credentials"; type Provider = "email" | "google" | "github"; function readLastProvider(): Provider | null { if (typeof window === "undefined") return null; try { const v = window.localStorage.getItem(LAST_PROVIDER_KEY); if (v === "email" || v === "google" || v === "github") return v; } catch { /* ignore */ } return null; } function rememberProvider(p: Provider) { try { window.localStorage.setItem(LAST_PROVIDER_KEY, p); } catch { /* ignore */ } } // Reports which social providers have credentials configured server-side, so // the form can hide buttons that would 503 on click (e.g. worktree envs with // no GOOGLE_CLIENT_ID / GITHUB_CLIENT_ID). type ProvidersInfo = { google: boolean; github: boolean }; function useAuthProviders(): ProvidersInfo { const query = useQuery({ queryKey: ["auth-providers"], queryFn: async (): Promise => { const res = await fetch(`${API_URL}/api/auth-providers`, { credentials: "include" }); if (!res.ok) throw new Error(`${res.status}`); return (await res.json()) as ProvidersInfo; }, // Until the response lands, assume nothing is configured — better to // render no social buttons for half a tick than to flash a broken one. staleTime: 60_000, }); return query.data ?? { google: false, github: false }; } export function AuthForm({ initialMode = "sign-in", onSuccess, onClose, }: { initialMode?: Mode; onSuccess?: () => void; onClose?: () => void; }) { const providers = useAuthProviders(); const anySocial = providers.google || providers.github; const [mode, setMode] = useState(initialMode); const [step, setStep] = useState("email"); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [name, setName] = useState(""); const [error, setError] = useState(null); const [submitting, setSubmitting] = useState(false); const lastProvider = useRef(readLastProvider()); const emailRef = useRef(null); const passwordRef = useRef(null); useEffect(() => { if (step === "email") emailRef.current?.focus(); else passwordRef.current?.focus(); }, [step]); function onEmailSubmit(e: FormEvent) { e.preventDefault(); setError(null); if (!/.+@.+\..+/.test(email)) { setError("Enter a valid email address."); return; } setStep("credentials"); } async function onCredentialsSubmit(e: FormEvent) { e.preventDefault(); setError(null); setSubmitting(true); try { if (mode === "sign-up") { const result = await authClient.signUp.email({ email, password, name: name || email.split("@")[0] || "", }); if (result.error) { setError(result.error.message ?? "Sign-up failed"); return; } } else { const result = await authClient.signIn.email({ email, password }); if (result.error) { setError(result.error.message ?? "Sign-in failed"); return; } } rememberProvider("email"); onSuccess?.(); } catch (err) { setError(err instanceof Error ? err.message : "Unexpected error"); } finally { setSubmitting(false); } } async function onGoogle() { setError(null); rememberProvider("google"); try { await authClient.signIn.social({ provider: "google", callbackURL: `${window.location.origin}/`, }); } catch (err) { setError(err instanceof Error ? err.message : "Google sign-in failed"); } } async function onGithub() { setError(null); rememberProvider("github"); try { await authClient.signIn.social({ provider: "github", callbackURL: `${API_URL}/api/github/post-signin`, }); } catch (err) { setError(err instanceof Error ? err.message : "GitHub sign-in failed"); } } const title = mode === "sign-up" ? step === "email" ? "Create your Superlog account" : "Almost there" : step === "email" ? "Sign in to Superlog" : "Enter your password"; const subtitle = mode === "sign-up" ? step === "email" ? "Logs, traces, metrics, and a fix agent — all from one prompt." : `Setting up ${email}` : step === "email" ? "Welcome back! Please sign in to continue" : `Signing in as ${email}`; return (
{onClose && ( )}

{title}

{subtitle}

{step === "email" ? ( <> {anySocial && (
{providers.google && ( )} {providers.github && ( )}
)} {anySocial ? :
}
setEmail(e.target.value)} className={inputClass} autoComplete={mode === "sign-up" ? "email" : "username"} /> {error &&

{error}

} Continue
) : (
{mode === "sign-up" && ( setName(e.target.value)} className={inputClass} autoComplete="name" /> )} setPassword(e.target.value)} className={inputClass} autoComplete={mode === "sign-up" ? "new-password" : "current-password"} /> {error &&

{error}

}
{mode === "sign-in" && ( Forgot password? )}
{mode === "sign-up" ? "Create account" : "Sign in"}
)}

{mode === "sign-up" ? "Already have an account? " : "Don't have an account? "}

); } const inputClass = "h-11 w-full rounded-[8px] border border-border bg-surface-2 px-3.5 text-[14px] text-fg placeholder:text-subtle focus:border-border-strong focus:outline-none"; function Field({ label, children }: { label: string; children: React.ReactNode }) { return ( ); } function PrimaryButton({ type = "button", loading, children, }: { type?: "button" | "submit"; loading?: boolean; children: React.ReactNode; }) { return ( ); } function SocialButton({ provider, lastUsed, onClick, }: { provider: "google" | "github"; lastUsed: boolean; onClick: () => void; }) { return (
{lastUsed && ( Last used )}
); } function Divider() { return (
or
); } function GoogleGlyph() { return ( ); } function GithubGlyph() { return ( ); }