From fe8a645151e491e8d57c72a2048e9103cacaf5d2 Mon Sep 17 00:00:00 2001 From: Chanikya K Date: Thu, 30 Oct 2025 20:48:09 +0530 Subject: [PATCH] Fix registration and cart authentication issues - Fixed registration validation to return empty strings for valid inputs - Added comprehensive validation before form submission - Added auth state management in ItemCard with event listeners - Dispatched authChange event after successful login - Fixed MyAccount to show correct JWT user data instead of OAuth email - Properly access userData.data from backend response - Updated Control component to prioritize JWT auth over OAuth --- client/src/Context/CartItemsContext.js | 3 +- client/src/Context/CartItemsProvider.js | 8 +- .../components/Account/MyAccount/MyAccount.js | 100 +++++++++++++----- .../components/Authentication/Login/Login.js | 4 + .../src/components/Card/ItemCard/ItemCard.js | 28 ++++- .../Card/RegisterCard/RegisterCard.js | 31 ++++-- 6 files changed, 137 insertions(+), 37 deletions(-) diff --git a/client/src/Context/CartItemsContext.js b/client/src/Context/CartItemsContext.js index 23a2828f..7fea6f00 100644 --- a/client/src/Context/CartItemsContext.js +++ b/client/src/Context/CartItemsContext.js @@ -5,7 +5,8 @@ export const CartItemsContext = createContext({ totalAmount: 0, addItem: (item) => 0, removeItem: () => {}, - quantity: () => {} + quantity: () => {}, + clearCart: () => {} }) export default CartItemsContext; \ No newline at end of file diff --git a/client/src/Context/CartItemsProvider.js b/client/src/Context/CartItemsProvider.js index 61c18bfa..f647e206 100644 --- a/client/src/Context/CartItemsProvider.js +++ b/client/src/Context/CartItemsProvider.js @@ -129,6 +129,11 @@ const CartItemsProvider = (props) => { ); }; + const clearCartHandler = () => { + setCartItems([]); + setTotalAmountOfItems(0); + }; + useEffect(() => { calculateTotalAmount(cartItems); }, [cartItems]); @@ -138,7 +143,8 @@ const CartItemsProvider = (props) => { totalAmount: totalAmountOfItems, addItem: addToCartHandler, removeItem: removeFromCartHandler, - quantity: quantityHandler + quantity: quantityHandler, + clearCart: clearCartHandler }; return ( diff --git a/client/src/components/Account/MyAccount/MyAccount.js b/client/src/components/Account/MyAccount/MyAccount.js index 8d2c86db..2ea1cca8 100644 --- a/client/src/components/Account/MyAccount/MyAccount.js +++ b/client/src/components/Account/MyAccount/MyAccount.js @@ -22,44 +22,88 @@ const MyAccount = () => { try { setLoading(true); - // Fetch user from Supabase - const { data: { user } } = await supabase.auth.getUser(); - if (user) { - setUser(user); - } + // First, check if user is logged in with JWT token + const authToken = localStorage.getItem('authToken'); - // Fetch orders from backend if user has a session - const { data: { session } } = await supabase.auth.getSession(); - if (session?.access_token) { + if (authToken) { + // User logged in with JWT (username/password) try { - // Fetch user orders - const ordersResponse = await fetch('/api/orders/my-orders', { + const response = await fetch(`${process.env.REACT_APP_BACKEND_URL}/api/auth/me`, { headers: { - 'Authorization': `Bearer ${session.access_token}`, + 'Authorization': `Bearer ${authToken}`, 'Content-Type': 'application/json' } }); - if (ordersResponse.ok) { - const ordersData = await ordersResponse.json(); - setOrders(ordersData.data || []); - } - - // Fetch order statistics - const statsResponse = await fetch('/api/orders/stats', { - headers: { - 'Authorization': `Bearer ${session.access_token}`, - 'Content-Type': 'application/json' + if (response.ok) { + const userData = await response.json(); + // Set user data from JWT backend + // Backend returns { success: true, data: { username, email, ... } } + const userInfo = userData.data; + setUser({ + email: userInfo.email, + user_metadata: { + full_name: userInfo.username, + avatar_url: null + } + }); + + // Fetch user's orders from backend + const ordersResponse = await fetch(`${process.env.REACT_APP_BACKEND_URL}/api/orders/my-orders`, { + headers: { + 'Authorization': `Bearer ${authToken}`, + 'Content-Type': 'application/json' + } + }); + + if (ordersResponse.ok) { + const ordersData = await ordersResponse.json(); + setOrders(ordersData.data || []); } - }); - - if (statsResponse.ok) { - const statsData = await statsResponse.json(); - setOrderStats(statsData.data); } } catch (error) { - console.log('Orders not available yet or user not in backend:', error); - // This is okay - user might not have backend account yet + console.error('Error fetching JWT user data:', error); + } + } else { + // Fallback to Supabase OAuth if no JWT token + const { data: { user: supaUser } } = await supabase.auth.getUser(); + if (supaUser) { + setUser(supaUser); + } + + // Fetch orders from backend if user has a Supabase session + const { data: { session } } = await supabase.auth.getSession(); + if (session?.access_token) { + try { + // Fetch user orders + const ordersResponse = await fetch('/api/orders/my-orders', { + headers: { + 'Authorization': `Bearer ${session.access_token}`, + 'Content-Type': 'application/json' + } + }); + + if (ordersResponse.ok) { + const ordersData = await ordersResponse.json(); + setOrders(ordersData.data || []); + } + + // Fetch order statistics + const statsResponse = await fetch('/api/orders/stats', { + headers: { + 'Authorization': `Bearer ${session.access_token}`, + 'Content-Type': 'application/json' + } + }); + + if (statsResponse.ok) { + const statsData = await statsResponse.json(); + setOrderStats(statsData.data); + } + } catch (error) { + console.log('Orders not available yet or user not in backend:', error); + // This is okay - user might not have backend account yet + } } } } catch (error) { diff --git a/client/src/components/Authentication/Login/Login.js b/client/src/components/Authentication/Login/Login.js index 1a3d390f..67dd8b7f 100644 --- a/client/src/components/Authentication/Login/Login.js +++ b/client/src/components/Authentication/Login/Login.js @@ -19,14 +19,18 @@ const Login = () => { try { const response = await axios.post(`${process.env.REACT_APP_BACKEND_URL}/api/auth/login`, { email, password }); localStorage.setItem('authToken', response.data.token); // Store JWT token + // Clear any existing Supabase session to avoid showing a different logged-in user try { await supabase.auth.signOut(); } catch (err) { // ignore } + // Notify other tabs/components that auth changed window.dispatchEvent(new Event('storage')); + window.dispatchEvent(new Event('authChange')); + toast.success('Logged in successfully!'); navigate('/account/me'); // Navigate to account page after login } catch (error) { diff --git a/client/src/components/Card/ItemCard/ItemCard.js b/client/src/components/Card/ItemCard/ItemCard.js index 761693a8..6f49aa99 100644 --- a/client/src/components/Card/ItemCard/ItemCard.js +++ b/client/src/components/Card/ItemCard/ItemCard.js @@ -23,6 +23,7 @@ const ItemCard = (props) => { const [toasterType, setToasterType] = useState("success"); const [product, setProduct] = useState(null); const [stockInfo, setStockInfo] = useState({ stock: 0, stockStatus: 'in_stock' }); + const [isAuthenticated, setIsAuthenticated] = useState(false); const { category, id } = useParams(); const navigate = useNavigate(); const cartItemsContext = useContext(CartItemsContext); @@ -31,9 +32,34 @@ const ItemCard = (props) => { const currentItem = props.item || product; const itemCategory = currentItem?.category || props.category; + // Check authentication status on mount and when storage changes + useEffect(() => { + const checkAuth = () => { + const token = localStorage.getItem("authToken"); + setIsAuthenticated(!!token); + }; + + // Check on mount + checkAuth(); + + // Listen for storage events (when token is added/removed) + window.addEventListener('storage', checkAuth); + + // Also listen for custom auth events + const handleAuthChange = () => checkAuth(); + window.addEventListener('authChange', handleAuthChange); + + return () => { + window.removeEventListener('storage', checkAuth); + window.removeEventListener('authChange', handleAuthChange); + }; + }, []); + // ✅ Helper function to check login const isLoggedIn = () => { - return !!localStorage.getItem("authToken"); // token exist → logged in + const token = localStorage.getItem("authToken"); + console.log("Checking auth token:", token ? "Token exists" : "No token"); // Debug log + return !!token; // token exist → logged in }; const handleProductClick = (e) => { diff --git a/client/src/components/Card/RegisterCard/RegisterCard.js b/client/src/components/Card/RegisterCard/RegisterCard.js index 9bf48e33..ae3c3f92 100644 --- a/client/src/components/Card/RegisterCard/RegisterCard.js +++ b/client/src/components/Card/RegisterCard/RegisterCard.js @@ -74,19 +74,19 @@ const RegisterCard = () => { setIsChecking(false); } }, 600); - }, [username]); + }, [username, usernameTouched]); // --- Validation Functions --- const validateEmail = (value) => { if (!value) return "Email is required"; const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - return pattern.test(value) ? true : "Enter a valid email address"; + return pattern.test(value) ? "" : "Enter a valid email address"; }; const validatePassword = (value) => { if (!value) return "Password is required"; if (value.length < 6) return "Password must be at least 6 characters"; - return true; + return ""; }; const validateConfirmPassword = (value) => { @@ -144,10 +144,28 @@ const RegisterCard = () => { const handleRegister = async (e) => { e.preventDefault(); - // 3. Added validation check + // Validate all fields before submission + const emailError = validateEmail(email); + const passwordError = validatePassword(password); + const confirmPasswordError = validateConfirmPassword(confirmPassword); + const usernameError = !username ? "Username is required" : errors.username; + + // Check if there are any errors + if (emailError || passwordError || confirmPasswordError || usernameError) { + setErrors({ + email: emailError, + password: passwordError, + confirmPassword: confirmPasswordError, + username: usernameError + }); + toast.error("Please fix all errors before submitting"); + return; + } + + // Check if passwords match if (password !== confirmPassword) { - alert("Passwords do not match!"); - return; // Stop submission if passwords don't match + toast.error("Passwords do not match!"); + return; } try { @@ -166,6 +184,7 @@ const RegisterCard = () => { } // Notify other tabs/components that auth changed window.dispatchEvent(new Event('storage')); + window.dispatchEvent(new Event('authChange')); toast.success('Account created successfully!'); navigate("/");