From 597102dc889b41c5b9993c938b9f9a773953ae57 Mon Sep 17 00:00:00 2001 From: dharshib Date: Sat, 3 Aug 2024 21:47:07 +0530 Subject: [PATCH 1/6] update --- src/App.jsx | 2 +- .../Popular_Categories/ProductGrid.jsx | 15 +-- .../components/Products/ProductDetails.jsx | 109 ++++++++++++------ 3 files changed, 81 insertions(+), 45 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index b81cd4638..84cff88c9 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -106,6 +106,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 */} @@ -134,7 +135,6 @@ export default function App() { } /> {/* Order details route */} } /> {/* My orders route */} } /> {/* Checkout route */} - } /> {/* Product details route */} } /> {/* Payment route */} } /> {/* Dashboard orders route */} } /> {/* Dashboard cart route */} diff --git a/src/User/components/Popular_Categories/ProductGrid.jsx b/src/User/components/Popular_Categories/ProductGrid.jsx index 5047d0d91..aa61334bb 100644 --- a/src/User/components/Popular_Categories/ProductGrid.jsx +++ b/src/User/components/Popular_Categories/ProductGrid.jsx @@ -8,10 +8,6 @@ import { FaHeart, FaRegHeart } from "react-icons/fa"; function ProductGrid({ products, headingText }) { - function getRandomDiscountPercent(price) { - return (price % 40) + 10; - } - function getNewPrice(discountPercent, actualPrice) { return ((100 - discountPercent) * actualPrice / 100).toFixed(2) } @@ -23,8 +19,9 @@ function ProductGrid({ products, headingText }) {
{products.map((product) => { - const discountPercent = getRandomDiscountPercent(product.price); - product.discountPercent = discountPercent + console.log(product) + console.log(product.discountPercent) + const discountPercent = product.discountPercent; product.newPrice = getNewPrice(discountPercent, product.price) return })} @@ -39,8 +36,8 @@ function ProductCard({ product }) { const cartItems = useSelector((state) => state.cart.items); const wishlistItems = useSelector((state) => state.wishlist.items); - const handleClick = () => { - navigate("/productDetails"); + const handleClick = (productId) => { + navigate(`/productDetails/${productId}`); }; const onAddToCart = (product) => { @@ -85,7 +82,7 @@ function ProductCard({ product }) { } {/* product imagee */} handleClick(product.id)} src={product.image} alt={product.title} className="w-full h-48 object-contain p-4" diff --git a/src/User/components/Products/ProductDetails.jsx b/src/User/components/Products/ProductDetails.jsx index 49f06c981..5cac70573 100644 --- a/src/User/components/Products/ProductDetails.jsx +++ b/src/User/components/Products/ProductDetails.jsx @@ -1,8 +1,10 @@ -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 { useParams } from "react-router-dom"; +import axios from "axios"; const sharedClasses = { textGray: "text-zinc-600 dark:white", @@ -14,19 +16,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 +37,41 @@ 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 }) => { + + function getNewPrice(discountPercent, actualPrice) { + return ((100 - discountPercent) * actualPrice / 100).toFixed(2) + } + + useEffect(() => { + if (product) { + const discountPercent = product?.discountPercent; + product.newPrice = product ? getNewPrice(discountPercent, product?.price) : 0 + } + }, [product]) + + return ( +
+

+ {product?.title} +

+

+ {product?.description} +

+

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

+

Inclusive of taxes

+ + + + + + + +
+ ) +}; const ColorOptions = () => (
@@ -190,16 +206,39 @@ const CustomerFeedback = () => (
); -const ProductPage = () => ( -
-
-
- - +const ProductPage = () => { + const { productId } = useParams(); + const [product, setProduct] = useState(null); + + console.log(product) + + const fetchProduct = async () => { + try { + const response = await axios.get(`https://dummyjson.com/products/${productId}`); + setProduct(response.data); + } catch (error) { + console.error("Axios error:", error); + } + }; + + useEffect(() => { + if (productId) { + fetchProduct(); + } + console.log(productId) + }, [productId]) + + return ( +
+
+
+ + +
+
-
-
-); + ) +}; export default ProductPage; From ae10e42d4352f2aabee41d464323707177d35c90 Mon Sep 17 00:00:00 2001 From: dharshib Date: Sun, 4 Aug 2024 09:58:57 +0530 Subject: [PATCH 2/6] made product detail page dynamic --- package-lock.json | 1 + .../Popular_Categories/ProductGrid.jsx | 5 +- .../components/Products/ProductDetails.jsx | 214 +++++++++++------- .../components/Products/Similarproducts.jsx | 73 ++++-- src/User/pages/Order/Wishlist.jsx | 2 +- .../Popular_Categories/Beauty-Wellness.jsx | 1 + .../pages/Popular_Categories/Body-Care.jsx | 1 + .../Popular_Categories/Customized-Gifts.jsx | 1 + .../Fashion-Accessories.jsx | 1 + .../Popular_Categories/Food-Beverages.jsx | 1 + .../Popular_Categories/Furniture-Decor.jsx | 1 + .../Popular_Categories/Health-Supplements.jsx | 1 + .../Printing-Stationery.jsx | 1 + src/User/redux/cartSlice.jsx | 2 +- 14 files changed, 200 insertions(+), 105 deletions(-) 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/User/components/Popular_Categories/ProductGrid.jsx b/src/User/components/Popular_Categories/ProductGrid.jsx index 9ad71719c..a9eb15ca3 100644 --- a/src/User/components/Popular_Categories/ProductGrid.jsx +++ b/src/User/components/Popular_Categories/ProductGrid.jsx @@ -21,9 +21,8 @@ function ProductGrid({ products, headingText }) {
{products.map((product) => { console.log(product) - console.log(product.discountPercent) - const discountPercent = product.discountPercent; - product.newPrice = getNewPrice(discountPercent, product.price) + console.log(product.discountPercentage) + product.newPrice = getNewPrice(product.discountPercentage, product.price) return })}
diff --git a/src/User/components/Products/ProductDetails.jsx b/src/User/components/Products/ProductDetails.jsx index 5cac70573..635be499e 100644 --- a/src/User/components/Products/ProductDetails.jsx +++ b/src/User/components/Products/ProductDetails.jsx @@ -5,6 +5,10 @@ import comb from "../../../assets/comb.jpg"; import Similarproducts from "../Products/Similarproducts"; import { useParams } from "react-router-dom"; import axios from "axios"; +import { useDispatch, useSelector } from "react-redux"; +import toast from "react-hot-toast"; +import { manageCartItem } from "../../redux/cartSlice"; +import { manageWishlistItem } from "../../redux/wishlist"; const sharedClasses = { textGray: "text-zinc-600 dark:white", @@ -39,16 +43,6 @@ const ProductImage = ({ thumbnail, images }) => ( const ProductDetails = ({ product }) => { - function getNewPrice(discountPercent, actualPrice) { - return ((100 - discountPercent) * actualPrice / 100).toFixed(2) - } - - useEffect(() => { - if (product) { - const discountPercent = product?.discountPercent; - product.newPrice = product ? getNewPrice(discountPercent, product?.price) : 0 - } - }, [product]) return (
@@ -65,10 +59,9 @@ const ProductDetails = ({ product }) => { - - - - + + +
) }; @@ -120,88 +113,132 @@ 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 onAddToCart = () => { + const quantity = 1; + dispatch(manageCartItem({ product, quantity })); + }; -const ProductInfo = () => ( + const onAddToWishlist = () => { + const quantity = 1; + dispatch(manageWishlistItem({ product, quantity })); + }; + + return ( +
+ + +
+ ) +}; + +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; + + 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 }); + + console.log(counts) + + 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 } + ]; + }; + + console.log(calculatePercentages()); + + 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 => { + return ( +
+
+ User Avatar +
+

{review?.reviewerName}

+

{review?.reviewerEmail}

+
+
+

+ {review?.comment} +

+
+ ) + }) + }
); @@ -210,12 +247,18 @@ const ProductPage = () => { const { productId } = useParams(); const [product, setProduct] = useState(null); - console.log(product) + 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}`); - setProduct(response.data); + const requiredProduct = response.data; + console.log(response.data) + requiredProduct.newPrice = getNewPrice(requiredProduct.discountPercentage, requiredProduct.price) + setProduct(requiredProduct); + console.log(requiredProduct); } catch (error) { console.error("Axios error:", error); } @@ -235,7 +278,8 @@ const ProductPage = () => {
- + +
) diff --git a/src/User/components/Products/Similarproducts.jsx b/src/User/components/Products/Similarproducts.jsx index c81409521..0bcefa063 100644 --- a/src/User/components/Products/Similarproducts.jsx +++ b/src/User/components/Products/Similarproducts.jsx @@ -1,8 +1,10 @@ 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 +14,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 +28,57 @@ 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)) { + 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(); + }, []); + + console.log(products) + return (

Similar Products

-
- {[...Array(visibleProducts)].map((_, index) => ( - - ))} +
+ + { + + products.slice(0, visibleProducts).map((product) => ( + + )) + }
- {visibleProducts < 18 ? ( + {visibleProducts < products.length/2 ? ( + { + cartItems.find((item) => item.id === product?.id) ? ( + + ) : + ( + + ) + } @@ -174,9 +177,9 @@ const ProductInfo = ({ product }) => ( const ProductRatings = ({ product }) => { const calculatePercentages = () => { - const totalReviews = product?.reviews.length; + const totalReviews = product?.reviews.length || 0; - const counts = product?.reviews.reduce((acc, review) => { + 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++; diff --git a/src/User/components/Products/Similarproducts.jsx b/src/User/components/Products/Similarproducts.jsx index 2174f077d..c85f7d878 100644 --- a/src/User/components/Products/Similarproducts.jsx +++ b/src/User/components/Products/Similarproducts.jsx @@ -60,7 +60,7 @@ const Similarproducts = ({category}) => { } }; fetchData(); - }, []); + }, [category]); return (