diff --git a/package-lock.json b/package-lock.json index b2641ca96..9a0deaebe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13758,6 +13758,7 @@ "version": "6.1.6", "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.1.6.tgz", "integrity": "sha512-x5h1Jcit7Qn03MuKlrWcMG9o12cp9SNDVHVJTNRi9TgtGPKcjKiXkou4NRfLAtXaFB3+Z8yZsVzONmPzhv2ErA==", + "license": "MIT", "dependencies": { "react-is": "^18.2.0", "styled-components": "^6.1.2" diff --git a/src/App.jsx b/src/App.jsx index 200a50e8f..af7a2dc1d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -120,6 +120,7 @@ export default function App() { } /> {/* Help page route */} } /> {/* Privacy policy page route */} } /> {/* Cart page route */} + } /> {/* Product details route */} } /> {/* Wishlist page route */} } /> {/* Contact page route */} } /> {/* Forgot password page route */} @@ -153,7 +154,6 @@ export default function App() { } /> {/* Order details route */} } /> {/* My orders route */} } /> {/* Checkout route */} - } /> {/* Product details route */} } /> {/* Payment route */} } /> {/* Dashboard orders route */} diff --git a/src/User/components/Popular_Categories/ProductGrid.jsx b/src/User/components/Popular_Categories/ProductGrid.jsx index 3d6af05c9..a8bfa8ae3 100644 --- a/src/User/components/Popular_Categories/ProductGrid.jsx +++ b/src/User/components/Popular_Categories/ProductGrid.jsx @@ -8,12 +8,7 @@ import { FaHeart, FaRegHeart } from "react-icons/fa"; // ProductGrid component to display a grid of products function ProductGrid({ products, headingText }) { - // Function to get a random discount percentage - function getRandomDiscountPercent(price) { - return (price % 40) + 10; - } - // Function to calculate the new price after discount function getNewPrice(discountPercent, actualPrice) { return ((100 - discountPercent) * actualPrice / 100).toFixed(2); } @@ -25,10 +20,8 @@ function ProductGrid({ products, headingText }) {
{products.map((product) => { - const discountPercent = getRandomDiscountPercent(product.price); - product.discountPercent = discountPercent; - product.newPrice = getNewPrice(discountPercent, product.price); - return ; + product.newPrice = getNewPrice(product.discountPercentage, product.price) + return })}
@@ -42,9 +35,8 @@ function ProductCard({ product }) { const cartItems = useSelector((state) => state.cart.items); const wishlistItems = useSelector((state) => state.wishlist.items); - // Function to handle product click and navigate to product details page - const handleClick = () => { - navigate("/productDetails"); + const handleClick = (productId) => { + navigate(`/productDetails/${productId}`); }; // Function to add product to cart @@ -86,7 +78,7 @@ function ProductCard({ product }) { )} {/* Product image */} handleClick(product.id)} src={product.image} alt={product.title} className="w-full h-60 object-contain" diff --git a/src/User/components/Products/ProductDetails.jsx b/src/User/components/Products/ProductDetails.jsx index 49f06c981..3ecba7e1c 100644 --- a/src/User/components/Products/ProductDetails.jsx +++ b/src/User/components/Products/ProductDetails.jsx @@ -1,8 +1,11 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import avatar from "../../../assets/avatar.png"; -import like from "../../../assets/like.png"; -import comb from "../../../assets/comb.jpg"; import Similarproducts from "../Products/Similarproducts"; +import { useNavigate, useParams } from "react-router-dom"; +import axios from "axios"; +import { useDispatch, useSelector } from "react-redux"; +import { manageCartItem } from "../../redux/cartSlice"; +import { manageWishlistItem } from "../../redux/wishlist"; const sharedClasses = { textGray: "text-zinc-600 dark:white", @@ -14,19 +17,19 @@ const sharedClasses = { buttonGreen: "px-4 py-2 bg-green-600 text-white rounded-lg", }; -const ProductImage = () => ( +const ProductImage = ({ thumbnail, images }) => (
Product Image
- {[1, 2, 3].map((i) => ( + {images?.map((i) => ( {`Thumbnail @@ -35,27 +38,30 @@ const ProductImage = () => (
); -const ProductDetails = () => ( -
-

- Bamboo India -

-

- Eco-friendly Handmade Bamboo Comb | Reduce Breakage and Hairfall -

-

- ₹175 ₹249 (30% OFF) -

-

Inclusive of taxes

- - - - - - - -
-); +const ProductDetails = ({ product }) => { + + + return ( +
+

+ {product?.title} +

+

+ {product?.description} +

+

+ ₹{product?.newPrice} {product?.price} ({product?.discountPercentage}% OFF) +

+

Inclusive of taxes

+ + + + + + +
+ ) +}; const ColorOptions = () => (
@@ -104,102 +110,185 @@ const DeliveryOptions = () => (
); -const ActionButtons = () => ( -
- - -
-); +const ActionButtons = ({ product }) => { + const dispatch = useDispatch(); + const cartItems = useSelector((state) => state.cart.items); + const wishlistItems = useSelector((state) => state.wishlist.items); + const navigate = useNavigate() -const ProductInfo = () => ( + const onAddToCart = () => { + const quantity = 1; + if (product && quantity > 0) { + dispatch(manageCartItem({ product, quantity })); + } + }; + + const onAddToWishlist = () => { + const quantity = 1; + dispatch(manageWishlistItem({ product, quantity })); + }; + + return ( +
+ { + cartItems.find((item) => item.id === product?.id) ? ( + + ) : + ( + + ) + } + +
+ ) +}; + +const ProductInfo = ({ product }) => (

Product Details

- {[ - "Pattern: Solid", - "Sleeve Length: Short Sleeves", - "Neck: Round Neck", - "Sustainable: Regular", - "Wash Care: Machine Wash", - "Country of Origin: India", - ].map((detail, index) => ( -

- {detail} -

- ))} + {product?.category &&

Category: {product.category}

} + {product?.availabilityStatus &&

Availability Status: {product.availabilityStatus}

} + {product?.brand &&

Brand: {product.brand}

} + {product?.returnPolicy &&

Return Policy: {product.returnPolicy}

} + {product?.warrantyInformation &&

Warranty Information: {product.warrantyInformation}

} + {product?.shippingInformation &&

Shipping Information: {product.shippingInformation}

}
); -const ProductRatings = () => ( -
-

Product Ratings & Reviews

-
- 3.7 - +const ProductRatings = ({ product }) => { + + const calculatePercentages = () => { + const totalReviews = product?.reviews.length || 0; + + const counts = product?.reviews?.reduce((acc, review) => { + if (review.rating === 5) acc.excellent++; + else if (review.rating === 4) acc.veryGood++; + else if (review.rating === 3 || review.rating === 2) acc.good++; + else if (review.rating === 1 || review.rating === 0) acc.low++; + return acc; + }, { excellent: 0, veryGood: 0, good: 0, low: 0 }); + + + return [ + { label: "Excellent", width: (counts?.excellent / totalReviews) * 100 }, + { label: "Very Good", width: (counts?.veryGood / totalReviews) * 100 }, + { label: "Good", width: (counts?.good / totalReviews) * 100 }, + { label: "Low", width: (counts?.low / totalReviews) * 100 } + ]; + }; + + + return ( +
+

Product Ratings & Reviews

+
+ {product?.rating} + +
+

Total reviews {product?.reviews.length}

+ {calculatePercentages().map((rating, index) => ( + + ))}
-

204 Ratings, 36 Reviews

- {[ - { label: "Excellent", width: "w-2/3", color: "bg-green-600" }, - { label: "Very Good", width: "w-1/2", color: "bg-green-500" }, - { label: "Good", width: "w-1/3", color: "bg-green-400" }, - { label: "Average", width: "w-1/4", color: "bg-green-300" }, - ].map((rating, index) => ( - - ))} -
-); + ) +}; -const RatingBar = ({ label, width, color }) => ( +const RatingBar = ({ label, width }) => (
{label} -
+
+
+
); -const CustomerFeedback = () => ( -
+const CustomerFeedback = ({ reviews }) => ( +

Customer Feedback

-
-
- User Avatar -
-

User Name

-

Posted on May 2023

-
-
-

- This is a good comb. It's very comfortable, and the size is just - right... but great. Nice buy! -

-
- Product Thumbnail - Likes: 21 -
+
+ { + reviews?.map((review, index) => { + return ( +
+
+ User Avatar +
+

{review?.reviewerName}

+

{review?.reviewerEmail}

+
+
+

+ {review?.comment} +

+
+ ) + }) + }
); -const ProductPage = () => ( -
-
-
- - +const ProductPage = () => { + const { productId } = useParams(); + const [product, setProduct] = useState(null); + + function getNewPrice(discountPercent, actualPrice) { + return ((100 - discountPercent) * actualPrice / 100).toFixed(2) + } + + const fetchProduct = async () => { + try { + const response = await axios.get(`https://dummyjson.com/products/${productId}`); + const requiredProduct = response.data; + console.log(response.data) + requiredProduct.newPrice = getNewPrice(requiredProduct.discountPercentage, requiredProduct.price) + setProduct(requiredProduct); + } catch (error) { + console.error("Axios error:", error); + } + }; + + useEffect(() => { + if (productId) { + fetchProduct(); + } + }, [productId]) + + return ( +
+
+
+ + +
+ +
-
-
-); + ) +}; export default ProductPage; diff --git a/src/User/components/Products/Similarproducts.jsx b/src/User/components/Products/Similarproducts.jsx index c81409521..c85f7d878 100644 --- a/src/User/components/Products/Similarproducts.jsx +++ b/src/User/components/Products/Similarproducts.jsx @@ -1,8 +1,9 @@ import React, { useState, useEffect } from 'react'; -import comb from '../../../assets/comb.jpg'; +import axios from 'axios'; -const Similarproducts = () => { - const [visibleProducts, setVisibleProducts] = useState(6); +const Similarproducts = ({category}) => { + const [visibleProducts, setVisibleProducts] = useState(4); + const [products, setProducts] = useState([]) const [screenSize, setScreenSize] = useState(window.innerWidth); useEffect(() => { @@ -12,9 +13,9 @@ const Similarproducts = () => { }, []); const getProductsPerRow = () => { - if (screenSize >= 1024) return 6; - if (screenSize >= 768) return 4; - if (screenSize >= 640) return 3; + if (screenSize >= 1024) return 4; + if (screenSize >= 768) return 3; + if (screenSize >= 640) return 2; return 2; }; @@ -26,16 +27,56 @@ const Similarproducts = () => { setVisibleProducts(getProductsPerRow()); }; + function getNewPrice(discountPercent, actualPrice) { + return ((100 - discountPercent) * actualPrice / 100).toFixed(2) + } + + useEffect(() => { + const fetchData = async () => { + try { + const response = await axios.get("https://dummyjson.com/products"); + if (response.data && Array.isArray(response.data.products) && response.data.products.length > 0) { + const mappedProducts = response.data.products.map((product) => ({ + id: product.id, + title: product.title, + price: product.price, + category: product.category, + image: product.images[0] || "", + discountPercentage: product.discountPercentage, + newPrice: getNewPrice(product.discountPercentage, product.price), + rating: { + rate: product.rating, + count: product.reviews ? product.reviews.length : 0, + }, + })); + setProducts([...mappedProducts.filter(product => product.category === category), ...mappedProducts.filter(product => product.category !== category)]); + } else { + setProducts([]); + toast.error("No products found."); + } + } catch (error) { + toast.error("Oops, can't get your products, sorry! Try refreshing the page."); + console.error("Fetching products failed:", error); + } + }; + fetchData(); + }, [category]); + + return (

Similar Products

-
- {[...Array(visibleProducts)].map((_, index) => ( - - ))} +
+ + { + + products.slice(0, visibleProducts).map((product) => ( + + )) + }
- {visibleProducts < 18 ? ( + {visibleProducts < products.length/2 ? (