diff options
| author | HATEC\SPVDEV001 <tri.susilo@altama.co.id> | 2023-05-08 16:44:09 +0700 |
|---|---|---|
| committer | HATEC\SPVDEV001 <tri.susilo@altama.co.id> | 2023-05-08 16:44:09 +0700 |
| commit | 486f85a45fc7e0669576f59824a31be472ed25bb (patch) | |
| tree | 0268afa8efe48746e040611ba41ad2cafda7ad08 /src/core | |
| parent | cff198277e14450f8d20d9e18548325e6f277682 (diff) | |
| parent | 30fc50600009ca54f085d594d838803c107e87f2 (diff) | |
Merge branch 'master' into development_tri/implementasi_raja_ongkir
# Conflicts:
# src/lib/checkout/components/Checkout.jsx
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/api/searchSpellApi.js | 14 | ||||
| -rw-r--r-- | src/core/api/searchSuggestApi.js | 4 | ||||
| -rw-r--r-- | src/core/components/elements/CountDown/CountDown.jsx | 64 | ||||
| -rw-r--r-- | src/core/components/elements/Footer/BasicFooter.jsx | 69 | ||||
| -rw-r--r-- | src/core/components/elements/Footer/SimpleFooter.jsx | 3 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/NavbarDesktop.jsx | 12 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/NavbarMobile.jsx | 4 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/NavbarUserDropdown.jsx | 1 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/Search.jsx | 10 | ||||
| -rw-r--r-- | src/core/components/elements/Navbar/TopBanner.jsx | 25 | ||||
| -rw-r--r-- | src/core/components/elements/Sidebar/Sidebar.jsx | 5 | ||||
| -rw-r--r-- | src/core/components/layouts/BasicLayout.jsx | 28 | ||||
| -rw-r--r-- | src/core/utils/slug.js | 6 | ||||
| -rw-r--r-- | src/core/utils/whatsappUrl.js | 24 |
14 files changed, 222 insertions, 47 deletions
diff --git a/src/core/api/searchSpellApi.js b/src/core/api/searchSpellApi.js new file mode 100644 index 00000000..4794c3e1 --- /dev/null +++ b/src/core/api/searchSpellApi.js @@ -0,0 +1,14 @@ +import axios from 'axios' + +const searchSpellApi = async ({ query }) => { + const dataSearchSpell = await axios( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/spell?q=${query.trim()}` + ) + return dataSearchSpell +} + +searchSpellApi.defaultProps = { + query: '' +} + +export default searchSpellApi diff --git a/src/core/api/searchSuggestApi.js b/src/core/api/searchSuggestApi.js index 3dabfbd6..c65405e3 100644 --- a/src/core/api/searchSuggestApi.js +++ b/src/core/api/searchSuggestApi.js @@ -1,8 +1,10 @@ import axios from 'axios' const searchSuggestApi = async ({ query }) => { + query = query.replace('&', '').trim() + if (!query) return {} const dataSearchSuggest = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/suggest?q=${query.trim()}` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/suggest?q=${query}` ) return dataSearchSuggest } diff --git a/src/core/components/elements/CountDown/CountDown.jsx b/src/core/components/elements/CountDown/CountDown.jsx new file mode 100644 index 00000000..91a1f68e --- /dev/null +++ b/src/core/components/elements/CountDown/CountDown.jsx @@ -0,0 +1,64 @@ +import { useEffect, useState } from 'react' + +const CountDown = ({ initialTime }) => { + const days = Math.floor(initialTime / 86400) + const hours = Math.floor((initialTime % 86400) / 3600) + const minutes = Math.floor((initialTime % 3600) / 60) + const seconds = initialTime % 60 + + const [timeLeft, setTimeLeft] = useState({ + day: days, + hour: hours, + minute: minutes, + second: seconds + }) + + useEffect(() => { + const timer = setInterval(() => { + const totalSeconds = + timeLeft.day * 86400 + timeLeft.hour * 3600 + timeLeft.minute * 60 + timeLeft.second + const secondsLeft = totalSeconds - 1 + if (secondsLeft < 0) { + clearInterval(timer) + } else { + const days = Math.floor(secondsLeft / 86400) + const hours = Math.floor((secondsLeft % 86400) / 3600) + const minutes = Math.floor((secondsLeft % 3600) / 60) + const seconds = secondsLeft % 60 + setTimeLeft({ day: days, hour: hours, minute: minutes, second: seconds }) + } + }, 1000) + return () => clearInterval(timer) + }, [timeLeft]) + + return ( + <div className='flex gap-x-2.5 w-fit'> + <div className='flex flex-col items-center'> + <span className='bg-red-600 text-white font-semibold w-10 h-10 flex items-center justify-center rounded'> + {timeLeft.day.toString().padStart(2, '0')} + </span> + <span className='text-caption-1 text-gray-700 mt-1'>Hari</span> + </div> + <div className='flex flex-col items-center'> + <span className='bg-red-600 text-white font-semibold w-10 h-10 flex items-center justify-center rounded'> + {timeLeft.hour.toString().padStart(2, '0')} + </span> + <span className='text-caption-1 text-gray-700 mt-1'>Jam</span> + </div> + <div className='flex flex-col items-center'> + <span className='bg-red-600 text-white font-semibold w-10 h-10 flex items-center justify-center rounded'> + {timeLeft.minute.toString().padStart(2, '0')} + </span> + <span className='text-caption-1 text-gray-700 mt-1'>Menit</span> + </div> + <div className='flex flex-col items-center'> + <span className='bg-red-600 text-white font-semibold w-10 h-10 flex items-center justify-center rounded'> + {timeLeft.second.toString().padStart(2, '0')} + </span> + <span className='text-caption-1 text-gray-700 mt-1'>Detik</span> + </div> + </div> + ) +} + +export default CountDown diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx index 42698cb2..d340ff3e 100644 --- a/src/core/components/elements/Footer/BasicFooter.jsx +++ b/src/core/components/elements/Footer/BasicFooter.jsx @@ -8,6 +8,7 @@ import { import Link from '../Link/Link' import MobileView from '../../views/MobileView' import DesktopView from '../../views/DesktopView' +import whatsappUrl from '@/core/utils/whatsappUrl' const BasicFooter = () => { return ( @@ -22,6 +23,7 @@ const BasicFooter = () => { </div> <OfficeLocation /> + <AboutUs /> {/* <WarehouseLocation /> */} <InformationCenter /> <OpenHours /> @@ -29,7 +31,7 @@ const BasicFooter = () => { </div> <div className='w-1/2 flex flex-col gap-y-4 pl-1.5'> - <AboutUs /> + <Form /> <CustomerGuide /> <Payments /> </div> @@ -49,6 +51,7 @@ const BasicFooter = () => { <InformationCenter /> </div> <CustomerGuide /> + <Form /> <AboutUs /> <div className='w-3/12'> <div className='grid grid-cols-1 gap-y-4'> @@ -99,31 +102,22 @@ const AboutUs = () => ( <div className={`${headerClassName} mb-3`}>Tentang Kami</div> <ul className='flex flex-col gap-y-2'> <li> - <InternalItemLink href='/'>Company Profile</InternalItemLink> - </li> - <li> - <InternalItemLink href='/'>Karir</InternalItemLink> - </li> - <li> - <InternalItemLink href='/'>Pelanggan Kami</InternalItemLink> + <InternalItemLink href='/tentang-kami'>Company Profile</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Menjadi Supplier</InternalItemLink> + <InternalItemLink href='/pelanggan-kami'>Pelanggan Kami</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Garansi dan Pengembalian</InternalItemLink> + <InternalItemLink href='/hubungi-kami'>Hubungi Kami</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Metode Pembayaran</InternalItemLink> + <InternalItemLink href='/karir'>Karir</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Metode Pengiriman</InternalItemLink> + <InternalItemLink href='/syarat-ketentuan'>Syarat & Ketentuan</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Testimonial</InternalItemLink> - </li> - <li> - <InternalItemLink href='/'>Kebijakan Privacy</InternalItemLink> + <InternalItemLink href='/kebijakan-privasi'>Kebijakan Privasi</InternalItemLink> </li> </ul> </div> @@ -131,34 +125,53 @@ const AboutUs = () => ( const CustomerGuide = () => ( <div> - <div className={`${headerClassName} mb-3`}>Panduan Pelanggan</div> + <div className={`${headerClassName} mb-3`}>Bantuan & Panduan</div> <ul className='flex flex-col gap-y-2'> <li> - <InternalItemLink href='/'>Panduan Belanja</InternalItemLink> + <InternalItemLink href='/metode-pembayaran'>Metode Pembayaran</InternalItemLink> </li> <li> - <InternalItemLink href='/'>F.A.Q</InternalItemLink> + <InternalItemLink href='/metode-pengiriman'>Metode Pengiriman</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Kebijakan Privasi</InternalItemLink> + <InternalItemLink href='/panduan-belanja'>Panduan Belanja</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Pengajuan Tempo</InternalItemLink> + <InternalItemLink href='/panduan-quotation'>Panduan Quotation</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Garansi Produk</InternalItemLink> + <InternalItemLink href='/pengembalian-barang-dana'> + Pengembalian Barang & Dana + </InternalItemLink> </li> <li> - <InternalItemLink href='/'>Online Quotation</InternalItemLink> + <InternalItemLink href='/informasi-garansi'>Informasi Garansi</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Pengiriman</InternalItemLink> + <InternalItemLink href='/panduan-pick-up-service'>Panduan Pick Up Service</InternalItemLink> + </li> + </ul> + </div> +) + +const Form = () => ( + <div> + <div className={`${headerClassName} mb-3`}>Formulir</div> + <ul className='flex flex-col gap-y-2'> + <li> + <InternalItemLink href='/my/kunjungan-sales'>Kunjungan Sales</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Pembayaran</InternalItemLink> + <InternalItemLink href='/my/kunjungan-service'>Kunjungan Service</InternalItemLink> </li> <li> - <InternalItemLink href='/'>Syarat & Ketentuan</InternalItemLink> + <InternalItemLink href='/my/pembayaran-tempo'>Pembayaran Tempo</InternalItemLink> + </li> + <li> + <InternalItemLink href='/my/surat-dukungan'>Surat Dukungan</InternalItemLink> + </li> + <li> + <InternalItemLink href='/my/daftar-merchant'>Daftar Merchant</InternalItemLink> </li> </ul> </div> @@ -178,7 +191,7 @@ const InformationCenter = () => ( </li> <li className='text-gray_r-12/80 flex items-center'> <DevicePhoneMobileIcon className='w-[18px] mr-2' /> - <a href='https://wa.me/628128080622' target='_blank' rel='noreferrer'> + <a href={whatsappUrl()} target='_blank' rel='noreferrer'> 0812-8080-622 </a> </li> @@ -205,7 +218,7 @@ const OpenHours = () => ( const SocialMedias = () => ( <div> <div className={headerClassName + 'block md:hidden'}>Temukan Kami</div> - <div className='flex flex-wrap gap-2'> + <div className='flex flex-wrap gap-2 mt-2'> <NextImage src='/images/socials/Whatsapp.png' alt='Whatsapp Logo' width={24} height={24} /> <NextImage src='/images/socials/Facebook.png' alt='Facebook Logo' width={24} height={24} /> <NextImage src='/images/socials/Twitter.png' alt='Twitter Logo' width={24} height={24} /> diff --git a/src/core/components/elements/Footer/SimpleFooter.jsx b/src/core/components/elements/Footer/SimpleFooter.jsx index 41ca6094..26f7f786 100644 --- a/src/core/components/elements/Footer/SimpleFooter.jsx +++ b/src/core/components/elements/Footer/SimpleFooter.jsx @@ -1,3 +1,4 @@ +import whatsappUrl from '@/core/utils/whatsappUrl' import { DevicePhoneMobileIcon, EnvelopeIcon, @@ -20,7 +21,7 @@ const SimpleFooter = () => ( </li> <li className='text-gray_r-12/80 flex items-center'> <DevicePhoneMobileIcon className='w-[18px] mr-2' /> - <a href='https://wa.me/628128080622' target='_blank' rel='noreferrer'> + <a href={whatsappUrl()} target='_blank' rel='noreferrer'> 0812-8080-622 </a> </li> diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index 837d436c..3da0035a 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -14,6 +14,8 @@ import { useEffect, useState } from 'react' import useAuth from '@/core/hooks/useAuth' import NavbarUserDropdown from './NavbarUserDropdown' import { getCart } from '@/core/utils/cart' +import TopBanner from './TopBanner' +import whatsappUrl from '@/core/utils/whatsappUrl' const Search = dynamic(() => import('./Search')) @@ -38,19 +40,17 @@ const NavbarDesktop = () => { return ( <DesktopView> + <TopBanner /> <div className='py-3 bg-warning-400' id='desktop-nav-top'> <div className='container mx-auto flex justify-between'> - <Link href='/' className='!text-gray_r-12'> + <Link href='/tentang-kami' className='!text-gray_r-12'> Tentang Indoteknik.com </Link> <div className='flex gap-x-6'> - <Link href='/' className='!text-gray_r-12'> + <Link href='/my/pembayaran-tempo' className='!text-gray_r-12'> Pembayaran Tempo </Link> <Link href='/' className='!text-gray_r-12'> - F.A.Q - </Link> - <Link href='/' className='!text-gray_r-12'> Fitur Layanan </Link> </div> @@ -90,7 +90,7 @@ const NavbarDesktop = () => { Wishlist </Link> <a - href='https://wa.me/628128080622' + href={whatsappUrl()} target='_blank' rel='noreferrer' className='flex items-center gap-x-1 !text-gray_r-12/80' diff --git a/src/core/components/elements/Navbar/NavbarMobile.jsx b/src/core/components/elements/Navbar/NavbarMobile.jsx index 0502dba5..7ac967fa 100644 --- a/src/core/components/elements/Navbar/NavbarMobile.jsx +++ b/src/core/components/elements/Navbar/NavbarMobile.jsx @@ -7,6 +7,7 @@ import dynamic from 'next/dynamic' import IndoteknikLogo from '@/images/logo.png' import { useEffect, useState } from 'react' import { getCart } from '@/core/utils/cart' +import TopBanner from './TopBanner' const Search = dynamic(() => import('./Search')) @@ -30,6 +31,7 @@ const NavbarMobile = () => { return ( <MobileView> + <TopBanner /> <nav className='px-4 py-2 pb-3 sticky top-0 z-50 bg-white shadow'> <div className='flex justify-between items-center mb-2'> <Link href='/'> @@ -45,7 +47,7 @@ const NavbarMobile = () => { {cartCount} </span> </Link> - <button type='button' onClick={open}> + <button type='button' aria-label='sidebarMenuButton' onClick={open}> <Bars3Icon className='w-6 text-gray_r-12' /> </button> </div> diff --git a/src/core/components/elements/Navbar/NavbarUserDropdown.jsx b/src/core/components/elements/Navbar/NavbarUserDropdown.jsx index 1d2429a7..7848124c 100644 --- a/src/core/components/elements/Navbar/NavbarUserDropdown.jsx +++ b/src/core/components/elements/Navbar/NavbarUserDropdown.jsx @@ -13,6 +13,7 @@ const NavbarUserDropdown = () => { return ( <div className='navbar-user-dropdown-wrapper'> <div className='navbar-user-dropdown'> + <Link href='/my/quotations'>Daftar Quotation</Link> <Link href='/my/transactions'>Daftar Transaksi</Link> <Link href='/my/invoices'>Invoice & Faktur Pajak</Link> <Link href='/my/wishlist'>Wishlist</Link> diff --git a/src/core/components/elements/Navbar/Search.jsx b/src/core/components/elements/Navbar/Search.jsx index 3046782b..47a9c235 100644 --- a/src/core/components/elements/Navbar/Search.jsx +++ b/src/core/components/elements/Navbar/Search.jsx @@ -13,7 +13,7 @@ const Search = () => { const loadSuggestion = useCallback(() => { if (query && document.activeElement == queryRef.current) { searchSuggestApi({ query }).then((response) => { - setSuggestions(response.data.suggestions) + setSuggestions(response.data?.suggestions || []) }) } else { setSuggestions([]) @@ -56,11 +56,15 @@ const Search = () => { onBlur={onInputBlur} onFocus={loadSuggestion} /> - <button type='submit' className='rounded-r border border-l-0 border-gray_r-6 px-2'> + <button + type='submit' + aria-label='SearchButton' + className='rounded-r border border-l-0 border-gray_r-6 px-2' + > <MagnifyingGlassIcon className='w-6' /> </button> - {suggestions.length > 1 && ( + {suggestions.length > 0 && ( <> <div className='absolute w-full top-[50px] rounded-b bg-gray_r-1 border border-gray_r-6 divide-y divide-gray_r-6 z-50'> {suggestions.map((suggestion, index) => ( diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx new file mode 100644 index 00000000..48b23a3d --- /dev/null +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -0,0 +1,25 @@ +import odooApi from '@/core/api/odooApi' +import { useQuery } from 'react-query' +import Image from 'next/image' + +const TopBanner = () => { + const fetchTopBanner = async () => await odooApi('GET', '/api/v1/banner?type=top-banner') + const topBanner = useQuery('topBanner', fetchTopBanner) + + return ( + topBanner.isFetched && + topBanner.data?.length > 0 && ( + <div style={{ backgroundColor: topBanner.data[0]?.backgroundColor || 'transparent' }}> + <Image + src={topBanner.data[0].image} + alt={topBanner.data[0].name} + width={1440} + height={40} + className='object-cover object-center h-full mx-auto' + /> + </div> + ) + ) +} + +export default TopBanner diff --git a/src/core/components/elements/Sidebar/Sidebar.jsx b/src/core/components/elements/Sidebar/Sidebar.jsx index c8fd8bab..7ea8f7c4 100644 --- a/src/core/components/elements/Sidebar/Sidebar.jsx +++ b/src/core/components/elements/Sidebar/Sidebar.jsx @@ -126,12 +126,9 @@ const Sidebar = ({ active, close }) => { <SidebarLink className={itemClassName} href='/video'> Indoteknik TV </SidebarLink> - <SidebarLink className={itemClassName} href='/about-us'> + <SidebarLink className={itemClassName} href='/tentang-kami'> Tentang Indoteknik </SidebarLink> - <SidebarLink className={itemClassName} href='/faqs'> - F.A.Q - </SidebarLink> <SidebarLink className={itemClassName} href='/contact-us'> Hubungi Kami </SidebarLink> diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index 1a7185cd..df42e403 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -1,5 +1,7 @@ import dynamic from 'next/dynamic' import BasicFooter from '../elements/Footer/BasicFooter' +import Image from 'next/image' +import whatsappUrl from '@/core/utils/whatsappUrl' const Navbar = dynamic(() => import('../elements/Navbar/Navbar')) const AnimationLayout = dynamic(() => import('./AnimationLayout')) @@ -8,7 +10,31 @@ const BasicLayout = ({ children }) => { return ( <> <Navbar /> - <AnimationLayout>{children}</AnimationLayout> + <AnimationLayout> + {children} + <div className='fixed bottom-4 right-4 sm:bottom-10 sm:right-8 z-50'> + <a + href={whatsappUrl(null)} + className='py-2 pl-3 pr-4 rounded-full bg-[#4FB84A] border border-green-300 flex items-center' + > + <Image + src='/images/socials/WHATSAPP.svg' + alt='Whatsapp' + className='block sm:hidden' + width={36} + height={36} + /> + <Image + src='/images/socials/WHATSAPP.svg' + alt='Whatsapp' + className='hidden sm:block' + width={44} + height={44} + /> + <span className='text-white font-bold ml-1.5'>Whatsapp</span> + </a> + </div> + </AnimationLayout> <BasicFooter /> </> ) diff --git a/src/core/utils/slug.js b/src/core/utils/slug.js index d5eecd3e..e91bcf83 100644 --- a/src/core/utils/slug.js +++ b/src/core/utils/slug.js @@ -9,7 +9,7 @@ import toTitleCase from './toTitleCase' * @param {number} id - The ID to be appended to the slug. * @returns {string} - The generated slug with the prefix, name, and ID. */ -const createSlug = (prefix, name, id) => { +const createSlug = (prefix, name, id, withHost = false) => { let slug = name ?.trim() @@ -19,7 +19,9 @@ const createSlug = (prefix, name, id) => { id let splitSlug = slug.split('-') let filterSlugFromEmptyChar = splitSlug.filter((x) => x != '') - return prefix + filterSlugFromEmptyChar.join('-') + slug = prefix + filterSlugFromEmptyChar.join('-') + if (withHost) slug = process.env.NEXT_PUBLIC_SELF_HOST + slug + return slug } /** diff --git a/src/core/utils/whatsappUrl.js b/src/core/utils/whatsappUrl.js new file mode 100644 index 00000000..6ca9722b --- /dev/null +++ b/src/core/utils/whatsappUrl.js @@ -0,0 +1,24 @@ +const whatsappUrl = (template = 'default', payload) => { + let url = 'https://wa.me/628128080622' + let text = '' + switch (template) { + case 'product': + text = `Halo, saya mau tanya ${payload.name}, bisa tolong bantu saya?\n\nBerikut ini linknya: ${payload.url}` + break + case 'productWeight': + text = `Mau tanya untuk berat ${payload.name}, bisa minta tolong informasikan beratnya?\n\nBerikut ini linknya: ${payload.url}` + break + case 'productSearch': + text = `Saya lagi cari-cari produk ${payload.name}, bisa bantu saya cari produknya?` + break + case null: + break; + default: + text = 'Halo, saya mau tanya-tanya seputar produk, bisa tolong bantu saya?' + break + } + if (text) url += `?text=${encodeURI(text)}` + return url +} + +export default whatsappUrl |
