iamfebin's picture
feat: implement sandbox mode
a95fdd1
Raw
History Blame Contribute Delete
11.6 kB
import { useState } from 'react';
import { supabase } from '../supabaseClient';
import { Mail, Lock, ShieldCheck, ArrowRight } from 'lucide-react';
export default function Auth() {
const [isSignUp, setIsSignUp] = useState(false);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState<{ type: 'error' | 'success'; text: string } | null>(null);
const handleAuth = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setMessage(null);
try {
if (isSignUp) {
const { error } = await supabase.auth.signUp({
email,
password,
});
if (error) throw error;
setMessage({ type: 'success', text: 'Registration successful! Check your email for validation link.' });
} else {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) throw error;
}
} catch (err: any) {
setMessage({ type: 'error', text: err.message || 'An error occurred during authentication' });
} finally {
setLoading(false);
}
};
const handleGoogleSignIn = async () => {
try {
setLoading(true);
const redirectUrl = window.location.origin.endsWith('/') ? window.location.origin : `${window.location.origin}/`;
alert('Sending redirect URL: ' + redirectUrl);
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: redirectUrl
}
});
if (error) throw error;
} catch (err: any) {
setMessage({ type: 'error', text: err.message || 'Google OAuth failed to initialize' });
setLoading(false);
}
};
const handleSandboxEntrance = () => {
localStorage.setItem('algospaced_sandbox_mode', 'true');
window.location.reload();
};
return (
<div className="min-h-screen w-screen flex items-center justify-center p-4 relative selection:bg-highlight-pink selection:text-white">
{/* Background Wallpaper image */}
<div
className="absolute inset-0 w-full h-full bg-cover bg-center z-0 saturate-150 contrast-125"
style={{ backgroundImage: "url('https://lh3.googleusercontent.com/aida-public/AB6AXuAMJHVhhK8_G7F8sbhn8F7w4AZWXb1O_-HLKvrZ_fJlbUVMcVuxxEOO_LlOh8qIwtAn2KvzvCwzmtNLLEZ5uYkCEsx8ZWXJR609qgSMRSX8LBBeskk4VzVXnyzsgIKCedeV2PvGxJlUNnledcWKCXqG9egQi8dgTcA7C2z82QyM73KZ4s7ZRzZHNupuQt1ocfcMl9E_x1PWFRridl751LIpyGZgednS5CmVw2rZvFc_tbp2QTxgVHJ_59myEBmy6aajbO06AhkKmCvI')" }}
/>
{/* OS System Setup Dialog Box */}
<div className="w-full max-w-md bg-paper-white border-[4px] border-ink-black window-shadow z-10 flex flex-col overflow-hidden shadow-[inset_2px_2px_0px_rgba(255,255,255,0.8)] relative animate-float">
{/* Title Bar */}
<div className="bg-[#0ea5e9] border-b-[4px] border-ink-black px-4 py-2.5 flex justify-between items-center text-white select-none">
<div className="flex items-center gap-2">
<span className="material-symbols-outlined select-none" style={{ fontVariationSettings: "'FILL' 1" }}>
sports_esports
</span>
<span className="font-window-title text-sm md:text-base font-bold tracking-wide">System_Setup.exe</span>
</div>
</div>
{/* Content Box */}
<div className="p-6 bg-[#fdf8e1] flex-1 flex flex-col">
{typeof window !== 'undefined' && window.self !== window.top && (
<div className="mb-5 p-3.5 border-[3px] border-[#ea580c] bg-[#ffedd5] text-[#9a3412] text-xs font-bold font-arcade leading-relaxed shadow-[2px_2px_0px_0px_rgba(30,41,59,1)]">
⚠️ RUNNING INSIDE IFRAME
<p className="mt-1 font-sans text-[11px] font-normal text-ink-black normal-case leading-normal">
Google Sign-In is restricted inside Hugging Face's iframe wrapper due to browser security.
</p>
<a
href="https://iamfebin-algospaced-dsa.hf.space/"
target="_top"
className="mt-2.5 inline-block w-full text-center bg-[#ea580c] hover:bg-[#c2410c] text-white border-[2.5px] border-ink-black py-1.5 px-3 rounded-lg font-window-title text-xs font-bold transition-all shadow-[1.5px_1.5px_0px_rgba(0,0,0,1)] active:translate-y-0.5 active:shadow-none"
>
OPEN FULLSCREEN / MOBILE SITE
</a>
</div>
)}
<div className="flex flex-col items-center mb-6">
<div className="w-16 h-16 bg-paper-white border-[3px] border-ink-black flex items-center justify-center gadget-shadow shadow-[inset_2px_2px_0px_rgba(255,255,255,0.8)] mb-4">
<ShieldCheck className="w-9 h-9 text-primary" />
</div>
<h2 className="text-3xl font-extrabold tracking-tight text-ink-black font-headline-md leading-none m-0">AlgoSpaced</h2>
<p className="text-xs font-arcade text-trash-gray mt-2 tracking-wide uppercase text-center">Spaced Repetition for DSA</p>
</div>
{/* Form Tabs */}
<div className="flex gap-2 mb-6 font-arcade text-[10px]">
<button
onClick={() => { setIsSignUp(false); setMessage(null); }}
className={`flex-1 py-2.5 border-[3px] border-ink-black cursor-pointer font-bold tracking-wider outline-none transition-all shadow-[2px_2px_0px_0px_rgba(30,41,59,1)] active:shadow-none active:translate-y-0.5 ${
!isSignUp
? 'bg-secondary-container text-ink-black translate-y-0.5 shadow-[1px_1px_0px_0px_rgba(30,41,59,1)]'
: 'bg-white text-trash-gray hover:bg-surface-container'
}`}
>
SIGN IN
</button>
<button
onClick={() => { setIsSignUp(true); setMessage(null); }}
className={`flex-1 py-2.5 border-[3px] border-ink-black cursor-pointer font-bold tracking-wider outline-none transition-all shadow-[2px_2px_0px_0px_rgba(30,41,59,1)] active:shadow-none active:translate-y-0.5 ${
isSignUp
? 'bg-secondary-container text-ink-black translate-y-0.5 shadow-[1px_1px_0px_0px_rgba(30,41,59,1)]'
: 'bg-white text-trash-gray hover:bg-surface-container'
}`}
>
CREATE COMP
</button>
</div>
{message && (
<div
className={`mb-6 p-4 border-[3px] border-ink-black text-xs font-arcade leading-relaxed ${
message.type === 'error'
? 'bg-red-100 border-red-500 text-error shadow-[2px_2px_0px_0px_rgba(30,41,59,1)]'
: 'bg-emerald-100 border-emerald-500 text-emerald-700 shadow-[2px_2px_0px_0px_rgba(30,41,59,1)]'
}`}
>
{message.text}
</div>
)}
<form onSubmit={handleAuth} className="space-y-4">
<div>
<label className="block text-xs font-bold text-ink-black uppercase tracking-wider mb-2 font-headline-md">Email Address</label>
<div className="relative">
<Mail className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-ink-black" />
<input
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="name@arcade.com"
className="w-full bg-white border-[3px] border-ink-black rounded-lg py-2.5 pl-10 pr-4 text-ink-black placeholder-trash-gray focus:outline-none focus:bg-white text-sm font-code-mono shadow-[inset_2px_2px_0px_rgba(0,0,0,0.1)]"
/>
</div>
</div>
<div>
<label className="block text-xs font-bold text-ink-black uppercase tracking-wider mb-2 font-headline-md">Password</label>
<div className="relative">
<Lock className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-ink-black" />
<input
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
className="w-full bg-white border-[3px] border-ink-black rounded-lg py-2.5 pl-10 pr-4 text-ink-black placeholder-trash-gray focus:outline-none focus:bg-white text-sm font-code-mono shadow-[inset_2px_2px_0px_rgba(0,0,0,0.1)]"
/>
</div>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-[#ffcc00] border-[4px] border-ink-black rounded-xl py-3.5 font-window-title text-base text-ink-black arcade-btn cursor-pointer disabled:opacity-50 flex items-center justify-center gap-2 uppercase font-bold tracking-wide"
>
{loading ? 'RUNNING...' : isSignUp ? 'RUN SETUP.EXE' : 'EXEC LOGON'}
{!loading && <ArrowRight className="w-4 h-4 stroke-[3px]" />}
</button>
</form>
<div className="relative my-5 select-none">
<div className="absolute inset-0 flex items-center"><div className="w-full border-t-[3px] border-ink-black border-dashed" /></div>
<div className="relative flex justify-center text-[10px] uppercase font-arcade"><span className="bg-[#fdf8e1] px-3 text-trash-gray font-bold">OR LOAD ALTERNATE</span></div>
</div>
<button
onClick={handleGoogleSignIn}
disabled={loading}
className="w-full bg-white hover:bg-slate-100 border-[3px] border-ink-black text-ink-black font-bold py-3 px-4 rounded-xl flex items-center justify-center gap-3 transition-all text-xs cursor-pointer shadow-[3px_3px_0px_0px_rgba(30,41,59,1)] active:translate-y-0.5 active:shadow-none"
>
<svg className="w-4 h-4 shrink-0" viewBox="0 0 24 24" fill="currentColor">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" 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.06H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.94l2.85-2.22.81-.63z" 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.06l3.66 2.84c.87-2.6 3.3-4.52 6.16-4.52z" fill="#EA4335" />
</svg>
SIGN IN WITH GOOGLE
</button>
<button
onClick={handleSandboxEntrance}
disabled={loading}
className="mt-3 w-full bg-[#10b981] hover:bg-emerald-600 text-white font-window-title py-3 px-4 border-[3px] border-ink-black rounded-xl flex items-center justify-center gap-2 cursor-pointer shadow-[3px_3px_0px_0px_rgba(30,41,59,1)] active:translate-y-0.5 active:shadow-none uppercase font-bold text-xs"
>
<span className="material-symbols-outlined text-sm font-bold">sports_esports</span>
Enter Sandbox Mode (No Login)
</button>
</div>
</div>
</div>
);
}