Spaces:
Sleeping
Sleeping
Download src/components/AuthModal.tsx from ihatebaselines/purcar-chat-api: direct link, hf CLI and curl.
- Browser
- Download file 7.28 kB
-
https://huggingface.co/spaces/ihatebaselines/purcar-chat-api/resolve/main/src/components/AuthModal.tsx
- Command line
-
hf download hf://spaces/ihatebaselines/purcar-chat-api/src/components/AuthModal.tsx
-
curl -L -o AuthModal.tsx https://huggingface.co/spaces/ihatebaselines/purcar-chat-api/resolve/main/src/components/AuthModal.tsx
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> | |
| ); | |
| }; | |