From 27a07336675d7aaca8b1d06726b46e6a0ac2e29f Mon Sep 17 00:00:00 2001 From: amirmoghi3 Date: Wed, 5 Mar 2025 18:26:11 +0330 Subject: [PATCH] pdp change --- package.json | 3 +- pnpm-lock.yaml | 14 + src/app/[locale]/product/[slug]/page.jsx | 257 ++++++++++++++----- src/components/BackgroundColor/index.jsx | 14 + src/components/ColorAvg/index.jsx | 48 ++-- src/components/ContactUs/index.jsx | 3 +- src/components/EnquiryForm/index.jsx | 131 ++++++++++ src/components/Gallery/index.jsx | 84 ++++++ src/components/NavBar/index.jsx | 6 +- src/components/Product/ProductProperties.jsx | 94 ++++++- src/view/Categories/components/Content.jsx | 10 +- 11 files changed, 556 insertions(+), 108 deletions(-) create mode 100644 src/components/BackgroundColor/index.jsx create mode 100644 src/components/EnquiryForm/index.jsx create mode 100644 src/components/Gallery/index.jsx diff --git a/package.json b/package.json index bccc1ad..44e2c77 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "react-intersection-observer": "^9.15.1", "react-toastify": "^11.0.3", "swiper": "^11.2.2", - "tailwind-merge": "^3.0.2" + "tailwind-merge": "^3.0.2", + "yet-another-react-lightbox": "^3.21.7" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2af746b..893af80 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ dependencies: tailwind-merge: specifier: ^3.0.2 version: 3.0.2 + yet-another-react-lightbox: + specifier: ^3.21.7 + version: 3.21.7(react-dom@19.0.0)(react@19.0.0) devDependencies: '@eslint/eslintrc': @@ -3500,6 +3503,17 @@ packages: hasBin: true dev: true + /yet-another-react-lightbox@3.21.7(react-dom@19.0.0)(react@19.0.0): + resolution: {integrity: sha512-dcdokNuCIl92f0Vl+uzeKULnQhztIGpoZFUMvtVNUPmtwsQWpqWufeieDPeg9JtFyVCcbj4vYw3V00DS0QNoWA==} + engines: {node: '>=14'} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + dev: false + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/src/app/[locale]/product/[slug]/page.jsx b/src/app/[locale]/product/[slug]/page.jsx index c75732d..42bd933 100644 --- a/src/app/[locale]/product/[slug]/page.jsx +++ b/src/app/[locale]/product/[slug]/page.jsx @@ -1,11 +1,11 @@ -import { ChevronRight, Home } from "lucide-react"; + +import { ArrowRight, Award, Boxes, CheckCircle2, ChevronRight, Link as LinkIcon, MessageSquare, ShieldCheck, Ship } from "lucide-react"; import { getLocale, getMessages } from "next-intl/server"; +import Image from "next/image"; import Link from "next/link"; import { notFound } from "next/navigation"; -import ColorAvg from "src/components/ColorAvg"; -import ProductDescription from "src/components/Product/ProductDescription"; -import ProductGallery from "src/components/Product/ProductGallery"; -import ProductInfo from "src/components/Product/ProductInfo"; +import BackgroundColor from "src/components/BackgroundColor"; +import { ProductGallery } from "src/components/Gallery"; import ProductProperties from "src/components/Product/ProductProperties"; import ProductRelated from "src/components/Product/ProductRelated"; import graphql from "src/utils/graphql"; @@ -47,6 +47,16 @@ query Products($locale: I18NLocaleCode, $slug: String!) { slug discount showPrice + properties { + id + key + value + } + subcategories { + title + documentId + slug + } seo { id metaTitle @@ -74,16 +84,7 @@ query Products($locale: I18NLocaleCode, $slug: String!) { } } } - properties { - id - key - value - } - subcategories { - title - documentId - slug - } + } } @@ -131,6 +132,8 @@ query Products($locale:I18NLocaleCode,$slug:String!) { } } ` + + export async function generateMetadata({ params }) { const { locale, slug } = await params; @@ -203,6 +206,23 @@ const getProduct = async (slug) => { }); return products[0]; }; +const features = [ + { + icon: Award, + title: "Certified Product", + description: "Meets international quality standards" + }, + { + icon: Ship, + title: "Bulk Shipping", + description: "Efficient worldwide delivery solutions" + }, + { + icon: ShieldCheck, + title: "Quality Assured", + description: "Rigorous testing at every stage" + } +]; export default async function ProductPage({ params }) { const { slug } = await params @@ -213,67 +233,172 @@ export default async function ProductPage({ params }) { } const locale = await getLocale(); const t = await getMessages({ locale }); - + return ( -
- {/* */} -
- -
-
-
- - - - { - product?.category?.slug && - <> - - + //
+ // {/* */} + //
+ // + //
+ //
+ //
+ // + // + // + // { + // product?.category?.slug && + // <> + // + // - {product.category.title} + // {product.category.title} + // + // + // } + // { + // product?.brand?.slug && + // <> + // + // + // {product.brand.title} + // + // + // } + // + // {product.title} + //
+ + //
+ //
+ + // + + //
+ //
+ //
+ //
+ // + //
+ //
+ // + //
+ //
+ // + //
+ //
+ +
+ {/* Hero Section */} + +
+ +
+
+
+

{product.title}

+
+ Price upon request +
+
+ {product?.brand && ( + +
+ {product.brand?.title} +
+ {product?.brand?.title} - - } - { - product?.brand?.slug && - <> - - - {product.brand.title} + )} + + + {product.category.title} + + {product.brand?.subcategories.map(subcat => ( + + + {subcat.title} - - } - - {product.title} + ))} +
+ +
+
+
+ +
- -
-
- - -
-
- + + + + {/* Product Description */} +
+
+
+
+

Product Description

+
+
+ + + + +
+
+ {/* Features */} +
+
+
+
+ {features.map((feature, index) => ( +
+
+ +
+
+

{feature.title}

+

{feature.description}

+
+
+ ))} +
+
+
+
-
- -
-
- + + + {/* Related Products */} +
+
+ + +
+ +
); } \ No newline at end of file diff --git a/src/components/BackgroundColor/index.jsx b/src/components/BackgroundColor/index.jsx new file mode 100644 index 0000000..fa8f2a7 --- /dev/null +++ b/src/components/BackgroundColor/index.jsx @@ -0,0 +1,14 @@ +"use client" +import React from 'react' +import useAvgColor from '../ColorAvg' + +const BackgroundColor = ({ image }) => { + + const {color} = useAvgColor(image) + + return ( +
+ ) +} + +export default BackgroundColor \ No newline at end of file diff --git a/src/components/ColorAvg/index.jsx b/src/components/ColorAvg/index.jsx index 0bc6d36..89a5518 100644 --- a/src/components/ColorAvg/index.jsx +++ b/src/components/ColorAvg/index.jsx @@ -1,24 +1,32 @@ -"use client" -import React, { useEffect } from 'react' +"use client"; +import { useEffect, useState } from 'react'; import { FastAverageColor } from 'fast-average-color'; -const ColorAvg = ({image}) => { + +const useAvgColor = (image) => { + const [color, setColor] = useState(""); + const [isDark, setIsDark] = useState(false); + useEffect(() => { const fac = new FastAverageColor(); - fac.getColorAsync(image) - .then(color => { - console.log("color",color) - // document.body.style.background = color.rgba - // document.body.style.color = color.isDark ? '#fff' : '#000'; - // container.style.backgroundColor = color.rgba; - // container.style.color = color.isDark ? '#fff' : '#000'; - }) - .catch(e => { - console.log(e); - }); - }, []) - return ( - <> - ) -} + console.log("image",image) -export default ColorAvg \ No newline at end of file + if (image) { + fac.getColorAsync(image) + .then((colorResult) => { + setColor(colorResult.hex ?? "#e18c8c"); + setIsDark(colorResult.isDark) + }) + .catch((e) => { + console.log(e); + }); + }else{ + setColor("#e18c8c") + } + + return () => fac.destroy(); // Clean up on component unmount + }, [image]); + + return {color,isDark}; +}; + +export default useAvgColor; diff --git a/src/components/ContactUs/index.jsx b/src/components/ContactUs/index.jsx index ee16133..e3e95aa 100644 --- a/src/components/ContactUs/index.jsx +++ b/src/components/ContactUs/index.jsx @@ -36,7 +36,8 @@ export default function ContactModal({ close, open }) { data: { email, companyName, - message + message, + }, locale }) diff --git a/src/components/EnquiryForm/index.jsx b/src/components/EnquiryForm/index.jsx new file mode 100644 index 0000000..d7ba005 --- /dev/null +++ b/src/components/EnquiryForm/index.jsx @@ -0,0 +1,131 @@ +import React from 'react'; +import { Send, X, User, Building, Mail, Phone, Package, MessageSquare } from 'lucide-react'; + + +export const EnquiryForm = ({ isOpen, onClose }) => { + const handleSubmit = (e) => { + e.preventDefault(); + // Handle form submission + }; + + if (!isOpen) return null; + + return ( +
+
+
+

Request Quote

+

Fill out the form below for pricing and more information

+ +
+ +
+
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+
+ + +
+
+
+ ); +}; \ No newline at end of file diff --git a/src/components/Gallery/index.jsx b/src/components/Gallery/index.jsx new file mode 100644 index 0000000..0ae7c4e --- /dev/null +++ b/src/components/Gallery/index.jsx @@ -0,0 +1,84 @@ +"use client" +import React, { useState } from 'react'; +import useEmblaCarousel from 'embla-carousel-react'; +import Autoplay from 'embla-carousel-autoplay'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; +import Lightbox from "yet-another-react-lightbox"; +import Zoom from "yet-another-react-lightbox/plugins/zoom"; +import "yet-another-react-lightbox/styles.css"; + + +export const ProductGallery = ({ images }) => { + const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }, [Autoplay()]); + const [lightboxOpen, setLightboxOpen] = useState(false); + const [currentImageIndex, setCurrentImageIndex] = useState(0); + + const scrollPrev = React.useCallback(() => { + if (emblaApi) emblaApi.scrollPrev(); + }, [emblaApi]); + + const scrollNext = React.useCallback(() => { + if (emblaApi) emblaApi.scrollNext(); + }, [emblaApi]); + + const openLightbox = (index) => { + setCurrentImageIndex(index); + setLightboxOpen(true); + }; + const slides = images.map(image => ({ + src: image.url, + alt: image.alternativeText || 'Product image' + })); + + return ( +
+
+
+ {images.map((image, index) => ( +
+ {image.alternativeText openLightbox(index)} + /> +
+ ))} +
+
+ + + + + + setLightboxOpen(false)} + index={currentImageIndex} + slides={slides} + plugins={[Zoom]} + zoom={{ + maxZoomPixelRatio: 5, + zoomInMultiplier: 2, + doubleTapDelay: 300, + doubleClickDelay: 300, + doubleClickMaxStops: 2, + keyboardMoveDistance: 50, + wheelZoomDistanceFactor: 100, + pinchZoomDistanceFactor: 100, + scrollToZoom: true + }} + /> +
+ ); +}; \ No newline at end of file diff --git a/src/components/NavBar/index.jsx b/src/components/NavBar/index.jsx index c6720d1..89a0283 100644 --- a/src/components/NavBar/index.jsx +++ b/src/components/NavBar/index.jsx @@ -86,7 +86,7 @@ const Navbar = ({ items }) => {

salam

*/} {isScrolled && ( -
+
)} @@ -185,14 +185,14 @@ const Navbar = ({ items }) => { { isScrolled && ( -
+ llc -
+ ) } {/*
diff --git a/src/components/Product/ProductProperties.jsx b/src/components/Product/ProductProperties.jsx index e20f11f..9b619cf 100644 --- a/src/components/Product/ProductProperties.jsx +++ b/src/components/Product/ProductProperties.jsx @@ -1,19 +1,89 @@ -import { getMessages } from "next-intl/server" +"use client" +import { CheckCircle2 } from "lucide-react"; +import { useTranslations } from "next-intl"; +import { useState } from "react"; +import ContactModal from "../ContactUs"; +import Image from "next/image"; +import Link from "next/link"; +import { LinkIcon } from "lucide-react"; +import { ArrowRight } from "lucide-react"; -export default async function ProductProperties({ properties }) { - const t = await getMessages() +export default function ProductProperties({ product }) { + const t = useTranslations("PDP") + const [open, setOpen] = useState(false); + const openModal = () => { + setOpen(true); + }; + const closeModal = () => { + setOpen(false); + } return ( -
-

{t.PDP.productSpecifications}

-
- {properties.map((prop) => ( -
-
{prop.key}
-
{prop.value}
+
+
+

{t("productSpecification")}

+
+ {product.properties.map(prop => ( +
+ +
+ {prop.key}: + {prop.value} +
+
+ ))} +
+
+ {product?.brand && +
+

Brand

+
+
+ {product?.brand?.image?.alternativeText} +
+
+ + {product?.brand?.title} + + + View All + +
- ))} -
+
+ } + {product.brand?.subcategories.lenght && +
+

Use Cases

+
+ {product.brand?.subcategories.map(subcat => ( + + + {subcat.title} + + ))} +
+
+ } + +
) } diff --git a/src/view/Categories/components/Content.jsx b/src/view/Categories/components/Content.jsx index 78a98ed..088ba56 100644 --- a/src/view/Categories/components/Content.jsx +++ b/src/view/Categories/components/Content.jsx @@ -7,7 +7,7 @@ import ContactModal from 'src/components/ContactUs'; const Content = ({ content }) => { - + const [open, setOpen] = useState(false); const openModal = () => { setOpen(true); @@ -27,7 +27,7 @@ const Content = ({ content }) => {
{content.image?.alternativeText { className="p-2" />
- + {/*
Detergents @@ -65,7 +65,7 @@ const Content = ({ content }) => { {/* */} -
@@ -73,7 +73,7 @@ const Content = ({ content }) => {
- + ) }