purcar-chat-api / src /components /AuthModal.tsx
ihatebaselines's picture
Deploy Purcar gateway and local model engine
7e92e00 verified
Raw History Blame Contribute Delete
7.28 kB
"use client";
import React, { useState } from "react";
import Image from "next/image";
import { X, Mail, Lock, User, ArrowRight, ShieldCheck, Check, Sparkles } from "lucide-react";
import { supabase } from "@/lib/supabase";
interface AuthModalProps {
isOpen: boolean;
onClose: () => void;
onSuccess?: () => void;
}
export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onClose, onSuccess }) => {
const [isSignUp, setIsSignUp] = useState(true);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [username, setUsername] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [successMsg, setSuccessMsg] = useState<string | null>(null);
if (!isOpen) return null;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);
setSuccessMsg(null);
try {
if (isSignUp) {
const { data, error: signUpErr } = await supabase.auth.signUp({
email,
password,
options: {
data: {
username: username || email.split("@")[0],
},
},
});
if (signUpErr) throw signUpErr;
setSuccessMsg(data.session ? "Account created and signed in!" : "Account created. Check your email to confirm.");
setTimeout(() => {
onClose();
if (onSuccess) onSuccess();
}, 1200);
} else {
const { data, error: signInErr } = await supabase.auth.signInWithPassword({
email,
password,
});
if (signInErr) throw signInErr;
setSuccessMsg("Signed in successfully!");
setTimeout(() => {
onClose();
if (onSuccess) onSuccess();
}, 1000);
}
} catch (err: any) {
setError(err?.message || "Authentication error occurred");
} finally {
setLoading(false);
}
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-xl">
<div className="relative w-full max-w-md p-6 rounded-3xl glass border border-white/10 shadow-2xl bg-[#09090b]">
{/* Close Button */}
<button
onClick={onClose}
className="absolute top-4 right-4 p-2 rounded-full text-zinc-400 hover:text-white hover:bg-white/10 transition-colors"
>
<X className="w-4 h-4" />
</button>
{/* Header */}
<div className="flex flex-col items-center text-center space-y-3 mb-6">
<div className="relative w-14 h-14 rounded-2xl overflow-hidden border border-white/20 shadow-xl ring-4 ring-white/5">
<Image src="/purcar-logo.png" alt="Purcar Logo" fill className="object-cover" />
</div>
<div>
<h3 className="text-lg font-bold text-white font-mono tracking-tight">
{isSignUp ? "Create Your Purcar Account" : "Welcome Back to Purcar"}
</h3>
<p className="text-xs text-zinc-400 mt-1">
{isSignUp ? "Get 5,000 free tokens daily & full API key access" : "Sign in to manage your reasoning quota & chats"}
</p>
</div>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-4">
{isSignUp && (
<div className="space-y-1.5">
<label className="text-xs font-mono text-zinc-400">Username</label>
<div className="relative">
<User className="w-4 h-4 text-zinc-500 absolute left-3.5 top-1/2 -translate-y-1/2" />
<input
type="text"
required
placeholder="e.g. Vlad"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full pl-10 pr-4 py-2.5 rounded-xl bg-black/50 border border-white/10 text-white text-xs font-mono placeholder:text-zinc-600 focus:outline-none focus:border-white/30 transition-colors"
/>
</div>
</div>
)}
<div className="space-y-1.5">
<label className="text-xs font-mono text-zinc-400">Email Address</label>
<div className="relative">
<Mail className="w-4 h-4 text-zinc-500 absolute left-3.5 top-1/2 -translate-y-1/2" />
<input
type="email"
required
placeholder="vlad@purcar.me"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full pl-10 pr-4 py-2.5 rounded-xl bg-black/50 border border-white/10 text-white text-xs font-mono placeholder:text-zinc-600 focus:outline-none focus:border-white/30 transition-colors"
/>
</div>
</div>
<div className="space-y-1.5">
<label className="text-xs font-mono text-zinc-400">Password</label>
<div className="relative">
<Lock className="w-4 h-4 text-zinc-500 absolute left-3.5 top-1/2 -translate-y-1/2" />
<input
type="password"
required
placeholder="••••••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full pl-10 pr-4 py-2.5 rounded-xl bg-black/50 border border-white/10 text-white text-xs font-mono placeholder:text-zinc-600 focus:outline-none focus:border-white/30 transition-colors"
/>
</div>
</div>
{error && (
<div className="p-3 rounded-xl bg-red-500/10 border border-red-500/20 text-red-400 text-xs font-mono">
{error}
</div>
)}
{successMsg && (
<div className="p-3 rounded-xl bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-xs font-mono flex items-center gap-2">
<Check className="w-4 h-4" />
<span>{successMsg}</span>
</div>
)}
<button
type="submit"
disabled={loading}
className="w-full py-3 rounded-xl bg-white text-black font-semibold text-xs hover:bg-zinc-200 transition-all shadow-lg active:scale-95 flex items-center justify-center gap-2 disabled:opacity-50"
>
<span>{loading ? "Authenticating..." : isSignUp ? "Create Free Account" : "Sign In"}</span>
<ArrowRight className="w-4 h-4" />
</button>
</form>
{/* Footer switch */}
<div className="mt-6 pt-4 border-t border-white/[0.08] text-center text-xs font-mono text-zinc-400">
<span>{isSignUp ? "Already have an account?" : "Don't have an account yet?"} </span>
<button
onClick={() => {
setIsSignUp(!isSignUp);
setError(null);
setSuccessMsg(null);
}}
className="text-white font-semibold underline underline-offset-4 hover:text-zinc-300 transition-colors"
>
{isSignUp ? "Sign In" : "Register"}
</button>
</div>
</div>
</div>
);
};