kioai / artifacts /image-gen /src /components /AuthModal.tsx
kinaiok
Initial deployment setup for Hugging Face Spaces
5ef6e9d
import { useState } from "react";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Sparkles, Loader2, Eye, EyeOff } from "lucide-react";
import { useAuth } from "@/contexts/AuthContext";
import { useLang } from "@/contexts/LanguageContext";
interface AuthModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
defaultTab?: "signin" | "signup";
}
export function AuthModal({ open, onOpenChange, defaultTab = "signin" }: AuthModalProps) {
const { t } = useLang();
const { signIn, signUp } = useAuth();
const [tab, setTab] = useState<"signin" | "signup">(defaultTab);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [displayName, setDisplayName] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const reset = () => {
setEmail("");
setPassword("");
setDisplayName("");
setError("");
setShowPassword(false);
};
const switchTab = (next: "signin" | "signup") => {
setTab(next);
setError("");
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
setLoading(true);
try {
if (tab === "signin") {
await signIn(email, password);
} else {
await signUp(email, password, displayName);
}
reset();
onOpenChange(false);
} catch (err: unknown) {
setError(err instanceof Error ? err.message : t.authError);
} finally {
setLoading(false);
}
};
const handleOpenChange = (next: boolean) => {
if (!next) reset();
onOpenChange(next);
};
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className="sm:max-w-[400px] bg-card border-border">
<DialogHeader>
<DialogTitle className="flex items-center gap-2 text-foreground">
<div className="w-7 h-7 rounded-lg bg-primary/15 border border-primary/30 flex items-center justify-center">
<Sparkles className="w-4 h-4 text-primary" />
</div>
{t.appName}
</DialogTitle>
</DialogHeader>
<div className="flex rounded-lg overflow-hidden border border-border/60 mb-4">
{(["signin", "signup"] as const).map((t_) => (
<button
key={t_}
type="button"
onClick={() => switchTab(t_)}
className={`flex-1 py-2 text-sm font-medium transition-colors ${
tab === t_
? "bg-primary text-primary-foreground"
: "text-muted-foreground hover:text-foreground hover:bg-secondary"
}`}
>
{t_ === "signin" ? t.authSignIn : t.authSignUp}
</button>
))}
</div>
<form onSubmit={handleSubmit} className="space-y-4">
{tab === "signup" && (
<div className="space-y-1.5">
<Label className="text-sm text-muted-foreground">{t.authDisplayName}</Label>
<Input
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
placeholder={t.authDisplayNamePlaceholder}
className="bg-background/50"
maxLength={50}
/>
</div>
)}
<div className="space-y-1.5">
<Label className="text-sm text-muted-foreground">{t.authEmail}</Label>
<Input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder={t.authEmailPlaceholder}
className="bg-background/50"
required
autoFocus
/>
</div>
<div className="space-y-1.5">
<Label className="text-sm text-muted-foreground">{t.authPassword}</Label>
<div className="relative">
<Input
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder={tab === "signup" ? t.authPasswordMinHint : t.authPasswordPlaceholder}
className="bg-background/50 pr-10"
required
minLength={tab === "signup" ? 6 : undefined}
/>
<button
type="button"
tabIndex={-1}
onClick={() => setShowPassword((v) => !v)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div>
{error && (
<p className="text-sm text-destructive bg-destructive/10 rounded-lg px-3 py-2">{error}</p>
)}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? (
<><Loader2 className="mr-2 h-4 w-4 animate-spin" />{t.authLoading}</>
) : (
tab === "signin" ? t.authSignIn : t.authSignUp
)}
</Button>
<p className="text-center text-xs text-muted-foreground">
{tab === "signin" ? (
<>{t.authNoAccount}{" "}
<button type="button" onClick={() => switchTab("signup")} className="text-primary hover:underline font-medium">
{t.authSignUp}
</button>
</>
) : (
<>{t.authHasAccount}{" "}
<button type="button" onClick={() => switchTab("signin")} className="text-primary hover:underline font-medium">
{t.authSignIn}
</button>
</>
)}
</p>
</form>
</DialogContent>
</Dialog>
);
}