summaryrefslogtreecommitdiff
path: root/src/lib/product/components/Product/ProductMobile.jsx
diff options
context:
space:
mode:
authorRafi Zadanly <zadanlyr@gmail.com>2024-01-04 10:05:25 +0700
committerRafi Zadanly <zadanlyr@gmail.com>2024-01-04 10:05:25 +0700
commit67398e6f10d6f7729d8f1ace7005ef13d32c5ddd (patch)
tree7d47ad6c1a7093e595e22bcecb40016a626162f6 /src/lib/product/components/Product/ProductMobile.jsx
parent89f32128f37d99b490de7590e2116a9cfd853f89 (diff)
Update promotion program feature
Diffstat (limited to 'src/lib/product/components/Product/ProductMobile.jsx')
-rw-r--r--src/lib/product/components/Product/ProductMobile.jsx411
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'>&#128498;</span>")
+ label.push("<span class='blink-color-flash-sale'>&#128498;</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;