107 lines
3.7 KiB
TypeScript
107 lines
3.7 KiB
TypeScript
|
|
"use client";
|
|||
|
|
|
|||
|
|
import { useState, FormEvent, useRef, MouseEvent, useEffect } from "react";
|
|||
|
|
|
|||
|
|
interface AuthModalProps {
|
|||
|
|
isOpen: boolean;
|
|||
|
|
onClose: () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
|
|||
|
|
const [isLogin, setIsLogin] = useState<boolean>(true);
|
|||
|
|
const modalRef = useRef<HTMLDivElement>(null);
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (isOpen) setIsLogin(true);
|
|||
|
|
}, [isOpen]);
|
|||
|
|
|
|||
|
|
if (!isOpen) return null;
|
|||
|
|
|
|||
|
|
const handleOverlayClick = (e: MouseEvent<HTMLDivElement>) => {
|
|||
|
|
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
|||
|
|
onClose();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
const formData = new FormData(e.currentTarget);
|
|||
|
|
const email = formData.get("email") as string;
|
|||
|
|
const password = formData.get("password") as string;
|
|||
|
|
const name = isLogin ? undefined : (formData.get("name") as string);
|
|||
|
|
|
|||
|
|
const endpoint = isLogin ? "/api/login" : "/api/signup";
|
|||
|
|
const body = isLogin ? { email, password } : { name: name!, email, password };
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const res = await fetch(endpoint, {
|
|||
|
|
method: "POST",
|
|||
|
|
headers: { "Content-Type": "application/json" },
|
|||
|
|
body: JSON.stringify(body),
|
|||
|
|
});
|
|||
|
|
if (res.ok) {
|
|||
|
|
console.log("Success!");
|
|||
|
|
onClose();
|
|||
|
|
} else {
|
|||
|
|
console.error("Error:", await res.text());
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("Request failed:", error instanceof Error ? error.message : String(error));
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50" onClick={handleOverlayClick}>
|
|||
|
|
<div ref={modalRef} className="bg-white rounded-lg shadow-lg p-6 w-full max-w-md relative">
|
|||
|
|
<button onClick={onClose} className="absolute text-xl top-0 right-2 text-gray-500 hover:text-gray-700" aria-label="Close modal">
|
|||
|
|
×
|
|||
|
|
</button>
|
|||
|
|
<h2 className="text-2xl font-bold text-center mb-4">{isLogin ? "Login" : "Sign Up"}</h2>
|
|||
|
|
|
|||
|
|
{/* Form */}
|
|||
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|||
|
|
{!isLogin && (
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700">Full Name</label>
|
|||
|
|
<input
|
|||
|
|
type="text"
|
|||
|
|
name="name"
|
|||
|
|
className="mt-1 w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
required={!isLogin}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700">Email</label>
|
|||
|
|
<input
|
|||
|
|
type="email"
|
|||
|
|
name="email"
|
|||
|
|
className="mt-1 w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
required
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<label className="block text-sm font-medium text-gray-700">Password</label>
|
|||
|
|
<input
|
|||
|
|
type="password"
|
|||
|
|
name="password"
|
|||
|
|
className="mt-1 w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|||
|
|
required
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
<button type="submit" className="w-full bg-blue-600 text-white p-2 rounded-md hover:bg-blue-700 transition">
|
|||
|
|
{isLogin ? "Login" : "Sign Up"}
|
|||
|
|
</button>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<p className="mt-4 text-center text-sm">
|
|||
|
|
{isLogin ? "Need an account?" : "Already have an account?"}{" "}
|
|||
|
|
<button onClick={() => setIsLogin(!isLogin)} className="text-blue-600 hover:underline">
|
|||
|
|
{isLogin ? "Sign Up" : "Login"}
|
|||
|
|
</button>
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|