diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2024-01-04 10:05:25 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2024-01-04 10:05:25 +0700 |
| commit | 67398e6f10d6f7729d8f1ace7005ef13d32c5ddd (patch) | |
| tree | 7d47ad6c1a7093e595e22bcecb40016a626162f6 /src/lib/product/components/Product/ProductMobile.jsx | |
| parent | 89f32128f37d99b490de7590e2116a9cfd853f89 (diff) | |
Update promotion program feature
Diffstat (limited to 'src/lib/product/components/Product/ProductMobile.jsx')
| -rw-r--r-- | src/lib/product/components/Product/ProductMobile.jsx | 411 |
1 files changed, 249 insertions, 162 deletions
diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx index e23e2fb9..e9e64469 100644 --- a/src/lib/product/components/Product/ProductMobile.jsx +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -1,60 +1,66 @@ -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' -import { createSlug } from '@/core/utils/slug' -import BottomPopup from '@/core/components/elements/Popup/BottomPopup' -import whatsappUrl from '@/core/utils/whatsappUrl' -import PromotionType from '@/lib/promotinProgram/components/PromotionType' -import { gtagAddToCart } from '@/core/utils/googleTag' -import odooApi from '@/core/api/odooApi' -import ImageNext from 'next/image' -import CountDown2 from '@/core/components/elements/CountDown/CountDown2' -import Breadcrumb from './Breadcrumb' -import useAuth from '@/core/hooks/useAuth' -import { sellingProductFormat } from '@/core/utils/formatValue' +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'; +import { createSlug } from '@/core/utils/slug'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import whatsappUrl from '@/core/utils/whatsappUrl'; +import PromotionType from '@/lib/promotinProgram/components/PromotionType'; +import { gtagAddToCart } from '@/core/utils/googleTag'; +import odooApi from '@/core/api/odooApi'; +import ImageNext from 'next/image'; +import CountDown2 from '@/core/components/elements/CountDown/CountDown2'; +import Breadcrumb from './Breadcrumb'; +import useAuth from '@/core/hooks/useAuth'; +import { sellingProductFormat } from '@/core/utils/formatValue'; +import ProductPromoSection from '~/modules/product-promo/components/Section'; const ProductMobile = ({ product, wishlist, toggleWishlist }) => { - const router = useRouter() - const auth = useAuth() - const { slug } = router.query - - const [quantity, setQuantity] = useState('1') - const [selectedVariant, setSelectedVariant] = useState(null) - const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) - const [addCartAlert, setAddCartAlert] = useState(false) - - const [isLoadingSLA, setIsLoadingSLA] = useState(true) - const [promotionType, setPromotionType] = useState(false) - const [promotionActiveId, setPromotionActiveId] = useState(null) - const [backgorundFlashSale, setBackgorundFlashSale] = useState(null) + const router = useRouter(); + const auth = useAuth(); + const { slug } = router.query; + + const [quantity, setQuantity] = useState('1'); + const [selectedVariant, setSelectedVariant] = useState(null); + const [informationTab, setInformationTab] = useState( + informationTabOptions[0].value + ); + const [addCartAlert, setAddCartAlert] = useState(false); + + const [isLoadingSLA, setIsLoadingSLA] = useState(true); + const [promotionType, setPromotionType] = useState(false); + const [promotionActiveId, setPromotionActiveId] = useState(null); + const [backgorundFlashSale, setBackgorundFlashSale] = useState(null); const getLowestPrice = () => { - const prices = product.variants.map((variant) => variant.price) + const prices = product.variants.map((variant) => variant.price); const lowest = prices.reduce((lowest, price) => { - return price.priceDiscount < lowest.priceDiscount ? price : lowest - }, prices[0]) - return lowest - } + return price.priceDiscount < lowest.priceDiscount ? price : lowest; + }, prices[0]); + return lowest; + }; useEffect(() => { const getBackgound = async () => { - const get = await odooApi('GET', '/api/v1/banner?type=flash-sale-background-banner') + const get = await odooApi( + 'GET', + '/api/v1/banner?type=flash-sale-background-banner' + ); if (get.length > 0) { - setBackgorundFlashSale(get[0].image) + setBackgorundFlashSale(get[0].image); } - } - getBackgound() - }, []) + }; + getBackgound(); + }, []); const [activeVariant, setActiveVariant] = useState({ id: null, @@ -64,40 +70,44 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { stock: product.stockTotal, weight: product.weight, hasProgram: false, - qtySold: product.qtySold - }) + qtySold: product.qtySold, + }); const variantOptions = product.variants?.map((variant) => { - let label = [] + let label = []; if (variant.isFlashsale) { - label.push("<span class='blink-color-flash-sale'>🗲</span>") + label.push("<span class='blink-color-flash-sale'>🗲</span>"); } if (variant.code) { - label.push(`[${variant.code}]`) + label.push(`[${variant.code}]`); } if (variant.attributes.length > 0) { - label.push(variant.attributes.join(', ')) + label.push(variant.attributes.join(', ')); } else { - label.push(product.name) + label.push(product.name); } return { value: variant.id, - label: label.join(' ') - } - }) + label: label.join(' '), + }; + }); useEffect(() => { if (!selectedVariant && variantOptions.length == 1) { - setSelectedVariant(variantOptions[0]) + setSelectedVariant(variantOptions[0]); } - }, [selectedVariant, variantOptions]) + }, [selectedVariant, variantOptions]); useEffect(() => { if (selectedVariant) { - const variant = product.variants.find((variant) => variant.id == selectedVariant.value) + const variant = product.variants.find( + (variant) => variant.id == selectedVariant.value + ); const variantAttributes = - variant.attributes.length > 0 ? ' - ' + variant.attributes.join(', ') : '' + variant.attributes.length > 0 + ? ' - ' + variant.attributes.join(', ') + : ''; const newActiveVariant = { id: variant.id, @@ -108,60 +118,63 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { weight: variant.weight, hasProgram: variant.hasProgram, isFlashsale: variant.isFlashsale, - qtySold: variant.qtySold - } + qtySold: variant.qtySold, + }; - setActiveVariant(newActiveVariant) + setActiveVariant(newActiveVariant); const fetchSLA = async () => { - const dataSLA = await odooApi('GET', `/api/v1/product_variant/${variant.id}/stock`) - setActiveVariant({ ...newActiveVariant, sla: dataSLA }) - } - fetchSLA() + const dataSLA = await odooApi( + 'GET', + `/api/v1/product_variant/${variant.id}/stock` + ); + setActiveVariant({ ...newActiveVariant, sla: dataSLA }); + }; + fetchSLA(); } - }, [selectedVariant, product]) + }, [selectedVariant, product]); const validAction = () => { - let isValid = true + let isValid = true; if (!selectedVariant) { - toast.error('Pilih varian terlebih dahulu') - isValid = false + toast.error('Pilih varian terlebih dahulu'); + isValid = false; } if (!quantity || quantity < 1 || isNaN(parseInt(quantity))) { - toast.error('Jumlah barang minimal 1') - isValid = false + toast.error('Jumlah barang minimal 1'); + isValid = false; } - return isValid - } + return isValid; + }; const redirectToLogin = (action) => { - const nextURL = `/shop/product/${slug}?action=${action}&variantId=${activeVariant.id}&qty=${quantity}` - router.push(`/login?next=${encodeURIComponent(nextURL)}`) - return true - } + const nextURL = `/shop/product/${slug}?action=${action}&variantId=${activeVariant.id}&qty=${quantity}`; + router.push(`/login?next=${encodeURIComponent(nextURL)}`); + return true; + }; const handleClickCart = () => { - if (!validAction()) return - gtagAddToCart(activeVariant, quantity) + if (!validAction()) return; + gtagAddToCart(activeVariant, quantity); if (!auth) { - return redirectToLogin('add_to_cart') + return redirectToLogin('add_to_cart'); } updateItemCart({ productId: activeVariant.id, quantity, programLineId: promotionActiveId, - selected: true - }) - setAddCartAlert(true) - } + selected: true, + }); + setAddCartAlert(true); + }; const handleClickBuy = () => { - if (!validAction()) return + if (!validAction()) return; if (!auth) { - return redirectToLogin('buy') + return redirectToLogin('buy'); } updateItemCart({ @@ -169,58 +182,62 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { quantity, programLineId: promotionActiveId, selected: true, - source: 'buy' - }) - router.push(`/shop/checkout?source=buy`) - } + source: 'buy', + }); + router.push(`/shop/checkout?source=buy`); + }; const productSimilarQuery = [ product?.name, `fq=-product_id_i:${product.id}`, - `fq=-manufacture_id_i:${product.manufacture?.id || 0}` - ].join('&') + `fq=-manufacture_id_i:${product.manufacture?.id || 0}`, + ].join('&'); return ( <MobileView> <Breadcrumb productId={product.id} productName={product.name} /> <div className='relative'> - {product?.flashSale?.remainingTime > 0 && activeVariant?.price.discountPercentage > 0 && ( - <div className={`absolute bottom-0 w-full`}> - <div className='absolute bottom-0 w-full'> - <ImageNext - src={backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg'} - width={1000} - height={100} - /> - </div> - <div className='relative'> - <div className='flex gap-x-2 items-center p-2'> - <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '> - <span className='text-lg font-bold'> - {Math.floor(product.lowestPrice.discountPercentage)}% - </span> - </div> - <div - className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`} - > - <ImageNext - src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' - width={17} - height={10} - /> - <span className='text-white text-lg font-semibold'> - {product?.flashSale?.tag != 'false' || product?.flashSale?.tag - ? product?.flashSale?.tag - : 'FLASH SALE'} - </span> - </div> - <div> - <CountDown2 initialTime={product.flashSale.remainingTime} /> + {product?.flashSale?.remainingTime > 0 && + activeVariant?.price.discountPercentage > 0 && ( + <div className={`absolute bottom-0 w-full`}> + <div className='absolute bottom-0 w-full'> + <ImageNext + src={ + backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg' + } + width={1000} + height={100} + /> + </div> + <div className='relative'> + <div className='flex gap-x-2 items-center p-2'> + <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '> + <span className='text-lg font-bold'> + {Math.floor(product.lowestPrice.discountPercentage)}% + </span> + </div> + <div + className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`} + > + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={17} + height={10} + /> + <span className='text-white text-lg font-semibold'> + {product?.flashSale?.tag != 'false' || + product?.flashSale?.tag + ? product?.flashSale?.tag + : 'FLASH SALE'} + </span> + </div> + <div> + <CountDown2 initialTime={product.flashSale.remainingTime} /> + </div> </div> </div> </div> - </div> - )} + )} <Image src={product.image} alt={product.name} @@ -232,7 +249,11 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { <div className='flex items-end mb-2'> {product.manufacture?.name ? ( <Link - href={createSlug('/shop/brands/', product.manufacture?.name, product.manufacture?.id)} + href={createSlug( + '/shop/brands/', + product.manufacture?.name, + product.manufacture?.id + )} > {product.manufacture?.name} </Link> @@ -249,18 +270,25 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { </div> <h1 className='leading-6 font-medium mb-3'>{activeVariant?.name}</h1> {product?.qtySold > 0 && ( - <div className='text-gray_r-9'>{sellingProductFormat(activeVariant?.qtySold) + ' Terjual'}</div> + <div className='text-gray_r-9'> + {sellingProductFormat(activeVariant?.qtySold) + ' Terjual'} + </div> )} {product.variants.length > 1 && activeVariant.price.priceDiscount > 0 && !selectedVariant && ( - <div className='text-gray_r-12/80 text-caption-2 mt-2 mb-1'>Harga mulai dari: </div> + <div className='text-gray_r-12/80 text-caption-2 mt-2 mb-1'> + Harga mulai dari:{' '} + </div> )} - {activeVariant.isFlashsale && activeVariant?.price?.discountPercentage > 0 ? ( + {activeVariant.isFlashsale && + activeVariant?.price?.discountPercentage > 0 ? ( <> <div className='flex gap-x-1 items-center'> - <div className='badge-solid-red'>{Math.floor(activeVariant?.price?.discountPercentage)}%</div> + <div className='badge-solid-red'> + {Math.floor(activeVariant?.price?.discountPercentage)}% + </div> <div className='text-gray_r-11 line-through text-caption-1'> {currencyFormat(activeVariant?.price?.price)} </div> @@ -270,7 +298,9 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { </div> <div className='text-gray_r-9 text-base font-normal mt-1'> Termasuk PPN:{' '} - {currencyFormat(activeVariant?.price.priceDiscount * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + activeVariant?.price.priceDiscount * process.env.NEXT_PUBLIC_PPN + )} </div> </> ) : ( @@ -280,7 +310,9 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { {currencyFormat(activeVariant?.price?.price)} <div className='text-gray_r-9 text-base font-normal mt-1'> Termasuk PPN:{' '} - {currencyFormat(activeVariant?.price.price * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + activeVariant?.price.price * process.env.NEXT_PUBLIC_PPN + )} </div> </> ) : ( @@ -289,7 +321,12 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { <a href={whatsappUrl('product', { name: product.name, - url: createSlug('/shop/product/', product.name, product.id, true) + url: createSlug( + '/shop/product/', + product.name, + product.id, + true + ), })} className='text-danger-500 underline' > @@ -307,13 +344,17 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { <div> <label className='flex justify-between'> Pilih Varian: - <span className='text-gray_r-11'>{product?.variantTotal} Varian</span> + <span className='text-gray_r-11'> + {product?.variantTotal} Varian + </span> </label> <Select name='variant' classNamePrefix='form-select' options={variantOptions} - formatOptionLabel={({ label }) => <div dangerouslySetInnerHTML={{ __html: label }} />} + formatOptionLabel={({ label }) => ( + <div dangerouslySetInnerHTML={{ __html: label }} /> + )} className='mt-2' value={selectedVariant} onChange={(option) => setSelectedVariant(option)} @@ -342,15 +383,27 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { onChange={(e) => setQuantity(e.target.value)} /> </div> - <button type='button' className='btn-yellow flex-1' onClick={handleClickCart}> + <button + type='button' + className='btn-yellow flex-1' + onClick={handleClickCart} + > Keranjang </button> - <button type='button' className='btn-solid-red flex-1' onClick={handleClickBuy}> + <button + type='button' + className='btn-solid-red flex-1' + onClick={handleClickBuy} + > Beli </button> </div> + + <div className='h-4' /> </div> + <ProductPromoSection productId={activeVariant.id} /> + <Divider /> <div className='p-4'> @@ -380,12 +433,16 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { type='button' title={`Masa Persiapan Barang ${activeVariant?.sla?.slaDate}`} className={`flex gap-x-1 items-center p-2 h-8 rounded-lg w-full ${ - activeVariant?.sla?.slaDate === 'indent' ? 'bg-indigo-900' : 'btn-light' + activeVariant?.sla?.slaDate === 'indent' + ? 'bg-indigo-900' + : 'btn-light' }`} > <div className={`flex-1 text-sm ${ - activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : '' + activeVariant?.sla?.slaDate === 'indent' + ? 'text-white' + : '' }`} > {activeVariant?.sla?.slaDate} @@ -397,7 +454,9 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { stroke='currentColor' stroke-width='1.5' className={`w-7 h-7 text-sm ${ - activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : '' + activeVariant?.sla?.slaDate === 'indent' + ? 'text-white' + : '' }`} > <path @@ -436,7 +495,12 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { <a href={whatsappUrl('product', { name: product.name, - url: createSlug('/shop/product/', product.name, product.id, true) + url: createSlug( + '/shop/product/', + product.name, + product.id, + true + ), })} className='text-danger-500 font-medium' > @@ -445,12 +509,19 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { )} </SpecificationContent> <SpecificationContent label='Berat Barang'> - {activeVariant?.weight > 0 && <span>{activeVariant?.weight} KG</span>} + {activeVariant?.weight > 0 && ( + <span>{activeVariant?.weight} KG</span> + )} {activeVariant?.weight == 0 && ( <a href={whatsappUrl('productWeight', { name: product.name, - url: createSlug('/shop/product/', product.name, product.id, true) + url: createSlug( + '/shop/product/', + product.name, + product.id, + true + ), })} className='text-danger-500 font-medium' > @@ -464,7 +535,10 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { active={informationTab == 'description'} className='leading-6 text-gray_r-11' dangerouslySetInnerHTML={{ - __html: product.description != '' ? product.description : 'Belum ada deskripsi produk.' + __html: + product.description != '' + ? product.description + : 'Belum ada deskripsi produk.', }} /> </div> @@ -491,50 +565,63 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { className='h-20 object-contain object-center w-full border border-gray_r-4' /> </div> - <div className='ml-3 flex flex-1 items-center text-sm font-normal'>{product.name}</div> + <div className='ml-3 flex flex-1 items-center text-sm font-normal'> + {product.name} + </div> <div className='ml-3 flex items-center text-sm font-normal'> - <Link href='/shop/cart' className='flex-1 py-2 text-gray_r-12 btn-yellow'> + <Link + href='/shop/cart' + className='flex-1 py-2 text-gray_r-12 btn-yellow' + > Lihat Keranjang </Link> </div> </div> <div className='mt-8 mb-4'> - <div className='text-h-sm font-semibold mb-6'>Kamu Mungkin Juga Suka</div> + <div className='text-h-sm font-semibold mb-6'> + Kamu Mungkin Juga Suka + </div> <LazyLoad> <ProductSimilar query={productSimilarQuery} /> </LazyLoad> </div> </BottomPopup> </MobileView> - ) -} + ); +}; const informationTabOptions = [ { value: 'specification', label: 'Spesifikasi' }, { value: 'description', label: 'Deskripsi' }, - { value: 'information', label: 'Info Penting' } -] + { value: 'information', label: 'Info Penting' }, +]; const TabButton = ({ children, active, ...props }) => { - const activeClassName = active ? 'text-danger-500 underline underline-offset-4' : 'text-gray_r-11' + const activeClassName = active + ? 'text-danger-500 underline underline-offset-4' + : 'text-gray_r-11'; return ( - <button {...props} type='button' className={`font-medium pb-1 ${activeClassName}`}> + <button + {...props} + type='button' + className={`font-medium pb-1 ${activeClassName}`} + > {children} </button> - ) -} + ); +}; const TabContent = ({ children, active, className, ...props }) => ( <div {...props} className={`${active ? 'block' : 'hidden'} ${className}`}> {children} </div> -) +); const SpecificationContent = ({ children, label }) => ( <div className='flex justify-between p-3 items-center'> <span className='text-gray_r-11'>{label}</span> {children} </div> -) +); -export default ProductMobile +export default ProductMobile; |
