diff options
| author | it-fixcomart <it@fixcomart.co.id> | 2024-06-07 17:08:09 +0700 |
|---|---|---|
| committer | it-fixcomart <it@fixcomart.co.id> | 2024-06-07 17:08:09 +0700 |
| commit | c88d98f06a6301bad6dd6d2e58b4908d8562638c (patch) | |
| tree | 282633b855acf235fe1d0d78b67d8cd46e032c03 | |
| parent | 6ac1792ee37e5a5a9438f61e708966c944b61914 (diff) | |
<iman> add promotion program
| -rw-r--r-- | src-migrate/modules/product-promo/components/Card.tsx | 144 | ||||
| -rw-r--r-- | src/api/promoApi.js | 12 | ||||
| -rw-r--r-- | src/lib/home/components/PromotionProgram.jsx | 31 | ||||
| -rw-r--r-- | src/lib/promo/components/Promocrumb.jsx | 40 | ||||
| -rw-r--r-- | src/pages/index.jsx | 7 | ||||
| -rw-r--r-- | src/pages/shop/promo/[slug].jsx | 46 | ||||
| -rw-r--r-- | src/pages/shop/promo/index.jsx | 0 |
7 files changed, 216 insertions, 64 deletions
diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index 59110098..be7d5b6e 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -18,36 +18,47 @@ import ProductPromoCardCountdown from "./CardCountdown" type Props = { promotion: IPromotion + slug?: string } -const ProductPromoCard = ({ promotion }: Props) => { +const ProductPromoCard = ({ promotion, slug }: Props) => { const [products, setProducts] = useState<IProductVariantPromo[]>([]) + const [freeProducts, setFreeProducts] = useState<IProductVariantPromo[]>([]) + const [error, setError] = useState<string | null>(null) useEffect(() => { const getProducts = async () => { - const datas = [] - for (const product of promotion.products) { - const res = await getVariantById(product.product_id) - res.data.qty = product.qty - datas.push(res.data) + try { + const datas = [] + for (const product of promotion.products) { + const res = await getVariantById(product.product_id) + res.data.qty = product.qty + datas.push(res.data) + } + setProducts(datas) + } catch (err) { + setError('Failed to fetch product variants.') + console.error(err) } - setProducts(datas) } getProducts() }, [promotion.products]) - const [freeProducts, setFreeProducts] = useState<IProductVariantPromo[]>([]) - useEffect(() => { const getFreeProducts = async () => { - const datas = [] - for (const product of promotion.free_products) { - const res = await getVariantById(product.product_id) - res.data.qty = product.qty - datas.push(res.data) + try { + const datas = [] + for (const product of promotion.free_products) { + const res = await getVariantById(product.product_id) + res.data.qty = product.qty + datas.push(res.data) + } + setFreeProducts(datas) + } catch (err) { + setError('Failed to fetch free product variants.') + console.error(err) } - setFreeProducts(datas) } getFreeProducts() @@ -63,62 +74,67 @@ const ProductPromoCard = ({ promotion }: Props) => { const allProducts = [...products, ...freeProducts] - return ( - <div className={style.card}> - <ProductPromoCardCountdown promotion={promotion} /> + const shouldRender = !slug || promotion.type.value === slug - <div className='px-4 mt-4 text-caption-1'> - <div className="flex justify-between items-center"> - <div className={style.title}>{promotion.name}</div> + return ( + shouldRender && ( + <div className={style.card}> + <ProductPromoCardCountdown promotion={promotion} /> + + <div className='px-4 mt-4 text-caption-1'> + <div className="flex justify-between items-center"> + <div className={style.title}>{promotion.name}</div> + + <Tooltip label={PROMO_CATEGORY[promotion.type.value].description} placement="top" bgColor='red.600' p={2} rounded={6}> + <div className={style.badgeType}> + Paket {PROMO_CATEGORY[promotion.type.value].alias} + <InfoIcon size={16} /> + </div> + </Tooltip> + </div> - <Tooltip label={PROMO_CATEGORY[promotion.type.value].description} placement="top" bgColor='red.600' p={2} rounded={6}> - <div className={style.badgeType}> - Paket {PROMO_CATEGORY[promotion.type.value].alias} - <InfoIcon size={16} /> + <Skeleton className={clsxm(style.productSection, { 'justify-center': allProducts.length === 2 })} isLoaded={allProducts.length > 0}> + {allProducts.map((product, index) => ( + <React.Fragment key={product.id}> + <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.6 }}> + <ProductPromoItem + variant={product} + isFree={index + 1 > products.length && promotion.type.value === 'merchandise'} + // isFree={index + 1 > products.length } + /> + </motion.div> + <motion.div initial={{ y: 30, opacity: 0 }} animate={{ y: 0, opacity: 1 }} transition={{ duration: 0.5, delay: 0.1 }}> + {index + 1 < allProducts.length && ( + <div className="h-fit p-1 rounded-full border border-danger-500 text-danger-500 mt-[38px]"> + <PlusIcon size={14} strokeWidth='2px' /> + </div> + )} + </motion.div> + </React.Fragment> + ))} + </Skeleton> + + <div className={style.priceSection}> + <div className={style.priceCol}> + <Skeleton className={style.priceRow} isLoaded={priceTotal > 0}> + <span className={style.basePrice}>Rp{formatCurrency(priceTotal)}</span> + <span>Hemat <span className={style.savingAmt}>Rp {formatCurrency(priceTotal - promotion.price)}</span></span> + </Skeleton> + + <div className={style.priceRow}> + <span className={style.price}>Rp{formatCurrency(promotion.price)}</span> + <span className={style.totalItems}>(Total {promotion.total_qty} barang)</span> + </div> </div> - </Tooltip> - </div> - - <Skeleton className={clsxm(style.productSection, { 'justify-center': allProducts.length === 2 })} isLoaded={allProducts.length > 0}> - {allProducts.map((product, index) => ( - <> - <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.6 }}> - <ProductPromoItem - variant={product} - isFree={index + 1 > products.length && promotion.type.value === 'merchandise'} - /> - </motion.div> - <motion.div initial={{ y: 30, opacity: 0 }} animate={{ y: 0, opacity: 1 }} transition={{ duration: 0.5, delay: 0.1 }}> - {index + 1 < allProducts.length && ( - <div className="h-fit p-1 rounded-full border border-danger-500 text-danger-500 mt-[38px]"> - <PlusIcon size={14} strokeWidth='2px' /> - </div> - )} - </motion.div> - </> - ))} - </Skeleton> - - <div className={style.priceSection}> - <div className={style.priceCol}> - <Skeleton className={style.priceRow} isLoaded={priceTotal > 0}> - <span className={style.basePrice}>Rp{formatCurrency(priceTotal)}</span> - <span>Hemat <span className={style.savingAmt}>Rp {formatCurrency(priceTotal - promotion.price)}</span></span> - </Skeleton> - - <div className={style.priceRow}> - <span className={style.price}>Rp{formatCurrency(promotion.price)}</span> - <span className={style.totalItems}>(Total {promotion.total_qty} barang)</span> + <div> + <ProductPromoAddToCart promotion={promotion} /> </div> - </div> - <div> - <ProductPromoAddToCart promotion={promotion} /> - </div> + </div> </div> </div> - </div> + ) ) } -export default ProductPromoCard
\ No newline at end of file +export default ProductPromoCard diff --git a/src/api/promoApi.js b/src/api/promoApi.js new file mode 100644 index 00000000..a4acc768 --- /dev/null +++ b/src/api/promoApi.js @@ -0,0 +1,12 @@ +// src/api/promoApi.js +import odooApi from '@/core/api/odooApi'; + +export const fetchPromoItems = async (type) => { + try { + const response = await odooApi('GET', `/api/v1/program-line?type=${type}&limit=3`); + return response.map((item) => ({ value: item.id, label: item.name, product: item.products ,price:item.price})); + } catch (error) { + console.error('Error fetching promo items:', error); + return []; + } +}; diff --git a/src/lib/home/components/PromotionProgram.jsx b/src/lib/home/components/PromotionProgram.jsx new file mode 100644 index 00000000..461383a1 --- /dev/null +++ b/src/lib/home/components/PromotionProgram.jsx @@ -0,0 +1,31 @@ +import Link from '@/core/components/elements/Link/Link' +import Image from 'next/image' +import { bannerApi } from '@/api/bannerApi'; + +const { useQuery } = require('react-query') + +const BannerSection = () => { + const promotionProgram = useQuery('promotionProgram', bannerApi({ type: 'banner-promotion' })); + + return ( + promotionProgram.data && + promotionProgram.data?.length > 0 && ( + <div className='grid grid-cols-3 sm:grid-cols-3 gap-4 rounded rounded-md'> + {promotionProgram.data?.map((banner) => ( + <Link key={banner.id} href={banner.url}> + <Image + width={439} + height={150} + quality={100} + src={banner.image} + alt={banner.name} + className='h-auto w-full rounded' + /> + </Link> + ))} + </div> + ) + ) +} + +export default BannerSection diff --git a/src/lib/promo/components/Promocrumb.jsx b/src/lib/promo/components/Promocrumb.jsx new file mode 100644 index 00000000..a96ec6b4 --- /dev/null +++ b/src/lib/promo/components/Promocrumb.jsx @@ -0,0 +1,40 @@ +import { Breadcrumb as ChakraBreadcrumb, BreadcrumbItem, BreadcrumbLink } from '@chakra-ui/react' +import Link from 'next/link' +import React from 'react' + +/** + * Renders a breadcrumb component with links to navigate through different pages. + * + * @param {Object} props - The props object containing the brand name. + * @param {string} props.brandName - The name of the brand to display in the breadcrumb. + * @return {JSX.Element} The rendered breadcrumb component. + */ +const Breadcrumb = ({ brandName }) => { + return ( + <div className='container mx-auto py-4 md:py-6'> + <ChakraBreadcrumb> + <BreadcrumbItem> + <BreadcrumbLink as={Link} href='/' className='!text-danger-500 whitespace-nowrap'> + Shop + </BreadcrumbLink> + </BreadcrumbItem> + + <BreadcrumbItem> + <BreadcrumbLink + as={Link} + href='/shop/promo' + className='!text-danger-500 whitespace-nowrap' + > + Promo + </BreadcrumbLink> + </BreadcrumbItem> + + <BreadcrumbItem isCurrentPage> + <BreadcrumbLink className='whitespace-nowrap'>{brandName}</BreadcrumbLink> + </BreadcrumbItem> + </ChakraBreadcrumb> + </div> + ) +} + +export default Breadcrumb diff --git a/src/pages/index.jsx b/src/pages/index.jsx index c097530c..ddc41cbe 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -41,6 +41,11 @@ const FlashSale = dynamic( loading: () => <FlashSaleSkeleton />, } ); + +const ProgramPromotion = dynamic(() => + import('@/lib/home/components/PromotionProgram') +); + const BannerSection = dynamic(() => import('@/lib/home/components/BannerSection') ); @@ -103,6 +108,7 @@ export default function Home() { <PreferredBrand /> </div> <FlashSale /> + <ProgramPromotion/> <PromotinProgram /> <CategoryHomeId /> <BannerSection /> @@ -126,6 +132,7 @@ export default function Home() { </DelayRender> <DelayRender renderAfter={600}> <FlashSale /> + <ProgramPromotion/> </DelayRender> <DelayRender renderAfter={600}> <PromotinProgram /> diff --git a/src/pages/shop/promo/[slug].jsx b/src/pages/shop/promo/[slug].jsx new file mode 100644 index 00000000..4211ceb8 --- /dev/null +++ b/src/pages/shop/promo/[slug].jsx @@ -0,0 +1,46 @@ +import dynamic from 'next/dynamic' +import { getIdFromSlug, getNameFromSlug } from '@/core/utils/slug' +import { useRouter } from 'next/router' +import _ from 'lodash' +import Seo from '@/core/components/Seo' +import Promocrumb from '@/lib/promo/components/Promocrumb' +import useBrand from '@/lib/brand/hooks/useBrand' + +const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout')) +const ProductSearch = dynamic(() => import('@/lib/product/components/ProductSearch')) +const Brand = dynamic(() => import('@/lib/brand/components/Brand')) + +export default function BrandDetail() { + const router = useRouter() + const { slug = '' } = router.query + console.log("apa itu slug",slug) + const brandName = getNameFromSlug(slug) + const id = getIdFromSlug(slug) + const {brand} = useBrand({id}) + return ( + <BasicLayout> + {/* seakarang arahkan web untuk menampilkan daftar promo sesuai slug */} + + <Seo + title={`Promo ${slug} Terkini`} + description='B2B Marketplace MRO & Industri dengan Layanan Pembayaran Tempo, Faktur Pajak, Online Quotation, Garansi Resmi & Harga Kompetitif' + + /> + + <Promocrumb brandName={slug} /> + + harusnya disini menampilkan barang promosimya {slug} + + + {/* <Brand brand={brand} /> + {!_.isEmpty(router.query) && ( + <ProductSearch + query={_.omit(router.query, 'slug')} + prefixUrl={`/shop/promo/${slug}`} + defaultBrand={getNameFromSlug(slug)} + brand={brand} + /> + )} */} + </BasicLayout> + ) +} diff --git a/src/pages/shop/promo/index.jsx b/src/pages/shop/promo/index.jsx new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/pages/shop/promo/index.jsx |
