From d178a520534af7d1cbcc03134034ad8a2327b461 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 20 Mar 2023 17:14:16 +0700 Subject: product detail and cart header --- src/lib/product/components/Product/Product.jsx | 37 +++ .../product/components/Product/ProductDesktop.jsx | 268 ++++++++++++++++++++ .../product/components/Product/ProductMobile.jsx | 280 +++++++++++++++++++++ 3 files changed, 585 insertions(+) create mode 100644 src/lib/product/components/Product/Product.jsx create mode 100644 src/lib/product/components/Product/ProductDesktop.jsx create mode 100644 src/lib/product/components/Product/ProductMobile.jsx (limited to 'src/lib/product/components/Product') diff --git a/src/lib/product/components/Product/Product.jsx b/src/lib/product/components/Product/Product.jsx new file mode 100644 index 00000000..9521cbe4 --- /dev/null +++ b/src/lib/product/components/Product/Product.jsx @@ -0,0 +1,37 @@ +import { toast } from 'react-hot-toast' +import useWishlist from '@/lib/wishlist/hooks/useWishlist' +import createOrDeleteWishlistApi from '@/lib/wishlist/api/createOrDeleteWishlistApi' +import ProductDesktop from './ProductDesktop' +import useAuth from '@/core/hooks/useAuth' +import ProductMobile from './ProductMobile' +import { useRouter } from 'next/router' + +const Product = ({ product }) => { + const auth = useAuth() + const router = useRouter() + const { wishlist } = useWishlist({ productId: product?.id }) + + const toggleWishlist = async () => { + if (!auth) { + router.push('/login') + return + } + const data = { product_id: product.id } + await createOrDeleteWishlistApi({ data }) + if (wishlist?.data?.productTotal > 0) { + toast.success('Berhasil menghapus dari wishlist') + } else { + toast.success('Berhasil menambahkan ke wishlist') + } + wishlist.refetch() + } + + return ( + <> + + + + ) +} + +export default Product diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx new file mode 100644 index 00000000..ac17ec6e --- /dev/null +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -0,0 +1,268 @@ +import Image from '@/core/components/elements/Image/Image' +import Link from '@/core/components/elements/Link/Link' +import DesktopView from '@/core/components/views/DesktopView' +import currencyFormat from '@/core/utils/currencyFormat' +import { HeartIcon } from '@heroicons/react/24/outline' +import { useRef, useState } from 'react' +import LazyLoad from 'react-lazy-load' +import ProductSimilar from '../ProductSimilar' +import { toast } from 'react-hot-toast' +import { updateItemCart } from '@/core/utils/cart' + +const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { + const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) + + const variantQuantityRefs = useRef([]) + + const setVariantQuantityRef = (variantId) => (element) => { + variantQuantityRefs.current[variantId] = element + } + + const validQuantity = (quantity) => { + let isValid = true + if (!quantity || quantity < 1 || isNaN(parseInt(quantity))) { + toast.error('Jumlah barang minimal 1') + isValid = false + } + return isValid + } + + const handleAddToCart = (variantId) => { + const quantity = variantQuantityRefs.current[variantId].value + if (!validQuantity(quantity)) return + updateItemCart({ + productId: variantId, + quantity + }) + toast.success('Berhasil menambahkan ke keranjang') + } + + const variantSectionRef = useRef(null) + const goToVariantSection = () => { + if (variantSectionRef.current) { + const position = variantSectionRef.current.getBoundingClientRect() + window.scrollTo({ + top: position.top - 120 + window.pageYOffset, + behavior: 'smooth' + }) + } + } + + const productSimilarQuery = [ + product?.name.replace(/[()/"&]/g, ''), + `fq=-product_id:${product.id}`, + `fq=-manufacture_id:${product.manufacture?.id || 0}` + ].join('&') + + return ( + +
+
+
+ {product.name} +
+
+

{product?.name}

+
+
+
Nomor SKU
+
SKU-{product.id}
+
+
+
Part Number
+
{product.code || '-'}
+
+
+
Manufacture
+
+ {product.manufacture?.name ? ( + {product.manufacture?.name} + ) : ( +
-
+ )} +
+
+
+
Berat Barang
+
+ {product?.weight > 0 && {product?.weight} KG} + {product?.weight == 0 && ( + + Tanya Berat + + )} +
+
+
+
+ +
+ {product.variants.length > 1 && product.lowestPrice.priceDiscount > 0 && ( +
Harga mulai dari:
+ )} + {product?.lowestPrice.discountPercentage > 0 && ( +
+
+ {product?.lowestPrice.discountPercentage}% +
+
+ {currencyFormat(product?.lowestPrice.price)} +
+
+ )} +

+ {product?.lowestPrice.priceDiscount > 0 ? ( + currencyFormat(product?.lowestPrice.priceDiscount) + ) : ( + + Hubungi kami untuk dapatkan harga terbaik,  + + klik disini + + + )} +

+ + +
+ +
+
+
+ +
+
Varian Produk
+
+ + + + + + + + + + + {product.variants.map((variant) => ( + + + + + + + ))} + +
Part NumberHargaJumlahAction
{variant.code} + {variant.price.discountPercentage > 0 && ( + <> + + {currencyFormat(variant.price.price)} + {' '} + + )} + {currencyFormat(variant.price.priceDiscount)} + + + + + +
+
+
+ +
+
Informasi Produk
+
+ {informationTabOptions.map((option) => ( + setInformationTab(option.value)} + > + {option.label} + + ))} +
+
+
+ + + + + Belum ada informasi. +
+
+
+ +
+
Kamu Mungkin Juga Suka
+ + + +
+
+
+ ) +} + +const informationTabOptions = [ + { value: 'description', label: 'Deskripsi' }, + { value: 'information', label: 'Info Penting' } +] + +const TabButton = ({ children, active, ...props }) => { + const activeClassName = active + ? 'text-red_r-11 underline underline-offset-4' + : 'text-gray_r-12/80' + return ( + + ) +} + +const TabContent = ({ children, active, className = '', ...props }) => ( +
+ {children} +
+) + +export default ProductDesktop diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx new file mode 100644 index 00000000..c572a58e --- /dev/null +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -0,0 +1,280 @@ +import Divider from '@/core/components/elements/Divider/Divider' +import Image from '@/core/components/elements/Image/Image' +import Link from '@/core/components/elements/Link/Link' +import currencyFormat from '@/core/utils/currencyFormat' +import { useEffect, useState } from 'react' +import Select from 'react-select' +import ProductSimilar from '../ProductSimilar' +import LazyLoad from 'react-lazy-load' +import { updateItemCart } from '@/core/utils/cart' +import { HeartIcon } from '@heroicons/react/24/outline' +import { useRouter } from 'next/router' +import MobileView from '@/core/components/views/MobileView' +import { toast } from 'react-hot-toast' + +const ProductMobile = ({ product, wishlist, toggleWishlist }) => { + const router = useRouter() + + const [quantity, setQuantity] = useState('1') + const [selectedVariant, setSelectedVariant] = useState(null) + const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) + + const [activeVariant, setActiveVariant] = useState({ + id: product.id, + code: product.code, + name: product.name, + price: product.lowestPrice, + stock: product.stockTotal, + weight: product.weight + }) + + const variantOptions = product.variants?.map((variant) => ({ + value: variant.id, + label: + (variant.code ? `[${variant.code}] ` : '') + + (variant.attributes.length > 0 ? variant.attributes.join(', ') : product.name) + })) + + useEffect(() => { + if (!selectedVariant && variantOptions.length == 1) { + setSelectedVariant(variantOptions[0]) + } + }, [selectedVariant, variantOptions]) + + useEffect(() => { + if (selectedVariant) { + const variant = product.variants.find((variant) => variant.id == selectedVariant.value) + const variantAttributes = + variant.attributes.length > 0 ? ' - ' + variant.attributes.join(', ') : '' + + setActiveVariant({ + id: variant.id, + code: variant.code, + name: variant.parent.name + variantAttributes, + price: variant.price, + stock: variant.stock, + weight: variant.weight + }) + } + }, [selectedVariant, product]) + + const validAction = () => { + let isValid = true + if (!selectedVariant) { + toast.error('Pilih varian terlebih dahulu') + isValid = false + } + if (!quantity || quantity < 1 || isNaN(parseInt(quantity))) { + toast.error('Jumlah barang minimal 1') + isValid = false + } + return isValid + } + + const handleClickCart = () => { + if (!validAction()) return + updateItemCart({ + productId: activeVariant.id, + quantity + }) + toast.success('Berhasil menambahkan ke keranjang') + } + + const handleClickBuy = () => { + if (!validAction()) return + router.push(`/shop/checkout?productId=${activeVariant.id}&quantity=${quantity}`) + } + + const productSimilarQuery = [ + product?.name.replace(/[()/"&]/g, ''), + `fq=-product_id:${product.id}`, + `fq=-manufacture_id:${product.manufacture?.id || 0}` + ].join('&') + + return ( + + {product.name} + +
+
+ {product.manufacture?.name ? ( + {product.manufacture?.name} + ) : ( +
-
+ )} + +
+

{activeVariant?.name}

+ {activeVariant?.price?.discountPercentage > 0 && ( +
+
+ {currencyFormat(activeVariant?.price?.price)} +
+
{activeVariant?.price?.discountPercentage}%
+
+ )} +

+ {activeVariant?.price?.priceDiscount > 0 ? ( + currencyFormat(activeVariant?.price?.priceDiscount) + ) : ( + + Hubungi kami untuk dapatkan harga terbaik,  + + klik disini + + + )} +

+
+ + + +
+
+ + setQuantity(e.target.value)} + /> +
+ + +
+ + + + +
+

Informasi Produk

+
+ {informationTabOptions.map((option) => ( + setInformationTab(option.value)} + > + {option.label} + + ))} +
+ + + + {product?.variantTotal} Varian + + + SKU-{product?.id} + + + {activeVariant?.code || '-'} + + + {activeVariant?.stock > 0 && ( + +
Ready Stock
+
{activeVariant?.stock > 5 ? '> 5' : '< 5'}
+
+ )} + {activeVariant?.stock == 0 && ( + + Tanya Stok + + )} +
+ + {activeVariant?.weight > 0 && {activeVariant?.weight} KG} + {activeVariant?.weight == 0 && ( + + Tanya Berat + + )} + +
+ + +
+ + + +
+

Kamu Mungkin Juga Suka

+ + + +
+
+ ) +} + +const informationTabOptions = [ + { value: 'specification', label: 'Spesifikasi' }, + { value: 'description', label: 'Deskripsi' }, + { value: 'information', label: 'Info Penting' } +] + +const TabButton = ({ children, active, ...props }) => { + const activeClassName = active ? 'text-red_r-11 underline underline-offset-4' : 'text-gray_r-11' + return ( + + ) +} + +const TabContent = ({ children, active, className, ...props }) => ( +
+ {children} +
+) + +const SpecificationContent = ({ children, label }) => ( +
+ {label} + {children} +
+) + +export default ProductMobile -- cgit v1.2.3