diff options
Diffstat (limited to 'src')
37 files changed, 815 insertions, 440 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/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/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 d72fe1f1..b6994f60 100644 --- a/src/lib/home/components/CategoryDynamic.jsx +++ b/src/lib/home/components/CategoryDynamic.jsx @@ -85,6 +85,7 @@ const CategoryDynamic = () => { 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'> @@ -127,6 +128,7 @@ const CategoryDynamic = () => { 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'> 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 fc23bf78..d8bf3edb 100644 --- a/src/lib/home/components/PromotionProgram.jsx +++ b/src/lib/home/components/PromotionProgram.jsx @@ -69,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> ))} @@ -82,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 16e20703..a8ed90a4 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -21,7 +21,6 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { voucherPastiHemat = product?.newVoucherPastiHemat[0] ? product?.newVoucherPastiHemat[0] : product?.newVoucherPastiHemat; - console.log('voucherPastiHemat', voucherPastiHemat); const callForPriceWhatsapp = whatsappUrl('product', { name: product.name, @@ -75,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} @@ -91,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> @@ -102,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> @@ -116,6 +117,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { className='h-full' width={1000} height={100} + loading='eager' /> </div> <div className='relative'> @@ -128,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' || @@ -151,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} > @@ -195,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> @@ -218,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> @@ -252,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} @@ -268,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> @@ -279,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> @@ -296,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'> {' '} @@ -309,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 && ( @@ -325,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} @@ -351,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> @@ -374,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/pages/api/flashsale-header.js b/src/pages/api/flashsale-header.js index 21cb9c9d..578801ae 100644 --- a/src/pages/api/flashsale-header.js +++ b/src/pages/api/flashsale-header.js @@ -20,13 +20,19 @@ export default async function handler(req, res) { let cachedData = await client.get(cacheKey); if (cachedData) { - const data = JSON.parse(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`); @@ -37,11 +43,11 @@ export default async function handler(req, res) { cacheKey, JSON.stringify(flashSale), 'EX', - flashSale.duration + flashSale.duration ); cachedData = await client.get(cacheKey); - const data = JSON.parse(cachedData); + const data = JSON.parse(cachedData); return res.status(200).json({ data }); } } 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 e685b382..fce2202a 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -1,23 +1,27 @@ 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') ); + +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 />, }); 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/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 |
