diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index be4deee..e27dc04 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,8 @@ import { useEffect, useMemo, useRef, useState } from 'react' -import { Link, Route, Routes, useNavigate } from 'react-router-dom' +import { Link, Route, Routes, useNavigate, Navigate } from 'react-router-dom' import { AnalysisPage } from './pages/Analysis' +import { LoginPage } from './pages/Login' +import { ProfilePage } from './pages/Profile' function usePrefersReducedMotion() { const [reduced, setReduced] = useState(false) @@ -395,6 +397,44 @@ function HomePage() { function App() { const [scrolled, setScrolled] = useState(false) + const [isAuthenticated, setIsAuthenticated] = useState(false) + const [userEmail, setUserEmail] = useState(null) + const [menuOpen, setMenuOpen] = useState(false) + const navigate = useNavigate() + + useEffect(() => { + try { + const raw = localStorage.getItem('s2s_auth') + if (raw) { + const obj = JSON.parse(raw) + setUserEmail(obj?.email ?? null) + setIsAuthenticated(true) + } else { + setUserEmail(null) + setIsAuthenticated(false) + } + } catch (err) { + setIsAuthenticated(false) + setUserEmail(null) + } + }, []) + + const handleLogin = (user?: { email: string }) => { + setIsAuthenticated(true) + setUserEmail(user?.email ?? null) + } + + const handleLogout = () => { + setIsAuthenticated(false) + try { + localStorage.removeItem('s2s_auth') + } catch (err) { + // ignore + } + setUserEmail(null) + setMenuOpen(false) + navigate('/') + } useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 14) @@ -430,19 +470,51 @@ function App() { Second-life - + Analysis Contact + {/* show account menu when authenticated */} + {isAuthenticated ? ( +
+ + + {menuOpen && ( +
+ setMenuOpen(false)} + className="block px-3 py-2 text-sm text-white/90 hover:bg-white/5" + > + Profile + + +
+ )} +
+ ) : null} - } /> - } /> + {/* root shows login when unauthenticated, profile when authenticated */} + : } /> + : } /> + : } /> ) diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx new file mode 100644 index 0000000..a99dedc --- /dev/null +++ b/frontend/src/pages/Login.tsx @@ -0,0 +1,90 @@ +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' + +type Props = { onLogin: (user?: { email: string }) => void } + +export function LoginPage({ onLogin }: Props) { + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [error, setError] = useState(null) + const navigate = useNavigate() + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + setError(null) + + // Minimal client-side "auth" — in a real app replace with API call + if (!email || !password) { + setError('Please enter email and password') + return + } + + // mark logged in + try { + localStorage.setItem('s2s_auth', JSON.stringify({ email })) + onLogin({ email }) + navigate('/') + } catch (err) { + setError('Unable to persist login state') + } + } + + return ( +
+
+ ) +} + +export default LoginPage diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx new file mode 100644 index 0000000..1ea341a --- /dev/null +++ b/frontend/src/pages/Profile.tsx @@ -0,0 +1,87 @@ +import { useEffect, useMemo, useState } from 'react' +import { useNavigate } from 'react-router-dom' + +type Props = { onLogout: () => void } + +export function ProfilePage({ onLogout }: Props) { + const [email, setEmail] = useState(null) + const navigate = useNavigate() + + useEffect(() => { + try { + const raw = localStorage.getItem('s2s_auth') + if (raw) { + const obj = JSON.parse(raw) + setEmail(obj.email ?? null) + } + } catch (err) { + setEmail(null) + } + }, []) + + const initials = useMemo(() => { + if (!email) return 'U' + const name = String(email).split('@')[0] || String(email) + const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean) + const chars = parts.map((p) => p[0]?.toUpperCase() ?? '') + return (chars[0] ?? 'U') + (chars[1] ?? '') + }, [email]) + + const handleLogout = () => { + localStorage.removeItem('s2s_auth') + onLogout() + navigate('/') + } + + return ( +
+
+ ) +} + +export default ProfilePage