diff options
| author | HATEC\SPVDEV001 <tri.susilo@altama.co.id> | 2023-09-01 09:36:52 +0700 |
|---|---|---|
| committer | HATEC\SPVDEV001 <tri.susilo@altama.co.id> | 2023-09-01 09:36:52 +0700 |
| commit | dfdeedf7141c9191952bdb3005e2e91d2a495044 (patch) | |
| tree | 2abb9a8090306791a76069baff6c91c5e151d3b5 | |
| parent | 57540da51be35b2d0a90f1d64b097fc4da608a25 (diff) | |
| parent | 7356bcc0d1b7bac8d05ac315fdcf2a46b3996e91 (diff) | |
Merge branch 'master' into Feature/google_sign_up
# Conflicts:
# src/core/components/elements/Navbar/NavbarDesktop.jsx
33 files changed, 766 insertions, 229 deletions
diff --git a/public/images/GAMBAR-BG-FLASH-SALE.jpg b/public/images/GAMBAR-BG-FLASH-SALE.jpg Binary files differnew file mode 100644 index 00000000..db26136a --- /dev/null +++ b/public/images/GAMBAR-BG-FLASH-SALE.jpg diff --git a/public/images/ICON_FLASH_SALE.svg b/public/images/ICON_FLASH_SALE.svg new file mode 100644 index 00000000..d6d90b18 --- /dev/null +++ b/public/images/ICON_FLASH_SALE.svg @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 230 230" style="enable-background:new 0 0 230 230;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#F0C83B;} + .st1{fill:#FEFEFE;} +</style> +<g> + <path class="st0" d="M142.9,104.6c-14,17.9-27.7,35.4-41.4,52.9c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.3,4.2-20.6,6.4-30.9 + c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.4,0-17.1,0h-2.8c1.6-3.7,3.1-7.1,4.6-10.5 + c4.6-10.3,9.3-20.6,13.8-30.9c0.8-1.8,1.7-2.4,3.6-2.3c7.1,0.1,14.3,0.1,21.5,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.3-8.3,16.8-12.5,25.1 + c-1,2-0.6,2.4,1.5,2.3C128.2,104.6,135.2,104.6,142.9,104.6z"/> + <path class="st0" d="M169.1,192.3c-1-1.2-0.9-1.7,0.3-2.6c2.4-1.9,4.9-3.8,7.1-5.9c26-25.7,34.9-56.6,26.6-92.1 + c-9.6-40.9-46.5-69.9-89.7-69.9c-4.8,0.1-11.4,0.7-17.8,2.2c-48.4,11-79.8,59.2-69.6,110c4.3,21.7,15.3,39.7,32.4,53.8 + c1.4,1.2,1.7,1.9,0.4,3.4c-4.4,5.4-8.7,11-13,16.4c-1.3,1.7-2.3,3.6-1.8,5.9c0.6,2.9,2.2,4.8,5.1,5.6c3.1,0.8,5.4-0.4,7.3-2.7 + c4.5-5.7,9.1-11.4,13.5-17.3c1.3-1.7,2.1-2,4-1c26.6,13.2,53.5,13.6,80.4,0.9c1.5-0.7,2.3-0.4,3.2,0.8c4.5,5.7,9.1,11.3,13.6,16.9 + c2.9,3.6,7.1,4.4,10.2,1.9c3.3-2.7,3.5-6.8,0.5-10.5C177.6,202.8,173.4,197.5,169.1,192.3z M121.8,194.1 + c-23.4,2-43.6-5.5-60.3-21.8c-13.5-13.1-21.2-29.4-23.3-48.2c-0.4-3.1-0.4-6.2-0.5-9.3c0.2-22.1,7.8-41.2,23.3-56.8 + c13-13.1,28.9-20.6,47.5-22.3c38.4-3.5,73.8,23.2,81.8,61.7c6.2,29.7-1.8,55.6-23.8,76.6C154.2,186,139.1,192.6,121.8,194.1z"/> + <path class="st0" d="M23.9,58.9c-5.3,0-8.4-5.5-5.8-10.3c4.6-8.4,10.6-15.8,17.6-22.3c6.2-5.7,12.9-10.6,20.3-14.6 + c4.6-2.5,9.6,0.1,10.3,5.1c0.4,2.9-1,5.4-4,7.1c-9.2,5.2-17.4,11.6-24.2,19.8c-3,3.6-5.6,7.3-7.9,11.4 + C28.8,57.5,26.8,58.9,23.9,58.9z"/> + <path class="st0" d="M169.9,10c1.2-0.2,2.4,0.3,3.6,1c15.6,8.4,28.4,19.8,37.7,35c1,1.7,1.9,3.4,1.6,5.5c-0.4,2.9-2.5,5.2-5.1,5.9 + c-2.9,0.7-5.9-0.3-7.4-2.9c-8.1-13.8-19.7-24-33.6-31.6c-3-1.6-4.2-4.8-3.3-7.8C164.1,12,166.6,10.1,169.9,10z"/> + <path class="st1" d="M142.9,104.6c-14,17.9-27.7,35.4-41.4,52.9c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.3,4.2-20.6,6.4-30.9 + c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.4,0-17.1,0h-2.8c1.6-3.7,3.1-7.1,4.6-10.5 + c4.6-10.3,9.3-20.6,13.8-30.9c0.8-1.8,1.7-2.4,3.6-2.3c7.1,0.1,14.3,0.1,21.5,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.3-8.3,16.8-12.5,25.1 + c-1,2-0.6,2.4,1.5,2.3C128.2,104.6,135.2,104.6,142.9,104.6z"/> + <path class="st0" d="M142.9,104.6c-14,17.9-27.7,35.4-41.4,52.9c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.3,4.2-20.6,6.4-30.9 + c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.4,0-17.1,0h-2.8c1.6-3.7,3.1-7.1,4.6-10.5 + c4.6-10.3,9.3-20.6,13.8-30.9c0.8-1.8,1.7-2.4,3.6-2.3c7.1,0.1,14.3,0.1,21.5,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.3-8.3,16.8-12.5,25.1 + c-1,2-0.6,2.4,1.5,2.3C128.2,104.6,135.2,104.6,142.9,104.6z"/> +</g> +</svg> diff --git a/public/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg b/public/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg new file mode 100644 index 00000000..2a8b40b8 --- /dev/null +++ b/public/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 199.8 213" style="enable-background:new 0 0 199.8 213;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#F0C83B;} + .st1{fill:#FEFEFE;} +</style> +<g> + <path class="st0" d="M128,96c-14.1,18-27.9,35.7-41.7,53.3c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.4,4.2-20.8,6.5-31.2 + c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.5,0-17.2,0h-2.8c1.6-3.7,3.1-7.2,4.6-10.6 + c4.6-10.4,9.4-20.8,13.9-31.2c0.8-1.8,1.7-2.4,3.6-2.3c7.2,0.1,14.4,0.1,21.7,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.4-8.4,16.9-12.6,25.3 + c-1,2-0.6,2.4,1.5,2.3C113.2,96,120.3,96,128,96z"/> + <path class="st0" d="M154.5,184.4c-1-1.2-0.9-1.7,0.3-2.6c2.4-1.9,4.9-3.8,7.2-5.9c26.2-25.9,35.2-57.1,26.8-92.9 + c-9.7-41.2-46.9-70.5-90.5-70.5c-4.8,0.1-11.5,0.7-17.9,2.2C31.5,25.8-0.1,74.4,10.2,125.6c4.3,21.9,15.4,40,32.7,54.3 + c1.4,1.2,1.7,1.9,0.4,3.4c-4.4,5.4-8.8,11.1-13.1,16.5c-1.3,1.7-2.3,3.6-1.8,5.9c0.6,2.9,2.2,4.8,5.1,5.6c3.1,0.8,5.4-0.4,7.4-2.7 + c4.5-5.7,9.2-11.5,13.6-17.4c1.3-1.7,2.1-2,4-1c26.8,13.3,53.9,13.7,81.1,0.9c1.5-0.7,2.3-0.4,3.2,0.8c4.5,5.7,9.2,11.4,13.7,17 + c2.9,3.6,7.2,4.4,10.3,1.9c3.3-2.7,3.5-6.9,0.5-10.6C163,195,158.8,189.7,154.5,184.4z M106.8,186.2c-23.6,2-44-5.5-60.8-22 + c-13.6-13.2-21.4-29.6-23.5-48.6c-0.4-3.1-0.4-6.3-0.5-9.4C22.2,84,29.8,64.7,45.5,49c13.1-13.2,29.1-20.8,47.9-22.5 + c38.7-3.5,74.4,23.4,82.5,62.2c6.3,29.9-1.8,56.1-24,77.2C139.4,178.1,124.2,184.7,106.8,186.2z"/> + <path class="st0" d="M8,49.9c-5.3,0-8.5-5.5-5.8-10.4C6.8,31.1,12.9,23.6,19.9,17c6.3-5.7,13-10.7,20.5-14.7 + c4.6-2.5,9.7,0.1,10.4,5.1c0.4,2.9-1,5.4-4,7.2c-9.3,5.2-17.5,11.7-24.4,20c-3,3.6-5.6,7.4-8,11.5C13,48.5,11,49.9,8,49.9z"/> + <path class="st0" d="M155.3,0.6c1.2-0.2,2.4,0.3,3.6,1c15.7,8.5,28.6,20,38,35.3c1,1.7,1.9,3.4,1.6,5.5c-0.4,2.9-2.5,5.2-5.1,5.9 + c-2.9,0.7-5.9-0.3-7.5-2.9c-8.2-13.9-19.9-24.2-33.9-31.9c-3-1.6-4.2-4.8-3.3-7.9C149.4,2.6,151.9,0.7,155.3,0.6z"/> + <path class="st1" d="M128,96c-14.1,18-27.9,35.7-41.7,53.3c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.4,4.2-20.8,6.5-31.2 + c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.5,0-17.2,0h-2.8c1.6-3.7,3.1-7.2,4.6-10.6 + c4.6-10.4,9.4-20.8,13.9-31.2c0.8-1.8,1.7-2.4,3.6-2.3c7.2,0.1,14.4,0.1,21.7,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.4-8.4,16.9-12.6,25.3 + c-1,2-0.6,2.4,1.5,2.3C113.2,96,120.3,96,128,96z"/> + <path class="st0" d="M128,96c-14.1,18-27.9,35.7-41.7,53.3c-0.2-0.1-0.4-0.2-0.6-0.3c2.1-10.4,4.2-20.8,6.5-31.2 + c0.4-1.9,0.8-3.8,1.3-5.7c0.4-1.5-0.1-1.9-1.6-1.9c-5.7,0.1-11.5,0-17.2,0h-2.8c1.6-3.7,3.1-7.2,4.6-10.6 + c4.6-10.4,9.4-20.8,13.9-31.2c0.8-1.8,1.7-2.4,3.6-2.3c7.2,0.1,14.4,0.1,21.7,0c2,0,2.4,0.2,1.4,2.2c-4.2,8.4-8.4,16.9-12.6,25.3 + c-1,2-0.6,2.4,1.5,2.3C113.2,96,120.3,96,128,96z"/> +</g> +</svg> diff --git a/public/robots.txt b/public/robots.txt index 4aca1276..205ec9f3 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -8,13 +8,14 @@ Disallow: /shop/product/pdf/* Disallow: /shop/product/wishlist/* Disallow: /shop/product/https://* Disallow: /v1/* -Disallow: /shop/search?product_name=* -Disallow: /shop/search?q=* +Disallow: /shop/search?*=* Disallow: /shop/cart/ Disallow: /shop/checkout/ Disallow: /my/* Disallow: /shop/search/* Disallow: /promo/* +Disallow: /shop/brands/*?* +Disallow: /shop/category/*?* User-agent: Adsbot-Google Allow: /my/* diff --git a/src/api/productApi.js b/src/api/productApi.js index cca052f7..009d95ef 100644 --- a/src/api/productApi.js +++ b/src/api/productApi.js @@ -3,7 +3,7 @@ import axios from 'axios' export const popularProductApi = () => { return async () => { const dataPopularProducts = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular-weekly&priceFrom=1` ) return dataPopularProducts.data.response } 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/CountDown/CountDown2.jsx b/src/core/components/elements/CountDown/CountDown2.jsx index 61503d17..e85a22cb 100644 --- a/src/core/components/elements/CountDown/CountDown2.jsx +++ b/src/core/components/elements/CountDown/CountDown2.jsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' const CountDown2 = ({ initialTime }) => { - const hours = Math.floor(initialTime / 3600) + /*const hours = Math.floor(initialTime / 3600) const minutes = Math.floor((initialTime % 3600) / 60) const seconds = initialTime % 60 @@ -25,24 +25,58 @@ const CountDown2 = ({ initialTime }) => { } }, 1000) return () => clearInterval(timer) + }, [timeLeft])*/ + + const days = Math.floor(initialTime / 86400) + const hours = Math.floor((initialTime % 86400) / 3600) + const minutes = Math.floor((initialTime % 3600) / 60) + const seconds = initialTime % 60 + + const [timeLeft, setTimeLeft] = useState({ + day: days, + hour: hours, + minute: minutes, + second: seconds + }) + + useEffect(() => { + const timer = setInterval(() => { + const totalSeconds = + timeLeft.day * 86400 + timeLeft.hour * 3600 + timeLeft.minute * 60 + timeLeft.second + const secondsLeft = totalSeconds - 1 + if (secondsLeft < 0) { + clearInterval(timer) + } else { + const days = Math.floor(secondsLeft / 86400) + const hours = Math.floor((secondsLeft % 86400) / 3600) + const minutes = Math.floor((secondsLeft % 3600) / 60) + const seconds = secondsLeft % 60 + setTimeLeft({ day: days, hour: hours, minute: minutes, second: seconds }) + } + }, 1000) + return () => clearInterval(timer) }, [timeLeft]) + return ( - <div className='flex justify-between gap-x-2'> + <div className='flex justify-between gap-x-2 mt-18'> <div className='flex flex-col items-center'> - <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'> - {timeLeft.hour.toString().padStart(2, '0')} + <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'> + {timeLeft.day.toString().padStart(2, '0')} </span> + <span className='text-xs text-white'>Hari</span> </div> <div className='flex flex-col items-center'> - <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'> - {timeLeft.minute.toString().padStart(2, '0')} + <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'> + {timeLeft.hour.toString().padStart(2, '0')} </span> + <span className='text-xs text-white'>Jam</span> </div> <div className='flex flex-col items-center'> - <span className='bg-red-200 border border-red-500 text-black font-sm w-10 h-8 flex items-center justify-center rounded'> - {timeLeft.second.toString().padStart(2, '0')} + <span className='bg-yellow-400 border border-yellow-400 text-black font-sm w-10 h-7 flex items-center justify-center rounded-lg'> + {timeLeft.minute.toString().padStart(2, '0')} </span> + <span className='text-xs text-white'>Menit</span> </div> </div> ) diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index 17aedeb6..6fdea644 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -14,7 +14,6 @@ import { useContext, 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, setAuth } from '@/core/utils/auth' @@ -22,8 +21,12 @@ import { createSlug, getIdFromSlug } from '@/core/utils/slug' import productApi from '@/lib/product/api/productApi' import { useSession } from 'next-auth/react' import { AuthContext } from '@/pages/_app' +import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton' const Search = dynamic(() => import('./Search')) +const TopBanner = dynamic(() => import('./TopBanner'), { + loading: () => <TopBannerSkeleton /> +}) const NavbarDesktop = () => { const [isOpenCategory, setIsOpenCategory] = useState(false) @@ -165,8 +168,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 272440c2..e737101a 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -38,7 +38,6 @@ const BasicLayout = ({ children }) => { const { slug } = router.query const getProduct = async () => { let product = await productApi({ id: getIdFromSlug(slug), headers: { Token: authToken } }) - console.log('ini product', product) setPayloadWa({ name: product[0]?.name, manufacture: product[0]?.manufacture.name, 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..afb94c10 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 @@ -301,7 +301,7 @@ const Checkout = () => { voucher: activeVoucher, type: 'sale_order' } - if(query){ + if (query) { data.source = 'buy' } if (poNumber.current.value) data.po_number = poNumber.current.value @@ -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> @@ -490,7 +490,78 @@ const Checkout = () => { <div className='absolute w-full h-full bg-gray_r-3/40 top-0 left-0 z-50' /> )} - <div className={`border border-solid mb-5 w-full hover:cursor-pointer p-4 `}> + <div className={`border border-solid mb-5 w-full hover:cursor-pointer p-2 pl-4 pr-4 `}> + {item.canApply && ( + <div + class='p-2 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:text-green-400' + role='alert' + > + <p> + Potensi potongan sebesar{' '} + <span className='text-green font-bold'> + {currencyFormat(item.discountVoucher)} + </span> + </p> + </div> + )} + {!item.canApply && item.applyStatus === 'MPA-HF' && ( + <div + class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400' + role='alert' + > + <p className='text-sm'>Voucher tidak bisa digabung dengan promo lainya</p> + </div> + )} + {!item.canApply && item.applyStatus === 'UM' && ( + <div + class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400' + role='alert' + > + <p className='text-sm'> + Tambah produk{' '} + <span className='text-red-500 font-bold '>{item.manufactureNames}</span> senilai{' '} + <span className='text-red-500 font-bold'> + {currencyFormat(item.minPurchaseAmount)} + </span>{' '} + untuk pakai promo ini + </p> + </div> + )} + + {!item.canApply && + item.applyStatus === 'MPA' && + item.manufactureNames != '' && ( + <div + class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400' + role='alert' + > + <p className='text-sm'> + Tambah produk{' '} + <span className='text-red-500 font-bold'>{item.manufactureNames}</span> senilai{' '} + <span className='text-red-500 font-bold'> + {currencyFormat(item.differenceToApply)} + </span>{' '} + untuk pakai promo ini + </p> + </div> + )} + + {!item.canApply && + item.applyStatus === 'MPA' && + item.manufactureNames === '' && ( + <div + class='p-2 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400' + role='alert' + > + <p className='text-sm'> + Tambah{' '} + <span className='text-red-500 font-bold'> + {currencyFormat(item.differenceToApply)} + </span>{' '} + untuk pakai promo ini{' '} + </p> + </div> + )} <div className={`flex gap-x-3`}> <div className='hidden md:w-[250px] md:block'> <Image src={item.image} alt={item.name} className={`object-cover`} /> @@ -516,7 +587,7 @@ const Checkout = () => { </label> </div> </div> - <hr className='mt-3 my-4 border-gray_r-8' /> + <hr className='mt-2 my-2 border-gray_r-8' /> <div className='flex justify-between items-center'> <p className='text-justify text-sm md:text-xs'> Kode Voucher :{' '} @@ -528,51 +599,30 @@ const Checkout = () => { )} </p> </div> + <div className='flex items-center mt-3'> + <svg + aria-hidden='true' + fill='none' + stroke='currentColor' + stroke-width='1.5' + viewBox='0 0 24 24' + className='w-5 text-black' + > + <path + d='M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z' + stroke-linecap='round' + stroke-linejoin='round' + ></path> + </svg> + <span className='text-left ml-3 text-sm '> + Berakhir dalam{' '} + <span className='text-red-600'>{item.remainingTime}</span> lagi{' '} + </span> + </div> </div> </div> <div className='mt-3'> <p className='text-justify text-sm '> - {!item.canApply && - item.applyStatus === 'MPA' && - item.manufactureNames != '' && ( - <p> - Tambah produk{' '} - <span className='text-red-500'>{item.manufactureNames}</span> senilai{' '} - <span className='text-red-500'> - {currencyFormat(item.differenceToApply)} - </span>{' '} - untuk pakai promo ini - </p> - )} - {!item.canApply && - item.applyStatus === 'MPA' && - item.manufactureNames === '' && ( - <p> - Tambah{' '} - <span className='text-red-500'> - {currencyFormat(item.differenceToApply)} - </span>{' '} - untuk pakai promo ini{' '} - </p> - )} - {!item.canApply && item.applyStatus === 'UM' && ( - <p> - Tambah produk{' '} - <span className='text-red-500'>{item.manufactureNames}</span> senilai{' '} - <span className='text-red-500'> - {currencyFormat(item.minPurchaseAmount)} - </span>{' '} - untuk pakai promo ini - </p> - )} - {item.canApply && ( - <p> - Potensi potongan sebesar{' '} - <span className='text-red-500'> - {currencyFormat(item.discountVoucher)} - </span> - </p> - )} {/* {item.canApply === false ? 'Tambah ' + currencyFormat(item.differenceToApply) + @@ -580,27 +630,6 @@ const Checkout = () => { : 'Potensi potongan sebesar ' + currencyFormat(hitungDiscountVoucher(item.code))} */} </p> - <hr className='mt-2 my-4 border-gray_r-8' /> - <div className='flex items-center'> - <svg - aria-hidden='true' - fill='none' - stroke='currentColor' - stroke-width='1.5' - viewBox='0 0 24 24' - className='w-5 text-black' - > - <path - d='M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z' - stroke-linecap='round' - stroke-linejoin='round' - ></path> - </svg> - <span className='text-left ml-3 text-sm '> - Berakhir dalam <span className='text-red-600'>{item.remainingTime}</span>{' '} - lagi{' '} - </span> - </div> </div> </div> </div> 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/api/popularProductApi.js b/src/lib/home/api/popularProductApi.js index 37e4390e..4bb5e580 100644 --- a/src/lib/home/api/popularProductApi.js +++ b/src/lib/home/api/popularProductApi.js @@ -2,7 +2,7 @@ import axios from 'axios' const popularProductApi = async () => { const dataPopularProducts = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=1&orderBy=popular-weekly` ) return dataPopularProducts.data.response } 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/api/productSimilarApi.js b/src/lib/product/api/productSimilarApi.js index 93c7f22c..c1bccd59 100644 --- a/src/lib/product/api/productSimilarApi.js +++ b/src/lib/product/api/productSimilarApi.js @@ -1,9 +1,30 @@ +import odooApi from '@/core/api/odooApi' import axios from 'axios' +import productSearchApi from './productSearchApi' -const productSimilarApi = async ({ query }) => { +const productSimilarApi = async ({ query, source }) => { + let dataflashSale = null + const flashSale = await odooApi('GET', '/api/v1/flashsale/header') + if (flashSale && flashSale.length > 0) { + const dataFlash = await productSearchApi({ + query: `fq=flashsale_id_i:${flashSale[0].pricelistId}&fq=flashsale_price_f:[1 TO *]&limit=${ + source === 'bottom' ? '4' : '1' + }`, + operation: 'AND' + }) + if (source === 'bottom') { + dataflashSale = dataFlash.response.products.slice('2', '4') + } else { + dataflashSale = dataFlash.response.products + } + } const dataProductSimilar = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular&operation=OR` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=${query}&page=1&orderBy=popular-weekly&operation=OR` ) + dataProductSimilar.data.response.products = [ + ...dataflashSale, + ...dataProductSimilar.data.response.products, + ]; return dataProductSimilar.data.response } diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index 7e1d0d3b..3b9296a8 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -18,6 +18,9 @@ import odooApi from '@/core/api/odooApi' import { Button, Spinner } from 'flowbite-react' import PromotionType from '@/lib/promotinProgram/components/PromotionType' import useAuth from '@/core/hooks/useAuth' +import ImageNext from 'next/image' +import CountDown2 from '@/core/components/elements/CountDown/CountDown2' +import CountDown from '@/core/components/elements/CountDown/CountDown' const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { const router = useRouter() @@ -33,6 +36,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { const [promotionType, setPromotionType] = useState(false) const [promotionActiveId, setPromotionActiveId] = useState(null) const [selectVariantPromoActive, setSelectVariantPromoActive] = useState(null) + const [backgorundFlashSale, setBackgorundFlashSale] = useState(null) const getLowestPrice = useCallback(() => { const prices = product.variants.map((variant) => variant.price) @@ -47,6 +51,14 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { setLowestPrice(lowest) }, [getLowestPrice]) + useEffect(() => { + const getBackgound = async () => { + const get = await odooApi('GET', '/api/v1/banner?type=flash-sale-background-banner') + setBackgorundFlashSale(get[0].image) + } + getBackgound() + }, []) + const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) const variantQuantityRefs = useRef([]) @@ -54,7 +66,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { const setVariantQuantityRef = (variantId) => (element) => { if (element) { let variantIndex = product.variants.findIndex((varian) => varian.id == variantId) - product.variants[variantIndex].quantity = element.value + product.variants[variantIndex].quantity = element?.value } variantQuantityRefs.current[variantId] = element } @@ -96,7 +108,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { router.push(`/login?next=/shop/product/${slug}`) return } - + const quantity = variantQuantityRefs.current[variantId].value if (!validQuantity(quantity)) return @@ -106,6 +118,15 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { setAddCartAlert(true) } + const handleQuantityChange = (variantId) => (event) => { + const { value } = event.target + const variantIndex = product.variants.findIndex((variant) => variant.id === variantId) + if (variantIndex !== -1) { + product.variants[variantIndex].quantity = parseInt(value, 10) // Pastikan untuk mengubah ke tipe number jika diperlukan + // Lakukan sesuatu jika nilai quantity diubah + } + } + const handleBuy = (variant) => { const quantity = variantQuantityRefs.current[variant].value if (!validQuantity(quantity)) return @@ -142,7 +163,8 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { useEffect(() => { const loadProductSimilarInBrand = async () => { const productSimilarQuery = [product?.name, `fq=-product_id_i:${product.id}`].join('&') - const dataProductSimilar = await productSimilarApi({ query: productSimilarQuery }) + const source = 'right' + const dataProductSimilar = await productSimilarApi({ query: productSimilarQuery, source }) setProductSimilarInBrand(dataProductSimilar.products) } if (!productSimilarInBrand) loadProductSimilarInBrand() @@ -171,11 +193,52 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { <div className='flex'> <div className='w-full flex flex-wrap'> <div className='w-5/12'> - <Image - src={product.image} - alt={product.name} - className='h-[430px] object-contain object-center w-full border border-gray_r-4' - /> + <div className='relative mb-2'> + {product?.flashSale?.remainingTime > 0 && ( + <div className={`absolute bottom-0 w-full`}> + <div className='absolute bottom-0 w-full h-full'> + <ImageNext src={backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg'} width={1000} height={100} /> + </div> + <div className='relative'> + <div className='flex gap-x-2 items-center p-2'> + <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '> + <span className='text-lg font-bold'> + {product.lowestPrice.discountPercentage}% + </span> + </div> + <div + className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`} + > + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={17} + height={10} + /> + <span className='text-white text-lg font-semibold'> + {product.flashSale.tag || 'FLASH SALE'} + </span> + </div> + <div> + <CountDown2 initialTime={product.flashSale.remainingTime} /> + </div> + </div> + </div> + </div> + )} + <Image + src={product.image} + alt={product.name} + className='h-[430px] object-contain object-center w-full border border-gray_r-4' + /> + </div> + <div> + <p className='text-justify text-xs leading-5'> + <span className='font-semibold '>Keterangan : </span>Gambar atau foto berperan + sebagai ilustrasi produk. Kadang tidak sesuai dengan kondisi terbaru dengan + berbagai perubahan dan perbaikan. Hubungi tim sales kami untuk informasi yang + lebih baik perihal gambar di 021-2933 8828. + </p> + </div> </div> <div className='w-7/12 px-4'> @@ -375,6 +438,18 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { <div className='text-gray_r-11 line-through text-caption-1'> {currencyFormat(lowestPrice?.price)} </div> + {product.flashSale.remainingTime > 0 && ( + <div className='bg-red-600 rounded-full mb-1 p-2 pl-3 pr-3 flex w-fit items-center gap-x-1'> + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={15} + height={10} + /> + <span className='text-white text-xs font-semibold'> + {product.flashSale.tag || 'FLASH SALE'} + </span> + </div> + )} </div> )} <h3 className='text-danger-500 font-semibold mt-1 text-title-md'> @@ -412,7 +487,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { type='number' className='form-input w-16 py-2 text-center bg-gray_r-1' ref={setVariantQuantityRef(product.variants[0].id)} - onChange={setVariantQuantityRef(product.variants[0].id)} + onChange={handleQuantityChange(product.variants[0].id)} defaultValue={1} /> <button @@ -579,7 +654,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { type='number' className='form-input w-16 py-2 text-center bg-gray_r-1' ref={setVariantQuantityRef(variant.id)} - onChange={setVariantQuantityRef(variant.id)} + onChange={handleQuantityChange(variant.id)} defaultValue={1} /> </td> diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx index 2edd1a5f..d25d0861 100644 --- a/src/lib/product/components/Product/ProductMobile.jsx +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -18,6 +18,8 @@ import PromotionType from '@/lib/promotinProgram/components/PromotionType' import { gtagAddToCart } from '@/core/utils/googleTag' import odooApi from '@/core/api/odooApi' import { Button, Spinner } from 'flowbite-react' +import ImageNext from 'next/image' +import CountDown2 from '@/core/components/elements/CountDown/CountDown2' const ProductMobile = ({ product, wishlist, toggleWishlist }) => { const router = useRouter() @@ -30,6 +32,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { const [isLoadingSLA, setIsLoadingSLA] = useState(true) const [promotionType, setPromotionType] = useState(false) const [promotionActiveId, setPromotionActiveId] = useState(null) + const [backgorundFlashSale, setBackgorundFlashSale] = useState(null) const getLowestPrice = () => { const prices = product.variants.map((variant) => variant.price) @@ -39,6 +42,16 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { return lowest } + useEffect(() => { + const getBackgound = async () => { + const get = await odooApi('GET', '/api/v1/banner?type=flash-sale-background-banner') + if (get.length > 0) { + setBackgorundFlashSale(get[0].image) + } + } + getBackgound() + }, []) + const [activeVariant, setActiveVariant] = useState({ id: null, code: product.code, @@ -69,11 +82,11 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { product.variants = variantData setIsLoadingSLA(false) - if(product.variants.length === 1){ + if (product.variants.length === 1) { setActiveVariant({ id: product.variants[0].id, code: product.variants[0].code, - name: product.variants[0].parent.name , + name: product.variants[0].parent.name, price: product.variants[0].price, stock: product.variants[0].stock, weight: product.variants[0].weight, @@ -143,7 +156,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { quantity, programLineId: promotionActiveId, selected: true, - source : 'buy' + source: 'buy' }) router.push(`/shop/checkout?source=buy`) } @@ -156,11 +169,48 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { return ( <MobileView> - <Image - src={product.image} - alt={product.name} - className='h-72 object-contain object-center w-full border-b border-gray_r-4' - /> + <div className='relative'> + {product?.flashSale?.remainingTime > 0 && ( + <div className={`absolute bottom-0 w-full`}> + <div className='absolute bottom-0 w-full'> + <ImageNext + src={backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg'} + width={1000} + height={100} + /> + </div> + <div className='relative'> + <div className='flex gap-x-2 items-center p-2'> + <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '> + <span className='text-lg font-bold'> + {product.lowestPrice.discountPercentage}% + </span> + </div> + <div + className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`} + > + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={17} + height={10} + /> + <span className='text-white text-lg font-semibold'> + {product.flashSale.tag || 'FLASH SALE'} + </span> + </div> + <div> + <CountDown2 initialTime={product.flashSale.remainingTime} /> + </div> + </div> + </div> + </div> + )} + <Image + src={product.image} + alt={product.name} + className='h-72 object-contain object-center w-full border-b border-gray_r-4' + /> + </div> <div className='p-4'> <div className='flex items-end mb-2'> @@ -294,40 +344,44 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { <Spinner aria-label='Alternate spinner button example' /> <span className='pl-3'>Loading...</span> </Button> - ) : selectedVariant ? activeVariant?.sla?.slaDate != '-' ? ( - <button - type='button' - title={`Masa Persiapan Barang ${activeVariant?.sla?.slaDate}`} - className={`flex gap-x-1 items-center p-2 h-8 rounded-lg w-full ${ - activeVariant?.sla?.slaDate === 'indent' ? 'bg-indigo-900' : 'btn-light' - }`} - > - <div - className={`flex-1 text-sm ${ - activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : '' + ) : selectedVariant ? ( + activeVariant?.sla?.slaDate != '-' ? ( + <button + type='button' + title={`Masa Persiapan Barang ${activeVariant?.sla?.slaDate}`} + className={`flex gap-x-1 items-center p-2 h-8 rounded-lg w-full ${ + activeVariant?.sla?.slaDate === 'indent' ? 'bg-indigo-900' : 'btn-light' }`} > - {activeVariant?.sla?.slaDate} - </div> - <div className='flex-end'> - <svg - aria-hidden='true' - fill='none' - stroke='currentColor' - stroke-width='1.5' - className={`w-7 h-7 text-sm ${ + <div + className={`flex-1 text-sm ${ activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : '' }`} > - <path - d='M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z' - stroke-linecap='round' - stroke-linejoin='round' - ></path> - </svg> - </div> - </button> - ):('-') : ( + {activeVariant?.sla?.slaDate} + </div> + <div className='flex-end'> + <svg + aria-hidden='true' + fill='none' + stroke='currentColor' + stroke-width='1.5' + className={`w-7 h-7 text-sm ${ + activeVariant?.sla?.slaDate === 'indent' ? 'text-white' : '' + }`} + > + <path + d='M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z' + stroke-linecap='round' + stroke-linejoin='round' + ></path> + </svg> + </div> + </button> + ) : ( + '-' + ) + ) : ( '-' )} </span> diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index a8964310..2c849bd6 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -3,8 +3,12 @@ import Link from '@/core/components/elements/Link/Link' import currencyFormat from '@/core/utils/currencyFormat' import { createSlug } from '@/core/utils/slug' import whatsappUrl from '@/core/utils/whatsappUrl' +import ImageNext from 'next/image' +import { useRouter } from 'next/router' const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { + const router = useRouter() + const callForPriceWhatsapp = whatsappUrl('product', { name: product.name, url: createSlug('/shop/product/', product.name, product.id, true) @@ -12,7 +16,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]'> + <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' @@ -22,6 +26,32 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { alt={product?.name} className='w-full object-contain object-center h-36 sm:h-48' /> + {router.pathname != '/' && product?.flashSale?.id > 0 && ( + <div className='absolute bottom-0 w-full grid'> + <div className='absolute bottom-0 w-full h-full'> + <ImageNext src='/images/GAMBAR-BG-FLASH-SALE.jpg' className='h-full' width={1000} height={100} /> + </div> + <div className='relative'> + <div className='flex gap-x-1 items-center p-2 justify-center'> + <div className='bg-yellow-400 rounded-full p-1 h-6 w-19 flex items-center justify-center '> + <span className='text-sm font-bold text-black'> + {product?.lowestPrice.discountPercentage}% + </span> + </div> + <div className='bg-red-600 border border-solid border-yellow-400 p-2 rounded-full h-6 flex w-fit items-center justify-center gap-x-2'> + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={13} + height={5} + /> + <span className='text-white text-[11px] font-semibold'> + {product?.flashSale?.tag != "false" || product?.flashSale?.tag != product?.flashSale?.tag ? product?.flashSale?.tag : 'FLASH SALE'} + </span> + </div> + </div> + </div> + </div> + )} {product.variantTotal > 1 && ( <div className='absolute badge-gray bottom-1.5 left-1.5'> {product.variantTotal} Varian @@ -101,6 +131,16 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { </Link> </div> <div className='w-8/12 p-2'> + {product.flashSale.id > 0 && ( + <div className='bg-red-600 rounded-full mb-1 p-2 pl-3 pr-3 flex w-fit items-center gap-x-1'> + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={15} + height={10} + /> + <span className='text-white text-xs font-semibold'>{product?.flashSale?.tag != "false" || product?.flashSale?.tag != product?.flashSale?.tag ? product?.flashSale?.tag : 'FLASH SALE'}</span> + </div> + )} {product?.manufacture?.name ? ( <Link href={createSlug( diff --git a/src/lib/product/components/ProductSimilar.jsx b/src/lib/product/components/ProductSimilar.jsx index 63a33089..1b82c2e5 100644 --- a/src/lib/product/components/ProductSimilar.jsx +++ b/src/lib/product/components/ProductSimilar.jsx @@ -3,7 +3,8 @@ import useProductSimilar from '../hooks/useProductSimilar' import ProductSlider from './ProductSlider' const ProductSimilar = ({ query }) => { - const { productSimilar } = useProductSimilar({ query }) + const source = "bottom" + const { productSimilar } = useProductSimilar({ query, source }) if (productSimilar.isLoading) { return <PopularProductSkeleton /> diff --git a/src/lib/product/components/ProductSlider.jsx b/src/lib/product/components/ProductSlider.jsx index b511eea5..dedbd6ab 100644 --- a/src/lib/product/components/ProductSlider.jsx +++ b/src/lib/product/components/ProductSlider.jsx @@ -66,7 +66,7 @@ const ProductSlider = ({ products, simpleTitle = false, bannerMode = false }) => </MobileView> <DesktopView> - <Swiper slidesPerView={5.6} spaceBetween={16} {...swiperProps}> + <Swiper slidesPerView={6.7} spaceBetween={16} {...swiperProps}> {swiperContent} </Swiper> </DesktopView> diff --git a/src/lib/product/hooks/useProductSimilar.js b/src/lib/product/hooks/useProductSimilar.js index d16e4c58..712d07ad 100644 --- a/src/lib/product/hooks/useProductSimilar.js +++ b/src/lib/product/hooks/useProductSimilar.js @@ -1,10 +1,9 @@ import productSimilarApi from '../api/productSimilarApi' import { useQuery } from 'react-query' -const useProductSimilar = ({ query }) => { - const fetchProductSimilar = async () => await productSimilarApi({ query }) +const useProductSimilar = ({ query, source }) => { + const fetchProductSimilar = async () => await productSimilarApi({ query, source }) const { data, isLoading } = useQuery(`productSimilar-${query}`, fetchProductSimilar) - return { productSimilar: { data, isLoading } } diff --git a/src/lib/transaction/api/airwayBillApi.js b/src/lib/transaction/api/airwayBillApi.js new file mode 100644 index 00000000..1ef579c7 --- /dev/null +++ b/src/lib/transaction/api/airwayBillApi.js @@ -0,0 +1,13 @@ +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' + +const airwayBillApi = async ({ orderId }) => { + const auth = getAuth() + const dataAirwayBill = await odooApi( + 'GET', + `/api/v1/partner/${auth.partnerId}/sale-order/${orderId}/awb` + ) + return dataAirwayBill +} + +export default airwayBillApi diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index dca85f0c..74f3dbd5 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -12,7 +12,6 @@ import currencyFormat from '@/core/utils/currencyFormat' import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon } from '@heroicons/react/24/outline' import Link from '@/core/components/elements/Link/Link' -import Alert from '@/core/components/elements/Alert/Alert' import checkoutPoApi from '../api/checkoutPoApi' import cancelTransactionApi from '../api/cancelTransactionApi' import MobileView from '@/core/components/views/MobileView' @@ -21,9 +20,13 @@ import Menu from '@/lib/auth/components/Menu' import Image from '@/core/components/elements/Image/Image' import { createSlug } from '@/core/utils/slug' import toTitleCase from '@/core/utils/toTitleCase' +import useAirwayBill from '../hooks/useAirwayBill' const Transaction = ({ id }) => { const { transaction } = useTransaction({ id }) + const { queryAirwayBill } = useAirwayBill({ orderId: id }) + + const [airwayBillPopup, setAirwayBillPopup] = useState(null) const poNumber = useRef(null) const poFile = useRef(null) @@ -173,6 +176,35 @@ const Transaction = ({ id }) => { <Divider /> + <div className='p-4'> + <div className='font-medium'>Pengiriman</div> + <div className='flex flex-col gap-y-3 mt-4'> + {queryAirwayBill.data?.airways?.map((airway) => ( + <button + className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between items-center text-left' + key={airway?.waybillNumber} + onClick={() => setAirwayBillPopup(airway?.waybillNumber)} + > + <div> + <span className='text-sm text-gray_r-11'>No Resi</span> + <p className='mt-1 font-medium'>{airway?.waybillNumber}</p> + </div> + <div className='flex gap-x-2'> + <div className='text-sm text-gray_r-11 badge-green'> + {airway?.deliveryStatus?.status} + </div> + <ChevronRightIcon className='w-5 stroke-2' /> + </div> + </button> + ))} + </div> + {!queryAirwayBill?.data?.airways && ( + <div className='badge-red text-sm px-2'>Belum ada pengiriman</div> + )} + </div> + + <Divider /> + <div className='p-4 flex flex-col gap-y-4'> <DescriptionRow label='Purchase Order'> {transaction.data?.purchaseOrderName || '-'} @@ -229,9 +261,7 @@ const Transaction = ({ id }) => { </Link> ))} {transaction.data?.invoices?.length === 0 && ( - <Alert type='info' className='text-center'> - Belum ada Invoice - </Alert> + <div className='badge-red text-sm px-2'>Belum ada invoice</div> )} </div> </div> @@ -268,20 +298,36 @@ const Transaction = ({ id }) => { <div className='w-3/12 pr-4'> <Menu /> </div> - <div className='w-9/12 p-4 bg-white border border-gray_r-6 rounded'> + <div className='w-9/12 p-4 py-6 bg-white border border-gray_r-6 rounded'> <h1 className='text-title-sm font-semibold mb-6'>Detail Transaksi</h1> <div className='flex items-center gap-x-2 mb-3'> <span className='text-h-sm font-medium'>{transaction?.data?.name}</span> <TransactionStatusBadge status={transaction?.data?.status} /> </div> - <button - type='button' - className='btn-solid-red px-3 py-2' - onClick={() => downloadQuotation(transaction.data)} - > - Download - </button> + <div className='flex gap-x-4'> + <button + type='button' + className='btn-solid-red px-3 py-2 mr-auto' + onClick={() => downloadQuotation(transaction.data)} + > + Download + </button> + {transaction.data?.status == 'draft' && ( + <button className='btn-yellow' onClick={checkout}> + Lanjutkan Transaksi + </button> + )} + {transaction.data?.status != 'draft' && ( + <button + className='btn-light' + disabled={transaction.data?.status != 'waiting'} + onClick={openCancelTransaction} + > + Batalkan Transaksi + </button> + )} + </div> <div className='grid grid-cols-2 gap-x-6 mt-6'> <div className='grid grid-cols-2 gap-y-4'> @@ -313,7 +359,7 @@ const Transaction = ({ id }) => { </div> </div> - <div className='text-h-sm font-semibold mt-6 mb-4'>Informasi Pelanggan</div> + <div className='text-h-sm font-semibold mt-10 mb-4'>Informasi Pelanggan</div> <div className='grid grid-cols-2 gap-x-4'> <div className='border border-gray_r-6 rounded p-3'> <div className='font-medium mb-4'>Detail Pelanggan</div> @@ -321,7 +367,32 @@ const Transaction = ({ id }) => { </div> </div> - <div className='text-h-sm font-semibold mt-6 mb-4'>Rincian Pembelian</div> + <div className='text-h-sm font-semibold mt-10 mb-4'>Pengiriman</div> + <div className='grid grid-cols-3 gap-4'> + {queryAirwayBill.data?.airways?.map((airway) => ( + <button + className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between items-center text-left' + key={airway?.waybillNumber} + onClick={() => setAirwayBillPopup(airway?.waybillNumber)} + > + <div> + <span className='text-sm text-gray_r-11'>No Resi</span> + <p className='mt-1 font-medium'>{airway?.waybillNumber}</p> + </div> + <div className='flex gap-x-2'> + <div className='text-sm text-gray_r-11 badge-green'> + {airway?.deliveryStatus?.status} + </div> + <ChevronRightIcon className='w-5 stroke-2' /> + </div> + </button> + ))} + </div> + {!queryAirwayBill.data?.airways && ( + <div className='badge-red text-sm'>Belum ada pengiriman</div> + )} + + <div className='text-h-sm font-semibold mt-10 mb-4'>Rincian Pembelian</div> <table className='table-data'> <thead> <tr> @@ -401,7 +472,7 @@ const Transaction = ({ id }) => { {currencyFormat(transaction.data?.amountTax)} </div> - <div className='text-right'>Biaya Pengiriman</div> + <div className='text-right whitespace-nowrap'>Biaya Pengiriman</div> <div className='text-right font-medium'> {currencyFormat(transaction.data?.deliveryAmount)} </div> @@ -413,8 +484,8 @@ const Transaction = ({ id }) => { </div> </div> - <div className='text-h-sm font-semibold mt-6 mb-4'>Invoice</div> - <div className='grid grid-cols-4 gap-4'> + <div className='text-h-sm font-semibold mt-10 mb-4'>Invoice</div> + <div className='grid grid-cols-3 gap-4'> {transaction.data?.invoices?.map((invoice, index) => ( <Link href={`/my/invoices/${invoice.id}`} key={index}> <div className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between'> @@ -437,30 +508,63 @@ const Transaction = ({ id }) => { ))} </div> {transaction.data?.invoices?.length === 0 && ( - <Alert type='info' className='text-center'> - Belum ada Invoice - </Alert> + <div className='badge-red text-sm'>Belum ada invoice</div> )} - - <div className='mt-6'> - {transaction.data?.status == 'draft' && ( - <button className='btn-yellow' onClick={checkout}> - Lanjutkan Transaksi - </button> - )} - {transaction.data?.status != 'draft' && ( - <button - className='btn-light' - disabled={transaction.data?.status != 'waiting'} - onClick={openCancelTransaction} - > - Batalkan Transaksi - </button> - )} - </div> </div> </div> </DesktopView> + + {queryAirwayBill.data?.airways?.map((airway) => ( + <BottomPopup + key={airway.waybillNumber} + title='Detail Pengiriman' + active={airwayBillPopup == airway.waybillNumber} + close={() => setAirwayBillPopup(null)} + > + <div className='flex flex-col gap-y-4 my-4'> + <div className='flex justify-between'> + <div className='text-gray_r-11'>No Pengiriman</div> + <div>{airway?.deliveryOrder?.name}</div> + </div> + <div className='flex justify-between'> + <div className='text-gray_r-11'>Kurir</div> + <div>{airway?.deliveryOrder?.carrier}</div> + </div> + <div className='flex justify-between'> + <div className='text-gray_r-11'>No Resi</div> + <div>{airway?.waybillNumber}</div> + </div> + </div> + + <div className='pt-4'> + <div className='font-semibold text-body-1 mb-4'>Status Pengiriman</div> + <ol class='relative border-l border-gray_r-7'> + {airway?.manifests?.map((manifest, index) => ( + <li class='mb-6 ml-4' key={index}> + <div + class={`absolute w-3 h-3 rounded-full mt-1.5 -left-1.5 border ${ + index == 0 ? 'bg-red-600 border-red-600' : 'bg-gray_r-7 border-white' + }`} + /> + <time class='text-sm leading-none text-gray-400'> + {manifest.datetime} + </time> + <p + class={`leading-6 font-medium text-body-2 mt-2 ${ + index == 0 ? 'text-red-600' : 'text-gray_r-11' + }`} + > + {manifest.description} + </p> + </li> + ))} + {(!airway?.manifests || airway?.manifests?.length == 0) && ( + <div className='badge-red text-sm'>Belum ada pengiriman</div> + )} + </ol> + </div> + </BottomPopup> + ))} </> ) ) @@ -536,7 +640,7 @@ const SectionContent = ({ address }) => { const DescriptionRow = ({ children, label }) => ( <div className='grid grid-cols-2'> <span className='text-gray_r-11'>{label}</span> - <span className='text-right break-all leading-6'>{children}</span> + <span className='text-right leading-6'>{children}</span> </div> ) diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index 642881a6..be63effd 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -252,7 +252,7 @@ const Transactions = ({ context = '' }) => { {transactions.data?.saleOrders?.map((saleOrder) => ( <tr key={saleOrder.id}> <td> - <Link href={`${router.pathname}/${saleOrder.id}`}>{saleOrder.name}</Link> + <Link className='whitespace-nowrap' href={`${router.pathname}/${saleOrder.id}`}>{saleOrder.name}</Link> </td> <td>{saleOrder.purchaseOrderName || '-'}</td> <td>{saleOrder.dateOrder || '-'}</td> diff --git a/src/lib/transaction/hooks/useAirwayBill.js b/src/lib/transaction/hooks/useAirwayBill.js new file mode 100644 index 00000000..c711ab77 --- /dev/null +++ b/src/lib/transaction/hooks/useAirwayBill.js @@ -0,0 +1,11 @@ +import { useQuery } from 'react-query' +import airwayBillApi from '../api/airwayBillApi' + +const useAirwayBill = ({ orderId }) => { + const fetchAirwayBill = async () => await airwayBillApi({ orderId }) + const queryAirwayBill = useQuery(`airwayBill-${orderId}`, fetchAirwayBill) + + return { queryAirwayBill } +} + +export default useAirwayBill diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx index 86cbe963..7f23b94b 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/api/shop/search.js b/src/pages/api/shop/search.js index 937c6d4c..d465d94b 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -135,7 +135,8 @@ const productResponseMap = (products, pricelist) => { categories: [], flashSale: { id: product?.flashsale_id_i, - name: product?.product?.flashsale_name_s + name: product?.product?.flashsale_name_s, + tag : product?.flashsale_tag_s || 'FLASH SALE' } } 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> |
