import React, { useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { LogIn, Loader2, ShieldCheck, UserPlus } from "lucide-react"; import { useToast } from "@/shared"; import { getErrorMessage } from "@/shared/utils/handleApiError"; import { login as apiLogin, fetchAuthStatus, setupInitialAdmin, type AuthResponse } from "@/api"; type Props = { onLogin: (response: AuthResponse) => void; }; const AuthPage: React.FC = ({ onLogin }) => { const navigate = useNavigate(); const { success: toastSuccess, error: toastError, info: toastInfo } = useToast(); // Auth status const [needsSetup, setNeedsSetup] = useState(null); const [checkingStatus, setCheckingStatus] = useState(true); // Login form const [loginUsername, setLoginUsername] = useState(""); const [loginPassword, setLoginPassword] = useState(""); const [loginLoading, setLoginLoading] = useState(false); const [loginError, setLoginError] = useState(null); // Setup form const [setupUsername, setSetupUsername] = useState(""); const [setupPassword, setSetupPassword] = useState(""); const [setupEmail, setSetupEmail] = useState(""); const [setupLoading, setSetupLoading] = useState(false); const [setupError, setSetupError] = useState(null); // Check if setup is needed on mount useEffect(() => { let cancelled = false; fetchAuthStatus() .then((status) => { if (!cancelled) { setNeedsSetup(status.needsSetup); setCheckingStatus(false); } }) .catch(() => { if (!cancelled) { setNeedsSetup(false); // fall back to login on error setCheckingStatus(false); } }); return () => { cancelled = true; }; }, []); // Handle setup (first admin registration) const handleSetup = async (e: React.FormEvent) => { e.preventDefault(); if (!setupUsername || !setupPassword) return; setSetupLoading(true); setSetupError(null); toastInfo("Creating admin account...", { title: "Setup", durationMs: 1200 }); try { const result = await setupInitialAdmin({ username: setupUsername, password: setupPassword, email: setupEmail || undefined, }); // setupInitialAdmin returns tokens — use them directly to avoid redundant login onLogin({ accessToken: result.accessToken, refreshToken: result.refreshToken, username: setupUsername, } as any); toastSuccess("Admin account created. Welcome!"); navigate("/home", { replace: true }); } catch (err: unknown) { const msg = getErrorMessage(err, "Setup failed. Please try again later."); setSetupError(msg); toastError(msg); } finally { setSetupLoading(false); } }; // Handle login const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); if (!loginUsername || !loginPassword) return; setLoginLoading(true); setLoginError(null); toastInfo("Logging in...", { title: "Login", durationMs: 1200 }); try { const response = await apiLogin({ username: loginUsername, password: loginPassword }); toastSuccess(`Welcome, ${response.username}!`); onLogin(response); navigate("/home", { replace: true }); } catch (err: unknown) { const raw = err as any; const msg = raw?.message?.includes("Failed to fetch") ? "Network or CORS error: Please check backend CORS or use Vite proxy in development." : getErrorMessage(err, "Login failed. Please try again later."); setLoginError(msg); toastError(msg); } finally { setLoginLoading(false); } }; if (checkingStatus) { return (
); } // Setup view — first admin registration if (needsSetup) { return (
); } // Regular login view return (
); }; export default AuthPage;