Snapshot: full project state
This commit is contained in:
176
pettalk-AuthPage-v2.tsx
Normal file
176
pettalk-AuthPage-v2.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Sparkles, Mail, Lock, User, PawPrint, ArrowLeft } from "lucide-react";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
|
||||
interface AuthPageProps {
|
||||
initialMode?: "login" | "register";
|
||||
onBack?: () => void;
|
||||
}
|
||||
|
||||
export const AuthPage: React.FC<AuthPageProps> = ({ initialMode = "login", onBack }) => {
|
||||
const { login, register } = useAuth();
|
||||
const [isRegistering, setIsRegistering] = useState(initialMode === "register");
|
||||
const [username, setUsername] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Sync with initialMode if it changes externally
|
||||
useEffect(() => {
|
||||
setIsRegistering(initialMode === "register");
|
||||
}, [initialMode]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError("");
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
if (isRegistering) {
|
||||
await register(username, email, password);
|
||||
} else {
|
||||
await login(email, password);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Something went wrong");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#F9F7F2] flex items-center justify-center px-4">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Back button */}
|
||||
{onBack && (
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="mb-4 flex items-center gap-2 text-sm text-[#5A6E85] hover:text-[#1A2A40] transition-colors cursor-pointer"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
Back to PetTAlk
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Logo */}
|
||||
<div className="text-center mb-8">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-[#1A2A40] text-[#F9F7F2] shadow-lg mb-4">
|
||||
<Sparkles className="w-8 h-8 text-[#F08C7D]" />
|
||||
</div>
|
||||
<h1 className="font-serif text-3xl font-bold text-[#1A2A40] tracking-tight">
|
||||
PetTalk
|
||||
</h1>
|
||||
<p className="text-[#5A6E85] text-sm mt-1">
|
||||
Understanding pet intent softly
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Form Card */}
|
||||
<div className="bg-white rounded-3xl border border-[#E5DFD3] shadow-2xs p-8">
|
||||
<div className="flex items-center justify-center gap-2 mb-6">
|
||||
<PawPrint className="w-5 h-5 text-[#F08C7D]" />
|
||||
<h2 className="font-serif text-xl font-bold text-[#1A2A40]">
|
||||
{isRegistering ? "Create Account" : "Welcome Back"}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mb-4 p-3 rounded-xl bg-red-50 border border-red-200 text-red-700 text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{isRegistering && (
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-[#4A5D78] mb-1.5">
|
||||
Username
|
||||
</label>
|
||||
<div className="relative">
|
||||
<User className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[#5A6E85]" />
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder="Your name"
|
||||
required
|
||||
className="w-full pl-10 pr-4 py-2.5 rounded-xl border border-[#E5DFD3] bg-[#F9F7F2] text-[#1A2A40] text-sm placeholder:text-[#5A6E85] focus:outline-none focus:ring-2 focus:ring-[#F08C7D]/40 focus:border-[#F08C7D]/50 transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-[#4A5D78] mb-1.5">
|
||||
Email
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[#5A6E85]" />
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
className="w-full pl-10 pr-4 py-2.5 rounded-xl border border-[#E5DFD3] bg-[#F9F7F2] text-[#1A2A40] text-sm placeholder:text-[#5A6E85] focus:outline-none focus:ring-2 focus:ring-[#F08C7D]/40 focus:border-[#F08C7D]/50 transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-[#4A5D78] mb-1.5">
|
||||
Password
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[#5A6E85]" />
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Min 6 characters"
|
||||
required
|
||||
minLength={6}
|
||||
className="w-full pl-10 pr-4 py-2.5 rounded-xl border border-[#E5DFD3] bg-[#F9F7F2] text-[#1A2A40] text-sm placeholder:text-[#5A6E85] focus:outline-none focus:ring-2 focus:ring-[#F08C7D]/40 focus:border-[#F08C7D]/50 transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full py-3 rounded-2xl bg-[#F08C7D] hover:bg-[#E57B6C] text-white font-semibold text-sm flex items-center justify-center gap-2 transition-all disabled:opacity-50 cursor-pointer"
|
||||
>
|
||||
{loading ? (
|
||||
<span className="animate-pulse">Please wait...</span>
|
||||
) : isRegistering ? (
|
||||
"Create Account"
|
||||
) : (
|
||||
"Sign In"
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="mt-6 text-center">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsRegistering(!isRegistering);
|
||||
setError("");
|
||||
}}
|
||||
className="text-sm text-[#5A6E85] hover:text-[#1A2A40] underline underline-offset-2 cursor-pointer transition-colors"
|
||||
>
|
||||
{isRegistering
|
||||
? "Already have an account? Sign in"
|
||||
: "Don't have an account? Create one"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<p className="text-center text-[10px] text-[#5A6E85] mt-6">
|
||||
PetTalk AI · Probabilistic Pet Behavioral Interpretation
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user