diff options
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/cart/components/Cart.jsx | 6 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductDesktop.jsx | 24 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductMobile.jsx | 6 | ||||
| -rw-r--r-- | src/lib/product/components/ProductCard.jsx | 2 | ||||
| -rw-r--r-- | src/lib/promotinProgram/api/homepageApi.js | 11 | ||||
| -rw-r--r-- | src/lib/promotinProgram/components/HomePage.jsx | 152 | ||||
| -rw-r--r-- | src/lib/promotinProgram/components/PromotionType.jsx | 254 |
7 files changed, 319 insertions, 136 deletions
diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 907d1267..718541af 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -17,10 +17,16 @@ import DesktopView from '@/core/components/views/DesktopView' import ProductCard from '@/lib/product/components/ProductCard' import productSearchApi from '@/lib/product/api/productSearchApi' import whatsappUrl from '@/core/utils/whatsappUrl' +import useAuth from '@/core/hooks/useAuth' const Cart = () => { const router = useRouter() const [products, setProducts] = useState(null) + const auth = useAuth() + + useEffect(() => { + if (!auth) return + }, [auth]) const { cart } = useCart({ enabled: !products }) const [totalPriceBeforeTax, setTotalPriceBeforeTax] = useState(0) diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index c3addbb9..f1a001a6 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -15,9 +15,12 @@ import ProductCard from '../ProductCard' import productSimilarApi from '../../api/productSimilarApi' import whatsappUrl from '@/core/utils/whatsappUrl' import PromotionType from '@/lib/promotinProgram/components/PromotionType' +import useAuth from '@/core/hooks/useAuth' const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { const router = useRouter() + const auth = useAuth() + const { slug } = router.query const [lowestPrice, setLowestPrice] = useState(null) @@ -55,6 +58,10 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { } const handleAddToCart = (variantId) => { + if(!auth) { + router.push(`/login?next=/shop/product/${slug}`) + return + } const quantity = variantQuantityRefs.current[variantId].value if (!validQuantity(quantity)) return updateItemCart({ @@ -159,8 +166,7 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { </div> </div> <div className='pt-3'> - <div className='text-h-lg font-semibold'>Promotion Program</div> - <div className='flex mt-3'> + <div className='flex mt-1'> <PromotionType></PromotionType> </div> </div> @@ -237,7 +243,7 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { </span> )} </h3> - <div className='mt-5'> + {/* <div className='mt-5'> <button type='button' onClick={() => setPromotionType(true)} @@ -246,7 +252,7 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { <span className='text-left'>%</span> <span className='text-left ml-5'>Makin Hemat Pakai Promo </span> </button> - </div> + </div> */} {product.variants.length > 1 ? ( <button type='button' @@ -420,16 +426,6 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { </LazyLoad> </div> </BottomPopup> - <BottomPopup - className='!w-[32%]' - title='Pakai Promo' - active={promotionType} - close={() => setPromotionType(false)} - > - <div className='flex mt-4'> - <PromotionType></PromotionType> - </div> - </BottomPopup> </div> </DesktopView> ) diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx index 5ed796ae..1c2974b0 100644 --- a/src/lib/product/components/Product/ProductMobile.jsx +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -172,11 +172,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { <div className='p-4'> <div className='mb-5'> - {/* <PromotionType></PromotionType> */} - <button type='button' className='text-gray-900 flex p-3 rounded-lg bg-white border border-gray-300 hover:bg-gray-100 py-2.5 w-[100%]'> - <span className='text-left'>%</span> - <span className='text-left ml-5'>Makin Hemat Pakai Promo</span> - </button> + <PromotionType></PromotionType> </div> <div> <label className='flex justify-between'> diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 8b48cf06..a8964310 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -12,7 +12,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { if (variant == 'vertical') { return ( - <div className='rounded shadow-sm border border-gray_r-4 h-full bg-white'> + <div className='rounded shadow-sm border border-gray_r-4 bg-white h-[350px]'> <Link href={createSlug('/shop/product/', product?.name, product?.id)} className='border-b border-gray_r-4 relative' diff --git a/src/lib/promotinProgram/api/homepageApi.js b/src/lib/promotinProgram/api/homepageApi.js new file mode 100644 index 00000000..d839101a --- /dev/null +++ b/src/lib/promotinProgram/api/homepageApi.js @@ -0,0 +1,11 @@ +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 +}
\ No newline at end of file diff --git a/src/lib/promotinProgram/components/HomePage.jsx b/src/lib/promotinProgram/components/HomePage.jsx index 93134117..9ebd82dc 100644 --- a/src/lib/promotinProgram/components/HomePage.jsx +++ b/src/lib/promotinProgram/components/HomePage.jsx @@ -1,68 +1,116 @@ -import React, { useState } from 'react' +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 ProductCard from '@/lib/product/components/ProductCard' +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('Tab 1') + 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) { + setActiveTab(titlePromotion[0].name) + setActiveId(titlePromotion[0].id) + setparentPromotions(titlePromotion) + setActiveBanner(titlePromotion[0].banner) + productPromotionRefetch() + } + }, [titlePromotion]) + + useEffect(() => { + if (productPromotion) { + setparentPromotions(() => { + return parentPromotions.map((title) => { + if (title.id === activeId && title.products === undefined) { + return { + ...title, + products: productPromotion + } + } + return title + }) + }) + } + }, [productPromotion, parentPromotions, activeId]) const { isMobile, isDesktop } = useDevice() - const handleTabClick = (label) => { + const handleTabClick = (id, label, banner) => { setActiveTab(label) + setActiveId(id) + setActiveBanner(banner) } - const tabItems = [ - { label: 'Tab 1', content: 'Content for Tab 1' }, - { label: 'Tab 2', content: 'Content for Tab 2' }, - { label: 'Tab 3', content: 'Content for Tab 3' }, - { label: 'Tab 4', content: 'Content for Tab 4' }, - { label: 'Tab 5', content: 'Content for Tab 5' }, - { label: 'Tab 6', content: 'Content for Tab 6' }, - { label: 'Tab 7', content: 'Content for Tab 7' }, - { label: 'Tab 8', content: 'Content for Tab 8' }, - { label: 'Tab 9', content: 'Content for Tab 9' }, - { label: 'Tab 10', content: 'Content for Tab 10' } - ] - return ( - <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> - <Swiper slidesPerView={isMobile ? 3.5 : 7.5} spaceBetween={isMobile ? 12 : 20}> - {tabItems.map((item, index) => ( - <SwiperSlide key={index}> - <button - className={`py-1 px-2 rounded border flex justify-center items-center h-30 ${ - activeTab === item.label ? 'border-red-500' : 'border-gray_r-6' - }`} - onClick={() => handleTabClick(item.label)} - > - {' '} - <Image - src='https://erp.indoteknik.com/api/image/x_manufactures/x_logo_manufacture/10' - alt='' - className='h-full w-full object-contain object-center' - /> - </button> - </SwiperSlide> - ))} - </Swiper> - - <div className='mt-4'> - {tabItems.map((item, index) => ( - <div - key={index} - className={`${ - activeTab === item.label ? 'block' : 'hidden' - } bg-gray-100 p-4 rounded-md`} - > - {item.content} - </div> - ))} + 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> - </div> + ) ) } diff --git a/src/lib/promotinProgram/components/PromotionType.jsx b/src/lib/promotinProgram/components/PromotionType.jsx index 371aa952..a6382cb1 100644 --- a/src/lib/promotinProgram/components/PromotionType.jsx +++ b/src/lib/promotinProgram/components/PromotionType.jsx @@ -1,47 +1,72 @@ import React, { useEffect, useState } from 'react' import Image from '@/core/components/elements/Image/Image' +import BottomPopup from '@/core/components/elements/Popup/BottomPopup' +import { Tabs } from 'flowbite-react' +import CountDown from '@/core/components/elements/CountDown/CountDown' +import CountDown2 from '@/core/components/elements/CountDown/CountDown2' -const PromotionType = ({ isModal = true }) => { +const PromotionType = ({ isModal = false }) => { const [selectedPromo, setSelectedPromo] = useState(null) + const [promotionType, setPromotionType] = useState(false) + const promos = [ { id: 1, - title: 'Promo 1', + title: 'Promo Bundling', description: 'Deskripsi Promo 1', - image: '/path/to/image1.jpg' + image: '/path/to/image1.jpg', + type: 'Promo Bundling' }, { id: 2, - title: 'Promo 2', + title: 'Promo Special', description: 'Deskripsi Promo 2', - image: '/path/to/image2.jpg' + image: '/path/to/image2.jpg', + type: 'Promo Special' }, { id: 3, - title: 'Promo 3', + title: 'Promo Free Merchandise', description: 'Deskripsi Promo 3', - image: '/path/to/image3.jpg' + image: '/path/to/image3.jpg', + type: 'Promo Free Merchandise' }, { id: 4, - title: 'Promo 1', + title: 'Promo 4', description: 'Deskripsi Promo 1', - image: '/path/to/image1.jpg' + image: '/path/to/image1.jpg', + type: 'Promo Bundling' }, { id: 5, - title: 'Promo 2', + title: 'Promo 5', description: 'Deskripsi Promo 2', - image: '/path/to/image2.jpg' + image: '/path/to/image2.jpg', + type: 'Promo Bundling' }, { id: 6, - title: 'Promo 3', + title: 'Promo 6', description: 'Deskripsi Promo 3', - image: '/path/to/image3.jpg' + image: '/path/to/image3.jpg', + type: 'Promo Special' } ] + + const [activeTitle, setActiveTitle] = useState(promos[0].type) + + const groupingData = promos.reduce((groups, item) => { + const promoType = item.type + if (!groups[promoType]) { + groups[promoType] = [] + } + groups[promoType].push(item) + + return groups + }, {}) + const handlePromoClick = (promoId) => { if (promoId == selectedPromo) { setSelectedPromo(null) @@ -49,60 +74,161 @@ const PromotionType = ({ isModal = true }) => { setSelectedPromo(promoId) } } + + const handlePopUp = () => { + if(isModal == false){ + setPromotionType(true) + } + } return ( - <div className={`w-full ${isModal == true ? '' : 'grid grid-cols-3 gap-1'}`}> - {promos.map((promo) => ( - <div - key={promo.id} - onClick={() => handlePromoClick(promo.id)} - className={`p-2 rounded-lg border border-solid border-gray-300 mb-5 w-full hover:cursor-pointer ${ - selectedPromo - ? selectedPromo === promo.id - ? 'opacity-100 border-red-500 bg-red-100' - : 'opacity-50 pointer-events-none' - : 'opacity-100' - } `} - > - <div className={`${isModal == true ? 'flex' : ''} items-center`}> - <div className=''> - <Image - src='https://placehold.co/400x400.png' - alt='' - className={`flex-1 w-full object-cover rounded-md ${isModal == true ? 'h-[100px]' : ''}`} - /> - </div> - <div className='p-2'> - <h2 className='text-xl font-semibold'>{promo.title}</h2> - {isModal == true && <p className='text-gray-500'>{promo.description}</p>} + <div className='h-[50%]'> + <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' : '' + } `} + > + {index} + </button> + </> + ) + })} </div> + {activeTitle && + groupingData[activeTitle].map((item, i) => ( + <div + key={i} + onClick={() => handlePromoClick(item.id)} + className={`border border-solid bg-white 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' + } `} + > + <div className={`flex`}> + <div className=''> + <Image + src='https://placehold.co/120x120.png' + alt='' + 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={3600}></CountDown2> + </div> + </div> + <p className='text-justify text-gray-500 line-clamp-3'> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut nibh at arcu + commodo auctor. Maecenas efficitur, ipsum sed mollis fermentum, nulla sem ada lah ini ditu kamu dia merek + </p> + <div className='text-danger-500 font-semibold mb-1 mt-2'>Rp. 999.000</div> + </div> + </div> + </div> + ))} </div> - </div> - // <div - // key={promo.id} - // onClick={() => handlePromoClick(promo.id)} - // className={`p-2 rounded-lg border border-solid border-gray-300 mb-5 w-full hover:cursor-pointer ${ - // selectedPromo - // ? selectedPromo === promo.id - // ? 'opacity-100 border-red-400 bg-red-100' - // : 'opacity-50 pointer-events-none' - // : 'opacity-100' - // } `} - // > - // <div className='items-center'> - // <div className=''> - // <Image - // src='https://placehold.co/600x600.png' - // alt='' - // className='flex-1 w-full object-cover rounded-md' - // /> - // </div> - // <div className='mt-1'> - // <h2 className='text-xl font-semibold'>{promo.title}</h2> - // {/* <p className='text-gray-500'>{promo.description}</p> */} - // </div> - // </div> - // </div> - ))} + ) : ( + promos.map((promo, index) => { + if (index > 2) { + return null + } else { + if (index === 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='https://placehold.co/120x120.png' + alt='' + className={`flex-1 w-full object-cover`} + /> + </div> + <div className='p-2'> + <div className='badge-yellow text-black mb-1'>{promo.title}</div> + <p className='text-justify line-clamp-2'>Lorem ipsum dolor sit amet, consectetur ...</p> + <div className='text-danger-500 font-semibold mb-1 mt-1'>999900</div> + <div> + <CountDown2 initialTime={360}></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}></PromotionType> + </div> + </BottomPopup> + </div> </div> ) } |
