diff options
Diffstat (limited to 'src-migrate/modules')
13 files changed, 466 insertions, 104 deletions
diff --git a/src-migrate/modules/cart/components/CartSummaryMobile.tsx b/src-migrate/modules/cart/components/CartSummaryMobile.tsx new file mode 100644 index 00000000..d9f72e0e --- /dev/null +++ b/src-migrate/modules/cart/components/CartSummaryMobile.tsx @@ -0,0 +1,111 @@ +import style from '../styles/summary.module.css'; + +import React, { useState } from 'react'; +import formatCurrency from '~/libs/formatCurrency'; +import clsxm from '~/libs/clsxm'; +import { Button, Skeleton } from '@chakra-ui/react'; +import _ from 'lodash'; +import { ChevronDownIcon } from '@heroicons/react/24/outline'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import useDevice from '@/core/hooks/useDevice'; + +type Props = { + total?: number; + discount?: number; + subtotal?: number; + tax?: number; + shipping?: number; + grandTotal?: number; + isLoaded: boolean; +}; + +const CartSummaryMobile = ({ + total, + discount, + subtotal, + tax, + shipping, + grandTotal, + isLoaded = false, +}: Props) => { + const [showPopup, setShowPopup] = useState(false); + return ( + <> + <BottomPopup + className=' !h-[35%]' + title='Ringkasan Pensanan' + active={showPopup} + close={() => setShowPopup(false)} + > + <div className='mt-4'> + <div className='flex flex-col gap-y-3'> + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={style.label}>Total Belanja</span> + <span className={style.value}> + Rp {formatCurrency(subtotal || 0)} + </span> + </Skeleton> + + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={style.label}>Total Diskon</span> + <span className={clsxm(style.value, style.discount)}> + - Rp {formatCurrency(discount || 0)} + </span> + </Skeleton> + + <div className={style.divider} /> + + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={style.label}>Subtotal</span> + <span className={style.value}> + Rp {formatCurrency(total || 0)} + </span> + </Skeleton> + + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={style.label}>Tax 11%</span> + <span className={style.value}>Rp {formatCurrency(tax || 0)}</span> + </Skeleton> + + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={style.label}>Biaya Kirim</span> + <span className={style.value}> + Rp {formatCurrency(shipping || 0)} + </span> + </Skeleton> + + <div className={style.divider} /> + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={clsxm(style.label, style.grandTotal)}> + Grand Total + </span> + <span className={style.value}> + Rp {formatCurrency(grandTotal || 0)} + </span> + </Skeleton> + </div> + </div> + </BottomPopup> + <div className='flex flex-col gap-y-3'> + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={clsxm(style.label, style.grandTotal)}> + Grand Total + </span> + <button + onClick={() => setShowPopup(true)} + className='bg-gray-300 w-6 h-6 items-center justify-center cursor-pointer hover:bg-red-400 md:hidden ' + > + <ChevronDownIcon className='h-6 w-6 text-white' /> + </button> + </Skeleton> + <Skeleton isLoaded={isLoaded} className={style.line}> + <span className={style.value}> + Rp {formatCurrency(grandTotal || 0)} + </span> + </Skeleton> + </div> + </> + ); +}; + +export default CartSummaryMobile; diff --git a/src-migrate/modules/footer-banner/index.tsx b/src-migrate/modules/footer-banner/index.tsx new file mode 100644 index 00000000..b214493d --- /dev/null +++ b/src-migrate/modules/footer-banner/index.tsx @@ -0,0 +1,32 @@ +import Link from "next/link" +import React, { useMemo } from "react"; +import { useQuery } from "react-query" +import Image from "~/components/ui/image" +import { getBanner } from "~/services/banner" +import { getRandomInt } from '@/utils/getRandomInt' + +const FooterBanner = () => { + const fetchFooterBanner = useQuery({ + queryKey: 'footerBanner', + queryFn: () => getBanner({ type: 'bottom-search-promotion' }) + }) + // ubah dari static menjadid dynamic dengan menggunakan random index + const length = useMemo(() => fetchFooterBanner.data?.length, [fetchFooterBanner.data]); + const randomIndex = useMemo(() => getRandomInt(length), [length]); + const banner = fetchFooterBanner?.data?.[randomIndex] || false; + + return banner && ( + <> + {banner.url && ( + <Link href={banner.url}> + <Image src={banner.image} alt={banner.name} width={924} height={150} className='object-cover object-center rounded-lg' /> + </Link> + )} + + {!banner.url && ( + <Image src={banner.image} alt={banner.name} width={924} height={150} className='object-cover object-center rounded-lg' /> + )} + </> + ) +} +export default FooterBanner
\ No newline at end of file diff --git a/src-migrate/modules/header/components/HeaderDesktop.tsx b/src-migrate/modules/header/components/HeaderDesktop.tsx index 8f5a8efa..131fa7da 100644 --- a/src-migrate/modules/header/components/HeaderDesktop.tsx +++ b/src-migrate/modules/header/components/HeaderDesktop.tsx @@ -54,7 +54,7 @@ const HeaderDesktop = () => { <Image src='/images/socials/Whatsapp-2.png' alt='Whatsapp' width={48} height={48} /> <div> <div className='font-semibold'>Whatsapp</div> - 0812 8080 622 (Chat) + 0817 1718 1922 (Chat) </div> </a> </div> diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index 3d537236..0d36f8e9 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -1,9 +1,9 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -import { Modal } from "~/components/ui/modal" + +import { Modal } from "~/components/ui/modal"; import { getAuth } from '~/libs/auth'; import PageContent from '../page-content'; -import Link from 'next/link'; const PagePopupInformation = () => { const router = useRouter(); @@ -12,9 +12,7 @@ const PagePopupInformation = () => { const [active, setActive] = useState<boolean>(false); useEffect(() => { - if (isHomePage && !auth) { - setActive(true); - } + if (isHomePage && !auth) setActive(true); }, [isHomePage, auth]); return ( <div className='group'> @@ -24,13 +22,8 @@ const PagePopupInformation = () => { close={() => setActive(false)} mode='desktop' > - <div className='w-[350px] md:w-[530px] '> - <Link href={'/register'}> - <PageContent path='/onbording-popup' /> - </Link> - {/* <Link href={'/register'} className='btn-yellow w-full mt-2'> - Daftar Sekarang - </Link> */} + <div className='w-[350px] md:w-[530px]' onClick={() => setActive(false)}> + <PageContent path='/onbording-popup' /> </div> </Modal> </div> diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 8487cd94..0febfadb 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -1,8 +1,10 @@ import style from '../styles/product-card.module.css' - +import ImageNext from 'next/image'; +import clsx from 'clsx' import Link from 'next/link' -import React, { useMemo } from 'react' +import React, { useEffect, useMemo, useState } from 'react' import Image from '~/components/ui/image' +import useUtmSource from '~/hooks/useUtmSource' import clsxm from '~/libs/clsxm' import formatCurrency from '~/libs/formatCurrency' import { formatToShortText } from '~/libs/formatNumber' @@ -15,8 +17,11 @@ type Props = { } const ProductCard = ({ product, layout = 'vertical' }: Props) => { + const utmSource = useUtmSource() + + const URL = { - product: createSlug('/shop/product/', product.name, product.id.toString()), + product: createSlug('/shop/product/', product.name, product.id.toString()) + `?utm_source=${utmSource}`, manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString()), } @@ -36,6 +41,8 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => { [style['image-h']]: layout === 'horizontal', })}> <Link href={URL.product}> + + <div className="relative"> <Image src={image} alt={product.name} @@ -43,6 +50,32 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => { height={128} className='object-contain object-center h-full w-full' /> + <div className="absolute top-0 right-0 flex mt-2"> + <div className="gambarB "> + {product.isSni && ( + <ImageNext + src="/images/sni-logo.png" + alt="SNI Logo" + className="w-3 h-4 object-contain object-top sm:h-4" + width={50} + height={50} + /> + )} + </div> + <div className="gambarC "> + {product.isTkdn && ( + <ImageNext + src="/images/TKDN.png" + alt="TKDN" + className="w-5 h-4 object-contain object-top ml-1 mr-1 sm:h-6" + width={50} + height={50} + /> + )} + </div> + </div> + </div> + {product.variant_total > 1 && ( <div className={style['variant-badge']}>{product.variant_total} Varian</div> )} diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index b69cc87f..30ca0d34 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -1,5 +1,5 @@ import style from '../styles/image.module.css'; - +import ImageNext from 'next/image'; import React, { useEffect, useMemo, useState } from 'react' import { InfoIcon } from 'lucide-react' import { Tooltip } from '@chakra-ui/react' @@ -14,9 +14,10 @@ type Props = { const Image = ({ product }: Props) => { const flashSale = product.flash_sale - const [count, setCount] = useState(flashSale?.remaining_time || 0); + + useEffect(() => { let interval: NodeJS.Timeout; @@ -42,15 +43,43 @@ const Image = ({ product }: Props) => { return ( <div className={style['wrapper']}> - <ImageUI - src={image} - alt={product.name} - width={256} - height={256} - className={style['image']} - loading='eager' - priority - /> + {/* <div className="relative"> */} + <ImageUI + src={image} + alt={product.name} + width={256} + height={256} + className={style['image']} + loading='eager' + priority + /> + <div className="absolute top-4 right-10 flex "> + <div className="gambarB "> + {product.isSni && ( + <ImageNext + src="/images/sni-logo.png" + alt="SNI Logo" + className="w-12 h-8 object-contain object-top sm:h-6" + width={50} + height={50} + /> + )} + </div> + <div className="gambarC "> + {product.isTkdn && ( + <ImageNext + src="/images/TKDN.png" + alt="TKDN" + className="w-16 h-8 object-contain object-top ml-1 mr-1 sm:h-6" + width={50} + height={50} + /> + )} + </div> + </div> + {/* </div> */} + + <div className={style['absolute-info']}> <Tooltip @@ -67,7 +96,7 @@ const Image = ({ product }: Props) => { <div className='absolute bottom-0 w-full h-14'> <div className="relative w-full h-full"> <ImageUI - src='/images/GAMBAR-BG-FLASH-SALE.jpg' + src='/images/BG-FLASH-SALE.jpg' alt='Flash Sale Indoteknik' width={200} height={100} diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index f25847a5..ad04de43 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -23,6 +23,16 @@ const PriceAction = ({ product }: Props) => { useEffect(() => { setActive(product.variants[0]) + if(product.variants.length > 2 && product.variants[0].price.price === 0){ + const variants = product.variants + for (let i = 0; i < variants.length; i++) { + if(variants[i].price.price > 0){ + setActive(variants[i]) + break; + } + } + } + }, [product, setActive]); return ( diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 3b1bdbea..bfdf5b43 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -23,6 +23,8 @@ import SimilarBottom from './SimilarBottom' import SimilarSide from './SimilarSide' import VariantList from './VariantList' +import { gtagProductDetail } from '@/core/utils/googleTag' + type Props = { product: IProductDetail } @@ -35,6 +37,10 @@ const ProductDetail = ({ product }: Props) => { const { setAskAdminUrl, askAdminUrl, activeVariantId } = useProductDetail() useEffect(() => { + gtagProductDetail(product); + },[product]) + + useEffect(() => { const createdAskUrl = whatsappUrl({ template: 'product', payload: { diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 192dd231..87017c14 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -7,6 +7,9 @@ import { getAuth } from '~/libs/auth' import { upsertUserCart } from '~/services/cart' import { IPromotion } from '~/types/promotion' +import DesktopView from '../../../../src/core/components/views/DesktopView'; +import MobileView from '../../../../src/core/components/views/MobileView'; + type Props = { promotion: IPromotion } @@ -55,21 +58,42 @@ const ProductPromoAddToCart = ({ promotion }: Props) => { }, [status]) return ( - <Button - colorScheme='yellow' - px={2} - w='110px' - gap={1} - isDisabled={status === 'loading'} - onClick={handleButton} - > - {status === 'success' && <CheckIcon size={16} />} - {status === 'loading' && <Spinner size='xs' mr={1.5} />} - {status === 'idle' && <PlusIcon size={16} />} + <div> + <MobileView> + <Button + colorScheme='yellow' + px={2} + w='36px' + gap={1} + isDisabled={status === 'loading'} + onClick={handleButton} + > + {status === 'success' && <CheckIcon size={16} />} + {status === 'loading' && <Spinner size='xs' mr={1.5} />} + {status === 'idle' && <PlusIcon size={16} />} + + {status === 'success' && <span>Berhasil</span>} + {/* {status !== 'success' && <span>Keranjang</span>} */} + </Button> + </MobileView> + <DesktopView> + <Button + colorScheme='yellow' + px={2} + w='110px' + gap={1} + isDisabled={status === 'loading'} + onClick={handleButton} + > + {status === 'success' && <CheckIcon size={16} />} + {status === 'loading' && <Spinner size='xs' mr={1.5} />} + {status === 'idle' && <PlusIcon size={16} />} - {status === 'success' && <span>Berhasil</span>} - {status !== 'success' && <span>Keranjang</span>} - </Button> + {status === 'success' && <span>Berhasil</span>} + {status !== 'success' && <span>Keranjang</span>} + </Button> + </DesktopView> + </div> ) } diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index 59110098..56e29e38 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -16,38 +16,52 @@ import ProductPromoItem from './Item' import ProductPromoAddToCart from "./AddToCart" import ProductPromoCardCountdown from "./CardCountdown" +import MobileView from '../../../../src/core/components/views/MobileView'; +import DesktopView from '../../../../src/core/components/views/DesktopView'; + type Props = { promotion: IPromotion + } -const ProductPromoCard = ({ promotion }: Props) => { +const ProductPromoCard = ({ promotion}: 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 +77,130 @@ const ProductPromoCard = ({ promotion }: Props) => { const allProducts = [...products, ...freeProducts] - return ( - <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> + return ( + <div> + <MobileView> + <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={1} rounded={6}> + {/* <div className={style.badgeType} > */} + {/* Paket {PROMO_CATEGORY[promotion.type.value].alias} */} + <InfoIcon className={style.badgeType} size={25} /> + {/* </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 className="text-[11px]">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> + </MobileView> + <DesktopView> + <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> + <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> + <div> + <ProductPromoAddToCart promotion={promotion} /> + </div> + + </div> </div> </div> + </DesktopView> </div> + // shouldRender && ( + + // ) ) } -export default ProductPromoCard
\ No newline at end of file +export default ProductPromoCard diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 5fc0da4c..4e8a7dd5 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -50,7 +50,7 @@ const ProductPromoSection = ({ productId }: Props) => { > {promotions?.data.map((promotion) => ( <div key={promotion.id} className="min-w-[400px] max-w-[400px]"> - <ProductPromoCard promotion={promotion} /> + <ProductPromoCard promotion={promotion} /> </div> ))} </Skeleton> diff --git a/src-migrate/modules/product-promo/styles/card.module.css b/src-migrate/modules/product-promo/styles/card.module.css index a2ad9af6..faa3b370 100644 --- a/src-migrate/modules/product-promo/styles/card.module.css +++ b/src-migrate/modules/product-promo/styles/card.module.css @@ -44,3 +44,15 @@ .totalItems { @apply text-gray_r-9; } + +@media only screen and (max-width: 384px) { + .basePrice { + @apply text-[13px]; + } + .price{ + @apply text-[15px]; + } + .totalItems{ + @apply text-[11px]; + } + }
\ No newline at end of file diff --git a/src-migrate/modules/side-banner/index.tsx b/src-migrate/modules/side-banner/index.tsx new file mode 100644 index 00000000..6214edfb --- /dev/null +++ b/src-migrate/modules/side-banner/index.tsx @@ -0,0 +1,30 @@ +import React, { useMemo } from "react"; +import Link from "next/link"; +import { useQuery } from "react-query"; +import Image from "~/components/ui/image"; +import { getBanner } from "~/services/banner"; +import { getRandomInt } from '@/utils/getRandomInt'; + +const SideBanner = () => { + const fetchSideBanner = useQuery({ + queryKey: 'sideBanner', + queryFn: () => getBanner({ type: 'side-banner-search' }) + }); + // ubah dari static menjadid dynamic dengan menggunakan random index + const length = useMemo(() => fetchSideBanner.data?.length, [fetchSideBanner.data]); + const randomIndex = useMemo(() => getRandomInt(length), [length]); + const banner = fetchSideBanner?.data?.[randomIndex] || false; + + return banner && ( + <> + {banner.url ? ( + <Link href={banner.url}> + <Image src={banner.image} alt={banner.name} width={315} height={450} className='object-cover object-center rounded-lg' /> + </Link> + ) : ( + <Image src={banner.image} alt={banner.name} width={315} height={450} className='object-cover object-center rounded-lg' /> + )} + </> + ); +} +export default SideBanner; |
