diff options
| author | IT Fixcomart <it@fixcomart.co.id> | 2025-10-16 10:59:56 +0000 |
|---|---|---|
| committer | IT Fixcomart <it@fixcomart.co.id> | 2025-10-16 10:59:56 +0000 |
| commit | 5590a8cf2423c14baa991abf18ca31179a62c2bd (patch) | |
| tree | d263d376c68279f6b7eecb6bebe5fb5fb81365f3 | |
| parent | f6b1aea824192572b241f94157ab3731dec6a4ea (diff) | |
| parent | 9fa82a1c56e6e1628ef5a255ba3f66fb6aec83ec (diff) | |
Merged in media_banner (pull request #469)
Media banner
| -rw-r--r-- | src/lib/brand/components/BrandCard.jsx | 1 | ||||
| -rw-r--r-- | src/lib/brand/components/MediaCard.jsx | 42 | ||||
| -rw-r--r-- | src/lib/home/api/mediaNews.js | 0 | ||||
| -rw-r--r-- | src/lib/home/components/MediaNews.jsx | 80 | ||||
| -rw-r--r-- | src/lib/home/components/Skeleton/MediaNewsSkeleton.jsx | 21 | ||||
| -rw-r--r-- | src/pages/index.jsx | 3 |
6 files changed, 147 insertions, 0 deletions
diff --git a/src/lib/brand/components/BrandCard.jsx b/src/lib/brand/components/BrandCard.jsx index dff61b24..411e2669 100644 --- a/src/lib/brand/components/BrandCard.jsx +++ b/src/lib/brand/components/BrandCard.jsx @@ -5,6 +5,7 @@ import { createSlug } from '@/core/utils/slug'; const BrandCard = ({ brand }) => { const { isMobile } = useDevice(); + // console.log("Brand logo:", brand.logo); return ( <Link href={createSlug('/shop/brands/', brand.name, brand.id)} diff --git a/src/lib/brand/components/MediaCard.jsx b/src/lib/brand/components/MediaCard.jsx new file mode 100644 index 00000000..a6591abd --- /dev/null +++ b/src/lib/brand/components/MediaCard.jsx @@ -0,0 +1,42 @@ +import NextImage from 'next/image'; +import Link from '@/core/components/elements/Link/Link'; +import useDevice from '@/core/hooks/useDevice'; +import { createSlug } from '@/core/utils/slug'; + +const MediaCard = ({ media }) => { + const { isMobile } = useDevice(); + +// console.log("Media logo:", media); + + return ( + <Link + href={createSlug('/shop/media/', media.name, media.id)} + className={` + py-1 px-2 border-gray_r-6 flex justify-center items-center + hover:scale-110 transition duration-500 ease-in-out + ${isMobile ? 'h-16' : 'h-24'} + `} + aria-label={media.name} + > + {media.image ? ( + <NextImage + src={media.image} + alt={media.name} + width={500} + height={500} + quality={85} + className="h-full w-[122px] object-contain object-center" + /> + ) : ( + <span + className="text-center" + style={{ fontSize: `${16 - media.name.length * 0.5}px` }} + > + {media.name} + </span> + )} + </Link> + ); +}; + +export default MediaCard; diff --git a/src/lib/home/api/mediaNews.js b/src/lib/home/api/mediaNews.js new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/lib/home/api/mediaNews.js diff --git a/src/lib/home/components/MediaNews.jsx b/src/lib/home/components/MediaNews.jsx new file mode 100644 index 00000000..6a0efbfe --- /dev/null +++ b/src/lib/home/components/MediaNews.jsx @@ -0,0 +1,80 @@ +import { Swiper, SwiperSlide } from 'swiper/react'; +import { Navigation, Pagination, Autoplay } from 'swiper'; +import { useEffect, useState, useCallback } from 'react'; +import MediaNewsSkeleton from './Skeleton/MediaNewsSkeleton'; +import MediaCard from '@/lib/brand/components/MediaCard'; +import useDevice from '@/core/hooks/useDevice'; +import Link from '@/core/components/elements/Link/Link'; + +const MediaNews = () => { + const [isLoading, setIsLoading] = useState(true); + const [media, setMedia] = useState([]); + const { isMobile, isDesktop } = useDevice(); + + const loadMedia = useCallback(async () => { + try { + setIsLoading(true); + const res = await fetch(`/api/hero-banner?type=media-berita`); + const result = await res.json(); + setMedia(result.data || []); + } catch (err) { + console.error('Failed to load media:', err); + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + loadMedia(); + }, [loadMedia]); + + const swiperConfig = { + modules: [Navigation, Pagination, Autoplay], + autoplay: { delay: 4000, disableOnInteraction: false }, + loop: true, + className: 'h-[70px] md:h-[100px] w-full', + slidesPerView: isMobile ? 4 : 8, + spaceBetween: isMobile ? 12 : 0, + pagination: { dynamicBullets: true, dynamicMainBullets: isMobile ? 6 : 8, clickable: true }, + }; + + const preferredMediaData = media.slice(0, 20); + + const shouldUseSlider = isMobile || (isDesktop && preferredMediaData.length > 8); + + if (!isLoading && preferredMediaData.length === 0) { + return null; + } + + return ( + <div className="px-4 sm:px-0"> + <div className="flex justify-between items-center mb-4"> + <h1 className="font-semibold text-[14px] sm:text-h-lg"> + <Link href="/" className="!text-black font-semibold"> + Telah Diliput Media + </Link> + </h1> + </div> + + {isLoading ? ( + <MediaNewsSkeleton /> + ) : shouldUseSlider ? ( + <Swiper {...swiperConfig}> + {preferredMediaData.map((item) => ( + <SwiperSlide key={item.id}> + <MediaCard media={item} /> + </SwiperSlide> + ))} + </Swiper> + ) : ( + <div className="flex flex-wrap justify-center gap-4"> + {preferredMediaData.map((item) => ( + <MediaCard key={item.id} media={item} /> + ))} + </div> + )} + </div> + ); +}; + +export default MediaNews; diff --git a/src/lib/home/components/Skeleton/MediaNewsSkeleton.jsx b/src/lib/home/components/Skeleton/MediaNewsSkeleton.jsx new file mode 100644 index 00000000..c0151efd --- /dev/null +++ b/src/lib/home/components/Skeleton/MediaNewsSkeleton.jsx @@ -0,0 +1,21 @@ +import useDevice from '@/core/hooks/useDevice' +import Skeleton from 'react-loading-skeleton' + +const MediaNewsSkeleton = () => { + const { isDesktop } = useDevice() + + return ( + <div className="grid grid-cols-4 md:grid-cols-8 gap-x-3"> + {Array.from({ length: isDesktop ? 8 : 4 }, (_, index) => ( + <Skeleton + key={index} + count={1} + height={isDesktop ? 84 : 56} + className="rounded-lg" + /> + ))} + </div> + ) +} + +export default MediaNewsSkeleton diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 809f00ae..28db4d3e 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -9,6 +9,7 @@ import PagePopupInformation from '@/lib/home/components/PopupBannerPromotion'; import dynamic from 'next/dynamic'; import { useRef } from 'react'; import { getAuth } from '~/libs/auth'; +import MediaNews from '../lib/home/components/MediaNews'; const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout') @@ -139,6 +140,7 @@ export default function Home({ categoryId }) { <div className='my-16 flex flex-col gap-y-8'> <ServiceList /> + <MediaNews /> <div id='flashsale'> <PreferredBrand /> </div> @@ -169,6 +171,7 @@ export default function Home({ categoryId }) { <div className='flex flex-col gap-y-4 my-6'> <DelayRender renderAfter={400}> <ServiceList /> + <MediaNews /> </DelayRender> <DelayRender renderAfter={400}> <div id='flashsale'> |
