diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/productApi.js | 2 | ||||
| -rw-r--r-- | src/core/components/elements/CountDown/CountDown2.jsx | 48 | ||||
| -rw-r--r-- | src/core/components/layouts/BasicLayout.jsx | 1 | ||||
| -rw-r--r-- | src/lib/home/api/popularProductApi.js | 2 | ||||
| -rw-r--r-- | src/lib/product/api/productSimilarApi.js | 25 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductDesktop.jsx | 60 | ||||
| -rw-r--r-- | src/lib/product/components/ProductCard.jsx | 59 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSimilar.jsx | 3 | ||||
| -rw-r--r-- | src/lib/product/hooks/useProductSimilar.js | 5 | ||||
| -rw-r--r-- | src/pages/api/shop/search.js | 3 |
10 files changed, 170 insertions, 38 deletions
diff --git a/src/api/productApi.js b/src/api/productApi.js index cca052f7..feb91257 100644 --- a/src/api/productApi.js +++ b/src/api/productApi.js @@ -3,7 +3,7 @@ import axios from 'axios' export const popularProductApi = () => { return async () => { const dataPopularProducts = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular-weekly` ) return dataPopularProducts.data.response } diff --git a/src/core/components/elements/CountDown/CountDown2.jsx b/src/core/components/elements/CountDown/CountDown2.jsx index 61503d17..5dafb790 100644 --- a/src/core/components/elements/CountDown/CountDown2.jsx +++ b/src/core/components/elements/CountDown/CountDown2.jsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' const CountDown2 = ({ initialTime }) => { - const hours = Math.floor(initialTime / 3600) + /*const hours = Math.floor(initialTime / 3600) const minutes = Math.floor((initialTime % 3600) / 60) const seconds = initialTime % 60 @@ -25,24 +25,58 @@ const CountDown2 = ({ initialTime }) => { } }, 1000) return () => clearInterval(timer) + }, [timeLeft])*/ + + const days = Math.floor(initialTime / 86400) + const hours = Math.floor((initialTime % 86400) / 3600) + const minutes = Math.floor((initialTime % 3600) / 60) + const seconds = initialTime % 60 + + const [timeLeft, setTimeLeft] = useState({ + day: days, + hour: hours, + minute: minutes, + second: seconds + }) + + useEffect(() => { + const timer = setInterval(() => { + const totalSeconds = + timeLeft.day * 86400 + timeLeft.hour * 3600 + timeLeft.minute * 60 + timeLeft.second + const secondsLeft = totalSeconds - 1 + if (secondsLeft < 0) { + clearInterval(timer) + } else { + const days = Math.floor(secondsLeft / 86400) + const hours = Math.floor((secondsLeft % 86400) / 3600) + const minutes = Math.floor((secondsLeft % 3600) / 60) + const seconds = secondsLeft % 60 + setTimeLeft({ day: days, hour: hours, minute: minutes, second: seconds }) + } + }, 1000) + return () => clearInterval(timer) }, [timeLeft]) + return ( <div className='flex justify-between gap-x-2'> <div className='flex flex-col items-center'> - <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'> - {timeLeft.hour.toString().padStart(2, '0')} + <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'> + {timeLeft.day.toString().padStart(2, '0')} </span> + <span className='text-xs text-white'>Hari</span> </div> <div className='flex flex-col items-center'> - <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'> - {timeLeft.minute.toString().padStart(2, '0')} + <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'> + {timeLeft.hour.toString().padStart(2, '0')} </span> + <span className='text-xs text-white'>Jam</span> </div> <div className='flex flex-col items-center'> - <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'> - {timeLeft.second.toString().padStart(2, '0')} + <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'> + {timeLeft.minute.toString().padStart(2, '0')} </span> + <span className='text-xs text-white'>Menit</span> </div> </div> ) diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index 073303fe..c6029f39 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -37,7 +37,6 @@ const BasicLayout = ({ children }) => { const { slug } = router.query const getProduct = async () => { let product = await productApi({ id: getIdFromSlug(slug), headers: { Token: authToken } }) - console.log('ini product', product) setPayloadWa({ name: product[0]?.name, manufacture: product[0]?.manufacture.name, diff --git a/src/lib/home/api/popularProductApi.js b/src/lib/home/api/popularProductApi.js index 37e4390e..4bb5e580 100644 --- a/src/lib/home/api/popularProductApi.js +++ b/src/lib/home/api/popularProductApi.js @@ -2,7 +2,7 @@ import axios from 'axios' const popularProductApi = async () => { const dataPopularProducts = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular-weekly` ) return dataPopularProducts.data.response } diff --git a/src/lib/product/api/productSimilarApi.js b/src/lib/product/api/productSimilarApi.js index 93c7f22c..f9599e86 100644 --- a/src/lib/product/api/productSimilarApi.js +++ b/src/lib/product/api/productSimilarApi.js @@ -1,9 +1,30 @@ +import odooApi from '@/core/api/odooApi' import axios from 'axios' +import productSearchApi from './productSearchApi' -const productSimilarApi = async ({ query }) => { +const productSimilarApi = async ({ query, source }) => { + let dataflashSale = null + const flashSale = await odooApi('GET', '/api/v1/flashsale/header') + if (flashSale && flashSale.length > 0) { + const dataFlash = await productSearchApi({ + query: `fq=flashsale_id_i:${flashSale[0].pricelistId}&fq=flashsale_price_f:[1 TO *]&limit=${ + source === 'bottom' ? '4' : '2' + }`, + operation: 'AND' + }) + if (source === 'bottom') { + dataflashSale = dataFlash.response.products.slice('2', '4') + } else { + dataflashSale = dataFlash.response.products + } + } const dataProductSimilar = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular&operation=OR` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular-weekly&operation=OR` ) + dataProductSimilar.data.response.products = [ + ...dataflashSale, + ...dataProductSimilar.data.response.products, + ]; return dataProductSimilar.data.response } diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index dfff91d7..5cbe8a8a 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -18,8 +18,12 @@ import odooApi from '@/core/api/odooApi' import { Button, Spinner } from 'flowbite-react' import PromotionType from '@/lib/promotinProgram/components/PromotionType' import useAuth from '@/core/hooks/useAuth' +import ImageNext from 'next/image' +import CountDown2 from '@/core/components/elements/CountDown/CountDown2' +import CountDown from '@/core/components/elements/CountDown/CountDown' const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { + console.log('ini product', products) const router = useRouter() const auth = useAuth() const { slug } = router.query @@ -96,6 +100,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { router.push(`/login?next=/shop/product/${slug}`) return } + const quantity = variantQuantityRefs.current[variantId].value if (!validQuantity(quantity)) return @@ -141,7 +146,8 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { useEffect(() => { const loadProductSimilarInBrand = async () => { const productSimilarQuery = [product?.name, `fq=-product_id_i:${product.id}`].join('&') - const dataProductSimilar = await productSimilarApi({ query: productSimilarQuery }) + const source = 'right' + const dataProductSimilar = await productSimilarApi({ query: productSimilarQuery, source }) setProductSimilarInBrand(dataProductSimilar.products) } if (!productSimilarInBrand) loadProductSimilarInBrand() @@ -170,11 +176,41 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { <div className='flex'> <div className='w-full flex flex-wrap'> <div className='w-5/12'> - <Image - src={product.image} - alt={product.name} - className='h-[430px] object-contain object-center w-full border border-gray_r-4' - /> + <div className='relative mb-2'> + {product?.flashSale?.remainingTime > 0 && ( + <div className='absolute bottom-0 bg-red-600 w-full h-15 grid'> + <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'>{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 || 'FLASH SALE'}</span> + </div> + <div> + <CountDown2 initialTime={product.flashSale.remainingTime} /> + </div> + </div> + </div> + )} + <Image + src={product.image} + alt={product.name} + className='h-[430px] object-contain object-center w-full border border-gray_r-4' + /> + </div> + <div> + <p className='text-justify text-xs leading-5'> + <span className='font-semibold '>Keterangan : </span>Gambar atau foto berperan + sebagai ilustrasi produk. Kadang tidak sesuai dengan kondisi terbaru dengan + berbagai perubahan dan perbaikan. Hubungi tim sales kami untuk informasi yang + lebih baik perihal gambar di 021-2933 8828. + </p> + </div> </div> <div className='w-7/12 px-4'> @@ -374,6 +410,16 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { <div className='text-gray_r-11 line-through text-caption-1'> {currencyFormat(lowestPrice?.price)} </div> + {product.flashSale.remainingTime > 0 && ( + <div className='bg-red-600 rounded-full mb-1 p-2 pl-3 pr-3 flex w-fit items-center gap-x-1'> + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={15} + height={10} + /> + <span className='text-white text-xs font-semibold'>FLASH SALE</span> + </div> + )} </div> )} <h3 className='text-danger-500 font-semibold mt-1 text-title-md'> @@ -615,7 +661,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { )} */} <button type='button' - onClick={() => handleAddToCart(variant)} + onClick={() => handleAddToCart(variant.id)} className='flex-1 py-2 btn-yellow' > Keranjang diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index a8964310..b38bb43c 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -1,10 +1,14 @@ +import CountDown2 from '@/core/components/elements/CountDown/CountDown2' import Image from '@/core/components/elements/Image/Image' import Link from '@/core/components/elements/Link/Link' import currencyFormat from '@/core/utils/currencyFormat' import { createSlug } from '@/core/utils/slug' import whatsappUrl from '@/core/utils/whatsappUrl' +import ImageNext from 'next/image' +import Product from './Product/Product' const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { + console.log('ini prodyct ', product) const callForPriceWhatsapp = whatsappUrl('product', { name: product.name, url: createSlug('/shop/product/', product.name, product.id, true) @@ -22,6 +26,23 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { alt={product?.name} className='w-full object-contain object-center h-36 sm:h-48' /> + {product?.flashSale?.id > 0 && ( + <div className='absolute bottom-0 bg-red-600 w-full h-10 grid'> + <div className='flex gap-x-1 items-center p-2 justify-center'> + <div className='bg-yellow-400 rounded-full p-1 h-6 w-19 flex items-center justify-center '> + <span className='text-sm font-bold'>{product?.lowestPrice.discountPercentage}%</span> + </div> + <div className='bg-red-600 border border-solid border-yellow-400 p-2 rounded-full h-6 flex w-fit items-center justify-center gap-x-2'> + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={13} + height={5} + /> + <span className='text-white text-[11px] font-semibold'>{product?.flashSale?.tag}</span> + </div> + </div> + </div> + )} {product.variantTotal > 1 && ( <div className='absolute badge-gray bottom-1.5 left-1.5'> {product.variantTotal} Varian @@ -29,20 +50,20 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { )} </Link> <div className='p-2 sm:p-3 pb-3 text-caption-2 sm:text-body-2 leading-5'> - {product?.manufacture?.name ? ( - <Link - href={createSlug( - '/shop/brands/', - product?.manufacture?.name, - product?.manufacture.id - )} - className='mb-1' - > - {product.manufacture.name} - </Link> - ) : ( - <div>-</div> - )} + {product?.manufacture?.name ? ( + <Link + href={createSlug( + '/shop/brands/', + product?.manufacture?.name, + product?.manufacture.id + )} + className='mb-1' + > + {product.manufacture.name} + </Link> + ) : ( + <div>-</div> + )} <Link href={createSlug('/shop/product/', product?.name, product?.id)} className={`mb-2 !text-gray_r-12 leading-6 block ${ @@ -101,6 +122,16 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { </Link> </div> <div className='w-8/12 p-2'> + {product.flashSale.id > 0 && ( + <div className='bg-red-600 rounded-full mb-1 p-2 pl-3 pr-3 flex w-fit items-center gap-x-1'> + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={15} + height={10} + /> + <span className='text-white text-xs font-semibold'>FLASH SALE</span> + </div> + )} {product?.manufacture?.name ? ( <Link href={createSlug( diff --git a/src/lib/product/components/ProductSimilar.jsx b/src/lib/product/components/ProductSimilar.jsx index 63a33089..1b82c2e5 100644 --- a/src/lib/product/components/ProductSimilar.jsx +++ b/src/lib/product/components/ProductSimilar.jsx @@ -3,7 +3,8 @@ import useProductSimilar from '../hooks/useProductSimilar' import ProductSlider from './ProductSlider' const ProductSimilar = ({ query }) => { - const { productSimilar } = useProductSimilar({ query }) + const source = "bottom" + const { productSimilar } = useProductSimilar({ query, source }) if (productSimilar.isLoading) { return <PopularProductSkeleton /> diff --git a/src/lib/product/hooks/useProductSimilar.js b/src/lib/product/hooks/useProductSimilar.js index d16e4c58..712d07ad 100644 --- a/src/lib/product/hooks/useProductSimilar.js +++ b/src/lib/product/hooks/useProductSimilar.js @@ -1,10 +1,9 @@ import productSimilarApi from '../api/productSimilarApi' import { useQuery } from 'react-query' -const useProductSimilar = ({ query }) => { - const fetchProductSimilar = async () => await productSimilarApi({ query }) +const useProductSimilar = ({ query, source }) => { + const fetchProductSimilar = async () => await productSimilarApi({ query, source }) const { data, isLoading } = useQuery(`productSimilar-${query}`, fetchProductSimilar) - return { productSimilar: { data, isLoading } } diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js index 937c6d4c..d465d94b 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -135,7 +135,8 @@ const productResponseMap = (products, pricelist) => { categories: [], flashSale: { id: product?.flashsale_id_i, - name: product?.product?.flashsale_name_s + name: product?.product?.flashsale_name_s, + tag : product?.flashsale_tag_s || 'FLASH SALE' } } |
