summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorit-fixcomart <it@fixcomart.co.id>2024-12-24 08:51:37 +0700
committerit-fixcomart <it@fixcomart.co.id>2024-12-24 08:51:37 +0700
commit22bf497d3fd524a95982e090f2c8a1be6bde656b (patch)
tree1c394154c78ea81e06b302eeffcb9dfb14497144 /src
parentb2afb5c847c9983b098a3223fbcfdb666e65c8cb (diff)
parentd76f96c44f85e7e0efbd544e6b97bd80920b0039 (diff)
Merge branch 'new-release' into Feature/switch-account
# Conflicts: # src-migrate/modules/register/components/FormBisnis.tsx # src/lib/auth/components/Menu.jsx
Diffstat (limited to 'src')
-rw-r--r--src/components/ui/HeroBanner.jsx13
-rw-r--r--src/components/ui/HeroBannerSecondary.jsx6
-rw-r--r--src/core/components/elements/Footer/BasicFooter.jsx12
-rw-r--r--src/core/components/elements/Navbar/Navbar.jsx6
-rw-r--r--src/core/components/elements/Navbar/NavbarDesktop.jsx99
-rw-r--r--src/core/components/elements/Navbar/NavbarMobile.jsx43
-rw-r--r--src/core/components/elements/Navbar/TopBanner.jsx1
-rw-r--r--src/core/components/elements/Sidebar/Sidebar.jsx2
-rw-r--r--src/core/components/layouts/BasicLayout.jsx4
-rw-r--r--src/lib/auth/components/Menu.jsx8
-rw-r--r--src/lib/brand/components/BrandCard.jsx2
-rw-r--r--src/lib/flashSale/components/FlashSale.jsx2
-rw-r--r--src/lib/form/components/KunjunganSales.jsx215
-rw-r--r--src/lib/form/components/KunjunganService.jsx361
-rw-r--r--src/lib/form/components/Merchant.jsx56
-rw-r--r--src/lib/form/components/RequestForQuotation.jsx215
-rw-r--r--src/lib/form/components/SuratDukungan.jsx33
-rw-r--r--src/lib/home/components/BannerSection.jsx1
-rw-r--r--src/lib/home/components/CategoryDynamic.jsx178
-rw-r--r--src/lib/home/components/CategoryDynamicMobile.jsx2
-rw-r--r--src/lib/home/components/PromotionProgram.jsx59
-rw-r--r--src/lib/home/components/ServiceList.jsx6
-rw-r--r--src/lib/product/components/Product/ProductDesktop.jsx1
-rw-r--r--src/lib/product/components/Product/ProductMobile.jsx1
-rw-r--r--src/lib/product/components/ProductCard.jsx30
-rw-r--r--src/lib/product/components/ProductSlider.jsx2
-rw-r--r--src/lib/transaction/api/checkoutPoApi.js19
-rw-r--r--src/lib/transaction/components/Transaction.jsx132
-rw-r--r--src/lib/treckingAwb/component/Manifest.jsx1
-rw-r--r--src/pages/api/flashsale-header.js36
-rw-r--r--src/pages/api/hero-banner.js2
-rw-r--r--src/pages/api/search-flashsale.js26
-rw-r--r--src/pages/daftar-merchant.jsx (renamed from src/pages/my/daftar-merchant.jsx)0
-rw-r--r--src/pages/index.jsx31
-rw-r--r--src/pages/kunjungan-sales.jsx (renamed from src/pages/my/kunjungan-sales.jsx)0
-rw-r--r--src/pages/kunjungan-service.jsx (renamed from src/pages/my/kunjungan-service.jsx)0
-rw-r--r--src/pages/my/menu.jsx97
-rw-r--r--src/pages/my/pembayaran-tempo.jsx15
-rw-r--r--src/pages/pembayaran-tempo-detail.jsx13
-rw-r--r--src/pages/pembayaran-tempo.jsx14
-rw-r--r--src/pages/request-for-quotation.jsx (renamed from src/pages/my/request-for-quotation.jsx)0
-rw-r--r--src/pages/sitemap/homepage.xml.js33
-rw-r--r--src/pages/surat-dukungan.jsx (renamed from src/pages/my/surat-dukungan.jsx)0
43 files changed, 1152 insertions, 625 deletions
diff --git a/src/components/ui/HeroBanner.jsx b/src/components/ui/HeroBanner.jsx
index 2eea5915..d5ff6247 100644
--- a/src/components/ui/HeroBanner.jsx
+++ b/src/components/ui/HeroBanner.jsx
@@ -7,9 +7,7 @@ import { Swiper, SwiperSlide } from 'swiper/react';
import Image from 'next/image';
import { useEffect, useMemo, useState } from 'react';
-import { useQuery } from 'react-query';
-import { bannerApi } from '@/api/bannerApi';
import Link from '@/core/components/elements/Link/Link';
import DesktopView from '@/core/components/views/DesktopView';
import MobileView from '@/core/components/views/MobileView';
@@ -52,28 +50,21 @@ const HeroBanner = () => {
pagination: { dynamicBullets: false, clickable: true },
};
- const customLoader = ({ src }) => {
- return src; // Loader yang mengembalikan URL gambar asli
- };
-
const BannerComponent = useMemo(() => {
if (!heroBanner) return null;
return heroBanner.map((banner, index) => (
<SwiperSlide key={index}>
- <Link href={banner.url} className='w-full h-auto'>
+ <Link href={banner?.url} aria-label={banner.name}>
<Image
- loader={customLoader}
src={banner.image}
alt={banner.name}
width={1152}
height={768}
- className='w-full h-auto'
- priority={index === 0}
- loading={index === 0 ? 'eager' : 'lazy'}
placeholder='blur'
blurDataURL='/images/indoteknik-placeholder.png'
sizes='(max-width: 768px) 100vw, 50vw'
+ priority={true}
/>
</Link>
</SwiperSlide>
diff --git a/src/components/ui/HeroBannerSecondary.jsx b/src/components/ui/HeroBannerSecondary.jsx
index 6074c9a6..0daf9c61 100644
--- a/src/components/ui/HeroBannerSecondary.jsx
+++ b/src/components/ui/HeroBannerSecondary.jsx
@@ -1,10 +1,8 @@
import Link from '@/core/components/elements/Link/Link';
import { getRandomInt } from '@/utils/getRandomInt';
import Image from 'next/image';
-import { useMemo, useEffect, useState } from 'react';
-import { useQuery } from 'react-query';
+import { useEffect, useMemo, useState } from 'react';
import { HeroBannerSkeleton } from '../skeleton/BannerSkeleton';
-import { bannerApi } from '@/api/bannerApi';
const HeroBannerSecondary = () => {
const [heroBannerSecondary, setHeroBannerSecondary] = useState([]);
@@ -45,7 +43,7 @@ const HeroBannerSecondary = () => {
height={1024}
alt={heroBannerSecondary[randomIndex]?.name}
className='object-cover object-center h-full'
- loading='lazy'
+ loading='eager'
placeholder='blur'
blurDataURL='/images/indoteknik-placeholder.png'
sizes='(max-width: 768px) 100vw, 50vw'
diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx
index 05dc4d8c..6434c928 100644
--- a/src/core/components/elements/Footer/BasicFooter.jsx
+++ b/src/core/components/elements/Footer/BasicFooter.jsx
@@ -224,32 +224,32 @@ const Form = () => (
<div className={headerClassName}>Formulir</div>
<ul className='flex flex-col gap-y-3'>
<li>
- <InternalItemLink href='/my/request-for-quotation'>
+ <InternalItemLink href='/request-for-quotation'>
Request for Quotation
</InternalItemLink>
</li>
<li>
- <InternalItemLink href='/my/kunjungan-sales'>
+ <InternalItemLink href='/kunjungan-sales'>
Kunjungan Sales
</InternalItemLink>
</li>
<li>
- <InternalItemLink href='/my/kunjungan-service'>
+ <InternalItemLink href='/kunjungan-service'>
Kunjungan Service
</InternalItemLink>
</li>
<li>
- <InternalItemLink href='/my/pembayaran-tempo'>
+ <InternalItemLink href='/pembayaran-tempo'>
Pembayaran Tempo
</InternalItemLink>
</li>
<li>
- <InternalItemLink href='/my/surat-dukungan'>
+ <InternalItemLink href='/surat-dukungan'>
Surat Dukungan
</InternalItemLink>
</li>
<li>
- <InternalItemLink href='/my/daftar-merchant'>
+ <InternalItemLink href='/daftar-merchant'>
Daftar Merchant
</InternalItemLink>
</li>
diff --git a/src/core/components/elements/Navbar/Navbar.jsx b/src/core/components/elements/Navbar/Navbar.jsx
index 57904498..59f743a2 100644
--- a/src/core/components/elements/Navbar/Navbar.jsx
+++ b/src/core/components/elements/Navbar/Navbar.jsx
@@ -3,10 +3,12 @@ import dynamic from 'next/dynamic'
const NavbarDesktop = dynamic(() => import('./NavbarDesktop'))
const NavbarMobile = dynamic(() => import('./NavbarMobile'))
-const Navbar = () => {
+const Navbar = ({isMobile} ) => {
+
+ if(isMobile) return <NavbarMobile />
+
return (
<>
- <NavbarMobile />
<NavbarDesktop />
</>
)
diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx
index fa3df5bf..2776c1f3 100644
--- a/src/core/components/elements/Navbar/NavbarDesktop.jsx
+++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx
@@ -12,12 +12,9 @@ import {
MenuButton,
MenuItem,
MenuList,
- useDisclosure
+ useDisclosure,
} from '@chakra-ui/react';
-import {
- ChevronDownIcon,
- HeartIcon
-} from '@heroicons/react/24/outline';
+import { ChevronDownIcon, HeartIcon } from '@heroicons/react/24/outline';
import dynamic from 'next/dynamic';
import { default as Image, default as NextImage } from 'next/image';
import { useRouter } from 'next/router';
@@ -81,21 +78,17 @@ const NavbarDesktop = () => {
};
window.addEventListener('scroll', handleScroll);
+
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
useEffect(() => {
- const handleScroll = () => {
- setIsTop(window.scrollY < 100);
- };
-
- window.addEventListener('scroll', handleScroll);
- return () => {
- window.removeEventListener('scroll', handleScroll);
- };
- }, []);
+ if (auth) {
+ loadCart(auth.id);
+ }
+ }, [auth]);
useEffect(() => {
setPendingTransactions(data);
@@ -115,20 +108,22 @@ const NavbarDesktop = () => {
}, [product, router]);
useEffect(() => {
- const handleCartChange = () => {
- const cart = async () => {
- const listCart = await getCountCart();
- setCartCount(listCart);
- };
- cart();
- };
- handleCartChange();
-
- window.addEventListener('localStorageChange', handleCartChange);
-
- return () => {
- window.removeEventListener('localStorageChange', handleCartChange);
- };
+ // const handleCartChange = () => {
+ // const cart = async () => {
+ // const listCart = await getCountCart();
+ // setCartCount(listCart);
+ // };
+ // cart();
+ // };
+ // handleCartChange();
+
+ setCartCount(cart?.products?.length);
+
+ // window.addEventListener('localStorageChange', handleCartChange);
+
+ // return () => {
+ // window.removeEventListener('localStorageChange', handleCartChange);
+ // };
}, [transactions.data, cart]);
useEffect(() => {
@@ -177,7 +172,7 @@ const NavbarDesktop = () => {
<MenuItem as='a' href='/tentang-kami'>
Tentang Indoteknik
</MenuItem>
- <MenuItem as='a' href='/my/pembayaran-tempo'>
+ <MenuItem as='a' href='/pembayaran-tempo'>
Pembayaran Tempo
</MenuItem>
</MenuList>
@@ -197,12 +192,14 @@ const NavbarDesktop = () => {
<nav className='pt-6 sticky top-0 z-50 bg-white border-b-2 border-danger-500'>
<div className='container mx-auto flex gap-x-6'>
- <Link href='/'>
+ <Link href='/' aria-label='Indoteknik Logo'>
<Image
src={IndoteknikLogo}
alt='Indoteknik Logo'
width={210}
- height={210 / 3}
+ height={70}
+ loading='eager'
+ priority={true}
/>
</Link>
<div className='flex-1 flex items-center'>
@@ -220,6 +217,7 @@ const NavbarDesktop = () => {
target='_blank'
rel='noreferrer'
href='/my/wishlist'
+ aria-label='Wishlist'
className='flex items-center gap-x-2 !text-gray_r-12/80'
>
<HeartIcon className='w-7' />
@@ -227,6 +225,7 @@ const NavbarDesktop = () => {
</Link>
<a
href={whatsappUrl(templateWA, payloadWA, urlPath)}
+ aria-label='Whatsapp'
target='_blank'
rel='noreferrer'
className='flex items-center gap-x-1 !text-gray_r-12/80'
@@ -236,6 +235,7 @@ const NavbarDesktop = () => {
alt='Whatsapp'
width={48}
height={48}
+ loading='eager'
/>
<div>
<div className='font-semibold'>Whatsapp</div>
@@ -268,6 +268,7 @@ const NavbarDesktop = () => {
<div className='w-6/12 flex px-1 divide-x divide-gray_r-6'>
<Link
href='/shop/promo'
+ aria-label='Promo'
className={`${
router.asPath === '/shop/promo' && 'bg-gray_r-3'
} flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition group relative`} // Added relative position
@@ -284,6 +285,7 @@ const NavbarDesktop = () => {
quality={100}
// className={`fixed ${isTop ? 'md:top-[145px] lg:top-[160px] ' : 'lg:top-[85px] top-[80px]'} rounded-3xl md:left-1/4 lg:left-1/4 xl:left-1/4 left-2/3 w-40 h-12 p-2 z-50 transition-all duration-300 animate-pulse`}
className={`inline-block relative -top-8 transition-all duration-300 z-20 animate-pulse`}
+ loading='eager'
/>
</div>
)}
@@ -301,6 +303,7 @@ const NavbarDesktop = () => {
<Link
href='/shop/brands'
+ aria-label='Brand'
className={`${
router.asPath === '/shop/brands' && 'bg-gray_r-3'
} p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition group`}
@@ -313,6 +316,7 @@ const NavbarDesktop = () => {
</Link>
<Link
href='/shop/search?orderBy=stock'
+ aria-label='Ready Stock'
className={`${
router.asPath.includes('/shop/search?orderBy=stock') &&
'bg-gray_r-3'
@@ -326,6 +330,7 @@ const NavbarDesktop = () => {
</Link>
<Link
href='https://blog.indoteknik.com/'
+ aria-label='Blog Indoteknik'
className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition group'
target='_blank'
rel='noreferrer noopener'
@@ -349,12 +354,14 @@ const NavbarDesktop = () => {
<>
<Link
href='/login'
+ aria-label='Login'
className='flex-1 flex justify-center items-center bg-danger-500 !text-gray_r-1 rounded-none rounded-t-xl'
>
Masuk
</Link>
<Link
href='/register'
+ aria-label='Register'
className='flex-1 flex justify-center items-center bg-danger-500 !text-gray_r-1 rounded-none rounded-t-xl'
>
Daftar
@@ -363,7 +370,11 @@ const NavbarDesktop = () => {
)}
{auth && (
<>
- <div href='/' className='navbar-user-dropdown-button'>
+ <div
+ href='/'
+ className='navbar-user-dropdown-button'
+ aria-label='User'
+ >
<span>Halo, {auth?.name}</span>
<div className='ml-auto'>
<ChevronDownIcon className='w-6' />
@@ -388,24 +399,28 @@ const SocialMedias = () => (
target='_blank'
rel='noreferrer'
href='https://www.youtube.com/@indoteknikcom'
+ aria-label='Youtube - Indoteknik.com'
>
<NextImage
src='/images/socials/youtube.webp'
- // alt='Youtube - Indoteknik.com'
+ alt='Ytube'
width={24}
height={24}
+ loading='eager'
/>
</a>
<a
target='_blank'
rel='noreferrer'
href='https://www.tiktok.com/@indoteknikcom'
+ aria-label='TikTok - Indoteknik.com'
>
<NextImage
src='/images/socials/tiktok.png'
- // alt='TikTok - Indoteknik.com'
+ alt='TikTok'
width={24}
height={24}
+ loading='eager'
/>
</a>
{/* <a target='_blank' rel='noreferrer' href={whatsappUrl(null)}>
@@ -420,48 +435,56 @@ const SocialMedias = () => (
target='_blank'
rel='noreferrer'
href='https://www.facebook.com/indoteknikcom'
+ aria-label='Facebook - Indoteknik.com'
>
<NextImage
src='/images/socials/Facebook.png'
- // alt='Facebook - Indoteknik.com'
+ alt='FB'
width={24}
height={24}
+ loading='eager'
/>
</a>
<a
target='_blank'
rel='noreferrer'
href='https://www.instagram.com/indoteknikcom/'
+ aria-label='Instagram - Indoteknik.com'
>
<NextImage
src='/images/socials/Instagram.png'
- // alt='Instagram - Indoteknik.com'
+ alt='IG'
width={24}
height={24}
+ loading='eager'
/>
</a>
<a
target='_blank'
rel='noreferrer'
href='https://www.linkedin.com/company/pt-indoteknik-dotcom-gemilang/'
+ aria-label='Linkedin - Indoteknik.com'
>
<NextImage
src='/images/socials/Linkedin.png'
- // alt='Linkedin - Indoteknik.com'
+ alt='Linkedin'
width={24}
height={24}
+ loading='eager'
/>
</a>
<a
target='_blank'
rel='noreferrer'
href='https://goo.gl/maps/GF8EmDjpQTHZPsJ1A'
+ aria-label='Maps - Indoteknik.com'
>
<NextImage
src='/images/socials/g_maps.png'
- // alt='Maps - Indoteknik.com'
+ alt='Maps'
width={24}
height={24}
+ loading='eager'
/>
</a>
</div>
diff --git a/src/core/components/elements/Navbar/NavbarMobile.jsx b/src/core/components/elements/Navbar/NavbarMobile.jsx
index 90314671..47182a47 100644
--- a/src/core/components/elements/Navbar/NavbarMobile.jsx
+++ b/src/core/components/elements/Navbar/NavbarMobile.jsx
@@ -12,30 +12,44 @@ import { useEffect, useState } from 'react';
import MobileView from '../../views/MobileView';
import Link from '../Link/Link';
import TopBanner from './TopBanner';
+import { useCartStore } from '~/modules/cart/stores/useCartStore';
+import useAuth from '@/core/hooks/useAuth';
const Search = dynamic(() => import('./Search'));
const NavbarMobile = () => {
const { Sidebar, open } = useSidebar();
+ const auth = useAuth();
const [cartCount, setCartCount] = useState(0);
+ const { loadCart, cart, summary, updateCartItem } = useCartStore();
- useEffect(() => {
- const handleCartChange = () => {
- const cart = async () => {
- const listCart = await getCountCart();
- setCartCount(listCart);
- };
- cart();
- };
- handleCartChange();
+ // useEffect(() => {
+ // const handleCartChange = () => {
+ // const cart = async () => {
+ // const listCart = await getCountCart();
+ // setCartCount(listCart);
+ // };
+ // cart();
+ // };
+ // handleCartChange();
+
+ // window.addEventListener('localStorageChange', handleCartChange);
- window.addEventListener('localStorageChange', handleCartChange);
+ // return () => {
+ // window.removeEventListener('localStorageChange', handleCartChange);
+ // };
+ // }, []);
- return () => {
- window.removeEventListener('localStorageChange', handleCartChange);
- };
- }, []);
+ useEffect(() => {
+ if(auth){
+ loadCart(auth?.id);
+ }
+ }, [auth]);
+
+ useEffect(() => {
+ setCartCount(cart?.products?.length);
+ }, [cart]);
return (
<MobileView>
@@ -48,6 +62,7 @@ const NavbarMobile = () => {
alt='Indoteknik Logo'
width={120}
height={40}
+ loading='eager'
/>
</Link>
<div className='flex gap-x-3'>
diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx
index 709495ce..7d44846c 100644
--- a/src/core/components/elements/Navbar/TopBanner.jsx
+++ b/src/core/components/elements/Navbar/TopBanner.jsx
@@ -50,6 +50,7 @@ const TopBanner = ({ onLoad = () => {} }) => {
<Link
href={data?.url}
className='block bg-cover bg-center h-3 md:h-6 lg:h-[36px]'
+ aria-label='panduan pick up barang'
style={{
backgroundImage: `url('${data?.image}')`,
}}
diff --git a/src/core/components/elements/Sidebar/Sidebar.jsx b/src/core/components/elements/Sidebar/Sidebar.jsx
index ddae3e20..08498759 100644
--- a/src/core/components/elements/Sidebar/Sidebar.jsx
+++ b/src/core/components/elements/Sidebar/Sidebar.jsx
@@ -140,7 +140,7 @@ const Sidebar = ({ active, close }) => {
<SidebarLink className={itemClassName} href='/tentang-kami'>
Tentang Indoteknik
</SidebarLink>
- <SidebarLink className={itemClassName} href='/contact-us'>
+ <SidebarLink className={itemClassName} href='/hubungi-kami'>
Hubungi Kami
</SidebarLink>
<button
diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx
index 1b62bf05..2998fa63 100644
--- a/src/core/components/layouts/BasicLayout.jsx
+++ b/src/core/components/layouts/BasicLayout.jsx
@@ -112,7 +112,7 @@ const BasicLayout = ({ children }) => {
onAnimationEnd={() => setHighlight(false)}
/>
)}
- <Navbar />
+ <Navbar isMobile = {isMobile} />
<AnimationLayout>
{children}
<div
@@ -149,6 +149,7 @@ const BasicLayout = ({ children }) => {
className='block sm:hidden'
width={36}
height={36}
+ loading='eager'
/>
<Image
src='/images/socials/WHATSAPP.svg'
@@ -156,6 +157,7 @@ const BasicLayout = ({ children }) => {
className='hidden sm:block'
width={44}
height={44}
+ loading='eager'
/>
</a>
</div>
diff --git a/src/lib/auth/components/Menu.jsx b/src/lib/auth/components/Menu.jsx
index 7b766968..86ceef22 100644
--- a/src/lib/auth/components/Menu.jsx
+++ b/src/lib/auth/components/Menu.jsx
@@ -6,7 +6,7 @@ import useAuth from '@/core/hooks/useAuth';
import switchAccountProgresApi from '@/lib/auth/api/switchAccountProgresApi.js';
import { useState, useEffect } from 'react';
import { InfoIcon } from 'lucide-react';
-
+import { deleteAuth } from '@/core/utils/auth';
const Menu = () => {
const router = useRouter();
const auth = useAuth();
@@ -21,6 +21,12 @@ const Menu = () => {
loadProgres();
}, []);
const routeStartWith = (route) => router.pathname.startsWith(route);
+
+ const logout = async () => {
+ deleteAuth().then(() => {
+ router.push('/login');
+ });
+ };
return (
<div className='grid grid-cols-1 bg-white border border-gray_r-6 rounded py-2 px-4 sticky top-48'>
<div className='flex justify-between py-4'>
diff --git a/src/lib/brand/components/BrandCard.jsx b/src/lib/brand/components/BrandCard.jsx
index ebd41a67..dff61b24 100644
--- a/src/lib/brand/components/BrandCard.jsx
+++ b/src/lib/brand/components/BrandCard.jsx
@@ -11,6 +11,7 @@ const BrandCard = ({ brand }) => {
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={brand.name}
>
{brand.logo && (
<NextImage
@@ -20,6 +21,7 @@ const BrandCard = ({ brand }) => {
height={500}
quality={85}
className='h-full w-[122px] object-contain object-center'
+ loading='eager'
/>
)}
{!brand.logo && (
diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx
index 6d90cad7..f4be279e 100644
--- a/src/lib/flashSale/components/FlashSale.jsx
+++ b/src/lib/flashSale/components/FlashSale.jsx
@@ -47,6 +47,7 @@ const FlashSale = () => {
width={1080}
height={192}
className='w-full rounded mb-4 hidden sm:block'
+ loading='eager'
/>
<Image
src={flashSale.bannerMobile}
@@ -54,6 +55,7 @@ const FlashSale = () => {
width={256}
height={48}
className='w-full rounded mb-4 block sm:hidden'
+ loading='eager'
/>
<FlashSaleProduct
flashSaleId={flashSale.pricelistId}
diff --git a/src/lib/form/components/KunjunganSales.jsx b/src/lib/form/components/KunjunganSales.jsx
index ffa8f135..3779b836 100644
--- a/src/lib/form/components/KunjunganSales.jsx
+++ b/src/lib/form/components/KunjunganSales.jsx
@@ -1,17 +1,18 @@
-import odooApi from '@/core/api/odooApi'
-import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'
-import cityApi from '@/lib/address/api/cityApi'
-import { yupResolver } from '@hookform/resolvers/yup'
-import React, { useEffect, useRef, useState } from 'react'
-import ReCAPTCHA from 'react-google-recaptcha'
-import { Controller, useForm } from 'react-hook-form'
-import { toast } from 'react-hot-toast'
-import * as Yup from 'yup'
-import createLeadApi from '../api/createLeadApi'
-import PageContent from '@/lib/content/components/PageContent'
-
-import useAuth from '@/core/hooks/useAuth'
-import { useRouter } from 'next/router'
+import odooApi from '@/core/api/odooApi';
+import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import { yupResolver } from '@hookform/resolvers/yup';
+import React, { useEffect, useRef, useState } from 'react';
+import ReCAPTCHA from 'react-google-recaptcha';
+import { Controller, useForm } from 'react-hook-form';
+import { toast } from 'react-hot-toast';
+import * as Yup from 'yup';
+import createLeadApi from '../api/createLeadApi';
+import PageContent from '@/lib/content/components/PageContent';
+
+import useAuth from '@/core/hooks/useAuth';
+import { useRouter } from 'next/router';
const KunjunganSales = () => {
const {
@@ -19,44 +20,72 @@ const KunjunganSales = () => {
handleSubmit,
formState: { errors },
control,
- reset
+ reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
- defaultValues
- })
- const [cities, setCities] = useState([])
- const [companyTypes, setCompanyTypes] = useState([])
- const router = useRouter()
+ defaultValues,
+ });
+ const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
+ const [companyTypes, setCompanyTypes] = useState([]);
+ const router = useRouter();
+
+ const auth = useAuth();
- const auth = useAuth()
+ const recaptchaRef = useRef(null);
-
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/kunjungan-sales')}`);
+ }
+
+ useEffect(() => {
+ const loadState = async () => {
+ let dataState = await stateApi();
+ dataState = dataState.map((state) => ({
+ value: state.id,
+ label: state.name,
+ }));
+ setState(dataState);
+ };
+ loadState();
+ }, []);
- const recaptchaRef = useRef(null)
+ const watchState = watch('state');
useEffect(() => {
- if(auth == false) {
- router.push('/login')
+ if (auth == false) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi()
- dataCities = dataCities.map((obj) => ({ value: obj.name, label: obj.name }))
- setCities(dataCities)
- }
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities?.map((obj) => ({
+ value: obj.name,
+ label: obj.name,
+ }));
+ setCities(dataCities);
+ };
const loadCompanyTypes = async () => {
- const dataCompanyTypes = await odooApi('GET', '/api/v1/partner/company_type')
- setCompanyTypes(dataCompanyTypes?.map((obj) => ({ value: obj.name, label: obj.name })))
- }
+ const dataCompanyTypes = await odooApi(
+ 'GET',
+ '/api/v1/partner/company_type'
+ );
+ setCompanyTypes(
+ dataCompanyTypes?.map((obj) => ({ value: obj.name, label: obj.name }))
+ );
+ };
- loadCompanyTypes()
- loadCities()
- }, [auth])
+ loadCompanyTypes();
+ loadCities();
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
- const recaptchaValue = recaptchaRef.current.getValue()
+ const recaptchaValue = recaptchaRef.current.getValue();
if (!recaptchaValue) {
- toast.error('Recaptcha harus diisi')
- return
+ toast.error('Recaptcha harus diisi');
+ return;
}
const data = {
@@ -71,29 +100,37 @@ const KunjunganSales = () => {
`Unit Perusahaan: ${values.companyType}`,
`No. Handphone: ${values.mobile}`,
`Alamat Email: ${values.email}`,
- `Keterangan: ${values.description}`
- ].join('\n')
- }
+ `Keterangan: ${values.description}`,
+ ].join('\n'),
+ };
- const createLead = await createLeadApi({ data })
+ const createLead = await createLeadApi({ data });
if (createLead) {
- toast.success('Berhasil mengirimkan formulir kunjungan sales')
- reset()
- recaptchaRef.current.reset()
+ toast.success('Berhasil mengirimkan formulir kunjungan sales');
+ reset();
+ recaptchaRef.current.reset();
}
+ };
+ if (!auth) {
+ return;
}
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
- <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Kunjungan Sales</h1>
+ <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
+ Kunjungan Sales
+ </h1>
<div className='w-full grid grid-cols-1 md:grid-cols-2 gap-x-2'>
- <form onSubmit={handleSubmit(onSubmitHandler)} className='grid grid-cols-1 gap-y-6'>
+ <form
+ onSubmit={handleSubmit(onSubmitHandler)}
+ className='grid grid-cols-1 gap-y-6'
+ >
<div
className='flex items-center bg-blue-100 border border-blue-300 text-blue-500 font-medium px-4 py-3 rounded leading-6'
role='alert'
>
- Hubungi kami untuk mendapatkan kunjungan sales kami dan dapatkan berbagai kelebihannya
- dengan menjadi pelanggan korporat kami.
+ Hubungi kami untuk mendapatkan kunjungan sales kami dan dapatkan
+ berbagai kelebihannya dengan menjadi pelanggan korporat kami.
</div>
<div>
<label className='form-label mb-2'>Nama Perusahan*</label>
@@ -104,7 +141,9 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.company?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.company?.message}
+ </div>
</div>
<div>
@@ -116,7 +155,9 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.phone?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.phone?.message}
+ </div>
</div>
<div>
@@ -128,7 +169,21 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.address?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.address?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.address?.message}
+ </div>
+ </div>
+
+ <div>
+ <label className='form-label mb-2'>Provinsi*</label>
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => <HookFormSelect {...props} options={state} />}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.stateId?.message}
+ </div>
</div>
<div>
@@ -138,7 +193,9 @@ const KunjunganSales = () => {
control={control}
render={(props) => <HookFormSelect {...props} options={cities} />}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city?.message}
+ </div>
</div>
<div>
@@ -146,9 +203,13 @@ const KunjunganSales = () => {
<Controller
name='companyType'
control={control}
- render={(props) => <HookFormSelect {...props} options={companyTypes} />}
+ render={(props) => (
+ <HookFormSelect {...props} options={companyTypes} />
+ )}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.companyType?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.companyType?.message}
+ </div>
</div>
<div>
@@ -160,7 +221,9 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.mobile?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile?.message}
+ </div>
</div>
<div>
@@ -172,34 +235,49 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.email?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.email?.message}
+ </div>
</div>
<div>
<label className='form-label mb-2'>Keterangan</label>
- <textarea {...register('description')} type='text' className='form-input' />
+ <textarea
+ {...register('description')}
+ type='text'
+ className='form-input'
+ />
</div>
- <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} />
+ <ReCAPTCHA
+ ref={recaptchaRef}
+ sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+ />
- <button type='submit' className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'>
+ <button
+ type='submit'
+ className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'
+ >
Simpan
</button>
</form>
<PageContent path='/kunjungan-sales' />
</div>
</div>
- )
-}
+ );
+};
const validationSchema = Yup.object().shape({
- email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'),
+ email: Yup.string()
+ .email('Format harus seperti contoh@email.com')
+ .required('Harus di-isi'),
company: Yup.string().required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
mobile: Yup.string().required('Harus di-isi'),
address: Yup.string().required('Harus di-isi'),
- city: Yup.string().required('Harus dipilih')
-})
+ city: Yup.string().required('Harus dipilih'),
+ state: Yup.string().required('Harus dipilih'),
+});
const defaultValues = {
email: '',
@@ -208,8 +286,9 @@ const defaultValues = {
mobile: '',
address: '',
city: '',
+ state: '',
companyType: '',
- description: ''
-}
+ description: '',
+};
-export default KunjunganSales
+export default KunjunganSales;
diff --git a/src/lib/form/components/KunjunganService.jsx b/src/lib/form/components/KunjunganService.jsx
index 5720d14e..e3c83f78 100644
--- a/src/lib/form/components/KunjunganService.jsx
+++ b/src/lib/form/components/KunjunganService.jsx
@@ -1,16 +1,17 @@
-import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'
-import cityApi from '@/lib/address/api/cityApi'
-import { yupResolver } from '@hookform/resolvers/yup'
-import React, { useEffect, useRef, useState } from 'react'
-import ReCAPTCHA from 'react-google-recaptcha'
-import { Controller, useForm } from 'react-hook-form'
-import { toast } from 'react-hot-toast'
-import * as Yup from 'yup'
-import createLeadApi from '../api/createLeadApi'
-import PageContent from '@/lib/content/components/PageContent'
-import { useRouter } from 'next/router'
+import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import { yupResolver } from '@hookform/resolvers/yup';
+import React, { useEffect, useRef, useState } from 'react';
+import ReCAPTCHA from 'react-google-recaptcha';
+import { Controller, useForm } from 'react-hook-form';
+import { toast } from 'react-hot-toast';
+import * as Yup from 'yup';
+import createLeadApi from '../api/createLeadApi';
+import PageContent from '@/lib/content/components/PageContent';
+import { useRouter } from 'next/router';
-import useAuth from '@/core/hooks/useAuth'
+import useAuth from '@/core/hooks/useAuth';
const CreateKunjunganService = () => {
const {
@@ -18,37 +19,61 @@ const CreateKunjunganService = () => {
handleSubmit,
formState: { errors },
control,
- reset
+ reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
- defaultValues
- })
- const [cities, setCities] = useState([])
- const [company_unit, setCompany_unit] = useState([])
-
- const router = useRouter()
+ defaultValues,
+ });
+ const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
+ const [company_unit, setCompany_unit] = useState([]);
- const auth = useAuth()
+ const router = useRouter();
- const recaptchaRef = useRef(null)
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/kunjungan-service')}`);
+ }
+
+ const recaptchaRef = useRef(null);
+
+ useEffect(() => {
+ const loadState = async () => {
+ let dataState = await stateApi();
+ dataState = dataState.map((state) => ({
+ value: state.id,
+ label: state.name,
+ }));
+ setState(dataState);
+ };
+ loadState();
+ }, []);
+
+ const watchState = watch('state');
useEffect(() => {
- if(auth == false) {
- router.push('/login')
+ if (auth == false) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi()
- dataCities = dataCities.map((city) => ({ value: city.id, label: city.name }))
- setCities(dataCities)
- }
- loadCities()
- }, [auth])
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities.map((city) => ({
+ value: city.id,
+ label: city.name,
+ }));
+ setCities(dataCities);
+ };
+ loadCities();
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
- const recaptchaValue = recaptchaRef.current.getValue()
+ const recaptchaValue = recaptchaRef.current.getValue();
if (!recaptchaValue) {
- toast.error('Catcha harus diisi')
- return
+ toast.error('Catcha harus diisi');
+ return;
}
const data = {
...values,
@@ -70,164 +95,212 @@ const CreateKunjunganService = () => {
' \r\n Email : ' +
values.email +
' \r\n Keterangan : ' +
- values.description
- }
+ values.description,
+ };
- const create_leads = await createLeadApi({ data })
+ const create_leads = await createLeadApi({ data });
if (create_leads) {
- toast.success('Berhasil menambahkan alamat')
- reset()
- recaptchaRef.current.reset()
+ toast.success('Berhasil menambahkan alamat');
+ reset();
+ recaptchaRef.current.reset();
}
+ };
+ if (!auth) {
+ return;
}
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
- <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Kunjungan Service</h1>
+ <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
+ Kunjungan Service
+ </h1>
<div className='w-full p-4 bg-white border border-gray_r-6 rounded'>
<div
className='flex items-center bg-blue-100 border border-blue-400 text-blue-500 font-bold px-4 py-3 mb-4'
role='alert'
>
<p>
- Tidak punya waktu untuk melakukan service atau perawatan rutin? Silahkan hubungi teknisi
- kami untuk melakukan kunjungan ke tempat Anda di Jabodetabek.
+ Tidak punya waktu untuk melakukan service atau perawatan rutin?
+ Silahkan hubungi teknisi kami untuk melakukan kunjungan ke tempat
+ Anda di Jabodetabek.
</p>
</div>
<div className='w-full grid grid-cols-2 gap-x-2'>
-
- <form onSubmit={handleSubmit(onSubmitHandler)}>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Nama Perusahan *</label>
- <input
- {...register('company')}
- placeholder='PT.Indoteknik'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div>
+ <form onSubmit={handleSubmit(onSubmitHandler)}>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Nama Perusahan *</label>
+ <input
+ {...register('company')}
+ placeholder='PT.Indoteknik'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.company?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>No. Telp *</label>
- <input
- {...register('phone')}
- placeholder='021-XXXX'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>No. Telp *</label>
+ <input
+ {...register('phone')}
+ placeholder='021-XXXX'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.phone?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Alamat*</label>
- <input
- {...register('address')}
- placeholder='jl. Bandengan no.31 '
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.address?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Alamat*</label>
+ <input
+ {...register('address')}
+ placeholder='jl. Bandengan no.31 '
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.address?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
<div>
- <label className='form-label mb-2'>Kota*</label>
+ <label className='form-label mb-2'>Provinsi*</label>
<Controller
- name='city'
+ name='state'
control={control}
- render={(props) => <HookFormSelect {...props} options={cities} />}
+ render={(props) => (
+ <HookFormSelect {...props} options={state} />
+ )}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state?.message}
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Contact Person*</label>
- <input
- {...register('cp')}
- placeholder='Jhone doe'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.cp?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Kota*</label>
+ <Controller
+ name='city'
+ control={control}
+ render={(props) => (
+ <HookFormSelect {...props} options={cities} />
+ )}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>No HP *</label>
- <input
- {...register('mobile')}
- placeholder='628XXXXXXX'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Contact Person*</label>
+ <input
+ {...register('cp')}
+ placeholder='Jhone doe'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.cp?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Alamat Email *</label>
- <input
- {...register('email')}
- placeholder='contoh@email.com'
- type='email'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>No HP *</label>
+ <input
+ {...register('mobile')}
+ placeholder='628XXXXXXX'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>
- Sebutkan: Merek, Tipe, Permasalahan, Service, Perawatan
- </label>
- <textarea {...register('description')} type='text' className='form-input' />
- <div className='text-caption-2 text-danger-500 mt-1'>
- {errors.description?.message}
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Alamat Email *</label>
+ <input
+ {...register('email')}
+ placeholder='contoh@email.com'
+ type='email'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.email?.message}
+ </div>
</div>
</div>
- </div>
- <div className=''>
- <div>
- <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} />
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>
+ Sebutkan: Merek, Tipe, Permasalahan, Service, Perawatan
+ </label>
+ <textarea
+ {...register('description')}
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.description?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <button type='submit' className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'>
- Simpan
- </button>
+ <div className=''>
+ <div>
+ <ReCAPTCHA
+ ref={recaptchaRef}
+ sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+ />
+ </div>
+ </div>
+ <div className=''>
+ <div>
+ <button
+ type='submit'
+ className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'
+ >
+ Simpan
+ </button>
+ </div>
</div>
- </div>
- </form>
- <PageContent path='/kunjungan-service' />
+ </form>
+ <PageContent path='/kunjungan-service' />
</div>
</div>
</div>
- )
-}
+ );
+};
const validationSchema = Yup.object().shape({
company: Yup.string().required('Harus di-isi'),
- email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'),
+ email: Yup.string()
+ .email('Format harus seperti contoh@email.com')
+ .required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
city: Yup.string().required('Harus di-isi'),
+ state: Yup.string().required('Harus dipilih'),
cp: Yup.string().required('Harus di-isi'),
mobile: Yup.string().required('Harus di-isi'),
email: Yup.string().required('Harus di-isi'),
- address: Yup.string().required('Harus di-isi')
-})
+ address: Yup.string().required('Harus di-isi'),
+});
const defaultValues = {
company: '',
email: '',
phone: '',
city: '',
+ state: '',
cp: '',
mobile: '',
email: '',
- address: ''
-}
+ address: '',
+};
-export default CreateKunjunganService
+export default CreateKunjunganService;
diff --git a/src/lib/form/components/Merchant.jsx b/src/lib/form/components/Merchant.jsx
index 85f72bf8..ee7d177d 100644
--- a/src/lib/form/components/Merchant.jsx
+++ b/src/lib/form/components/Merchant.jsx
@@ -1,5 +1,6 @@
import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
import { yupResolver } from '@hookform/resolvers/yup';
import React, { useEffect, useRef, useState } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
@@ -9,8 +10,7 @@ import * as Yup from 'yup';
import createLeadApi from '../api/createLeadApi';
import PageContent from '@/lib/content/components/PageContent';
import { useRouter } from 'next/router';
-import useAuth from '@/core/hooks/useAuth'
-
+import useAuth from '@/core/hooks/useAuth';
const CreateMerchant = () => {
const {
@@ -19,6 +19,8 @@ const CreateMerchant = () => {
formState: { errors },
control,
reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
defaultValues,
@@ -50,27 +52,45 @@ const CreateMerchant = () => {
},
];
const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
const [company_unit, setCompany_unit] = useState(list_unit);
const recaptchaRef = useRef(null);
- const router = useRouter()
+ const router = useRouter();
+
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`);
+ }
- const auth = useAuth()
+ useEffect(() => {
+ const loadState = async () => {
+ let dataState = await stateApi();
+ dataState = dataState.map((state) => ({
+ value: state.id,
+ label: state.name,
+ }));
+ setState(dataState);
+ };
+ loadState();
+ }, []);
+ const watchState = watch('state');
useEffect(() => {
- if(auth == false) {
- router.push('/login')
+ if (auth == false) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi();
- dataCities = dataCities.map((city) => ({
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities?.map((city) => ({
value: city.id,
label: city.name,
}));
setCities(dataCities);
};
loadCities();
- }, [auth]);
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
const recaptchaValue = recaptchaRef.current.getValue();
@@ -111,6 +131,9 @@ const CreateMerchant = () => {
recaptchaRef.current.reset();
}
};
+ if (!auth) {
+ return;
+ }
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
<h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
@@ -172,6 +195,19 @@ const CreateMerchant = () => {
</div>
</div>
</div>
+ <div>
+ <label className='form-label mb-2'>Provinsi*</label>
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => (
+ <HookFormSelect {...props} options={state} />
+ )}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state?.message}
+ </div>
+ </div>
<div className=''>
<div>
<label className='form-label mb-2'>Kota*</label>
@@ -290,6 +326,7 @@ const validationSchema = Yup.object().shape({
.required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
cp: Yup.string().required('Harus di-isi'),
+ state: Yup.string().required('Harus dipilih'),
city: Yup.string().required('Harus di-isi'),
company_unit: Yup.string().required('Harus di-isi'),
address: Yup.string().required('Harus di-isi'),
@@ -300,6 +337,7 @@ const defaultValues = {
company: '',
email: '',
phone: '',
+ state: '',
city: '',
company_unit: '',
cp: '',
diff --git a/src/lib/form/components/RequestForQuotation.jsx b/src/lib/form/components/RequestForQuotation.jsx
index 68b7fa17..170a5c62 100644
--- a/src/lib/form/components/RequestForQuotation.jsx
+++ b/src/lib/form/components/RequestForQuotation.jsx
@@ -1,18 +1,19 @@
-import odooApi from '@/core/api/odooApi'
-import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'
-import cityApi from '@/lib/address/api/cityApi'
-import { yupResolver } from '@hookform/resolvers/yup'
-import React, { useEffect, useRef, useState } from 'react'
-import ReCAPTCHA from 'react-google-recaptcha'
-import { Controller, useForm } from 'react-hook-form'
-import { toast } from 'react-hot-toast'
-import * as Yup from 'yup'
-import createLeadApi from '../api/createLeadApi'
-import getFileBase64 from '@/core/utils/getFileBase64'
-import PageContent from '@/lib/content/components/PageContent'
-import { useRouter } from 'next/router'
-
-import useAuth from '@/core/hooks/useAuth'
+import odooApi from '@/core/api/odooApi';
+import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import { yupResolver } from '@hookform/resolvers/yup';
+import React, { useEffect, useRef, useState } from 'react';
+import ReCAPTCHA from 'react-google-recaptcha';
+import { Controller, useForm } from 'react-hook-form';
+import { toast } from 'react-hot-toast';
+import * as Yup from 'yup';
+import createLeadApi from '../api/createLeadApi';
+import getFileBase64 from '@/core/utils/getFileBase64';
+import PageContent from '@/lib/content/components/PageContent';
+import { useRouter } from 'next/router';
+
+import useAuth from '@/core/hooks/useAuth';
const RequestForQuotation = () => {
const {
@@ -20,47 +21,69 @@ const RequestForQuotation = () => {
handleSubmit,
formState: { errors },
control,
- reset
+ reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
- defaultValues
- })
- const [cities, setCities] = useState([])
+ defaultValues,
+ });
+ const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
- const quotationFileRef = useRef(null)
- const recaptchaRef = useRef(null)
- const router = useRouter()
+ const quotationFileRef = useRef(null);
+ const recaptchaRef = useRef(null);
+ const router = useRouter();
- const auth = useAuth()
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/request-for-quotation')}`);
+ }
+ useEffect(() => {
+ const loadState = async () => {
+ let dataState = await stateApi();
+ dataState = dataState.map((state) => ({
+ value: state.id,
+ label: state.name,
+ }));
+ setState(dataState);
+ };
+ loadState();
+ }, []);
+ const watchState = watch('state');
useEffect(() => {
- if(auth == false) {
- router.push('/login')
+ if (!auth) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi()
- dataCities = dataCities.map((obj) => ({ value: obj.name, label: obj.name }))
- setCities(dataCities)
- }
- loadCities()
- }, [auth])
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities?.map((obj) => ({
+ value: obj.name,
+ label: obj.name,
+ }));
+ setCities(dataCities);
+ };
+ loadCities();
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
- const recaptchaValue = recaptchaRef.current.getValue()
+ const recaptchaValue = recaptchaRef.current.getValue();
if (!recaptchaValue) {
- toast.error('Recaptcha harus diisi')
- return
+ toast.error('Recaptcha harus diisi');
+ return;
}
- const file = quotationFileRef.current.files[0]
- let fileBase64 = null
+ const file = quotationFileRef.current.files[0];
+ let fileBase64 = null;
if (typeof file !== 'undefined') {
if (file.size > 5000000) {
- toast.error('Maksimal ukuran file adalah 5MB')
- return
+ toast.error('Maksimal ukuran file adalah 5MB');
+ return;
}
- fileBase64 = await getFileBase64(file)
+ fileBase64 = await getFileBase64(file);
}
const data = {
@@ -73,33 +96,42 @@ const RequestForQuotation = () => {
`Kota: ${values.city}`,
`No. Handphone: ${values.mobile}`,
`Alamat Email: ${values.email}`,
- `Keterangan: ${values.description}`
- ].join('\n')
- }
+ `Keterangan: ${values.description}`,
+ ].join('\n'),
+ };
- if (fileBase64) data.file_quotation = fileBase64
+ if (fileBase64) data.file_quotation = fileBase64;
- const createLead = await createLeadApi({ data })
+ const createLead = await createLeadApi({ data });
if (createLead) {
- toast.success('Berhasil mengirimkan formulir request for quotation')
- reset()
- recaptchaRef.current.reset()
+ toast.success('Berhasil mengirimkan formulir request for quotation');
+ reset();
+ recaptchaRef.current.reset();
}
+ };
+ if (!auth) {
+ return;
}
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
- <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Request for Quotation</h1>
+ <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
+ Request for Quotation
+ </h1>
<div className='w-full grid grid-cols-1 md:grid-cols-2 gap-x-2'>
- <form onSubmit={handleSubmit(onSubmitHandler)} className='grid grid-cols-1 gap-y-6'>
+ <form
+ onSubmit={handleSubmit(onSubmitHandler)}
+ className='grid grid-cols-1 gap-y-6'
+ >
<div
className='flex items-center bg-blue-100 border border-blue-300 text-blue-500 font-medium px-4 py-3 rounded leading-6'
role='alert'
>
- Halaman untuk pengajuan penawaran harga, lengkapi data di bawah ini dengan jelas untuk
- mempermudah tim support kami melayani kebutuhan Anda. Tim kami akan sesegera mungkin
- untuk membuatkan penawaran harga terbaik, hubungi kami melalui telpon jika ada
- keterlambatan pelayanan.
+ Halaman untuk pengajuan penawaran harga, lengkapi data di bawah ini
+ dengan jelas untuk mempermudah tim support kami melayani kebutuhan
+ Anda. Tim kami akan sesegera mungkin untuk membuatkan penawaran
+ harga terbaik, hubungi kami melalui telpon jika ada keterlambatan
+ pelayanan.
</div>
<div>
<label className='form-label mb-2'>Nama Perusahan*</label>
@@ -110,7 +142,9 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.company?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.company?.message}
+ </div>
</div>
<div>
@@ -122,7 +156,21 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.phone?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.phone?.message}
+ </div>
+ </div>
+
+ <div>
+ <label className='form-label mb-2'>Provinsi*</label>
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => <HookFormSelect {...props} options={state} />}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state?.message}
+ </div>
</div>
<div>
@@ -132,7 +180,9 @@ const RequestForQuotation = () => {
control={control}
render={(props) => <HookFormSelect {...props} options={cities} />}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city?.message}
+ </div>
</div>
<div>
@@ -144,7 +194,9 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.contactPerson?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.contactPerson?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.contactPerson?.message}
+ </div>
</div>
<div>
@@ -156,7 +208,9 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.mobile?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile?.message}
+ </div>
</div>
<div>
@@ -168,39 +222,59 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.email?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.email?.message}
+ </div>
</div>
<div>
<label className='form-label mb-2'>Keterangan</label>
- <textarea {...register('description')} type='text' className='form-input' />
+ <textarea
+ {...register('description')}
+ type='text'
+ className='form-input'
+ />
</div>
<div>
<label className='form-label mb-2'>File Daftar Produk</label>
- <input type="file" name="quotationFile" className='form-input' ref={quotationFileRef} />
+ <input
+ type='file'
+ name='quotationFile'
+ className='form-input'
+ ref={quotationFileRef}
+ />
</div>
- <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} />
+ <ReCAPTCHA
+ ref={recaptchaRef}
+ sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+ />
- <button type='submit' className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'>
+ <button
+ type='submit'
+ className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'
+ >
Simpan
</button>
</form>
<PageContent path='/request-for-quotation' />
</div>
</div>
- )
-}
+ );
+};
const validationSchema = Yup.object().shape({
- email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'),
+ email: Yup.string()
+ .email('Format harus seperti contoh@email.com')
+ .required('Harus di-isi'),
company: Yup.string().required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
mobile: Yup.string().required('Harus di-isi'),
city: Yup.string().required('Harus dipilih'),
+ state: Yup.string().required('Harus dipilih'),
contactPerson: Yup.string().required('Harus dipilih'),
-})
+});
const defaultValues = {
email: '',
@@ -209,7 +283,8 @@ const defaultValues = {
mobile: '',
address: '',
city: '',
- description: ''
-}
+ state: '',
+ description: '',
+};
-export default RequestForQuotation
+export default RequestForQuotation;
diff --git a/src/lib/form/components/SuratDukungan.jsx b/src/lib/form/components/SuratDukungan.jsx
index 31e7ee83..fadb2c57 100644
--- a/src/lib/form/components/SuratDukungan.jsx
+++ b/src/lib/form/components/SuratDukungan.jsx
@@ -1,5 +1,6 @@
import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
import { yupResolver } from '@hookform/resolvers/yup';
import React, { useEffect, useRef, useState } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
@@ -10,7 +11,7 @@ import createLeadsApi from '../api/createLeadApi';
import PageContent from '@/lib/content/components/PageContent';
-import useAuth from '@/core/hooks/useAuth'
+import useAuth from '@/core/hooks/useAuth';
import { useRouter } from 'next/router';
const CreateSuratDukungan = () => {
@@ -20,32 +21,21 @@ const CreateSuratDukungan = () => {
formState: { errors },
control,
reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
defaultValues,
});
- const [cities, setCities] = useState([]);
const [company_unit, setCompany_unit] = useState([]);
const recaptchaRef = useRef(null);
- const router = useRouter()
+ const router = useRouter();
- const auth = useAuth()
-
- useEffect(() => {
- if(auth == false) {
- router.push('/login')
- }
- const loadCities = async () => {
- let dataCities = await cityApi();
- dataCities = dataCities.map((city) => ({
- value: city.id,
- label: city.name,
- }));
- setCities(dataCities);
- };
- loadCities();
- }, [auth]);
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/surat-dukungan')}`);
+ }
const onSubmitHandler = async (values) => {
const recaptchaValue = recaptchaRef.current.getValue();
@@ -85,6 +75,9 @@ const CreateSuratDukungan = () => {
recaptchaRef.current.reset();
}
};
+ if (!auth) {
+ return;
+ }
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
<h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
@@ -241,7 +234,7 @@ const CreateSuratDukungan = () => {
</div>
</form>
</div>
- <PageContent path='/surat-dukungan'/>
+ <PageContent path='/surat-dukungan' />
</div>
</div>
</div>
diff --git a/src/lib/home/components/BannerSection.jsx b/src/lib/home/components/BannerSection.jsx
index 303b5c4b..898f1bf5 100644
--- a/src/lib/home/components/BannerSection.jsx
+++ b/src/lib/home/components/BannerSection.jsx
@@ -51,6 +51,7 @@ const BannerSection = () => {
src={banner.image}
alt={banner.name}
className='h-auto w-full rounded'
+ loading='eager'
/>
</Link>
))}
diff --git a/src/lib/home/components/CategoryDynamic.jsx b/src/lib/home/components/CategoryDynamic.jsx
index cc4f42b7..b6994f60 100644
--- a/src/lib/home/components/CategoryDynamic.jsx
+++ b/src/lib/home/components/CategoryDynamic.jsx
@@ -9,6 +9,7 @@ import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import { Pagination } from 'swiper';
+import { LazyLoadComponent } from 'react-lazy-load-image-component';
const CategoryDynamic = () => {
const [categoryManagement, setCategoryManagement] = useState([]);
@@ -22,12 +23,17 @@ const CategoryDynamic = () => {
if (data) {
setCategoryManagement(data);
}
- setIsLoading(false);
};
fetchCategoryData();
}, []);
+ useEffect(() => {
+ if (categoryManagement?.length > 0) {
+ setIsLoading(false);
+ }
+ }, [categoryManagement]);
+
const swiperBanner = {
modules: [Pagination],
classNames: 'mySwiper',
@@ -44,95 +50,105 @@ const CategoryDynamic = () => {
{categoryManagement &&
categoryManagement.map((category) => (
<Skeleton key={category.id} isLoaded={!isLoading}>
- <div key={category.id}>
- <div className='bagian-judul flex flex-row justify-start items-center gap-3 mb-4 mt-4'>
- <h1 className='font-semibold text-[14px] sm:text-h-lg mr-2'>
- {category.name}
- </h1>
- <Link
- href={createSlug(
- '/shop/category/',
- category?.name,
- category?.category_id
- )}
- className='!text-red-500 font-semibold'
- >
- Lihat Semua
- </Link>
- </div>
+ <LazyLoadComponent key={category.id}>
+ <div key={category.id}>
+ <div className='bagian-judul flex flex-row justify-start items-center gap-3 mb-4 mt-4'>
+ <h1 className='font-semibold text-[14px] sm:text-h-lg mr-2'>
+ {category.name}
+ </h1>
+ <Link
+ href={createSlug(
+ '/shop/category/',
+ category?.name,
+ category?.category_id
+ )}
+ className='!text-red-500 font-semibold'
+ >
+ Lihat Semua
+ </Link>
+ </div>
- <Swiper {...swiperBanner}>
- {category?.categories?.map((subCategory) => (
- <SwiperSlide key={subCategory.id}>
- <div className='border rounded justify-start items-start '>
- <div className='p-3'>
- <div className='flex flex-row border rounded mb-2 justify-start items-center'>
- <NextImage
- src={
- subCategory.image
- ? subCategory.image
- : '/images/noimage.jpeg'
- }
- alt={subCategory.name}
- width={90}
- height={30}
- className='object-fit p-4'
- />
- <div className='bagian-judul flex flex-col justify-center items-start gap-2 ml-2'>
- <h2 className='font-semibold text-lg mr-2'>
- {subCategory?.name}
- </h2>
- <Link
- href={createSlug(
- '/shop/category/',
- subCategory?.name,
- subCategory?.id_level_2
- )}
- className='!text-red-500 font-semibold'
- >
- Lihat Semua
- </Link>
- </div>
- </div>
- <div className='grid grid-cols-2 gap-2 overflow-y-auto max-h-[240px] min-h-[240px] content-start'>
- {subCategory.child_frontend_id_i.map(
- (childCategory) => (
- <div key={childCategory.id} className=''>
+ <Swiper {...swiperBanner}>
+ {category?.categories?.map((subCategory) => (
+ <LazyLoadComponent key={subCategory.id}>
+ <SwiperSlide key={subCategory.id}>
+ <div className='border rounded justify-start items-start '>
+ <div className='p-3'>
+ <div className='flex flex-row border rounded mb-2 justify-start items-center'>
+ <NextImage
+ src={
+ subCategory.image
+ ? subCategory.image
+ : '/images/noimage.jpeg'
+ }
+ alt={subCategory.name}
+ width={90}
+ height={30}
+ className='object-fit p-4'
+ loading='eager'
+ />
+ <div className='bagian-judul flex flex-col justify-center items-start gap-2 ml-2'>
+ <h2 className='font-semibold text-lg mr-2'>
+ {subCategory?.name}
+ </h2>
<Link
href={createSlug(
'/shop/category/',
- childCategory?.name,
- childCategory?.id_level_3
+ subCategory?.name,
+ subCategory?.id_level_2
)}
- className='flex flex-row gap-2 border rounded group hover:border-red-500'
+ className='!text-red-500 font-semibold'
>
- <NextImage
- src={
- childCategory.image
- ? childCategory.image
- : '/images/noimage.jpeg'
- }
- alt={childCategory.name}
- className='p-2 ml-1'
- width={40}
- height={40}
- />
- <div className='bagian-judul flex flex-col justify-center items-center gap-2 break-words line-clamp-2 group-hover:text-red-500'>
- <h3 className='font-semibold line-clamp-2 group-hover:text-red-500 text-sm mr-2'>
- {childCategory.name}
- </h3>
- </div>
+ Lihat Semua
</Link>
</div>
- )
- )}
+ </div>
+ <div className='grid grid-cols-2 gap-2 overflow-y-auto max-h-[240px] min-h-[240px] content-start'>
+ {subCategory.child_frontend_id_i.map(
+ (childCategory) => (
+ <LazyLoadComponent key={childCategory.id}>
+ <div key={childCategory.id} className=''>
+ <Link
+ href={createSlug(
+ '/shop/category/',
+ childCategory?.name,
+ childCategory?.id_level_3
+ )}
+ className='flex flex-row gap-2 border rounded group hover:border-red-500'
+ >
+ <NextImage
+ src={
+ childCategory.image
+ ? childCategory.image
+ : '/images/noimage.jpeg'
+ }
+ alt={childCategory.name}
+ className='p-2 ml-1'
+ width={40}
+ height={40}
+ placeholder='blur'
+ blurDataURL='/icon.jpg'
+ loading='eager'
+ />
+ <div className='bagian-judul flex flex-col justify-center items-center gap-2 break-words line-clamp-2 group-hover:text-red-500'>
+ <h3 className='font-semibold line-clamp-2 group-hover:text-red-500 text-sm mr-2'>
+ {childCategory.name}
+ </h3>
+ </div>
+ </Link>
+ </div>
+ </LazyLoadComponent>
+ )
+ )}
+ </div>
+ </div>
</div>
- </div>
- </div>
- </SwiperSlide>
- ))}
- </Swiper>
- </div>
+ </SwiperSlide>
+ </LazyLoadComponent>
+ ))}
+ </Swiper>
+ </div>
+ </LazyLoadComponent>
</Skeleton>
))}
</div>
diff --git a/src/lib/home/components/CategoryDynamicMobile.jsx b/src/lib/home/components/CategoryDynamicMobile.jsx
index 67ae6f5f..5d9e872c 100644
--- a/src/lib/home/components/CategoryDynamicMobile.jsx
+++ b/src/lib/home/components/CategoryDynamicMobile.jsx
@@ -90,6 +90,7 @@ const CategoryDynamicMobile = () => {
width={30}
height={30}
className=''
+ loading='eager'
/>
<div className='bagian-judul flex flex-col justify-center items-start gap-1 ml-2'>
<h2 className='font-semibold text-[10px] line-clamp-1'>
@@ -123,6 +124,7 @@ const CategoryDynamicMobile = () => {
width={40}
height={40}
className='p-2'
+ loading='eager'
/>
<div className='bagian-judul flex flex-col justify-center items-start gap-1 break-words line-clamp-2 group-hover:text-red-500'>
<h3 className='font-semibold line-clamp-2 group-hover:text-red-500 text-[10px]'>
diff --git a/src/lib/home/components/PromotionProgram.jsx b/src/lib/home/components/PromotionProgram.jsx
index 562fa138..d8bf3edb 100644
--- a/src/lib/home/components/PromotionProgram.jsx
+++ b/src/lib/home/components/PromotionProgram.jsx
@@ -4,56 +4,35 @@ import { bannerApi } from '@/api/bannerApi';
import useDevice from '@/core/hooks/useDevice';
import { Swiper, SwiperSlide } from 'swiper/react';
import BannerPromoSkeleton from '../components/Skeleton/BannerPromoSkeleton';
+
import { useEffect, useState } from 'react';
const { useQuery } = require('react-query');
const BannerSection = () => {
const { isMobile, isDesktop } = useDevice();
const [data, setData] = useState(null);
const [shouldFetch, setShouldFetch] = useState(false);
+ const [ isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
- const res = await fetch(`/api/hero-banner?type=banner-promotion`);
- const { data } = await res.json();
- if (data) {
- setData(data);
+ try {
+ const res = await fetch(`/api/hero-banner?type=banner-promotion`);
+ const { data } = await res.json();
+ if (data) {
+ setData(data);
+ }
+ } catch (e) {
+ console.log(e);
+ } finally {
+ setIsLoading(false);
}
};
fetchData();
}, []);
- // useEffect(() => {
- // const localData = localStorage.getItem('Homepage_promotionProgram');
- // if (localData) {
- // setData(JSON.parse(localData));
- // } else {
- // setShouldFetch(true);
- // }
- // }, []);
-
- // const getPromotionProgram = useQuery(
- // 'promotionProgram',
- // bannerApi({ type: 'banner-promotion' }),
- // {
- // enabled: shouldFetch,
- // onSuccess: (data) => {
- // if (data) {
- // localStorage.setItem(
- // 'Homepage_promotionProgram',
- // JSON.stringify(data)
- // );
- // setData(data);
- // }
- // },
- // }
- // );
-
const promotionProgram = data;
- // if (getPromotionProgram?.isLoading && !data) {
- // return <BannerPromoSkeleton />;
- // }
- if (!data) {
+ if (isLoading) {
return <BannerPromoSkeleton />;
}
@@ -90,7 +69,8 @@ const BannerSection = () => {
quality={85}
src={banner.image}
alt={banner.name}
- className='h-auto w-full rounded hover:scale-105 transition duration-500 ease-in-out'
+ className='rounded hover:scale-105 transition duration-500 ease-in-out'
+ loading='eager'
/>
</Link>
))}
@@ -103,12 +83,13 @@ const BannerSection = () => {
<SwiperSlide key={banner.id}>
<Link key={banner.id} href={banner.url}>
<Image
- width={439}
- height={150}
- quality={85}
+ width={350}
+ height={100}
+ quality={70}
src={banner.image}
alt={banner.name}
- className='h-auto w-full rounded '
+ className='rounded '
+ loading='eager'
/>
</Link>
</SwiperSlide>
diff --git a/src/lib/home/components/ServiceList.jsx b/src/lib/home/components/ServiceList.jsx
index b3cc8fe5..e32e8747 100644
--- a/src/lib/home/components/ServiceList.jsx
+++ b/src/lib/home/components/ServiceList.jsx
@@ -18,6 +18,7 @@ const ServiceList = () => {
src='/images/icon_service/ONE-STOP-SOLUTIONS.svg'
alt=''
className='h-20 w-20 rounded'
+ loading='eager'
/>
</div>
<div className=''>
@@ -43,6 +44,7 @@ const ServiceList = () => {
src='/images/icon_service/WARRANTY.svg'
alt=''
className='h-20 w-20 rounded'
+ loading='eager'
/>
</div>
<div>
@@ -57,7 +59,7 @@ const ServiceList = () => {
</div>
<div className='w-full '>
<Link
- href='/pembayaran-tempo'
+ href='/pembayaran-tempo-detail'
className='border border-gray-200 p-2 flex items-center gap-x-2 rounded-lg'
>
<div className=''>
@@ -68,6 +70,7 @@ const ServiceList = () => {
src='/images/icon_service/DUE-PAYMENT.svg'
alt=''
className='h-20 w-20 rounded'
+ loading='eager'
/>
</div>
<div>
@@ -93,6 +96,7 @@ const ServiceList = () => {
src='/images/icon_service/TAX.svg'
alt=''
className='h-20 w-20 rounded'
+ loading='eager'
/>
</div>
<div>
diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx
index 444ddd8e..19e76a2b 100644
--- a/src/lib/product/components/Product/ProductDesktop.jsx
+++ b/src/lib/product/components/Product/ProductDesktop.jsx
@@ -255,6 +255,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => {
>
<ImageNext
src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
+ alt='Flash Sale'
width={17}
height={10}
/>
diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx
index 113a1e42..4cfd3755 100644
--- a/src/lib/product/components/Product/ProductMobile.jsx
+++ b/src/lib/product/components/Product/ProductMobile.jsx
@@ -219,6 +219,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
>
<ImageNext
src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
+ alt='Flash Sale'
width={17}
height={10}
/>
diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx
index 3e6a6913..a8ed90a4 100644
--- a/src/lib/product/components/ProductCard.jsx
+++ b/src/lib/product/components/ProductCard.jsx
@@ -18,7 +18,9 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
const [discount, setDiscount] = useState(0);
const { isDesktop, isMobile } = useDevice();
let voucherPastiHemat = 0;
- voucherPastiHemat = product?.newVoucherPastiHemat[0];
+ voucherPastiHemat = product?.newVoucherPastiHemat[0]
+ ? product?.newVoucherPastiHemat[0]
+ : product?.newVoucherPastiHemat;
const callForPriceWhatsapp = whatsappUrl('product', {
name: product.name,
@@ -72,7 +74,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
if (variant == 'vertical') {
return (
<div className='rounded shadow-sm border border-gray_r-4 bg-white h-[330px] md:h-[380px]'>
- <Link href={URL.product} className='border-b border-gray_r-4 relative'>
+ <Link href={URL.product} className='border-b border-gray_r-4 relative' aria-label='Produk'>
<div className='relative'>
<Image
src={image}
@@ -88,6 +90,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
className='w-4 h-5 object-contain object-top sm:h-6'
width={50}
height={50}
+ loading='eager'
/>
)}
</div>
@@ -99,6 +102,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
className='w-11 h-6 object-contain object-top ml-1 mr-1 sm:h-6'
width={50}
height={50}
+ loading='eager'
/>
)}
</div>
@@ -113,6 +117,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
className='h-full'
width={1000}
height={100}
+ loading='eager'
/>
</div>
<div className='relative'>
@@ -125,8 +130,10 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
<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'
+ alt='flash sale'
width={13}
height={5}
+ loading='eager'
/>
<span className='text-white text-[9px] md:text-[10px] font-semibold'>
{product?.flashSale?.tag != 'false' ||
@@ -148,26 +155,28 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
<div className='p-2 sm:p-3 pb-3 text-caption-2 sm:text-body-2 leading-5'>
<div className='flex justify-between '>
{product?.manufacture?.name ? (
- <Link href={URL.manufacture} className='mb-1 mt-1 truncate'>
+ <Link href={URL.manufacture} className='mb-1 mt-1 truncate' aria-label={product.manufacture.name}>
{product.manufacture.name}
</Link>
) : (
<div>-</div>
)}
{product?.isInBu && (
- <Link href='/panduan-pick-up-service' className='group'>
+ <Link href='/panduan-pick-up-service' className='group' aria-label='pickup now'>
<Image
src='/images/PICKUP-NOW.png'
className='group-hover:scale-105 transition-transform duration-200'
alt='pickup now'
width={90}
height={12}
+ loading='eager'
/>
</Link>
)}
</div>
<Link
href={URL.product}
+ aria-label={product?.name}
className={`mb-2 !text-gray_r-12 leading-6 block line-clamp-3 h-[64px]`}
title={product?.name}
>
@@ -192,6 +201,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
rel='noopener noreferrer'
target='_blank'
href={callForPriceWhatsapp}
+ aria-label='Call for Inquiry'
>
Call for Inquiry
</a>
@@ -215,6 +225,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
rel='noopener noreferrer'
target='_blank'
href={callForPriceWhatsapp}
+ aria-label='Call for Inquiry'
>
Call for Inquiry
</a>
@@ -249,7 +260,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
return (
<div className='flex bg-white'>
<div className='w-4/12'>
- <Link href={URL.product} className='relative'>
+ <Link href={URL.product} className='relative' aria-label={product?.name}>
<div className='relative'>
<Image
src={image}
@@ -265,6 +276,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
className='w-4 h-5 object-contain object-top sm:h-6'
width={50}
height={50}
+ loading='eager'
/>
)}
</div>
@@ -276,6 +288,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
className='w-11 h-6 object-contain object-top ml-1 sm:h-6'
width={50}
height={50}
+ loading='eager'
/>
)}
</div>
@@ -293,8 +306,10 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
<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'
+ alt='flash sae'
width={15}
height={10}
+ loading='eager'
/>
<span className='text-white text-xs font-semibold'>
{' '}
@@ -306,7 +321,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
)}
{product?.manufacture?.name ? (
<div className='flex justify-between'>
- <Link href={URL.manufacture} className='mb-1'>
+ <Link href={URL.manufacture} className='mb-1' aria-label={product?.manufacture.name}>
{product.manufacture.name}
</Link>
{/* {product?.is_in_bu && (
@@ -322,6 +337,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
)}
<Link
href={URL.product}
+ aria-label={product?.name}
className={`mb-3 !text-gray_r-12 leading-6 line-clamp-3`}
>
{product?.name}
@@ -348,6 +364,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
rel='noopener noreferrer'
target='_blank'
href={callForPriceWhatsapp}
+ aria-label='Call for Inquiry'
>
Call for Inquiry
</a>
@@ -371,6 +388,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
rel='noopener noreferrer'
target='_blank'
href={callForPriceWhatsapp}
+ aria-label='Call for Inquiry'
>
Call for Inquiry
</a>
diff --git a/src/lib/product/components/ProductSlider.jsx b/src/lib/product/components/ProductSlider.jsx
index 54f209cc..91d199a6 100644
--- a/src/lib/product/components/ProductSlider.jsx
+++ b/src/lib/product/components/ProductSlider.jsx
@@ -35,7 +35,7 @@ const ProductSlider = ({ products, simpleTitle = false, bannerMode = false }) =>
<>
{bannerMode && (
<SwiperSlide>
- <Link href={products.banner.url} className='w-full h-full block'></Link>
+ <Link href={products.banner.url} className='w-full h-full block' aria-label={products.banner.name}></Link>
</SwiperSlide>
)}
{products?.products?.map((product, index) => (
diff --git a/src/lib/transaction/api/checkoutPoApi.js b/src/lib/transaction/api/checkoutPoApi.js
index 04421368..3376e773 100644
--- a/src/lib/transaction/api/checkoutPoApi.js
+++ b/src/lib/transaction/api/checkoutPoApi.js
@@ -1,13 +1,14 @@
-import odooApi from '@/core/api/odooApi'
-import { getAuth } from '@/core/utils/auth'
+import odooApi from '@/core/api/odooApi';
+import { getAuth } from '@/core/utils/auth';
-const checkoutPoApi = async ({ id }) => {
- const auth = getAuth()
+const checkoutPoApi = async ({ id, status }) => {
+ const auth = getAuth();
const dataCheckout = await odooApi(
'POST',
- `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout`
- )
- return dataCheckout
-}
+ `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout`,
+ { status }
+ );
+ return dataCheckout;
+};
-export default checkoutPoApi
+export default checkoutPoApi;
diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx
index d001c7f4..f5dc507a 100644
--- a/src/lib/transaction/components/Transaction.jsx
+++ b/src/lib/transaction/components/Transaction.jsx
@@ -38,7 +38,9 @@ import aprpoveApi from '../api/approveApi';
import rejectApi from '../api/rejectApi';
import rejectProductApi from '../api/rejectProductApi';
import { useRouter } from 'next/router';
-
+import { gtagPurchase } from '@/core/utils/googleTag';
+import { deleteItemCart } from '@/core/utils/cart';
+import axios from 'axios';
const Transaction = ({ id }) => {
const router = useRouter();
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -46,9 +48,8 @@ const Transaction = ({ id }) => {
const [reason, setReason] = useState('');
const auth = useAuth();
const { transaction } = useTransaction({ id });
-
const statusApprovalWeb = transaction.data?.approvalStep;
-
+ const [isLoading, setIsLoading] = useState(false);
const { queryAirwayBill } = useAirwayBill({ orderId: id });
const [airwayBillPopup, setAirwayBillPopup] = useState(null);
@@ -84,8 +85,26 @@ const Transaction = ({ id }) => {
};
const [cancelTransaction, setCancelTransaction] = useState(false);
+ const [continueNoPo, setContinueNoPo] = useState(false);
+ const [continueTransaction, setContinueTransaction] = useState(false);
const openCancelTransaction = () => setCancelTransaction(true);
+ const openContinueTransaction = () => {
+ if (auth.partnerTempo) {
+ checkout();
+ } else {
+ if (!transaction.data?.purchaseOrderFile) {
+ setContinueTransaction(true);
+ } else {
+ checkoutNoPO();
+ }
+ }
+ };
+ // const ContinueTransaction = () => {
+ // setContinueNoPo(true);
+ // checkoutNoPO();
+ // };
const closeCancelTransaction = () => setCancelTransaction(false);
+ const closeContinueTransaction = () => setContinueTransaction(false);
const [rejectTransaction, setRejectTransaction] = useState(false);
@@ -101,15 +120,70 @@ const Transaction = ({ id }) => {
}
closeCancelTransaction();
};
-
const checkout = async () => {
if (!transaction.data?.purchaseOrderFile) {
toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan');
return;
}
- await checkoutPoApi({ id });
+ await checkoutPoApi({ id, status: true });
+ toast.success('Berhasil melanjutkan pesanan');
+ transaction.refetch();
+ };
+
+ const checkoutNoPO = async () => {
+ setIsLoading(true);
+ gtagPurchase(
+ transaction.data.products,
+ transaction.data.deliveryAmount,
+ transaction.data.name
+ );
+
+ gtag('event', 'conversion', {
+ send_to: 'AW-954540379/nDymCL3BhaQYENvClMcD',
+ value:
+ transaction.data?.amountTotal +
+ Math.round(parseInt(transaction.data.deliveryAmount * 1.1) / 1000) *
+ 1000,
+ currency: 'IDR',
+ transaction_id: transaction.data.id,
+ });
+
+ for (const product of transaction.data.products)
+ deleteItemCart({ productId: product.id });
+ if (transaction.data?.amountTotal > 0) {
+ const payment = await axios.post(
+ `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${transaction.data.id}`
+ );
+ setIsLoading(false);
+ window.location.href = payment.data.redirectUrl;
+ } else {
+ window.location.href = `${
+ process.env.NEXT_PUBLIC_SELF_HOST
+ }/shop/checkout/success?order_id=${transaction.data.name.replace(
+ /\//g,
+ '-'
+ )}`;
+ }
toast.success('Berhasil melanjutkan pesanan');
transaction.refetch();
+
+ /* const midtrans = async () => {
+ for (const product of products) deleteItemCart({ productId: product.id });
+ if (grandTotal > 0) {
+ const payment = await axios.post(
+ `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${isCheckouted.id}`
+ );
+ setIsLoading(false);
+ window.location.href = payment.data.redirectUrl;
+ } else {
+ window.location.href = `${
+ process.env.NEXT_PUBLIC_SELF_HOST
+ }/shop/checkout/success?order_id=${isCheckouted.name.replace(
+ /\//g,
+ '-'
+ )}`;
+ }
+ };*/
};
const handleApproval = async () => {
@@ -208,6 +282,32 @@ const Transaction = ({ id }) => {
transaction.data?.name && (
<>
<BottomPopup
+ active={continueTransaction}
+ close={closeContinueTransaction}
+ title='Lanjutkan Transaksi'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Apakah anda yakin melanjutkan tanpa upload PO?{' '}
+ <span className='underline'>{transaction.data?.name}</span>?
+ </div>
+ <div className='flex justify-end mt-6 gap-x-4'>
+ <button
+ className='btn-solid-red w-full md:w-fit'
+ type='button'
+ onClick={checkoutNoPO}
+ >
+ Ya, Lanjutkan
+ </button>
+ <button
+ className='btn-light w-full md:w-fit'
+ type='button'
+ onClick={closeContinueTransaction}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+ <BottomPopup
active={cancelTransaction}
close={closeCancelTransaction}
title='Batalkan Transaksi'
@@ -469,7 +569,10 @@ const Transaction = ({ id }) => {
)}
{transaction.data?.status == 'draft' &&
!auth?.feature?.soApproval && (
- <button className='btn-yellow w-full mt-4' onClick={checkout}>
+ <button
+ className='btn-yellow w-full mt-4'
+ onClick={openContinueTransaction}
+ >
Lanjutkan Transaksi
</button>
)}
@@ -563,7 +666,10 @@ const Transaction = ({ id }) => {
)}
{transaction.data?.status == 'draft' &&
!auth?.feature.soApproval && (
- <button className='btn-yellow' onClick={checkout}>
+ <button
+ className='btn-yellow'
+ onClick={openContinueTransaction}
+ >
Lanjutkan Transaksi
</button>
)}
@@ -778,10 +884,14 @@ const Transaction = ({ id }) => {
? `| ${product?.attributes.join(', ')}`
: ''}
</div>
- <div className='text-[10px] text-red-500 italic mt-2'>
- {product.availableQuantity} barang ini bisa di
- pickup maksimal pukul 16.00
- </div>
+ {product.soQty && product.reservedStockQty && (
+ <div className='text-[10px] text-red-500 italic mt-2'>
+ {product.soQty !== product.reservedStockQty
+ ? 'Barang sedang disiapkan'
+ : `${product.reservedStockQty} barang bisa di
+ kirim/pickup`}
+ </div>
+ )}
</div>
</td>
{/* <td>
diff --git a/src/lib/treckingAwb/component/Manifest.jsx b/src/lib/treckingAwb/component/Manifest.jsx
index 02d0bc7a..87e01e38 100644
--- a/src/lib/treckingAwb/component/Manifest.jsx
+++ b/src/lib/treckingAwb/component/Manifest.jsx
@@ -10,7 +10,6 @@ import { list } from 'postcss';
const Manifest = ({ idAWB, closePopup }) => {
const [manifests, setManifests] = useState(null);
const [isLoading, setIsLoading] = useState(false);
- console.log('manifests', manifests);
const formatCustomDate = (date) => {
const months = [
'Jan',
diff --git a/src/pages/api/flashsale-header.js b/src/pages/api/flashsale-header.js
index 31f8efdd..578801ae 100644
--- a/src/pages/api/flashsale-header.js
+++ b/src/pages/api/flashsale-header.js
@@ -15,23 +15,41 @@ export default async function handler(req, res) {
try {
await connectRedis();
const cacheKey = `flashsale_header`;
- // await client.del(cacheKey);
+
+ // Cek data yang ada di Redis
let cachedData = await client.get(cacheKey);
if (cachedData) {
const data = JSON.parse(cachedData);
+
+ // Periksa apakah data adalah array dan panjangnya 0
+ if (!data || (Array.isArray(data) && data.length === 0)) {
+ await client.del(cacheKey); // Hapus kunci jika data kosong
+ return res.status(200).json({ data: [] });
+ }
+ // Periksa apakah end_date lebih besar dari waktu saat ini
+ const currentTime = new Date();
+ if (data[0].endDate && new Date(data[0].endDate) < currentTime) {
+ await client.del(cacheKey); // Hapus kunci jika end_date lebih kecil dari waktu saat ini
+ return res.status(200).json({ data: [] });
+ }
return res.status(200).json({ data });
} else {
const flashSale = await odooApi('GET', `/api/v1/flashsale/header`);
+ if (flashSale.length === 0) {
+ return res.status(200).json({ data: [] });
+ } else {
+ await client.set(
+ cacheKey,
+ JSON.stringify(flashSale),
+ 'EX',
+ flashSale.duration
+ );
- await client.set(
- cacheKey,
- JSON.stringify(flashSale),
- 'EX',
- flashSale.duration
- );
- cachedData = await client.get(cacheKey);
- return res.status(200).json({ data: cachedData });
+ cachedData = await client.get(cacheKey);
+ const data = JSON.parse(cachedData);
+ return res.status(200).json({ data });
+ }
}
} catch (error) {
console.error('Error interacting with Redis or fetching data:', error);
diff --git a/src/pages/api/hero-banner.js b/src/pages/api/hero-banner.js
index 7a348cfa..b7f3c1c5 100644
--- a/src/pages/api/hero-banner.js
+++ b/src/pages/api/hero-banner.js
@@ -28,6 +28,8 @@ export default async function handler(req, res) {
`/api/v1/banner?type=${type}`
);
+ if(!dataBannerSections) return res.status(200).json({ data: [] });
+
// Simpan hasil fetch ke Redis dengan masa kadaluarsa 3 hari (259200 detik)
await client.set(
cacheKey,
diff --git a/src/pages/api/search-flashsale.js b/src/pages/api/search-flashsale.js
index d9e56c83..c00f6c64 100644
--- a/src/pages/api/search-flashsale.js
+++ b/src/pages/api/search-flashsale.js
@@ -23,20 +23,28 @@ export default async function handler(req, res) {
if (cachedData) {
const data = JSON.parse(cachedData);
+ // Periksa apakah data adalah array dan panjangnya 0
+ if (!data || (Array.isArray(data) && data.length === 0)) {
+ await client.del(cacheKey); // Hapus kunci jika data kosong
+ return res.status(200).json({ data: [] });
+ }
return res.status(200).json({ data });
} else {
const dataProductSearch = await axios(
`${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?${query}&operation=${operation}]`
);
-
- await client.set(
- cacheKey,
- JSON.stringify(dataProductSearch.data),
- 'EX',
- duration
- );
- cachedData = await client.get(cacheKey);
- return res.status(200).json({ data: cachedData });
+ if (dataProductSearch.data.length === 0) {
+ return res.status(200).json({ data: [] });
+ } else {
+ await client.set(
+ cacheKey,
+ JSON.stringify(dataProductSearch.data),
+ 'EX',
+ duration
+ );
+ cachedData = await client.get(cacheKey);
+ return res.status(200).json({ data: cachedData });
+ }
}
} catch (error) {
console.error('Error interacting with Redis or fetching data:', error);
diff --git a/src/pages/my/daftar-merchant.jsx b/src/pages/daftar-merchant.jsx
index e1fa9bcb..e1fa9bcb 100644
--- a/src/pages/my/daftar-merchant.jsx
+++ b/src/pages/daftar-merchant.jsx
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index 2ec1231a..fce2202a 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -1,22 +1,26 @@
import { HeroBannerSkeleton } from '@/components/skeleton/BannerSkeleton';
import { PopularProductSkeleton } from '@/components/skeleton/PopularProductSkeleton';
-import odooApi from '@/core/api/odooApi';
import Seo from '@/core/components/Seo';
import DelayRender from '@/core/components/elements/DelayRender/DelayRender';
import DesktopView from '@/core/components/views/DesktopView';
import MobileView from '@/core/components/views/MobileView';
-import { FlashSaleSkeleton } from '@/lib/flashSale/skeleton/FlashSaleSkeleton';
-import BannerPromoSkeleton from '@/lib/home/components/Skeleton/BannerPromoSkeleton';
import PreferredBrandSkeleton from '@/lib/home/components/Skeleton/PreferredBrandSkeleton';
import dynamic from 'next/dynamic';
-import { useEffect, useRef, useState } from 'react';
+import { useRef } from 'react';
import { getAuth } from '~/libs/auth';
-import PagePopupIformation from '~/modules/popup-information'; // need change to dynamic and ssr : false
-import CategoryPilihan from '../lib/home/components/CategoryPilihan';
-// import { getAuth } from '~/libs/auth';
const BasicLayout = dynamic(() =>
- import('@/core/components/layouts/BasicLayout'),{ssr: false}
+ import('@/core/components/layouts/BasicLayout')
+);
+
+const PagePopupIformation = dynamic(() =>
+ import('~/modules/popup-information'), {
+ ssr: false
+ }
+);
+
+const CategoryPilihan = dynamic(() =>
+ import('../lib/home/components/CategoryPilihan')
);
const HeroBanner = dynamic(() => import('@/components/ui/HeroBanner'), {
loading: () => <HeroBannerSkeleton />,
@@ -40,24 +44,17 @@ const PreferredBrand = dynamic(
const FlashSale = dynamic(
() => import('@/lib/flashSale/components/FlashSale'),
- {
- loading: () => <FlashSaleSkeleton />,
- }
);
const ProgramPromotion = dynamic(
- () => import('@/lib/home/components/PromotionProgram'),
- {
- loading: () => <BannerPromoSkeleton />,
- }
+ () => import('@/lib/home/components/PromotionProgram')
);
const BannerSection = dynamic(() =>
import('@/lib/home/components/BannerSection')
);
const CategoryHomeId = dynamic(
- () => import('@/lib/home/components/CategoryHomeId'),
- { ssr: false }
+ () => import('@/lib/home/components/CategoryHomeId')
);
const CategoryDynamic = dynamic(() =>
diff --git a/src/pages/my/kunjungan-sales.jsx b/src/pages/kunjungan-sales.jsx
index 052991d9..052991d9 100644
--- a/src/pages/my/kunjungan-sales.jsx
+++ b/src/pages/kunjungan-sales.jsx
diff --git a/src/pages/my/kunjungan-service.jsx b/src/pages/kunjungan-service.jsx
index 37de5a0b..37de5a0b 100644
--- a/src/pages/my/kunjungan-service.jsx
+++ b/src/pages/kunjungan-service.jsx
diff --git a/src/pages/my/menu.jsx b/src/pages/my/menu.jsx
index a0ce223e..1b35d6ba 100644
--- a/src/pages/my/menu.jsx
+++ b/src/pages/my/menu.jsx
@@ -1,24 +1,24 @@
-import Divider from '@/core/components/elements/Divider/Divider'
-import Link from '@/core/components/elements/Link/Link'
-import AppLayout from '@/core/components/layouts/AppLayout'
-import useAuth from '@/core/hooks/useAuth'
-import { deleteAuth } from '@/core/utils/auth'
-import IsAuth from '@/lib/auth/components/IsAuth'
-import { ChevronRightIcon, UserIcon } from '@heroicons/react/24/solid'
-import { signOut, useSession } from 'next-auth/react'
-import { useRouter } from 'next/router'
-import ImageNext from 'next/image'
-
+import Divider from '@/core/components/elements/Divider/Divider';
+import Link from '@/core/components/elements/Link/Link';
+import AppLayout from '@/core/components/layouts/AppLayout';
+import useAuth from '@/core/hooks/useAuth';
+import { deleteAuth } from '@/core/utils/auth';
+import IsAuth from '@/lib/auth/components/IsAuth';
+import { ChevronRightIcon, UserIcon } from '@heroicons/react/24/solid';
+import { signOut, useSession } from 'next-auth/react';
+import { useRouter } from 'next/router';
+import ImageNext from 'next/image';
+import whatsappUrl from '@/core/utils/whatsappUrl';
export default function Menu() {
- const auth = useAuth()
- const router = useRouter()
- const { data: session } = useSession()
+ const auth = useAuth();
+ const router = useRouter();
+ const { data: session } = useSession();
const logout = () => {
deleteAuth().then(() => {
- router.push('/login')
- })
- }
+ router.push('/login');
+ });
+ };
return (
<IsAuth>
@@ -29,8 +29,12 @@ export default function Menu() {
</div>
<div className='ml-4'>
<div className='font-semibold text-gray_r-12'>{auth?.name}</div>
- {auth?.company && <div className='badge-solid-red mt-1'>Akun Bisnis</div>}
- {!auth?.company && <div className='badge-gray mt-1'>Akun Individu</div>}
+ {auth?.company && (
+ <div className='badge-solid-red mt-1'>Akun Bisnis</div>
+ )}
+ {!auth?.company && (
+ <div className='badge-gray mt-1'>Akun Individu</div>
+ )}
</div>
<div className='ml-auto !text-gray_r-12'>
<ChevronRightIcon className='w-6' />
@@ -47,32 +51,52 @@ export default function Menu() {
<LinkItem href='/my/quotations'>
{' '}
<div className='flex gap-x-3 items-center'>
- <ImageNext src='/images/icon/icon_daftar_quotation.svg' width={18} height={20} />
+ <ImageNext
+ src='/images/icon/icon_daftar_quotation.svg'
+ width={18}
+ height={20}
+ />
<p>Daftar Quotation</p>
</div>
</LinkItem>
<LinkItem href='/my/transactions'>
<div className='flex gap-x-3 items-center'>
- <ImageNext src='/images/icon/icon_daftar_transaksi.svg' width={18} height={20} />
+ <ImageNext
+ src='/images/icon/icon_daftar_transaksi.svg'
+ width={18}
+ height={20}
+ />
<p>Daftar Transaksi</p>
</div>
</LinkItem>
<LinkItem href='/my/shipments'>
<div className='flex gap-x-3 items-center'>
- <ImageNext src='/images/icon/icon_pengiriman.svg' width={18} height={20} />
+ <ImageNext
+ src='/images/icon/icon_pengiriman.svg'
+ width={18}
+ height={20}
+ />
<p>Daftar Pengiriman</p>
</div>
</LinkItem>
<LinkItem href='/my/invoices'>
{' '}
<div className='flex gap-x-3 items-center'>
- <ImageNext src='/images/icon/icon_invoice.svg' width={18} height={20} />
+ <ImageNext
+ src='/images/icon/icon_invoice.svg'
+ width={18}
+ height={20}
+ />
<p>Invoice & Faktur Pajak</p>
</div>
</LinkItem>
<LinkItem href='/my/wishlist'>
<div className='flex gap-x-3 items-center'>
- <ImageNext src='/images/icon/icon_wishlist.svg' width={18} height={20} />
+ <ImageNext
+ src='/images/icon/icon_wishlist.svg'
+ width={18}
+ height={20}
+ />
<p>Wishlist</p>
</div>
</LinkItem>
@@ -83,10 +107,14 @@ export default function Menu() {
<MenuHeader>Pusat Bantuan</MenuHeader>
<div className='divide-y divide-gray_r-6 border-y border-gray_r-6 mt-4'>
- <LinkItem href='/'>
+ <LinkItem href={whatsappUrl('', '', '')}>
{' '}
<div className='flex gap-x-3 items-center'>
- <ImageNext src='/images/icon/icon_layanan_pelanggan.svg' width={18} height={20} />
+ <ImageNext
+ src='/images/icon/icon_layanan_pelanggan.svg'
+ width={18}
+ height={20}
+ />
<p>Layanan Pelanggan</p>
</div>
</LinkItem>
@@ -99,7 +127,11 @@ export default function Menu() {
<div className='divide-y divide-gray_r-6 border-y border-gray_r-6 mt-4'>
<LinkItem href='/my/address'>
<div className='flex gap-x-3 items-center'>
- <ImageNext src='/images/icon/icon_daftar_alamat.svg' width={18} height={20} />
+ <ImageNext
+ src='/images/icon/icon_daftar_alamat.svg'
+ width={18}
+ height={20}
+ />
<p>Daftar Alamat</p>
</div>
</LinkItem>
@@ -112,20 +144,23 @@ export default function Menu() {
</div>
</AppLayout>
</IsAuth>
- )
+ );
}
const MenuHeader = ({ children, ...props }) => (
<div {...props} className='font-medium px-4 flex'>
{children}
</div>
-)
+);
const LinkItem = ({ children, ...props }) => (
- <Link {...props} className='!text-gray_r-12/70 !font-normal p-4 flex items-center'>
+ <Link
+ {...props}
+ className='!text-gray_r-12/70 !font-normal p-4 flex items-center'
+ >
{children}
<div className='ml-auto !text-gray_r-11'>
<ChevronRightIcon className='w-5' />
</div>
</Link>
-)
+);
diff --git a/src/pages/my/pembayaran-tempo.jsx b/src/pages/my/pembayaran-tempo.jsx
deleted file mode 100644
index 8947bdd9..00000000
--- a/src/pages/my/pembayaran-tempo.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import Seo from '@/core/components/Seo'
-import BasicLayout from '@/core/components/layouts/BasicLayout'
-import PembayaranTempo from '@/lib/form/components/PembayaranTempo'
-
-export default function pembayaranTempo() {
- return (
- <>
- <Seo title='Pembayaran Tempo - Indoteknik.com' />
-
- <BasicLayout>
- <PembayaranTempo />
- </BasicLayout>
- </>
- )
-}
diff --git a/src/pages/pembayaran-tempo-detail.jsx b/src/pages/pembayaran-tempo-detail.jsx
new file mode 100644
index 00000000..363e3099
--- /dev/null
+++ b/src/pages/pembayaran-tempo-detail.jsx
@@ -0,0 +1,13 @@
+import Seo from '@/core/components/Seo'
+import BasicLayout from '@/core/components/layouts/BasicLayout'
+import IframeContent from '@/lib/iframe/components/IframeContent'
+
+export default function PembnayaranTempo() {
+ return (
+ <BasicLayout>
+ <Seo title='Pambayaran Tempo - Indoteknik.com' />
+
+ <IframeContent url={`${process.env.NEXT_PUBLIC_ODOO_HOST}/content?url=pembayaran-tempo`} />
+ </BasicLayout>
+ )
+}
diff --git a/src/pages/pembayaran-tempo.jsx b/src/pages/pembayaran-tempo.jsx
index 363e3099..8947bdd9 100644
--- a/src/pages/pembayaran-tempo.jsx
+++ b/src/pages/pembayaran-tempo.jsx
@@ -1,13 +1,15 @@
import Seo from '@/core/components/Seo'
import BasicLayout from '@/core/components/layouts/BasicLayout'
-import IframeContent from '@/lib/iframe/components/IframeContent'
+import PembayaranTempo from '@/lib/form/components/PembayaranTempo'
-export default function PembnayaranTempo() {
+export default function pembayaranTempo() {
return (
- <BasicLayout>
- <Seo title='Pambayaran Tempo - Indoteknik.com' />
+ <>
+ <Seo title='Pembayaran Tempo - Indoteknik.com' />
- <IframeContent url={`${process.env.NEXT_PUBLIC_ODOO_HOST}/content?url=pembayaran-tempo`} />
- </BasicLayout>
+ <BasicLayout>
+ <PembayaranTempo />
+ </BasicLayout>
+ </>
)
}
diff --git a/src/pages/my/request-for-quotation.jsx b/src/pages/request-for-quotation.jsx
index 40fda009..40fda009 100644
--- a/src/pages/my/request-for-quotation.jsx
+++ b/src/pages/request-for-quotation.jsx
diff --git a/src/pages/sitemap/homepage.xml.js b/src/pages/sitemap/homepage.xml.js
new file mode 100644
index 00000000..fa622d5c
--- /dev/null
+++ b/src/pages/sitemap/homepage.xml.js
@@ -0,0 +1,33 @@
+import { create } from 'xmlbuilder';
+
+export async function getServerSideProps({ res }) {
+ const links = [
+ { label: 'Hubungi Kami', url: 'https://indoteknik.com/hubungi-kami' },
+ { label: 'Tentang Kami', url: 'https://indoteknik.com/tentang-kami' },
+ { label: 'Karir', url: 'https://indoteknik.com/karir' },
+ { label: 'Daftar Tempo', url: 'https://indoteknik.com/pembayaran-tempo' },
+ ];
+ const sitemap = create('urlset', { encoding: 'utf-8' }).att(
+ 'xmlns',
+ 'http://www.sitemaps.org/schemas/sitemap/0.9'
+ );
+
+ const date = new Date();
+ links.forEach((link) => {
+ const url = sitemap.ele('url');
+ url.ele('loc', link.url);
+ url.ele('lastmod', date.toISOString().slice(0, 10));
+ url.ele('changefreq', 'daily');
+ url.ele('priority', '1.0');
+ });
+
+ res.setHeader('Content-Type', 'text/xml');
+ res.write(sitemap.end());
+ res.end();
+
+ return { props: {} };
+}
+
+export default function SitemapProducts() {
+ return null;
+}
diff --git a/src/pages/my/surat-dukungan.jsx b/src/pages/surat-dukungan.jsx
index 8058f34d..8058f34d 100644
--- a/src/pages/my/surat-dukungan.jsx
+++ b/src/pages/surat-dukungan.jsx