Skip to content

Commit

Permalink
Agregado contexto del carrito para su actualizacion
Browse files Browse the repository at this point in the history
  • Loading branch information
Adolfopgv committed May 29, 2024
1 parent bbe5523 commit e0f6430
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 84 deletions.
32 changes: 18 additions & 14 deletions backend/controllers/cartController.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
const Cart = require("../models/cartModel");
const User = require("../models/userModel");
const Product = require("../models/productModel");

const addToCart = async (req, res) => {
try {
const userId = req.params.userid;
const productId = req.params.product;

if (!userId && !productId) {
return res.json({
error: "Error al añadir producto",
});
if (!userId || !productId) {
return res.json({ error: "Error al añadir producto" });
}

const user = await User.findById(userId);
const product = await Product.findById(productId);
const cart = await Cart.findOne({ userId });

let cart = await Cart.findOne({ userId: user._id });
if (!cart) {
cart = await Cart.create({
userId: user._id,
products: [product._id],
const newCart = new Cart({
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
);

if (productIndex !== -1) {
cart.products[productIndex].quantity += 1;
} else {
cart.products.push([product._id]);
cart.products.push({ product: productId, quantity: 1 });
}

await cart.save();

res.status(200).json({ message: "Producto añadido al carrito " });
res.status(200).json({ message: "Producto añadido al carrito" });
} catch (error) {
res.status(500).json({ error: "Error del servidor" });
}
Expand Down
4 changes: 4 additions & 0 deletions backend/models/cartModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const cartSchema = new Schema(
type: Schema.Types.ObjectId,
ref: "Product",
},
quantity: {
type: Number,
default: 1,
},
},
],
},
Expand Down
57 changes: 33 additions & 24 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import {
Error,
EmailVerification,
AdminDashboard,
AdminProducts
AdminProducts,
} from "./Routes";
import axios from "axios";
import { Toaster } from "react-hot-toast";
import { UserContextProvider } from "./context/userContext";
import { CartContextProvider } from "./context/cartContext";
import { GoogleOAuthProvider } from "@react-oauth/google";

axios.defaults.baseURL = import.meta.env.VITE_APP_AXIOS_BASE_URL;
Expand All @@ -32,29 +33,37 @@ export default function App() {
clientId={`${import.meta.env.VITE_APP_GOOGLE_API_TOKEN}`}
>
<UserContextProvider>
<Navbar />
<Toaster
position="top-center"
reverseOrder={false}
toastOptions={{ duration: 2500 }}
/>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/profile" element={<Profile />} />
<Route path="/cart" element={<Cart />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/news" element={<News />} />
<Route path="/store/:genre" element={<Store />} />
<Route path="/store/:genre/:productName" element={<Product />} />
<Route path="/users/:id/verify/:token" element={<EmailVerification />} />
<Route path="/admin-dashboard" element={<AdminDashboard />} />
<Route path="/admin-dashboard/products" element={<AdminProducts />} />
<Route path="/*" element={<Error />} />
</Routes>
<Footer />
<CartContextProvider>
<Navbar />
<Toaster
position="top-center"
reverseOrder={false}
toastOptions={{ duration: 2500 }}
/>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/profile" element={<Profile />} />
<Route path="/cart" element={<Cart />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/news" element={<News />} />
<Route path="/store/:genre" element={<Store />} />
<Route path="/store/:genre/:productName" element={<Product />} />
<Route
path="/users/:id/verify/:token"
element={<EmailVerification />}
/>
<Route path="/admin-dashboard" element={<AdminDashboard />} />
<Route
path="/admin-dashboard/products"
element={<AdminProducts />}
/>
<Route path="/*" element={<Error />} />
</Routes>
<Footer />
</CartContextProvider>
</UserContextProvider>
</GoogleOAuthProvider>
);
Expand Down
19 changes: 1 addition & 18 deletions frontend/src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import axios from "axios";
export default function Navbar() {
const { user } = useContext(UserContext);
const [genres, setGenres] = useState([]);
const [cartProducts, setCartProducts] = useState([]);

useEffect(() => {
const getGenres = async () => {
Expand All @@ -24,26 +23,10 @@ export default function Navbar() {
getGenres();
}, []);

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]);

return (
<>
{user ? (
<NavBarLoggedIn genres={genres} cartProducts={cartProducts} />
<NavBarLoggedIn genres={genres} />
) : (
<NavBarLoggedOut genres={genres} />
)}
Expand Down
29 changes: 23 additions & 6 deletions frontend/src/components/NavbarLoggedIn.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
import { Link } from "react-router-dom";
import { useContext } from "react";
import { useContext, useEffect, useState } from "react";
import { UserContext } from "../context/userContext";
import { CartContext } from "../context/cartContext";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import toast from "react-hot-toast";
import TextBoxWithTextOnTop from "./TextBoxWithTextOnTop";
import ShopMenuComponent from "./ShopMenuComponent";
import ShoppingCartComponent from "./ShoppingCartComponent";

export default function NavbarLoggedIn({ genres, cartProducts }) {
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 {
Expand Down Expand Up @@ -116,10 +135,8 @@ export default function NavbarLoggedIn({ genres, cartProducts }) {

{/** Icono de busqueda (Queda mal en mobiles) */}

{/** Carrito de compra pantallas pequeñas */}
{user.role !== 1 && (
<ShoppingCartComponent cartProducts={cartProducts} />
)}
{/** Carrito de compra */}
{user.role !== 1 && <ShoppingCartComponent cartProducts={cartProducts} />}

{/**Avatar desplegable */}
<div className="mr-4 dropdown dropdown-end">
Expand Down
47 changes: 26 additions & 21 deletions frontend/src/components/ShoppingCartComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,33 @@ const ShoppingCartComponent = ({ cartProducts }) => {
const [quantity, setQuantity] = useState(0);

useEffect(() => {
console.log("Cart Products:", cartProducts);

if (!Array.isArray(cartProducts)) {
console.error("cartProducts is not an array:", cartProducts);
return;
}

const getProducts = async () => {
try {
const productIds = cartProducts.map((cartProduct) => cartProduct._id);
const productDetails = await Promise.all(
productIds.map(async (productId) => {
const response = await axios.get(`/get-product/${productId}`);
return response.data;
cartProducts.map(async (cartProduct) => {
const response = await axios.get(
`/get-product/${cartProduct.product}`
);
return { ...response.data, quantity: cartProduct.quantity };
})
);

const productMap = productDetails.reduce((acc, product) => {
if (!acc[product._id]) {
acc[product._id] = { ...product, quantity: 1 };
} else {
acc[product._id].quantity += 1;
}
// setQuantity(acc[product._id].quantity);
return acc;
}, {});

const uniqueProducts = Object.values(productMap);
setProducts(productDetails);

setProducts(uniqueProducts);
const totalQuantity = productDetails.reduce(
(acc, product) => acc + product.quantity,
0
);
setQuantity(totalQuantity);

const total = uniqueProducts.reduce(
const total = productDetails.reduce(
(acc, curr) => acc + Number(curr.price) * curr.quantity,
0
);
Expand All @@ -41,10 +43,13 @@ const ShoppingCartComponent = ({ cartProducts }) => {
console.error(error);
}
};

getProducts();
}, [cartProducts]);

const deleteCartProduct = () => {};
const deleteCartProduct = (productId) => {
// Lógica para eliminar el producto del carrito
};

return (
<div className="mr-1 dropdown dropdown-end">
Expand Down Expand Up @@ -72,8 +77,8 @@ const ShoppingCartComponent = ({ cartProducts }) => {
className="mt-3 z-[1] card card-compact dropdown-content w-72 bg-accent shadow"
>
<div className="card-body">
<span className="font-bold text-lg">{products.length} Productos</span>
<span className="text-black">Total: {totalPrice}</span>
<span className="font-bold text-lg">{quantity} Productos</span>
<span className="text-black">Total: {totalPrice.toFixed(2)}</span>
<div className="card-actions overflow-auto max-h-96">
{products.map((product) => (
<div
Expand All @@ -88,7 +93,7 @@ const ShoppingCartComponent = ({ cartProducts }) => {
</div>
<button
className="btn btn-ghost ml-4 flex items-center justify-center"
onClick={() => deleteCartProduct()}
onClick={() => deleteCartProduct(product._id)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/context/cartContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createContext, useState } from "react";

export const CartContext = createContext({
productAdded: false,
setProductAdded: () => {},
});

export function CartContextProvider({ children }) {
const [productAdded, setProductAdded] = useState(false);
return (
<CartContext.Provider value={{ productAdded, setProductAdded }}>
{children}
</CartContext.Provider>
);
}
3 changes: 2 additions & 1 deletion frontend/src/context/userContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ export function UserContextProvider({ children }) {
});
}
}, [user]);

return (
<UserContext.Provider value={{user, setUser}}>
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/pages/Product.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useContext } from "react";
import { useLocation } from "react-router-dom";
import { UserContext } from "../context/userContext";
import { CartContext } from "../context/cartContext";
import { toast } from "react-hot-toast";
import axios from "axios";
import Error from "./error/Error";
Expand All @@ -9,6 +10,7 @@ import StarRating from "../components/StarRating";

export default function Product() {
const { user } = useContext(UserContext);
const { productAdded, setProductAdded } = useContext(CartContext);
const location = useLocation();
const product = location.state.product;

Expand All @@ -21,6 +23,7 @@ export default function Product() {
toast.error(response.data.error);
} else {
toast.success(response.data.message);
setProductAdded((val) => !val);
}
} catch (error) {
toast.error(error.response.data.error);
Expand Down

0 comments on commit e0f6430

Please sign in to comment.