diff --git a/backend/controllers/cartController.js b/backend/controllers/cartController.js index c7c559f..293a559 100644 --- a/backend/controllers/cartController.js +++ b/backend/controllers/cartController.js @@ -12,20 +12,19 @@ const addToCart = async (req, res) => { const cart = await Cart.findOne({ userId }); if (!cart) { - const newCart = new Cart({ + await Cart.create({ userId, products: [{ product: productId, quantity: 1 }], }); - await newCart.save(); return res.status(200).json({ message: "Producto añadido al carrito" }); } - const productIndex = cart.products.findIndex( - (p) => p.product.toString() === productId + const productInCart = cart.products.find( + (item) => item.product.toString() === productId ); - if (productIndex !== -1) { - cart.products[productIndex].quantity += 1; + if (productInCart) { + productInCart.quantity += 1; } else { cart.products.push({ product: productId, quantity: 1 }); } @@ -40,7 +39,29 @@ const addToCart = async (req, res) => { const removeFromCart = async (req, res) => { try { - } catch (error) {} + const userId = req.params.userid; + const productId = req.params.product; + + const cart = await Cart.findOne({ userId }); + + const productInCart = cart.products.find( + (item) => item.product.toString() === productId + ); + + if (productInCart && productInCart.quantity > 1) { + productInCart.quantity -= 1; + } else if (productInCart.quantity <= 1) { + productInCart.deleteOne(); + } else { + return res.json({ error: "No se ha podido eliminar el producto" }); + } + + await cart.save(); + + res.status(200).json({ message: "Producto eliminado" }); + } catch (error) { + res.status(500).json({ error: "Error del servidor" }); + } }; const getCartProducts = async (req, res) => { diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index ac70b0e..b2091ce 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -17,6 +17,7 @@ import { EmailVerification, AdminDashboard, AdminProducts, + Checkout, } from "./Routes"; import axios from "axios"; import { Toaster } from "react-hot-toast"; @@ -60,6 +61,7 @@ export default function App() { path="/admin-dashboard/products" element={} /> + } /> } />
diff --git a/frontend/src/Routes.js b/frontend/src/Routes.js index 6986258..49d9dde 100644 --- a/frontend/src/Routes.js +++ b/frontend/src/Routes.js @@ -12,6 +12,7 @@ import EmailVerification from "./pages/error/EmailVerification.jsx"; import AdminDashboard from "./pages/admin/AdminDashboard.jsx"; import AdminProducts from "./pages/admin/AdminProducts.jsx"; import Product from "./pages/Product.jsx"; +import Checkout from "./pages/Checkout.jsx"; export { Login, @@ -27,5 +28,6 @@ export { Error, EmailVerification, AdminDashboard, - AdminProducts + AdminProducts, + Checkout }; diff --git a/frontend/src/components/Footer.jsx b/frontend/src/components/Footer.jsx index 02f663b..9ea2f7a 100644 --- a/frontend/src/components/Footer.jsx +++ b/frontend/src/components/Footer.jsx @@ -8,37 +8,29 @@ export default function Footer() { const footer = (
- - - -
- ) - - return ( - <> - {user && user.role !== 1 ? ( - footer - ) : ( - footer - )} - + + + +
); + + return <>{user && user.role !== 1 ? footer :
}; } diff --git a/frontend/src/components/NavbarLoggedIn.jsx b/frontend/src/components/NavbarLoggedIn.jsx index 9c879b8..ff8e31a 100644 --- a/frontend/src/components/NavbarLoggedIn.jsx +++ b/frontend/src/components/NavbarLoggedIn.jsx @@ -12,24 +12,6 @@ import ShoppingCartComponent from "./ShoppingCartComponent"; export default function NavbarLoggedIn({ genres }) { const navigate = useNavigate(); const { user, setUser } = useContext(UserContext); - const { productAdded, setProductAdded } = useContext(CartContext); - const [cartProducts, setCartProducts] = useState([]); - - useEffect(() => { - if (user) { - const fetchCartProducts = async () => { - try { - const response = await axios.get(`/get-cart-products/${user._id}`); - if (!response.data.error) { - setCartProducts(response.data); - } - } catch (error) { - console.error(error); - } - }; - fetchCartProducts(); - } - }, [user, productAdded]); const logoutUser = async () => { try { @@ -136,7 +118,7 @@ export default function NavbarLoggedIn({ genres }) { {/** Icono de busqueda (Queda mal en mobiles) */} {/** Carrito de compra */} - {user.role !== 1 && } + {user.role !== 1 && } {/**Avatar desplegable */}
diff --git a/frontend/src/components/ShoppingCartComponent.jsx b/frontend/src/components/ShoppingCartComponent.jsx index 0109cfe..714a39e 100644 --- a/frontend/src/components/ShoppingCartComponent.jsx +++ b/frontend/src/components/ShoppingCartComponent.jsx @@ -1,54 +1,28 @@ import { Link } from "react-router-dom"; -import { memo, useState, useEffect } from "react"; +import { memo, useState, useEffect, useContext } from "react"; +import { UserContext } from "../context/userContext"; +import { CartContext } from "../context/cartContext"; import axios from "axios"; +import toast from "react-hot-toast"; -const ShoppingCartComponent = ({ cartProducts }) => { - const [products, setProducts] = useState([]); - const [totalPrice, setTotalPrice] = useState(0); - const [quantity, setQuantity] = useState(0); +const ShoppingCartComponent = () => { + const { user } = useContext(UserContext); + const { setCartChanged, products, quantity, totalPrice } = + useContext(CartContext); - useEffect(() => { - console.log("Cart Products:", cartProducts); + const deleteCartProduct = async (productId) => { + try { + const response = await axios.delete( + `/remove-from-cart/${user._id}/${productId}` + ); - if (!Array.isArray(cartProducts)) { - console.error("cartProducts is not an array:", cartProducts); - return; - } - - const getProducts = async () => { - try { - const productDetails = await Promise.all( - cartProducts.map(async (cartProduct) => { - const response = await axios.get( - `/get-product/${cartProduct.product}` - ); - return { ...response.data, quantity: cartProduct.quantity }; - }) - ); - - setProducts(productDetails); - - const totalQuantity = productDetails.reduce( - (acc, product) => acc + product.quantity, - 0 - ); - setQuantity(totalQuantity); - - const total = productDetails.reduce( - (acc, curr) => acc + Number(curr.price) * curr.quantity, - 0 - ); - setTotalPrice(total); - } catch (error) { - console.error(error); + if (!response.data.error) { + toast.success(response.data.message); + setCartChanged((val) => !val); + } else { + toast.error(response.data.error); } - }; - - getProducts(); - }, [cartProducts]); - - const deleteCartProduct = (productId) => { - // Lógica para eliminar el producto del carrito + } catch (error) {} }; return ( diff --git a/frontend/src/context/cartContext.jsx b/frontend/src/context/cartContext.jsx index 1215b70..8132d0c 100644 --- a/frontend/src/context/cartContext.jsx +++ b/frontend/src/context/cartContext.jsx @@ -1,14 +1,76 @@ -import { createContext, useState } from "react"; +import { createContext, useState, useEffect, useContext } from "react"; +import { UserContext } from "./userContext"; +import axios from "axios"; -export const CartContext = createContext({ - productAdded: false, - setProductAdded: () => {}, -}); +export const CartContext = createContext({}); export function CartContextProvider({ children }) { - const [productAdded, setProductAdded] = useState(false); + const [cartChanged, setCartChanged] = useState(false); + const [cartProducts, setCartProducts] = useState([]); + const { user } = useContext(UserContext); + const [products, setProducts] = useState([]); + const [totalPrice, setTotalPrice] = useState(0); + const [quantity, setQuantity] = useState(0); + + useEffect(() => { + if (user) { + const fetchCartProducts = async () => { + try { + const response = await axios.get(`/get-cart-products/${user._id}`); + if (!response.data.error) { + setCartProducts(response.data); + } + } catch (error) { + console.error(error); + } + }; + fetchCartProducts(); + } + }, [user, cartChanged]); + + useEffect(() => { + const getProducts = async () => { + try { + const productDetails = await Promise.all( + cartProducts.map(async (cartProduct) => { + const response = await axios.get( + `/get-product/${cartProduct.product}` + ); + return { ...response.data, quantity: cartProduct.quantity }; + }) + ); + + setProducts(productDetails); + + const totalQuantity = productDetails.reduce( + (acc, product) => acc + product.quantity, + 0 + ); + setQuantity(totalQuantity); + + const total = productDetails.reduce( + (acc, curr) => acc + Number(curr.price) * curr.quantity, + 0 + ); + setTotalPrice(total); + } catch (error) { + console.error(error); + } + }; + + getProducts(); + }, [cartProducts]); + return ( - + {children} ); diff --git a/frontend/src/pages/Cart.jsx b/frontend/src/pages/Cart.jsx index 0bfa435..01a4cbe 100644 --- a/frontend/src/pages/Cart.jsx +++ b/frontend/src/pages/Cart.jsx @@ -1,7 +1,134 @@ -import React from 'react' +import { useContext } from "react"; +import { UserContext } from "../context/userContext"; +import { CartContext } from "../context/cartContext"; +import { Link } from "react-router-dom"; +import Error from "./error/Error"; +import axios from "axios"; +import toast from "react-hot-toast"; export default function Cart() { + const { user } = useContext(UserContext); + const { setCartChanged, products, quantity, totalPrice } = + useContext(CartContext); + + const deleteCartProduct = async (productId) => { + try { + const response = await axios.delete( + `/remove-from-cart/${user._id}/${productId}` + ); + + if (!response.data.error) { + toast.success(response.data.message); + setCartChanged((val) => !val); + } else { + toast.error(response.data.error); + } + } catch (error) { + toast.error("Error del servidor"); + } + }; + + const addCartProduct = async (productId) => { + try { + const response = await axios.post( + `/add-to-cart/${user._id}/${productId}` + ); + if (!response.data.error) { + toast.success(response.data.message); + setCartChanged((val) => !val); + } else { + toast.error(response.data.error); + } + } catch (error) { + toast.error("Error del servidor"); + } + }; + return ( -
Cart
- ) + <> + {user ? ( +
+

{quantity} Productos

+ {products ? ( + products?.map((product) => ( +
+
+ {product.productName} +
+

{product.productName}

+

{product.description}

+
+
+
+ {/** Boton restar */} + + {product.quantity} + {/** Boton sumar */} + +
+
{product.price}€
+
+
+
+ )) + ) : ( + No hay productos + )} +
+

Total:

+

{totalPrice.toFixed(2)}€

+
+
+ + +
+
+ ) : ( + + )} + + ); } diff --git a/frontend/src/pages/Checkout.jsx b/frontend/src/pages/Checkout.jsx new file mode 100644 index 0000000..2403673 --- /dev/null +++ b/frontend/src/pages/Checkout.jsx @@ -0,0 +1,141 @@ +import { useState, useContext } from "react"; +import { CartContext } from "../context/cartContext"; +import TextBoxWithTextOnTop from "../components/TextBoxWithTextOnTop"; + +export default function Checkout() { + const { totalPrice } = useContext(CartContext); + const [paymentMethod, setPaymentMethod] = useState("card"); + + const handlePaymentMethodChange = (e) => { + setPaymentMethod(e.target.value); + }; + + return ( +
+
+
+
+
+
+ Detalles de envio +
+
+
+ + + + +
+ + +
+
+ + +
+
+
+
+
+
+ Método de pago +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+ Resumen del pedido +
+
+
+
+ Total productos + + {totalPrice.toFixed(2)}€ + +
+
+ Envío + 0.00€ +
+
+
+ Total + {totalPrice.toFixed(2)}€ +
+
+
+
+ +
+
+
+
+
+
+ ); +} diff --git a/frontend/src/pages/Product.jsx b/frontend/src/pages/Product.jsx index 9851cda..ea44f2a 100644 --- a/frontend/src/pages/Product.jsx +++ b/frontend/src/pages/Product.jsx @@ -10,9 +10,9 @@ import StarRating from "../components/StarRating"; export default function Product() { const { user } = useContext(UserContext); - const { productAdded, setProductAdded } = useContext(CartContext); + const { setCartChanged } = useContext(CartContext); const location = useLocation(); - const product = location.state.product; + const product = location.state?.product; const addToCart = async () => { try { @@ -23,7 +23,7 @@ export default function Product() { toast.error(response.data.error); } else { toast.success(response.data.message); - setProductAdded((val) => !val); + setCartChanged((val) => !val); } } catch (error) { toast.error(error.response.data.error);