summaryrefslogtreecommitdiff
path: root/src/lib/promotinProgram
diff options
context:
space:
mode:
authorHATEC\SPVDEV001 <tri.susilo@altama.co.id>2023-07-24 15:33:17 +0700
committerHATEC\SPVDEV001 <tri.susilo@altama.co.id>2023-07-24 15:33:17 +0700
commit2de322571bad7490baaf439fa2bb12124c646bb5 (patch)
treed7c4151eb2bf83b85cd0532d45dedd02aa06694e /src/lib/promotinProgram
parent8b8ab7680be37e84d0dc6cc1683fe4996f7cc8c1 (diff)
parentd4f3cce1b07c5d4f75892ffc49c8dbbbbb58922f (diff)
Merge branch 'master' into CR/widget_WA
Diffstat (limited to 'src/lib/promotinProgram')
-rw-r--r--src/lib/promotinProgram/api/homepageApi.js17
-rw-r--r--src/lib/promotinProgram/components/HomePage.jsx116
-rw-r--r--src/lib/promotinProgram/components/PromotionType.jsx304
3 files changed, 437 insertions, 0 deletions
diff --git a/src/lib/promotinProgram/api/homepageApi.js b/src/lib/promotinProgram/api/homepageApi.js
new file mode 100644
index 00000000..496af9d6
--- /dev/null
+++ b/src/lib/promotinProgram/api/homepageApi.js
@@ -0,0 +1,17 @@
+import odooApi from "@/core/api/odooApi"
+
+export const getPromotionHome = async () => {
+ const response = await odooApi('GET', '/api/v1/promotion/home')
+ return response
+}
+
+export const getProductPromotionHome = async ({id}) => {
+ const response = await odooApi('GET', `/api/v1/promotion/home/${id}`)
+ return response
+}
+
+export const getPromotionProgram = async ({ id }) => {
+ const listProgram = await odooApi('GET', `/api/v1/product_variant/${id}/promotions`)
+
+ return listProgram
+} \ No newline at end of file
diff --git a/src/lib/promotinProgram/components/HomePage.jsx b/src/lib/promotinProgram/components/HomePage.jsx
new file mode 100644
index 00000000..c0968161
--- /dev/null
+++ b/src/lib/promotinProgram/components/HomePage.jsx
@@ -0,0 +1,116 @@
+import React, { use, useEffect, useState } from 'react'
+import { Swiper, SwiperSlide } from 'swiper/react'
+import 'swiper/swiper.min.css'
+import Image from '@/core/components/elements/Image/Image'
+import useDevice from '@/core/hooks/useDevice'
+import odooApi from '@/core/api/odooApi'
+import { LazyLoadComponent } from 'react-lazy-load-image-component'
+import ProductSlider from '@/lib/product/components/ProductSlider'
+import { PopularProductSkeleton } from '@/components/skeleton/PopularProductSkeleton'
+
+const { useQuery } = require('react-query')
+const { getPromotionHome } = require('../api/homepageApi')
+const { getProductPromotionHome } = require('../api/homepageApi')
+
+const HomePage = () => {
+ const [activeTab, setActiveTab] = useState(null)
+ const [activeId, setActiveId] = useState(null)
+ const [activeBanner, setActiveBanner] = useState(null)
+ const [parentPromotions, setparentPromotions] = useState(null)
+
+ const { data: titlePromotion } = useQuery('titlePromotion', getPromotionHome)
+ const { data: productPromotion, refetch: productPromotionRefetch } = useQuery(
+ ['productPromotion', activeId],
+ async () => {
+ if (!activeId) return null
+ return await getProductPromotionHome({ id: activeId })
+ }
+ )
+
+ useEffect(() => {
+ if (titlePromotion && titlePromotion.length > 0) {
+ setActiveTab(titlePromotion[0].name)
+ setActiveId(titlePromotion[0].id)
+ setparentPromotions(titlePromotion)
+ setActiveBanner(titlePromotion[0].banner)
+ productPromotionRefetch()
+ }
+ }, [titlePromotion, productPromotionRefetch])
+
+ useEffect(() => {
+ if (productPromotion) {
+ setparentPromotions((parentPromotions) => {
+ return parentPromotions.map((title) => {
+ if (title.id === activeId && title.products === undefined) {
+ return {
+ ...title,
+ products: productPromotion
+ }
+ }
+ return title
+ })
+ })
+ }
+ }, [productPromotion, activeId])
+
+ const { isMobile, isDesktop } = useDevice()
+ const handleTabClick = (id, label, banner) => {
+ setActiveTab(label)
+ setActiveId(id)
+ setActiveBanner(banner)
+ }
+
+ return (
+ activeBanner && (
+ <div className='px-4 sm:px-0'>
+ <div className='flex justify-between items-center mb-4'>
+ <div className='font-medium sm:text-h-lg'>{activeTab}</div>
+ </div>
+ <div className='mb-4'>
+ <Image src={activeBanner} alt='' className='h-full w-full object-contain object-center' />
+ </div>
+ <Swiper slidesPerView={isMobile ? 3.5 : 7.5} spaceBetween={isMobile ? 12 : 20}>
+ {titlePromotion?.map((item, index) => (
+ <SwiperSlide key={index}>
+ <button
+ className={`py-1 px-2 rounded border flex justify-center items-center h-30 ${
+ activeTab === item.name ? 'border-red-500' : 'border-gray_r-6'
+ }`}
+ onClick={() => handleTabClick(item.id, item.name, item.banner)}
+ >
+ {' '}
+ <Image
+ src={item.icon}
+ alt=''
+ className='h-full w-full object-contain object-center'
+ />
+ </button>
+ </SwiperSlide>
+ ))}
+ </Swiper>
+ <div className='mt-4 relative min-h-[150px]'>
+ {parentPromotions &&
+ parentPromotions?.map((item, index) => (
+ <div
+ key={index}
+ className={`${activeId === item.id ? 'block' : 'hidden'} rounded-md`}
+ >
+ {item.products ? (
+ <ProductSlider
+ key={index}
+ products={{
+ products: item.products
+ }}
+ />
+ ) : (
+ <PopularProductSkeleton />
+ )}
+ </div>
+ ))}
+ </div>
+ </div>
+ )
+ )
+}
+
+export default HomePage
diff --git a/src/lib/promotinProgram/components/PromotionType.jsx b/src/lib/promotinProgram/components/PromotionType.jsx
new file mode 100644
index 00000000..ad7185e3
--- /dev/null
+++ b/src/lib/promotinProgram/components/PromotionType.jsx
@@ -0,0 +1,304 @@
+import React, { useEffect, useState } from 'react'
+import Image from '@/core/components/elements/Image/Image'
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup'
+import CountDown2 from '@/core/components/elements/CountDown/CountDown2'
+import currencyFormat from '@/core/utils/currencyFormat'
+import { getPromotionProgram } from '../api/homepageApi'
+
+const PromotionType = ({
+ isModal = false,
+ variantId,
+ setPromotionActiveId,
+ promotionActiveId,
+ quantity,
+ product = null,
+ setProducts = null
+}) => {
+ const [selectedPromo, setSelectedPromo] = useState(null)
+ const [promotionType, setPromotionType] = useState(false)
+ const [promos, setPromotionList] = useState(null)
+ const [activeTitle, setActiveTitle] = useState(null)
+ const [quantitySet, setQuantity] = useState(null)
+
+ useEffect(() => {
+ const id = variantId
+ const listProgram = async () => {
+ const programs = await getPromotionProgram({ id })
+ if (programs.length > 0) {
+ setPromotionList(programs)
+ setActiveTitle(programs?.[0].type.value)
+ }
+ }
+ listProgram()
+ setSelectedPromo(promotionActiveId)
+ if (product) {
+ const variant = product.variants.find((variant) => variant.id === variantId)
+ setQuantity(variant.quantity)
+ }else{
+ setQuantity(quantity)
+ }
+ }, [])
+
+ const groupingData = promos?.reduce((groups, item) => {
+ const promoType = item.type.value
+ if (!groups[promoType]) {
+ groups[promoType] = []
+ }
+ groups[promoType].push(item)
+
+ return groups
+ }, {})
+
+ const handlePromoClick = (promoId, minQty) => {
+ if (quantitySet >= minQty) {
+ if (promoId == selectedPromo) {
+ setSelectedPromo(null)
+ setPromotionActiveId(null)
+ if (product) {
+ const updateProdcuts = () => {
+ let variantIndex = product.variants.findIndex((varian) => varian.id == variantId)
+ product.variants[variantIndex].programActive = null
+
+ setProducts(product)
+ }
+ updateProdcuts()
+ }
+ } else {
+ setSelectedPromo(promoId)
+ setPromotionActiveId(promoId)
+ if (product) {
+ const updateProdcuts = () => {
+ let variantIndex = product.variants.findIndex((varian) => varian.id == variantId)
+ product.variants[variantIndex].programActive = promoId
+
+ setProducts(product)
+ }
+ updateProdcuts()
+ }
+ }
+ }
+ }
+
+ const handlePopUp = () => {
+ if (isModal == false) {
+ setPromotionType(true)
+ }
+ }
+
+ return (
+ promos && (
+ <>
+ <div className='h-[50%] relative'>
+ <div className='relative rounded-lg border border-solid border-gray-300 mb-2 w-full'>
+ <Image src='https://placehold.co/537x50.png' alt='' layout='fill' objectFit='cover' />
+ <div className='h-full absolute top-0 left-0 w-full flex items-center justify-between p-2'>
+ <span className='font-semibold text-lg text-white'>Promo Tersedia</span>
+ <button type='button' onClick={() => handlePopUp()} className='py-2 btn-yellow'>
+ Lihat Semua
+ </button>
+ </div>
+ </div>
+ <div
+ className={`w-full ${
+ isModal == true ? '' : 'grid grid-cols-3 gap-1 bg-gray-200 '
+ } p-2 rounded-lg`}
+ >
+ {isModal === true ? (
+ <div>
+ <div className='flex gap-2 mb-3'>
+ {Object.keys(groupingData).map((index) => {
+ return (
+ <>
+ <button
+ onClick={() => setActiveTitle(index)}
+ className={`py-1 px-2 rounded-lg flex justify-center items-center text-sm ${
+ activeTitle === index ? 'badge-yellow text-black' : ''
+ } `}
+ >
+ {groupingData[index][0].type.label}
+ </button>
+ </>
+ )
+ })}
+ </div>
+ {activeTitle &&
+ groupingData[activeTitle].map((item, i) => (
+ <div
+ key={i}
+ onClick={() => handlePromoClick(item.id, item.minimumPurchaseQty)}
+ className={`border border-solid mb-5 w-full hover:cursor-pointer ${
+ selectedPromo
+ ? selectedPromo === item.id
+ ? 'opacity-100 border-red-500 bg-red-100'
+ : 'opacity-50 pointer-events-none'
+ : 'opacity-100'
+ } ${
+ quantitySet >= item.minimumPurchaseQty
+ ? ''
+ : 'opacity-50 pointer-events-none'
+ } `}
+ >
+ <div className={`flex`}>
+ <div className=''>
+ <Image
+ src={item.Image}
+ alt={item.name}
+ className={`flex-1 w-[170px] object-cover`}
+ />
+ </div>
+ <div className='p-2 w-full'>
+ <div className='flex justify-between mb-1'>
+ <div className='text-danger-500 font-semibold mb-1 mt-1'>
+ Waktu Tersisa
+ </div>
+ <div>
+ <CountDown2 initialTime={item.remainingTime}></CountDown2>
+ </div>
+ </div>
+ <p className='text-justify text-gray-500 line-clamp-3'>{item.name}</p>
+ <div className='mt-4'>
+ {/* {item.type.value === 'bundling' && (
+ <> */}
+ <div className='flex gap-x-2 mt-3 justify-between items-center'>
+ <div className='flex gap-x-2 items-center '>
+ <div className='text-gray_r-11 line-through text-caption-1 mt-1'>
+ {currencyFormat(item.totalSavings)}
+ </div>
+ <div className='text-danger-500 font-semibold '>Gratis</div>
+ </div>
+ <div className='text-danger-500 font-semibold '>
+ {quantitySet < item.minimumPurchaseQty
+ ? 'Tambah ' +
+ (parseInt(item.minimumPurchaseQty) -
+ parseInt(quantitySet)) +
+ ' lagi'
+ : ''}
+ </div>
+ </div>
+ {/* </>
+ )} */}
+ {/* {item.type.value === 'special_price' && (
+ <>
+ <div className='flex gap-x-2 mt-3 items-center'>
+ <div className='text-danger-500 font-semibold '> {currencyFormat(item.totalSavings)}</div>
+ </div>
+ </>
+ )}
+ {item.type.value === 'discount_loading' && (
+ <>
+ <div className='flex justify-between'>
+ <div className='text-danger-500 font-semibold '>
+ {currencyFormat(item.totalSavings)}
+ </div>
+ <div className='text-danger-500 font-semibold '>
+ {quantitySet < item.minimumPurchaseQty
+ ? 'Tambah ' +
+ (parseInt(item.minimumPurchaseQty) -
+ parseInt(quantitySet)) +
+ ' lagi'
+ : ''}
+ </div>
+ </div>
+ </>
+ )} */}
+ </div>
+ </div>
+ </div>
+ </div>
+ ))}
+ </div>
+ ) : (
+ promos.map((promo, index) => {
+ if (index > 2) {
+ return null
+ } else {
+ if (index === 2 && promos.length > 2) {
+ return (
+ <>
+ <div
+ onClick={() => setPromotionType(true)}
+ className={` border border-solid bg-white mb-5 w-full hover:cursor-pointer opacity-100 flex flex-col justify-center items-center`}
+ >
+ <div className='flex justify-center items-center'>
+ <div className='rounded-full shadow-lg w-10 h-10 flex justify-center items-center'>
+ <svg
+ aria-hidden='true'
+ fill='none'
+ stroke='currentColor'
+ stroke-width='1.5'
+ viewBox='0 0 24 24'
+ className='text-red-500 w-20 h-20'
+ >
+ <path
+ d='M12 6v12m6-6H6'
+ stroke-linecap='round'
+ stroke-linejoin='round'
+ ></path>
+ </svg>
+ </div>
+ </div>
+
+ <span className='mt-2 text-sm'>Lihat Promo Lainya</span>
+ </div>
+ </>
+ )
+ }
+ return (
+ <>
+ <div
+ key={promo.id}
+ onClick={() => setPromotionType(true)}
+ className={`border border-solid bg-white mb-5 w-full hover:cursor-pointer`}
+ >
+ <div className={`items-center`}>
+ <div className=''>
+ <Image
+ src={promo.Image}
+ alt={promo.name}
+ className={`flex-1 w-full object-cover`}
+ />
+ </div>
+ <div className='p-2'>
+ <div className='badge-yellow text-black mb-1'>{promo.type.label}</div>
+ <p className='text-justify line-clamp-2'>{promo.name}</p>
+ <div className='text-danger-500 font-semibold mb-1 mt-1'>
+ {/* {currencyFormat(promo.totalSavings)} */}
+ </div>
+ {/* <div className='w-full bg-yellow-200 rounded-full h-1.5 mb-2'>
+ <div className='bg-yellow-500 h-1.5 rounded-full w-[45%]'></div>
+ </div> */}
+ <div>
+ <CountDown2 initialTime={promo.remainingTime}></CountDown2>
+ </div>
+ </div>
+ </div>
+ </div>
+ </>
+ )
+ }
+ })
+ )}
+ <BottomPopup
+ className=' !h-[75%]'
+ title='Pakai Promo'
+ active={promotionType}
+ close={() => setPromotionType(false)}
+ >
+ <div className='flex mt-4'>
+ <PromotionType
+ isModal={true}
+ variantId={variantId}
+ setPromotionActiveId={setPromotionActiveId}
+ promotionActiveId={promotionActiveId}
+ quantity={quantitySet}
+ ></PromotionType>
+ </div>
+ </BottomPopup>
+ </div>
+ </div>
+ </>
+ )
+ )
+}
+
+export default PromotionType