Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion client/src/Context/CartItemsContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export const CartItemsContext = createContext({
totalAmount: 0,
addItem: (item) => 0,
removeItem: () => {},
quantity: () => {}
quantity: () => {},
clearCart: () => {}
})

export default CartItemsContext;
8 changes: 7 additions & 1 deletion client/src/Context/CartItemsProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ const CartItemsProvider = (props) => {
);
};

const clearCartHandler = () => {
setCartItems([]);
setTotalAmountOfItems(0);
};

useEffect(() => {
calculateTotalAmount(cartItems);
}, [cartItems]);
Expand All @@ -138,7 +143,8 @@ const CartItemsProvider = (props) => {
totalAmount: totalAmountOfItems,
addItem: addToCartHandler,
removeItem: removeFromCartHandler,
quantity: quantityHandler
quantity: quantityHandler,
clearCart: clearCartHandler
};

return (
Expand Down
100 changes: 72 additions & 28 deletions client/src/components/Account/MyAccount/MyAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions client/src/components/Authentication/Login/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
28 changes: 27 additions & 1 deletion client/src/components/Card/ItemCard/ItemCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) => {
Expand Down
31 changes: 25 additions & 6 deletions client/src/components/Card/RegisterCard/RegisterCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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 {
Expand All @@ -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("/");
Expand Down