From 3b19ddcd0051f094b4659a35107646d678c2fd0c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 14 Mar 2023 17:18:16 +0700 Subject: product detail --- src/lib/product/components/Product.jsx | 300 +------------------------- src/lib/product/components/ProductCard.jsx | 2 +- src/lib/product/components/ProductDesktop.jsx | 160 ++++++++++++++ src/lib/product/components/ProductMobile.jsx | 280 ++++++++++++++++++++++++ src/styles/globals.css | 33 ++- 5 files changed, 478 insertions(+), 297 deletions(-) create mode 100644 src/lib/product/components/ProductDesktop.jsx create mode 100644 src/lib/product/components/ProductMobile.jsx (limited to 'src') diff --git a/src/lib/product/components/Product.jsx b/src/lib/product/components/Product.jsx index ae476b80..d1586ef9 100644 --- a/src/lib/product/components/Product.jsx +++ b/src/lib/product/components/Product.jsx @@ -1,105 +1,15 @@ -import Badge from '@/core/components/elements/Badge/Badge' -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 { toast } from 'react-hot-toast' -import { updateItemCart } from '@/core/utils/cart' import useWishlist from '@/lib/wishlist/hooks/useWishlist' -import { HeartIcon } from '@heroicons/react/24/outline' +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' -import createOrDeleteWishlistApi from '@/lib/wishlist/api/createOrDeleteWishlistApi' - -const informationTabOptions = [ - { value: 'specification', label: 'Spesifikasi' }, - { value: 'description', label: 'Deskripsi' }, - { value: 'important', label: 'Info Penting' } -] const Product = ({ product }) => { const auth = useAuth() const router = useRouter() const { wishlist } = useWishlist({ productId: product?.id }) - const [quantity, setQuantity] = useState('1') - const [selectedVariant, setSelectedVariant] = useState(null) - const [informationTab, setInformationTab] = useState(null) - - 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]) - - useEffect(() => { - if (!informationTab) { - setInformationTab(informationTabOptions[0].value) - } - }, [informationTab]) - - 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 toggleWishlist = async () => { if (!auth) { @@ -118,210 +28,10 @@ const Product = ({ product }) => { 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 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 Product diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 0c11137d..3454d4fd 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -39,7 +39,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { )} diff --git a/src/lib/product/components/ProductDesktop.jsx b/src/lib/product/components/ProductDesktop.jsx new file mode 100644 index 00000000..dc733eac --- /dev/null +++ b/src/lib/product/components/ProductDesktop.jsx @@ -0,0 +1,160 @@ +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 { useEffect, useState } from 'react' + +const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { + const [variantQuantity, setVariantQuantity] = useState(null) + + useEffect(() => { + const mapVariantQuantity = product.variants.reduce((acc, cur) => { + acc[cur.id] = 1 + return acc + }, {}) + setVariantQuantity(mapVariantQuantity) + }, [product]) + + const changeQuantity = (variantId, quantity) => { + setVariantQuantity((variantQuantity) => ({ ...variantQuantity, [variantId]: quantity })) + } + + 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) => ( + + + + + + + ))} + +
No. SKUHargaJumlahAction
{variant.code} + {variant.price.discountPercentage > 0 && ( + <> + + {currencyFormat(variant.price.price)} + {' '} + + )} + {currencyFormat(variant.price.priceDiscount)} + + changeQuantity(variant.id, e.target.value)} + /> + + + +
+
+
+
+
+ ) +} + +export default ProductDesktop diff --git a/src/lib/product/components/ProductMobile.jsx b/src/lib/product/components/ProductMobile.jsx new file mode 100644 index 00000000..790fcd57 --- /dev/null +++ b/src/lib/product/components/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(null) + + 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]) + + useEffect(() => { + if (!informationTab) { + setInformationTab(informationTabOptions[0].value) + } + }, [informationTab]) + + 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}`) + } + + 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: 'important', 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 diff --git a/src/styles/globals.css b/src/styles/globals.css index b482aa30..b9dfbe38 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -382,6 +382,37 @@ button { @apply !border-yellow_r-9; } +.table-specification { + @apply max-h-[500px] overflow-y-auto border border-gray_r-6; +} + +.table-specification > table { + @apply table-fixed + border-collapse + w-full; +} + +.table-specification > table > thead { + @apply sticky top-0 border-b; +} + +.table-specification > table > thead > tr { + @apply bg-gray_r-1/80 backdrop-blur-lg; +} + +.table-specification th { + @apply font-semibold; +} + +.table-specification th, +.table-specification td { + @apply py-4 px-2 text-center; +} + +.table-specification > table > tbody > tr { + @apply odd:bg-gray_r-3 even:bg-gray_r-1; +} + .category-mega-box-wrapper { @apply absolute opacity-0 @@ -407,6 +438,7 @@ button { py-2 border border-t-0 + bg-white border-gray_r-6 h-full w-full; @@ -469,4 +501,3 @@ button { duration-100 font-normal; } - -- cgit v1.2.3