146 lines
6.3 KiB
TypeScript
146 lines
6.3 KiB
TypeScript
|
|
import React, { useState, useRef, useEffect } from "react";
|
||
|
|
import { useLocation, useNavigate } from "react-router-dom";
|
||
|
|
import { toast } from "react-hot-toast";
|
||
|
|
import { AnimatePresence, motion } from "framer-motion";
|
||
|
|
import { useForm } from "react-hook-form";
|
||
|
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||
|
|
import { RegisterForm } from "./register";
|
||
|
|
import { LoginForm } from "./login";
|
||
|
|
import { LoginFormInputs, RegisterFormInputs } from "./types";
|
||
|
|
import { Button } from "@web/src/components/common/element/Button";
|
||
|
|
|
||
|
|
const AuthPage: React.FC = () => {
|
||
|
|
const [showLogin, setShowLogin] = useState(true);
|
||
|
|
const [isLoading, setIsLoading] = useState(false);
|
||
|
|
const { login, isAuthenticated, signup } = useAuth();
|
||
|
|
const location = useLocation();
|
||
|
|
const navigate = useNavigate();
|
||
|
|
const loginForm = useForm<LoginFormInputs>({
|
||
|
|
mode: "onChange"
|
||
|
|
});
|
||
|
|
const registerForm = useForm<RegisterFormInputs>({
|
||
|
|
mode: "onChange"
|
||
|
|
});
|
||
|
|
|
||
|
|
const onSubmitLogin = async (data: LoginFormInputs) => {
|
||
|
|
try {
|
||
|
|
setIsLoading(true);
|
||
|
|
console.log(data)
|
||
|
|
await login(data.username, data.password);
|
||
|
|
toast.success("Welcome back!");
|
||
|
|
} catch (err: any) {
|
||
|
|
toast.error(err?.response?.data?.message || "Invalid credentials");
|
||
|
|
} finally {
|
||
|
|
setIsLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const onSubmitRegister = async (data: RegisterFormInputs) => {
|
||
|
|
try {
|
||
|
|
setIsLoading(true);
|
||
|
|
await signup(data);
|
||
|
|
toast.success("Registration successful!");
|
||
|
|
setShowLogin(true);
|
||
|
|
} catch (err: any) {
|
||
|
|
toast.error(err?.response?.data?.message);
|
||
|
|
} finally {
|
||
|
|
setIsLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (isAuthenticated) {
|
||
|
|
const params = new URLSearchParams(location.search);
|
||
|
|
const redirectUrl = params.get("redirect_url") || "/";
|
||
|
|
navigate(redirectUrl, { replace: true });
|
||
|
|
}
|
||
|
|
}, [isAuthenticated, location]);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen bg-gradient-to-br from-[#1B2735] to-[#090A0F] flex items-center justify-center p-4">
|
||
|
|
<motion.div
|
||
|
|
initial={{ opacity: 0, y: 20 }}
|
||
|
|
animate={{ opacity: 1, y: 0 }}
|
||
|
|
className="w-full max-w-5xl bg-[#1C2C41]/90 backdrop-blur-xl rounded-lg shadow-2xl overflow-hidden border border-[#2A4562]"
|
||
|
|
>
|
||
|
|
<div className="flex flex-col md:flex-row h-full">
|
||
|
|
{/* Left Panel */}
|
||
|
|
<motion.div
|
||
|
|
className="md:w-1/2 p-8 lg:p-12 text-white relative"
|
||
|
|
initial={false}
|
||
|
|
animate={{ x: 0, opacity: 1 }}
|
||
|
|
transition={{
|
||
|
|
type: "spring",
|
||
|
|
stiffness: 100,
|
||
|
|
damping: 20
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<motion.div
|
||
|
|
className="space-y-8"
|
||
|
|
animate={{ opacity: 1, scale: 1 }}
|
||
|
|
initial={{ opacity: 0, scale: 0.95 }}
|
||
|
|
key={showLogin ? "login" : "register"}
|
||
|
|
transition={{ duration: 0.3 }}
|
||
|
|
>
|
||
|
|
{/* Logo Section */}
|
||
|
|
<div className="relative">
|
||
|
|
<div className="flex flex-col items-center space-y-4">
|
||
|
|
<img
|
||
|
|
src="/usaf-logo.svg"
|
||
|
|
alt="United States Air Force Logo"
|
||
|
|
className="w-28 h-28 mx-auto filter drop-shadow-lg"
|
||
|
|
/>
|
||
|
|
<div className="absolute inset-0 bg-gradient-to-t from-[#1B2735]/40 to-transparent rounded-full blur-2xl" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Title Section */}
|
||
|
|
<div className="space-y-4">
|
||
|
|
<h1 className="text-3xl lg:text-4xl font-bold text-center tracking-tight">
|
||
|
|
<span className="bg-gradient-to-r from-[#E6E9F0] to-[#B4C3D8] bg-clip-text text-transparent">
|
||
|
|
USAF Leadership Portal
|
||
|
|
</span>
|
||
|
|
</h1>
|
||
|
|
<p className="text-[#A3B8D3] text-center text-base lg:text-lg font-medium">
|
||
|
|
{showLogin
|
||
|
|
? "Access your secure USAF portal"
|
||
|
|
: "Create your authorized account"}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Switch Form Button */}
|
||
|
|
<Button variant="soft-primary" fullWidth
|
||
|
|
onClick={() => setShowLogin(!showLogin)}
|
||
|
|
aria-label={showLogin ? "Switch to registration form" : "Switch to login form"}
|
||
|
|
>
|
||
|
|
{showLogin ? "New User Registration" : "Return to Login"}
|
||
|
|
</Button>
|
||
|
|
</motion.div>
|
||
|
|
</motion.div>
|
||
|
|
|
||
|
|
{/* Right Panel - Forms */}
|
||
|
|
<div className="md:w-1/2 bg-[#1C2C41]/30 p-8 lg:p-12 backdrop-blur-sm relative">
|
||
|
|
<AnimatePresence mode="wait">
|
||
|
|
{showLogin ? (
|
||
|
|
<LoginForm
|
||
|
|
form={loginForm}
|
||
|
|
onSubmit={onSubmitLogin}
|
||
|
|
isLoading={isLoading}
|
||
|
|
/>
|
||
|
|
) : (
|
||
|
|
<RegisterForm
|
||
|
|
form={registerForm}
|
||
|
|
onSubmit={onSubmitRegister}
|
||
|
|
isLoading={isLoading}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
</AnimatePresence>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default AuthPage;
|