fix(web): rewrite CLI auth login as standalone component

Remove dependency on SocialProviders/RegisterForm which need
React Query providers. Self-contained with authClient directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-13 09:06:42 +01:00
parent 0f46c787a7
commit 465ff9a10e

View File

@@ -1,11 +1,7 @@
"use client"; "use client";
import { SocialProvider } from "@turbostarter/auth"; import { useState } from "react";
import { authClient } from "~/lib/auth/client";
import { authConfig } from "~/config/auth";
import { SocialProviders } from "~/modules/auth/form/social-providers";
import { RegisterForm } from "~/modules/auth/form/register-form";
import { AuthDivider } from "~/modules/auth/layout/divider";
interface Props { interface Props {
code: string; code: string;
@@ -13,45 +9,136 @@ interface Props {
export function CliAuthLogin({ code }: Props) { export function CliAuthLogin({ code }: Props) {
const redirectTo = `/cli-auth?code=${encodeURIComponent(code)}`; const redirectTo = `/cli-auth?code=${encodeURIComponent(code)}`;
const [loading, setLoading] = useState<string | null>(null);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [name, setName] = useState("");
const [mode, setMode] = useState<"login" | "register">("login");
const [error, setError] = useState("");
const handleSocial = async (provider: "google" | "github") => {
setLoading(provider);
setError("");
try {
await authClient.signIn.social({
provider,
callbackURL: redirectTo,
});
} catch (e) {
setError(e instanceof Error ? e.message : "Sign-in failed");
setLoading(null);
}
};
const handleEmailSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading("email");
setError("");
try {
if (mode === "register") {
await authClient.signUp.email({
email,
password,
name: name || email.split("@")[0] || "User",
callbackURL: redirectTo,
});
} else {
await authClient.signIn.email({
email,
password,
callbackURL: redirectTo,
});
}
window.location.href = redirectTo;
} catch (e) {
setError(e instanceof Error ? e.message : "Failed");
setLoading(null);
}
};
const btnBase = "w-full flex items-center justify-center gap-3 rounded-lg px-4 py-3 text-[15px] font-medium transition-all";
const btnOutline = `${btnBase} border border-[var(--cm-border,#333)] text-[var(--cm-fg,#fafafa)] hover:bg-[var(--cm-bg-elevated,#1a1a1a)]`;
const btnPrimary = `${btnBase} bg-[var(--cm-clay,#b07a56)] text-[var(--cm-fg,#fafafa)] hover:opacity-90`;
const inputBase = "w-full rounded-lg border border-[var(--cm-border,#333)] bg-[var(--cm-bg,#0a0a0a)] px-4 py-3 text-[15px] text-[var(--cm-fg,#fafafa)] placeholder:text-[var(--cm-fg-muted,#666)] focus:outline-none focus:ring-2 focus:ring-[var(--cm-clay,#b07a56)]/50 focus:border-[var(--cm-clay,#b07a56)]";
return ( return (
<div className="w-full max-w-md space-y-8 p-8"> <div className="w-full max-w-[400px] space-y-6 p-8">
{/* Header */} {/* Header */}
<div className="text-center space-y-3"> <div className="text-center space-y-3">
<div <div className="mx-auto w-14 h-14 rounded-2xl flex items-center justify-center" style={{ background: "var(--cm-clay, #b07a56)" }}>
className="mx-auto w-14 h-14 rounded-2xl flex items-center justify-center text-2xl font-bold" <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
style={{ background: "var(--cm-accent, #f97316)", color: "#000" }} <circle cx="12" cy="4" r="2" fill="#fff" />
> <circle cx="4" cy="12" r="2" fill="#fff" />
cm <circle cx="20" cy="12" r="2" fill="#fff" />
<circle cx="12" cy="20" r="2" fill="#fff" />
<path d="M12 4L4 12M12 4L20 12M4 12L12 20M20 12L12 20" stroke="#fff" strokeWidth="1.2" opacity="0.5" />
</svg>
</div> </div>
<h1 className="text-2xl font-bold tracking-tight"> <h1 className="text-[22px] font-bold tracking-tight">
Connect to claudemesh CLI Connect to claudemesh CLI
</h1> </h1>
<p className="text-sm" style={{ color: "var(--cm-fg-muted, #888)" }}> <p className="text-[14px]" style={{ color: "var(--cm-fg-muted, #888)" }}>
Sign in or create an account to connect your terminal session. {mode === "login" ? "Sign in" : "Create an account"} to connect your terminal session.
</p> </p>
</div> </div>
{/* Social providers */} {/* Social buttons */}
<SocialProviders <div className="space-y-2.5">
providers={authConfig.providers.oAuth as SocialProvider[]} <button onClick={() => handleSocial("google")} disabled={!!loading} className={btnOutline}>
redirectTo={redirectTo} {loading === "google" ? (
/> <span className="animate-spin"></span>
) : (
<svg width="18" height="18" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
)}
Continue with Google
</button>
<button onClick={() => handleSocial("github")} disabled={!!loading} className={btnOutline}>
{loading === "github" ? (
<span className="animate-spin"></span>
) : (
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2.2c-3.3.7-4-1.4-4-1.4-.5-1.4-1.3-1.8-1.3-1.8-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6a4.7 4.7 0 011.3-3.3c-.2-.3-.6-1.6.1-3.3 0 0 1-.3 3.3 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 3 .1 3.3a4.7 4.7 0 011.3 3.3c0 4.7-2.8 5.7-5.5 6 .4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3"/></svg>
)}
Continue with GitHub
</button>
</div>
<AuthDivider /> {/* Divider */}
<div className="flex items-center gap-4">
<div className="flex-1 h-px" style={{ background: "var(--cm-border, #333)" }} />
<span className="text-[12px] uppercase tracking-wider" style={{ color: "var(--cm-fg-muted, #666)" }}>or</span>
<div className="flex-1 h-px" style={{ background: "var(--cm-border, #333)" }} />
</div>
{/* Email + password form */} {/* Email form */}
<RegisterForm redirectTo={redirectTo} /> <form onSubmit={handleEmailSubmit} className="space-y-3">
{mode === "register" && (
<input type="text" placeholder="Name" value={name} onChange={e => setName(e.target.value)} className={inputBase} />
)}
<input type="email" placeholder="Email" value={email} onChange={e => setEmail(e.target.value)} required className={inputBase} />
<input type="password" placeholder="Password" value={password} onChange={e => setPassword(e.target.value)} required minLength={8} className={inputBase} />
{/* Device code footer */} {error && <p className="text-[13px] text-red-400">{error}</p>}
<button type="submit" disabled={!!loading} className={btnPrimary}>
{loading === "email" ? "..." : mode === "login" ? "Sign in" : "Create account"}
</button>
</form>
{/* Toggle mode */}
<p className="text-center text-[13px]" style={{ color: "var(--cm-fg-muted, #888)" }}>
{mode === "login" ? (
<>Don&apos;t have an account?{" "}<button onClick={() => { setMode("register"); setError(""); }} className="underline hover:text-[var(--cm-fg)]">Register</button></>
) : (
<>Already have an account?{" "}<button onClick={() => { setMode("login"); setError(""); }} className="underline hover:text-[var(--cm-fg)]">Sign in</button></>
)}
</p>
{/* Device code */}
<div className="pt-2 text-center"> <div className="pt-2 text-center">
<div <div className="inline-block rounded-lg px-5 py-2.5 font-mono text-lg tracking-[0.25em]" style={{ background: "var(--cm-bg-elevated, #1a1a1a)", border: "1px solid var(--cm-border, #333)" }}>
className="inline-block rounded-lg px-4 py-2 font-mono text-sm tracking-widest"
style={{ background: "var(--cm-bg-muted, #1a1a1a)", color: "var(--cm-fg-muted, #888)" }}
>
{code} {code}
</div> </div>
<p className="mt-2 text-xs" style={{ color: "var(--cm-fg-muted, #666)" }}> <p className="mt-2 text-[12px]" style={{ color: "var(--cm-fg-muted, #666)" }}>
Confirm this code matches your terminal Confirm this code matches your terminal
</p> </p>
</div> </div>