diff options
| -rw-r--r-- | src/components/skeleton/BannerSkeleton.jsx | 1 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/NavbarDesktop.jsx | 9 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/Search.jsx | 30 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/TopBanner.jsx | 5 | ||||
| -rw-r--r-- | src/core/components/elements/Sidebar/Sidebar.jsx | 2 | ||||
| -rw-r--r-- | src/core/components/elements/Skeleton/TopBannerSkeleton.jsx | 19 | ||||
| -rw-r--r-- | src/core/components/layouts/BasicLayout.jsx | 1 | ||||
| -rw-r--r-- | src/lib/checkout/api/getVoucher.js | 18 | ||||
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 6 | ||||
| -rw-r--r-- | src/lib/flashSale/components/FlashSale.jsx | 13 | ||||
| -rw-r--r-- | src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx | 36 | ||||
| -rw-r--r-- | src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx | 22 | ||||
| -rw-r--r-- | src/lib/product/components/ProductCard.jsx | 2 | ||||
| -rw-r--r-- | src/pages/_app.jsx | 1 | ||||
| -rw-r--r-- | src/pages/index.jsx | 36 |
15 files changed, 143 insertions, 58 deletions
diff --git a/src/components/skeleton/BannerSkeleton.jsx b/src/components/skeleton/BannerSkeleton.jsx index 7cb3952d..da86aef9 100644 --- a/src/components/skeleton/BannerSkeleton.jsx +++ b/src/components/skeleton/BannerSkeleton.jsx @@ -1,7 +1,6 @@ import useDevice from '@/core/hooks/useDevice' import classNames from 'classnames' import Skeleton from 'react-loading-skeleton' -import 'react-loading-skeleton/dist/skeleton.css' const HeroBannerSkeleton = () => { const { isDesktop, isMobile } = useDevice() diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index ea4b03ae..acd2d1ee 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -14,14 +14,17 @@ import { useEffect, useState } from 'react' import useAuth from '@/core/hooks/useAuth' import NavbarUserDropdown from './NavbarUserDropdown' import { getCountCart } from '@/core/utils/cart' -import TopBanner from './TopBanner' import whatsappUrl from '@/core/utils/whatsappUrl' import { useRouter } from 'next/router' import { getAuth } from '@/core/utils/auth' import { createSlug, getIdFromSlug } from '@/core/utils/slug' import productApi from '@/lib/product/api/productApi' +import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton' const Search = dynamic(() => import('./Search')) +const TopBanner = dynamic(() => import('./TopBanner'), { + loading: () => <TopBannerSkeleton /> +}) const NavbarDesktop = () => { const [isOpenCategory, setIsOpenCategory] = useState(false) @@ -162,8 +165,10 @@ const NavbarDesktop = () => { Ready Stock </Link> <Link - href='/blog' + href='https://blog.indoteknik.com/' className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition' + target='_blank' + rel='noreferrer noopener' > Blog Indoteknik </Link> diff --git a/src/core/components/elements/Navbar/Search.jsx b/src/core/components/elements/Navbar/Search.jsx index 47a9c235..f4a8ab3a 100644 --- a/src/core/components/elements/Navbar/Search.jsx +++ b/src/core/components/elements/Navbar/Search.jsx @@ -64,21 +64,21 @@ const Search = () => { <MagnifyingGlassIcon className='w-6' /> </button> - {suggestions.length > 0 && ( - <> - <div className='absolute w-full top-[50px] rounded-b bg-gray_r-1 border border-gray_r-6 divide-y divide-gray_r-6 z-50'> - {suggestions.map((suggestion, index) => ( - <Link - href={`/shop/search?q=${suggestion.term}`} - key={index} - className='px-3 py-3 !text-gray_r-12 font-normal' - > - {suggestion.term} - </Link> - ))} - </div> - </> - )} + <div + className={`absolute w-full top-[50px] rounded-b bg-gray_r-1 border border-gray_r-6 divide-y divide-gray_r-6 z-50 ${ + suggestions.length > 0 ? 'block' : 'hidden' + }`} + > + {suggestions.map((suggestion, index) => ( + <Link + href={`/shop/search?q=${suggestion.term}`} + key={index} + className='px-3 py-3 !text-gray_r-12 font-normal' + > + {suggestion.term} + </Link> + ))} + </div> </form> </> ) diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx index 9efd0a8d..dca2e930 100644 --- a/src/core/components/elements/Navbar/TopBanner.jsx +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -2,11 +2,16 @@ import odooApi from '@/core/api/odooApi' import { useQuery } from 'react-query' import Image from 'next/image' import Link from '../Link/Link' +import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton' const TopBanner = () => { const fetchTopBanner = async () => await odooApi('GET', '/api/v1/banner?type=top-banner') const topBanner = useQuery('topBanner', fetchTopBanner) + if (topBanner.isLoading) { + return <TopBannerSkeleton /> + } + return ( topBanner.isFetched && topBanner.data?.length > 0 && ( diff --git a/src/core/components/elements/Sidebar/Sidebar.jsx b/src/core/components/elements/Sidebar/Sidebar.jsx index 7ea8f7c4..f0bcb7b7 100644 --- a/src/core/components/elements/Sidebar/Sidebar.jsx +++ b/src/core/components/elements/Sidebar/Sidebar.jsx @@ -120,7 +120,7 @@ const Sidebar = ({ active, close }) => { <SidebarLink className={itemClassName} href='/shop/brands'> Semua Brand </SidebarLink> - <SidebarLink className={itemClassName} href='/blog'> + <SidebarLink className={itemClassName} href='https://blog.indoteknik.com/' target="_blank" rel="noreferrer noopener"> Blog Indoteknik </SidebarLink> <SidebarLink className={itemClassName} href='/video'> diff --git a/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx b/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx new file mode 100644 index 00000000..f7d2e748 --- /dev/null +++ b/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx @@ -0,0 +1,19 @@ +import useDevice from '@/core/hooks/useDevice' +import classNames from 'classnames' +import Skeleton from 'react-loading-skeleton' + +const TopBannerSkeleton = () => { + const { isDesktop, isMobile } = useDevice() + + const deviceClassName = { + 'h-10': isDesktop, + 'h-2.5': isMobile + } + const combinedClassName = classNames(deviceClassName) + + return ( + <Skeleton className={combinedClassName} count={1} containerClassName='w-full h-full block' /> + ) +} + +export { TopBannerSkeleton } diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index c6029f39..2e98eb61 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -45,7 +45,6 @@ const BasicLayout = ({ children }) => { } getProduct() setTemplateWA('product') - } }, []) return ( diff --git a/src/lib/checkout/api/getVoucher.js b/src/lib/checkout/api/getVoucher.js index 57d8acf5..07cf376e 100644 --- a/src/lib/checkout/api/getVoucher.js +++ b/src/lib/checkout/api/getVoucher.js @@ -1,11 +1,21 @@ import odooApi from '@/core/api/odooApi' -export const getVoucher = async (id) => { - const dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher`) +export const getVoucher = async (id, source) => { + let dataVoucher = null + if(source){ + dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?source=${source}`) + }else { + dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher`) + } return dataVoucher } -export const findVoucher = async (code, id) => { - const dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?code=${code}`) +export const findVoucher = async (code, id, source) => { + let dataVoucher = null + if(source){ + dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?code=${code}&source=${source}`) + }else{ + dataVoucher = await odooApi('GET', `/api/v1/user/${id}/voucher?code=${code}`) + } return dataVoucher } diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 53ac63e1..c6ea73f2 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -107,7 +107,7 @@ const Checkout = () => { const voucher = async () => { if (!listVouchers) { try { - let dataVoucher = await getVoucher(auth?.id) + let dataVoucher = await getVoucher(auth?.id, query) SetListVoucher(dataVoucher) } finally { setLoadingVoucher(false) @@ -115,7 +115,7 @@ const Checkout = () => { } } const VoucherCode = async (code) => { - let dataVoucher = await findVoucher(code, auth.id) + let dataVoucher = await findVoucher(code, auth.id, query) if (dataVoucher.length <= 0) { SetFindVoucher(1) return @@ -427,7 +427,7 @@ const Checkout = () => { <hr className='mt-10 my-4 border-gray_r-10' /> <div className=''> - {!loadingVoucher && listVouchers.length === 0 ? ( + {!loadingVoucher && listVouchers?.length === 0 ? ( <div className='flex items-center justify-center mt-4 mb-4'> <div className='text-center'> <h1 className='font-bold mb-4'>Tidak ada voucher tersedia</h1> diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx index e4a4a25c..87545d8d 100644 --- a/src/lib/flashSale/components/FlashSale.jsx +++ b/src/lib/flashSale/components/FlashSale.jsx @@ -1,21 +1,28 @@ import { useEffect, useState } from 'react' import flashSaleApi from '../api/flashSaleApi' -import Image from '@/core/components/elements/Image/Image' +import Image from 'next/image' import CountDown from '@/core/components/elements/CountDown/CountDown' import productSearchApi from '@/lib/product/api/productSearchApi' import ProductSlider from '@/lib/product/components/ProductSlider' +import { FlashSaleSkeleton } from '../skeleton/FlashSaleSkeleton' const FlashSale = () => { const [flashSales, setFlashSales] = useState(null) + const [isLoading, setIsLoading] = useState(true) useEffect(() => { const loadFlashSales = async () => { const dataFlashSales = await flashSaleApi() setFlashSales(dataFlashSales) + setIsLoading(false) } loadFlashSales() }, []) + if (isLoading) { + return <FlashSaleSkeleton /> + } + return ( flashSales?.length > 0 && ( <div className='px-4 sm:px-0 grid grid-cols-1 gap-y-8'> @@ -30,11 +37,15 @@ const FlashSale = () => { <Image src={flashSale.banner} alt={flashSale.name} + width={1080} + height={192} className='w-full rounded mb-4 hidden sm:block' /> <Image src={flashSale.bannerMobile} alt={flashSale.name} + width={256} + height={48} className='w-full rounded mb-4 block sm:hidden' /> <FlashSaleProduct flashSaleId={flashSale.pricelistId} /> diff --git a/src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx b/src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx new file mode 100644 index 00000000..e9a200d9 --- /dev/null +++ b/src/lib/flashSale/skeleton/FlashSaleSkeleton.jsx @@ -0,0 +1,36 @@ +import useDevice from '@/core/hooks/useDevice' +import PopularProductSkeleton from '@/lib/home/components/Skeleton/PopularProductSkeleton' +import Skeleton from 'react-loading-skeleton' + +const FlashSaleSkeleton = () => { + return ( + <div className='px-4 md:px-0'> + <TitleSkeleton /> + <div className='my-4'> + <BannerSkeleton /> + </div> + <PopularProductSkeleton /> + </div> + ) +} + +const TitleSkeleton = () => { + return ( + <div className='w-full md:w-[36%] flex gap-x-4'> + <Skeleton containerClassName='block w-1/2' height={24} /> + <div className='w-1/2 flex gap-x-1'> + <Skeleton height={40} containerClassName='w-full' /> + <Skeleton height={40} containerClassName='w-full' /> + <Skeleton height={40} containerClassName='w-full' /> + <Skeleton height={40} containerClassName='w-full' /> + </div> + </div> + ) +} + +const BannerSkeleton = () => { + const { isDesktop } = useDevice() + return <Skeleton duration={1.2} height={isDesktop ? 192 : 48} containerClassName='w-full' /> +} + +export { FlashSaleSkeleton, TitleSkeleton, BannerSkeleton } diff --git a/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx b/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx index 00589342..bd783053 100644 --- a/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx +++ b/src/lib/home/components/Skeleton/PreferredBrandSkeleton.jsx @@ -1,12 +1,16 @@ -import BrandSkeleton from '@/core/components/elements/Skeleton/BrandSkeleton' +import useDevice from '@/core/hooks/useDevice' +import Skeleton from 'react-loading-skeleton' -const PreferredBrandSkeleton = () => ( - <div className='grid grid-cols-4 gap-x-3'> - <BrandSkeleton /> - <BrandSkeleton /> - <BrandSkeleton /> - <BrandSkeleton /> - </div> -) +const PreferredBrandSkeleton = () => { + 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 count={1} height={isDesktop ? 84 : 56} key={index} /> + ))} + </div> + ) +} export default PreferredBrandSkeleton diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index d33516dd..5b859905 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -29,7 +29,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { if (variant == 'vertical') { return ( - <div className='rounded shadow-sm border border-gray_r-4 bg-white h-[350px] w-[165px]'> + <div className='rounded shadow-sm border border-gray_r-4 bg-white h-[300px] md:h-[350px]'> <Link href={createSlug('/shop/product/', product?.name, product?.id)} className='border-b border-gray_r-4 relative' diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx index 4c4fed89..03147219 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -1,4 +1,5 @@ import '../styles/globals.css' +import 'react-loading-skeleton/dist/skeleton.css' import NextProgress from 'next-progress' import { useRouter, Router } from 'next/router' import { AnimatePresence, motion } from 'framer-motion' diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 12d2ab46..47a0a493 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -7,6 +7,8 @@ import DelayRender from '@/core/components/elements/DelayRender/DelayRender' import { HeroBannerSkeleton } from '@/components/skeleton/BannerSkeleton' import { PopularProductSkeleton } from '@/components/skeleton/PopularProductSkeleton' import PromotinProgram from '@/lib/promotinProgram/components/HomePage' +import PreferredBrandSkeleton from '@/lib/home/components/Skeleton/PreferredBrandSkeleton' +import { FlashSaleSkeleton } from '@/lib/flashSale/skeleton/FlashSaleSkeleton' const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout')) const HeroBanner = dynamic(() => import('@/components/ui/HeroBanner'), { @@ -19,9 +21,13 @@ const PopularProduct = dynamic(() => import('@/components/ui/PopularProduct'), { loading: () => <PopularProductSkeleton /> }) -const PreferredBrand = dynamic(() => import('@/lib/home/components/PreferredBrand')) +const PreferredBrand = dynamic(() => import('@/lib/home/components/PreferredBrand'), { + loading: () => <PreferredBrandSkeleton /> +}) -const FlashSale = dynamic(() => import('@/lib/flashSale/components/FlashSale')) +const FlashSale = dynamic(() => import('@/lib/flashSale/components/FlashSale'), { + loading: () => <FlashSaleSkeleton /> +}) const BannerSection = dynamic(() => import('@/lib/home/components/BannerSection')) const CategoryHomeId = dynamic(() => import('@/lib/home/components/CategoryHomeId')) const CustomerReviews = dynamic(() => import('@/lib/review/components/CustomerReviews')) @@ -66,22 +72,12 @@ export default function Home() { </div> <div className='my-16 flex flex-col gap-y-16'> - <DelayRender renderAfter={400}> - <PreferredBrand /> - </DelayRender> - <DelayRender renderAfter={600}> - <FlashSale /> - </DelayRender> - <DelayRender renderAfter={600}> - <PromotinProgram /> - </DelayRender> - <DelayRender renderAfter={1000}> - <CategoryHomeId /> - <BannerSection /> - </DelayRender> - <DelayRender renderAfter={1200}> - <CustomerReviews /> - </DelayRender> + <PreferredBrand /> + <FlashSale /> + <PromotinProgram /> + <CategoryHomeId /> + <BannerSection /> + <CustomerReviews /> </div> </div> </DesktopView> @@ -98,8 +94,8 @@ export default function Home() { <FlashSale /> </DelayRender> <DelayRender renderAfter={600}> - <PromotinProgram /> - </DelayRender> + <PromotinProgram /> + </DelayRender> <DelayRender renderAfter={800}> <PopularProduct /> </DelayRender> |
