diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 72c3ede..40ebd34 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -import { AuthProvider } from './contexts/AuthContext'; +import { AuthProvider, useAuth } from './contexts/AuthContext'; import Navbar from './components/Navbar'; import Footer from './components/Footer'; +import AuthModal from './components/AuthModal'; import Home from './pages/Home'; -import Login from './pages/Login'; -import Register from './pages/Register'; import GoogleCallback from './pages/GoogleCallback'; import VerifyEmail from './pages/VerifyEmail'; import ItemList from './pages/ItemList'; @@ -27,17 +26,17 @@ import EarningsDashboard from './pages/EarningsDashboard'; import PrivateRoute from './components/PrivateRoute'; import './App.css'; -function App() { +const AppContent: React.FC = () => { + const { showAuthModal, authModalMode, closeAuthModal } = useAuth(); + return ( - + <>
} /> - } /> - } /> } /> } /> } /> @@ -138,6 +137,20 @@ function App() {
+ + + + ); +}; + +function App() { + return ( + + ); } diff --git a/frontend/src/components/AuthButton.tsx b/frontend/src/components/AuthButton.tsx new file mode 100644 index 0000000..ae8ae88 --- /dev/null +++ b/frontend/src/components/AuthButton.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useAuth } from '../contexts/AuthContext'; + +interface AuthButtonProps { + mode: 'login' | 'signup'; + className?: string; + children: React.ReactNode; + asLink?: boolean; +} + +const AuthButton: React.FC = ({ mode, className = '', children, asLink = false }) => { + const { openAuthModal } = useAuth(); + + const handleClick = (e: React.MouseEvent) => { + e.preventDefault(); + openAuthModal(mode); + }; + + if (asLink) { + return ( + + {children} + + ); + } + + return ( + + ); +}; + +export default AuthButton; diff --git a/frontend/src/components/AuthModal.tsx b/frontend/src/components/AuthModal.tsx index 41d8d9d..6445ed8 100644 --- a/frontend/src/components/AuthModal.tsx +++ b/frontend/src/components/AuthModal.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef, useCallback } from "react"; +import React, { useState, useEffect } from "react"; import { useAuth } from "../contexts/AuthContext"; import PasswordStrengthMeter from "./PasswordStrengthMeter"; import PasswordInput from "./PasswordInput"; diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index f669998..62f7693 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -1,15 +1,10 @@ import React, { useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useAuth } from "../contexts/AuthContext"; -import AuthModal from "./AuthModal"; const Navbar: React.FC = () => { - const { user, logout } = useAuth(); + const { user, logout, openAuthModal } = useAuth(); const navigate = useNavigate(); - const [showAuthModal, setShowAuthModal] = useState(false); - const [authModalMode, setAuthModalMode] = useState<"login" | "signup">( - "login" - ); const [searchFilters, setSearchFilters] = useState({ search: "", location: "", @@ -20,11 +15,6 @@ const Navbar: React.FC = () => { navigate("/"); }; - const openAuthModal = (mode: "login" | "signup") => { - setAuthModalMode(mode); - setShowAuthModal(true); - }; - const handleSearch = (e: React.FormEvent) => { e.preventDefault(); const params = new URLSearchParams(); @@ -204,12 +194,6 @@ const Navbar: React.FC = () => { - - setShowAuthModal(false)} - initialMode={authModalMode} - /> ); }; diff --git a/frontend/src/components/PrivateRoute.tsx b/frontend/src/components/PrivateRoute.tsx index e74aea4..8e7170a 100644 --- a/frontend/src/components/PrivateRoute.tsx +++ b/frontend/src/components/PrivateRoute.tsx @@ -1,18 +1,25 @@ -import React from 'react'; -import { Navigate, useLocation } from 'react-router-dom'; -import { useAuth } from '../contexts/AuthContext'; +import React, { useEffect } from "react"; +import { useAuth } from "../contexts/AuthContext"; interface PrivateRouteProps { children: React.ReactNode; } const PrivateRoute: React.FC = ({ children }) => { - const { user, loading } = useAuth(); - const location = useLocation(); + const { user, loading, openAuthModal } = useAuth(); + + useEffect(() => { + if (!loading && !user) { + openAuthModal("login"); + } + }, [loading, user, openAuthModal]); if (loading) { return ( -
+
Loading...
@@ -20,7 +27,21 @@ const PrivateRoute: React.FC = ({ children }) => { ); } - return user ? <>{children} : ; + if (!user) { + return ( +
+
+ +

Please log in or sign up to access this page.

+
+
+ ); + } + + return <>{children}; }; -export default PrivateRoute; \ No newline at end of file +export default PrivateRoute; diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index 216ec60..3e1b769 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -9,7 +9,6 @@ import React, { import { User } from "../types"; import { authAPI, - userAPI, fetchCSRFToken, resetCSRFToken, } from "../services/api"; @@ -23,6 +22,10 @@ interface AuthContextType { logout: () => Promise; updateUser: (user: User) => void; checkAuth: () => Promise; + showAuthModal: boolean; + authModalMode: "login" | "signup"; + openAuthModal: (mode: "login" | "signup") => void; + closeAuthModal: () => void; } const AuthContext = createContext(undefined); @@ -42,6 +45,8 @@ interface AuthProviderProps { export const AuthProvider: React.FC = ({ children }) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); + const [showAuthModal, setShowAuthModal] = useState(false); + const [authModalMode, setAuthModalMode] = useState<"login" | "signup">("login"); const isAuthenticating = useRef(false); const checkAuth = async () => { @@ -132,6 +137,15 @@ export const AuthProvider: React.FC = ({ children }) => { setUser(user); }; + const openAuthModal = (mode: "login" | "signup") => { + setAuthModalMode(mode); + setShowAuthModal(true); + }; + + const closeAuthModal = () => { + setShowAuthModal(false); + }; + return ( = ({ children }) => { logout, updateUser, checkAuth, + showAuthModal, + authModalMode, + openAuthModal, + closeAuthModal, }} > {children} diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index bc48aa3..8784630 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -4,6 +4,7 @@ import { useAuth } from '../contexts/AuthContext'; import { itemAPI } from '../services/api'; import { Item } from '../types'; import ItemCard from '../components/ItemCard'; +import AuthButton from '../components/AuthButton'; const Home: React.FC = () => { const { user } = useAuth(); @@ -48,9 +49,9 @@ const Home: React.FC = () => { List Your Items ) : ( - + Start Earning - + )}
@@ -292,9 +293,9 @@ const Home: React.FC = () => { List an Item ) : ( - + Sign Up Free - + )} diff --git a/frontend/src/pages/ItemRequestDetail.tsx b/frontend/src/pages/ItemRequestDetail.tsx index b9e9705..57d1d14 100644 --- a/frontend/src/pages/ItemRequestDetail.tsx +++ b/frontend/src/pages/ItemRequestDetail.tsx @@ -4,6 +4,7 @@ import { useAuth } from '../contexts/AuthContext'; import { itemRequestAPI } from '../services/api'; import { ItemRequest, ItemRequestResponse } from '../types'; import RequestResponseModal from '../components/RequestResponseModal'; +import AuthButton from '../components/AuthButton'; const ItemRequestDetail: React.FC = () => { const { id } = useParams<{ id: string }>(); @@ -330,9 +331,9 @@ const ItemRequestDetail: React.FC = () => { ) : !user ? (
- + Log in to Respond - +
) : null} diff --git a/frontend/src/pages/ItemRequests.tsx b/frontend/src/pages/ItemRequests.tsx index 52503d0..efa68b3 100644 --- a/frontend/src/pages/ItemRequests.tsx +++ b/frontend/src/pages/ItemRequests.tsx @@ -4,6 +4,7 @@ import { useAuth } from '../contexts/AuthContext'; import { itemRequestAPI } from '../services/api'; import { ItemRequest } from '../types'; import ItemRequestCard from '../components/ItemRequestCard'; +import AuthButton from '../components/AuthButton'; const ItemRequests: React.FC = () => { const { user } = useAuth(); @@ -200,7 +201,7 @@ const ItemRequests: React.FC = () => {
- Log in to create your own item requests or respond to existing ones. + Log in to create your own item requests or respond to existing ones.
)} diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx deleted file mode 100644 index c7bc228..0000000 --- a/frontend/src/pages/Login.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React, { useState } from 'react'; -import { Link, useNavigate, useLocation } from 'react-router-dom'; -import { useAuth } from '../contexts/AuthContext'; -import PasswordInput from '../components/PasswordInput'; - -const Login: React.FC = () => { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - const { login } = useAuth(); - const navigate = useNavigate(); - const location = useLocation(); - - const from = location.state?.from?.pathname || '/'; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(''); - setLoading(true); - - try { - await login(email, password); - navigate(from, { replace: true }); - } catch (err: any) { - setError(err.response?.data?.error || 'Failed to login'); - } finally { - setLoading(false); - } - }; - - return ( -
-
-
-
-
-

Login

- {error && ( -
- {error} -
- )} -
-
- - setEmail(e.target.value)} - required - /> -
- setPassword(e.target.value)} - required - /> - - -
-

- Don't have an account?{' '} - - Sign up - -

-
-
-
-
-
-
- ); -}; - -export default Login; \ No newline at end of file diff --git a/frontend/src/pages/MyRequests.tsx b/frontend/src/pages/MyRequests.tsx index f21c777..3f7681d 100644 --- a/frontend/src/pages/MyRequests.tsx +++ b/frontend/src/pages/MyRequests.tsx @@ -1,13 +1,12 @@ import React, { useState, useEffect } from 'react'; -import { Link, useNavigate } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; import { itemRequestAPI } from '../services/api'; import { ItemRequest, ItemRequestResponse } from '../types'; import ConfirmationModal from '../components/ConfirmationModal'; const MyRequests: React.FC = () => { - const { user } = useAuth(); - const navigate = useNavigate(); + const { user, openAuthModal } = useAuth(); const [requests, setRequests] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -20,9 +19,9 @@ const MyRequests: React.FC = () => { if (user) { fetchMyRequests(); } else { - navigate('/login'); + openAuthModal('login'); } - }, [user, navigate]); + }, [user, openAuthModal]); const fetchMyRequests = async () => { try { diff --git a/frontend/src/pages/Register.tsx b/frontend/src/pages/Register.tsx deleted file mode 100644 index 08cd73c..0000000 --- a/frontend/src/pages/Register.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { useState } from 'react'; -import { Link, useNavigate, useLocation } from 'react-router-dom'; -import { useAuth } from '../contexts/AuthContext'; -import PasswordInput from '../components/PasswordInput'; - -const Register: React.FC = () => { - const [formData, setFormData] = useState({ - username: '', - email: '', - password: '', - firstName: '', - lastName: '', - phone: '' - }); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - const { register } = useAuth(); - const navigate = useNavigate(); - const location = useLocation(); - - const from = location.state?.from?.pathname || '/'; - - const handleChange = (e: React.ChangeEvent) => { - setFormData({ - ...formData, - [e.target.name]: e.target.value - }); - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(''); - setLoading(true); - - try { - await register(formData); - navigate(from, { replace: true }); - } catch (err: any) { - setError(err.response?.data?.error || 'Failed to create account'); - } finally { - setLoading(false); - } - }; - - return ( -
-
-
-
-
-

Create Account

- {error && ( -
- {error} -
- )} -
-
-
- - -
-
- - -
-
-
- - -
-
- - -
-
- - -
- - - -
-

- Already have an account?{' '} - - Login - -

-
-
-
-
-
-
- ); -}; - -export default Register; \ No newline at end of file