tremor-tracker/src/components/AuthModal.tsx

147 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import axios from "axios";
import { useStoreActions } from "@hooks/store";
import { FormEvent, MouseEvent, useEffect, useRef, useState } from "react";
import { ErrorRes } from "@appTypes/Axios";
interface AuthModalProps {
isOpen: boolean; // bool for if the modal should be visible
onClose: () => void; //A function that will be executed to close the modal
}
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
// todo add login successful message
const [isLogin, setIsLogin] = useState<boolean>(true);
const modalRef = useRef<HTMLDivElement>(null);
const [isFailed, setIsFailed] = useState<boolean>(false);
const [failMessage, setFailMessage] = useState<string>();
const setUser = useStoreActions((actions) => actions.setUser);
useEffect(() => {
if (isOpen) setIsLogin(true); // runs when isOpen changes, if it is true, the login is shown
}, [isOpen]);
if (!isOpen) return null; // if is open is false, the model isnt shown
const handleOverlayClick = (e: MouseEvent<HTMLDivElement>) => {
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
onClose();
}
};
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsFailed(false);
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);
try {
const res = await axios.post(
`/api/${isLogin ? "login" : "signup"}`,
isLogin ? { email, password } : { name, email, password },
{
headers: { "Content-Type": "application/json" },
}
);
if (res.status === 200) {
setUser(res.data.user);
onClose();
} else {
console.error("Unexpected response status:", res.status, res.data);
setFailMessage("Unexpected error occurred");
setIsFailed(true);
}
} catch (error) {
const axiosError = error as ErrorRes;
if (axiosError.response) {
const { status, data } = axiosError.response;
if (status >= 400 && status < 500) {
console.log("4xx error:", data);
setFailMessage(data.message || "Invalid request");
setIsFailed(true);
} else {
console.error("Server error:", data);
setFailMessage("Server error occurred");
setIsFailed(true);
}
} else {
console.error("Request failed:", axiosError.message);
setFailMessage("Network error occurred");
setIsFailed(true);
}
}
};
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={() => {
setIsFailed(false);
onClose();
}}
className="absolute text-xl top-0 right-2 text-neutral-500 hover:text-neutral-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-neutral-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-neutral-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-neutral-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>
<div>
{isFailed && (
<div>
<label className="block text-sm font-medium text-red-700">{failMessage}</label>
</div>
)}
</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>
);
}