summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2024-01-19 02:32:43 +0000
committerIT Fixcomart <it@fixcomart.co.id>2024-01-19 02:32:43 +0000
commit8bcadf6d43a44169c422305522784424c30c7b02 (patch)
tree4666802b65784a949db4acad665a81de7297fc74 /src
parent065396828266e2de42cb0182c81ea2d7a5b00e2b (diff)
parent91086d8b1af2e1c0ca9db38d037f6331c9e6131a (diff)
Merged in Feature/perf/product-detail (pull request #127)
Feature/perf/product detail
Diffstat (limited to 'src')
-rw-r--r--src/core/components/elements/Navbar/NavbarDesktop.jsx124
-rw-r--r--src/core/components/elements/Navbar/NavbarMobile.jsx69
-rw-r--r--src/core/components/elements/Skeleton/TopBannerSkeleton.jsx24
-rw-r--r--src/core/components/layouts/AppLayout.jsx18
-rw-r--r--src/core/components/layouts/BasicLayout.jsx82
-rw-r--r--src/fonts/Inter/inter.css85
-rw-r--r--src/lib/product/components/ProductSearch.jsx6
-rw-r--r--src/pages/_app.jsx108
-rw-r--r--src/pages/_document.jsx36
-rw-r--r--src/pages/shop/cart.jsx2
-rw-r--r--src/pages/shop/product/[slug].jsx118
-rw-r--r--src/utils/solrMapping.js94
12 files changed, 325 insertions, 441 deletions
diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx
index d9f5658e..9bcd4df2 100644
--- a/src/core/components/elements/Navbar/NavbarDesktop.jsx
+++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx
@@ -2,74 +2,72 @@ import {
ChevronDownIcon,
HeartIcon,
ShoppingCartIcon,
- DocumentCheckIcon
-} from '@heroicons/react/24/outline'
-import Link from '../Link/Link'
-import Image from 'next/image'
-import DesktopView from '../../views/DesktopView'
-import dynamic from 'next/dynamic'
-import IndoteknikLogo from '@/images/logo.png'
-import Category from '@/lib/category/components/Category'
-import { useCallback, useContext, useEffect, useState } from 'react'
-import useAuth from '@/core/hooks/useAuth'
-import NavbarUserDropdown from './NavbarUserDropdown'
-import { getCartApi, getCountCart } from '@/core/utils/cart'
-import whatsappUrl from '@/core/utils/whatsappUrl'
-import { useRouter } from 'next/router'
-import { getAuth, setAuth } from '@/core/utils/auth'
-import { createSlug, getIdFromSlug } from '@/core/utils/slug'
-import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton'
-import { useProductContext } from '@/contexts/ProductContext'
-import Cardheader from '@/lib/cart/components/Cartheader'
-
-const Search = dynamic(() => import('./Search'))
-const TopBanner = dynamic(() => import('./TopBanner'), {
- loading: () => <TopBannerSkeleton />
-})
+ DocumentCheckIcon,
+} from '@heroicons/react/24/outline';
+import Link from '../Link/Link';
+import Image from 'next/image';
+import DesktopView from '../../views/DesktopView';
+import dynamic from 'next/dynamic';
+import IndoteknikLogo from '@/images/logo.png';
+import Category from '@/lib/category/components/Category';
+import { useCallback, useContext, useEffect, useState } from 'react';
+import useAuth from '@/core/hooks/useAuth';
+import NavbarUserDropdown from './NavbarUserDropdown';
+import { getCartApi, getCountCart } from '@/core/utils/cart';
+import whatsappUrl from '@/core/utils/whatsappUrl';
+import { useRouter } from 'next/router';
+import { getAuth, setAuth } from '@/core/utils/auth';
+import { createSlug, getIdFromSlug } from '@/core/utils/slug';
+import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton';
+import { useProductContext } from '@/contexts/ProductContext';
+import Cardheader from '@/lib/cart/components/Cartheader';
+
+const Search = dynamic(() => import('./Search'), { ssr: false });
+const TopBanner = dynamic(() => import('./TopBanner'), { ssr: false });
const NavbarDesktop = () => {
- const [isOpenCategory, setIsOpenCategory] = useState(false)
- const auth = useAuth()
+ const [isOpenCategory, setIsOpenCategory] = useState(false);
+ const auth = useAuth();
- const [cartCount, setCartCount] = useState(0)
+ const [cartCount, setCartCount] = useState(0);
- const [templateWA, setTemplateWA] = useState(null)
- const [payloadWA, setPayloadWa] = useState(null)
- const [urlPath, setUrlPath] = useState(null)
+ const [templateWA, setTemplateWA] = useState(null);
+ const [payloadWA, setPayloadWa] = useState(null);
+ const [urlPath, setUrlPath] = useState(null);
- const router = useRouter()
+ const router = useRouter();
- const { product } = useProductContext()
+ const { product } = useProductContext();
useEffect(() => {
if (router.pathname === '/shop/product/[slug]') {
setPayloadWa({
name: product?.name,
manufacture: product?.manufacture.name,
- url: createSlug('/shop/product/', product?.name, product?.id, true)
- })
- setTemplateWA('product')
+ url: createSlug('/shop/product/', product?.name, product?.id, true),
+ });
+ setTemplateWA('product');
- setUrlPath(router.asPath)
+ setUrlPath(router.asPath);
}
- }, [product, router])
+ }, [product, router]);
useEffect(() => {
const handleCartChange = () => {
const cart = async () => {
- const listCart = await getCountCart()
- setCartCount(listCart)
- }
- cart()
- }
- handleCartChange()
+ const listCart = await getCountCart();
+ setCartCount(listCart);
+ };
+ cart();
+ };
+ handleCartChange();
- window.addEventListener('localStorageChange', handleCartChange)
+ window.addEventListener('localStorageChange', handleCartChange);
return () => {
- window.removeEventListener('localStorageChange', handleCartChange)
- }
- }, [])
+ window.removeEventListener('localStorageChange', handleCartChange);
+ };
+ }, []);
return (
<DesktopView>
@@ -93,7 +91,12 @@ 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='/'>
- <Image src={IndoteknikLogo} alt='Indoteknik Logo' width={210} height={210 / 3} />
+ <Image
+ src={IndoteknikLogo}
+ alt='Indoteknik Logo'
+ width={210}
+ height={210 / 3}
+ />
</Link>
<div className='flex-1 flex items-center'>
<Search />
@@ -128,7 +131,12 @@ const NavbarDesktop = () => {
rel='noreferrer'
className='flex items-center gap-x-1 !text-gray_r-12/80'
>
- <Image src='/images/socials/Whatsapp-2.png' alt='Whatsapp' width={48} height={48} />
+ <Image
+ src='/images/socials/Whatsapp-2.png'
+ alt='Whatsapp'
+ width={48}
+ height={48}
+ />
<div>
<div className='font-semibold'>Whatsapp</div>
0812 8080 622 (Chat)
@@ -146,9 +154,15 @@ const NavbarDesktop = () => {
className='w-3/12 p-4 font-semibold border border-gray_r-6 rounded-t-xl flex items-center relative'
>
<div>Kategori Produk</div>
- <ChevronDownIcon className={`ml-auto w-6 ${isOpenCategory ? 'rotate-180' : ''}`} />
-
- <div className={`category-mega-box-wrapper ${isOpenCategory ? 'show' : ''}`}>
+ <ChevronDownIcon
+ className={`ml-auto w-6 ${isOpenCategory ? 'rotate-180' : ''}`}
+ />
+
+ <div
+ className={`category-mega-box-wrapper ${
+ isOpenCategory ? 'show' : ''
+ }`}
+ >
<Category />
</div>
</button>
@@ -220,7 +234,7 @@ const NavbarDesktop = () => {
</div>
</nav>
</DesktopView>
- )
-}
+ );
+};
-export default NavbarDesktop
+export default NavbarDesktop;
diff --git a/src/core/components/elements/Navbar/NavbarMobile.jsx b/src/core/components/elements/Navbar/NavbarMobile.jsx
index 704e91b6..92bd5627 100644
--- a/src/core/components/elements/Navbar/NavbarMobile.jsx
+++ b/src/core/components/elements/Navbar/NavbarMobile.jsx
@@ -1,51 +1,60 @@
-import Image from 'next/image'
-import MobileView from '../../views/MobileView'
-import Link from '../Link/Link'
-import { Bars3Icon, HeartIcon, ShoppingCartIcon } from '@heroicons/react/24/outline'
-import useSidebar from '@/core/hooks/useSidebar'
-import dynamic from 'next/dynamic'
-import IndoteknikLogo from '@/images/logo.png'
-import { useEffect, useState } from 'react'
-import { getCart, getCountCart } from '@/core/utils/cart'
-import TopBanner from './TopBanner'
+import Image from 'next/image';
+import MobileView from '../../views/MobileView';
+import Link from '../Link/Link';
+import {
+ Bars3Icon,
+ HeartIcon,
+ ShoppingCartIcon,
+} from '@heroicons/react/24/outline';
+import useSidebar from '@/core/hooks/useSidebar';
+import dynamic from 'next/dynamic';
+import IndoteknikLogo from '@/images/logo.png';
+import { useEffect, useState } from 'react';
+import { getCart, getCountCart } from '@/core/utils/cart';
+// import TopBanner from './TopBanner';
-const Search = dynamic(() => import('./Search'))
+const Search = dynamic(() => import('./Search'));
const NavbarMobile = () => {
- const { Sidebar, open } = useSidebar()
+ const { Sidebar, open } = useSidebar();
- const [cartCount, setCartCount] = useState(0)
+ const [cartCount, setCartCount] = useState(0);
useEffect(() => {
const handleCartChange = () => {
const cart = async () => {
- const listCart = await getCountCart()
- setCartCount(listCart)
- }
- cart()
- }
- handleCartChange()
+ const listCart = await getCountCart();
+ setCartCount(listCart);
+ };
+ cart();
+ };
+ handleCartChange();
- window.addEventListener('localStorageChange', handleCartChange)
+ window.addEventListener('localStorageChange', handleCartChange);
return () => {
- window.removeEventListener('localStorageChange', handleCartChange)
- }
- }, [])
+ window.removeEventListener('localStorageChange', handleCartChange);
+ };
+ }, []);
return (
<MobileView>
- <TopBanner />
+ {/* <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='/'>
- <Image src={IndoteknikLogo} alt='Indoteknik Logo' width={120} height={40} />
+ <Image
+ src={IndoteknikLogo}
+ alt='Indoteknik Logo'
+ width={120}
+ height={40}
+ />
</Link>
<div className='flex gap-x-3'>
- <Link href='/my/wishlist'>
+ <Link href='/my/wishlist' aria-label='Wishlist'>
<HeartIcon className='w-6 text-gray_r-12' />
</Link>
- <Link href='/shop/cart' className='relative'>
+ <Link href='/shop/cart' className='relative' aria-label='Cart'>
<ShoppingCartIcon className='w-6 text-gray_r-12' />
{cartCount > 0 && (
<span className='absolute -top-2 -right-2 badge-solid-red rounded-full w-5 h-5 flex items-center justify-center'>
@@ -62,7 +71,7 @@ const NavbarMobile = () => {
</nav>
{Sidebar}
</MobileView>
- )
-}
+ );
+};
-export default NavbarMobile
+export default NavbarMobile;
diff --git a/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx b/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx
index f7d2e748..8d1a51d2 100644
--- a/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx
+++ b/src/core/components/elements/Skeleton/TopBannerSkeleton.jsx
@@ -1,19 +1,17 @@
-import useDevice from '@/core/hooks/useDevice'
-import classNames from 'classnames'
-import Skeleton from 'react-loading-skeleton'
+import useDevice from '@/core/hooks/useDevice';
+import classNames from 'classnames';
+import { Skeleton } from '@chakra-ui/react';
const TopBannerSkeleton = () => {
- const { isDesktop, isMobile } = useDevice()
+ const { isDesktop, isMobile } = useDevice();
const deviceClassName = {
- 'h-10': isDesktop,
- 'h-2.5': isMobile
- }
- const combinedClassName = classNames(deviceClassName)
+ '!h-[36px]': isDesktop,
+ 'h-2.5': isMobile,
+ };
+ const combinedClassName = classNames(deviceClassName);
- return (
- <Skeleton className={combinedClassName} count={1} containerClassName='w-full h-full block' />
- )
-}
+ return <Skeleton className={combinedClassName} />;
+};
-export { TopBannerSkeleton }
+export { TopBannerSkeleton };
diff --git a/src/core/components/layouts/AppLayout.jsx b/src/core/components/layouts/AppLayout.jsx
index d74d61e3..ebbc1ad5 100644
--- a/src/core/components/layouts/AppLayout.jsx
+++ b/src/core/components/layouts/AppLayout.jsx
@@ -1,6 +1,12 @@
-import AppBar from '../elements/Appbar/Appbar'
-import BasicFooter from '../elements/Footer/BasicFooter'
-import AnimationLayout from './AnimationLayout'
+import dynamic from 'next/dynamic';
+import AnimationLayout from './AnimationLayout';
+
+const AppBar = dynamic(() => import('../elements/Appbar/Appbar'), {
+ ssr: false,
+});
+const BasicFooter = dynamic(() => import('../elements/Footer/BasicFooter'), {
+ ssr: false,
+});
const AppLayout = ({ children, title, withFooter = true }) => {
return (
@@ -11,7 +17,7 @@ const AppLayout = ({ children, title, withFooter = true }) => {
</AnimationLayout>
{withFooter && <BasicFooter />}
</>
- )
-}
+ );
+};
-export default AppLayout
+export default AppLayout;
diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx
index 9441dbd7..2962a08b 100644
--- a/src/core/components/layouts/BasicLayout.jsx
+++ b/src/core/components/layouts/BasicLayout.jsx
@@ -1,55 +1,61 @@
-import dynamic from 'next/dynamic'
-import BasicFooter from '../elements/Footer/BasicFooter'
-import Image from 'next/image'
-import whatsappUrl from '@/core/utils/whatsappUrl'
-import { useEffect, useState } from 'react'
-import axios from 'axios'
-import odooApi from '@/core/api/odooApi'
-import { useRouter } from 'next/router'
-import productApi from '@/lib/product/api/productApi'
-import { getAuth, setAuth } from '@/core/utils/auth'
-import { createSlug, getIdFromSlug } from '@/core/utils/slug'
-import { useSession } from 'next-auth/react'
-import { setCookie } from 'cookies-next'
-import { useProductContext } from '@/contexts/ProductContext'
+import dynamic from 'next/dynamic';
+import Image from 'next/image';
+import { useEffect, useState } from 'react';
+import axios from 'axios';
-const Navbar = dynamic(() => import('../elements/Navbar/Navbar'))
-const AnimationLayout = dynamic(() => import('./AnimationLayout'))
+import whatsappUrl from '@/core/utils/whatsappUrl';
+import odooApi from '@/core/api/odooApi';
+import { useRouter } from 'next/router';
+import { useProductContext } from '@/contexts/ProductContext';
+
+const Navbar = dynamic(() => import('../elements/Navbar/Navbar'), {
+ ssr: false,
+});
+const AnimationLayout = dynamic(() => import('./AnimationLayout'), {
+ ssr: false,
+});
+const BasicFooter = dynamic(() => import('../elements/Footer/BasicFooter'), {
+ ssr: false,
+});
const BasicLayout = ({ children }) => {
- const [templateWA, setTemplateWA] = useState(null)
- const [payloadWA, setPayloadWa] = useState(null)
- const [urlPath, setUrlPath] = useState(null)
+ const [templateWA, setTemplateWA] = useState(null);
+ const [payloadWA, setPayloadWa] = useState(null);
+ const [urlPath, setUrlPath] = useState(null);
- const router = useRouter()
+ const router = useRouter();
- const { product } = useProductContext()
+ const { product } = useProductContext();
useEffect(() => {
- if (router.pathname === '/shop/product/[slug]' || router.pathname === '/shop/product/variant/[slug]') {
+ if (
+ router.pathname === '/shop/product/[slug]' ||
+ router.pathname === '/shop/product/variant/[slug]'
+ ) {
setPayloadWa({
name: product?.name,
manufacture: product?.manufacture.name,
- url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath
- })
- setTemplateWA('product')
+ url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath,
+ });
+ setTemplateWA('product');
- setUrlPath(router.asPath)
+ setUrlPath(router.asPath);
}
- }, [product, router])
-
+ }, [product, router]);
useEffect(() => {
const getIP = async () => {
- const ip = await odooApi('GET', '/api/ip-address')
+ const ip = await odooApi('GET', '/api/ip-address');
const data = {
page_title: document.title,
url: window.location.href,
- ip: ip
- }
- axios.get(`/api/user-activity?page_title=${data.page_title}&url=${data.url}&ip=${data.ip}`)
- }
- getIP()
- }, [])
+ ip: ip,
+ };
+ axios.get(
+ `/api/user-activity?page_title=${data.page_title}&url=${data.url}&ip=${data.ip}`
+ );
+ };
+ getIP();
+ }, []);
return (
<>
<Navbar />
@@ -82,7 +88,7 @@ const BasicLayout = ({ children }) => {
</AnimationLayout>
<BasicFooter />
</>
- )
-}
+ );
+};
-export default BasicLayout
+export default BasicLayout;
diff --git a/src/fonts/Inter/inter.css b/src/fonts/Inter/inter.css
index de6ce273..3a1de02a 100644
--- a/src/fonts/Inter/inter.css
+++ b/src/fonts/Inter/inter.css
@@ -1,57 +1,6 @@
@font-face {
font-family: 'Inter';
font-style: normal;
- font-weight: 100;
- font-display: swap;
- src: url('Inter-Thin.woff2?v=3.19') format('woff2'),
- url('Inter-Thin.woff?v=3.19') format('woff');
-}
-@font-face {
- font-family: 'Inter';
- font-style: italic;
- font-weight: 100;
- font-display: swap;
- src: url('Inter-ThinItalic.woff2?v=3.19') format('woff2'),
- url('Inter-ThinItalic.woff?v=3.19') format('woff');
-}
-
-@font-face {
- font-family: 'Inter';
- font-style: normal;
- font-weight: 200;
- font-display: swap;
- src: url('Inter-ExtraLight.woff2?v=3.19') format('woff2'),
- url('Inter-ExtraLight.woff?v=3.19') format('woff');
-}
-@font-face {
- font-family: 'Inter';
- font-style: italic;
- font-weight: 200;
- font-display: swap;
- src: url('Inter-ExtraLightItalic.woff2?v=3.19') format('woff2'),
- url('Inter-ExtraLightItalic.woff?v=3.19') format('woff');
-}
-
-@font-face {
- font-family: 'Inter';
- font-style: normal;
- font-weight: 300;
- font-display: swap;
- src: url('Inter-Light.woff2?v=3.19') format('woff2'),
- url('Inter-Light.woff?v=3.19') format('woff');
-}
-@font-face {
- font-family: 'Inter';
- font-style: italic;
- font-weight: 300;
- font-display: swap;
- src: url('Inter-LightItalic.woff2?v=3.19') format('woff2'),
- url('Inter-LightItalic.woff?v=3.19') format('woff');
-}
-
-@font-face {
- font-family: 'Inter';
- font-style: normal;
font-weight: 400;
font-display: swap;
src: url('Inter-Regular.woff2?v=3.19') format('woff2'),
@@ -117,40 +66,6 @@
url('Inter-BoldItalic.woff?v=3.19') format('woff');
}
-@font-face {
- font-family: 'Inter';
- font-style: normal;
- font-weight: 800;
- font-display: swap;
- src: url('Inter-ExtraBold.woff2?v=3.19') format('woff2'),
- url('Inter-ExtraBold.woff?v=3.19') format('woff');
-}
-@font-face {
- font-family: 'Inter';
- font-style: italic;
- font-weight: 800;
- font-display: swap;
- src: url('Inter-ExtraBoldItalic.woff2?v=3.19') format('woff2'),
- url('Inter-ExtraBoldItalic.woff?v=3.19') format('woff');
-}
-
-@font-face {
- font-family: 'Inter';
- font-style: normal;
- font-weight: 900;
- font-display: swap;
- src: url('Inter-Black.woff2?v=3.19') format('woff2'),
- url('Inter-Black.woff?v=3.19') format('woff');
-}
-@font-face {
- font-family: 'Inter';
- font-style: italic;
- font-weight: 900;
- font-display: swap;
- src: url('Inter-BlackItalic.woff2?v=3.19') format('woff2'),
- url('Inter-BlackItalic.woff?v=3.19') format('woff');
-}
-
/* -------------------------------------------------------
Variable font.
Usage:
diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx
index e2b961f1..ed4365a8 100644
--- a/src/lib/product/components/ProductSearch.jsx
+++ b/src/lib/product/components/ProductSearch.jsx
@@ -253,12 +253,14 @@ const ProductSearch = ({
router.push(`${prefixUrl}?${params}`);
};
+ const isNotReadyStockPage = router.asPath !== '/shop/search?orderBy=stock';
+
return (
<>
<MobileView>
{productSearch.isLoading && <ProductSearchSkeleton />}
<div className='p-4 pt-0'>
- {isBrand && isBrand.logo && (
+ {isNotReadyStockPage && isBrand && isBrand.logo && (
<div className='mb-3'>
<h1 className='mb-2 font-semibold text-h-sm'>
Brand Pencarian {q}
@@ -403,7 +405,7 @@ const ProductSearch = ({
</div>
)}
- {isBrand && isBrand.logo && (
+ {isNotReadyStockPage && isBrand && isBrand.logo && (
<div className='mb-3'>
<h1 className='text-2xl mb-2 font-semibold'>
Brand Pencarian {q}
diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx
index 3fe1d3cf..9067fd03 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -1,58 +1,82 @@
-import '@/fonts/Inter/inter.css'
-import '@/styles/globals.css'
-import 'react-loading-skeleton/dist/skeleton.css'
+import '@/fonts/Inter/inter.css';
+import '@/styles/globals.css';
+// import 'react-loading-skeleton/dist/skeleton.css';
-import NextProgress from 'next-progress'
-import { useRouter, Router } from 'next/router'
-import { AnimatePresence, motion } from 'framer-motion'
-import { Toaster } from 'react-hot-toast'
-import { QueryClient, QueryClientProvider } from 'react-query'
-import useDevice from '@/core/hooks/useDevice'
-import { useEffect, useState } from 'react'
-import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner'
-import { SessionProvider } from 'next-auth/react'
-import { ProductProvider } from '@/contexts/ProductContext'
-import { ProductCartProvider } from '@/contexts/ProductCartContext'
-import { ChakraProvider } from '@chakra-ui/react'
-import theme from '../../chakra.theme'
+import { useEffect, useState } from 'react';
+import dynamic from 'next/dynamic';
+import { useRouter, Router } from 'next/router';
+import { AnimatePresence, motion } from 'framer-motion';
+import { QueryClient, QueryClientProvider } from 'react-query';
-const queryClient = new QueryClient()
+import useDevice from '@/core/hooks/useDevice';
+import theme from '../../chakra.theme';
+
+const NextProgress = dynamic(() => import('next-progress'), { ssr: false });
+const ChakraProvider = dynamic(
+ () => import('@chakra-ui/react').then((mod) => mod.ChakraProvider),
+ { ssr: false }
+);
+const ProductProvider = dynamic(
+ () => import('@/contexts/ProductContext').then((mod) => mod.ProductProvider),
+ { ssr: false }
+);
+const ProductCartProvider = dynamic(
+ () =>
+ import('@/contexts/ProductCartContext').then(
+ (mod) => mod.ProductCartProvider
+ ),
+ { ssr: false }
+);
+const SessionProvider = dynamic(
+ () => import('next-auth/react').then((mod) => mod.SessionProvider),
+ { ssr: false }
+);
+const LogoSpinner = dynamic(
+ () => import('@/core/components/elements/Spinner/LogoSpinner'),
+ { ssr: false }
+);
+const Toaster = dynamic(
+ () => import('react-hot-toast').then((mod) => mod.Toaster),
+ { ssr: false }
+);
+
+const queryClient = new QueryClient();
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
- const router = useRouter()
- const { isMobile } = useDevice()
+ const router = useRouter();
+ const { isMobile } = useDevice();
- const [animateLoader, setAnimateLoader] = useState(false)
+ const [animateLoader, setAnimateLoader] = useState(false);
useEffect(() => {
- const handleRouteChangeStart = () => setAnimateLoader(true)
- const handleRouteChangeComplete = () => setAnimateLoader(false)
+ const handleRouteChangeStart = () => setAnimateLoader(true);
+ const handleRouteChangeComplete = () => setAnimateLoader(false);
- Router.events.on('routeChangeStart', handleRouteChangeStart)
- Router.events.on('routeChangeComplete', handleRouteChangeComplete)
- Router.events.on('routeChangeError', handleRouteChangeComplete)
+ Router.events.on('routeChangeStart', handleRouteChangeStart);
+ Router.events.on('routeChangeComplete', handleRouteChangeComplete);
+ Router.events.on('routeChangeError', handleRouteChangeComplete);
return () => {
- Router.events.off('routeChangeStart', handleRouteChangeStart)
- Router.events.off('routeChangeComplete', handleRouteChangeComplete)
- Router.events.off('routeChangeError', handleRouteChangeComplete)
- }
- }, [])
+ Router.events.off('routeChangeStart', handleRouteChangeStart);
+ Router.events.off('routeChangeComplete', handleRouteChangeComplete);
+ Router.events.off('routeChangeError', handleRouteChangeComplete);
+ };
+ }, []);
- const [toasterStyle, setToasterStyle] = useState({})
+ const [toasterStyle, setToasterStyle] = useState({});
useEffect(() => {
- let elems = document.querySelectorAll('nav')
- let totalNavHeight = 0
+ let elems = document.querySelectorAll('nav');
+ let totalNavHeight = 0;
elems.forEach(function (elem) {
- totalNavHeight += elem.offsetHeight
- })
+ totalNavHeight += elem.offsetHeight;
+ });
setToasterStyle({
- marginTop: isMobile ? totalNavHeight - 8 : totalNavHeight
- })
- }, [isMobile])
+ marginTop: isMobile ? totalNavHeight - 8 : totalNavHeight,
+ });
+ }, [isMobile]);
return (
<SessionProvider session={session}>
@@ -63,7 +87,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) {
animate={{ opacity: 1 }}
exit={{ opacity: 0.4 }}
transition={{
- duration: 0.1
+ duration: 0.1,
}}
className='fixed w-screen h-screen z-[500] bg-white flex justify-center items-center'
>
@@ -76,7 +100,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) {
containerStyle={toasterStyle}
toastOptions={{
duration: 3000,
- className: 'border border-gray_r-8'
+ className: 'border border-gray_r-8',
}}
/>
<NextProgress color='#F01C21' options={{ showSpinner: false }} />
@@ -90,7 +114,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) {
</ProductProvider>
</QueryClientProvider>
</SessionProvider>
- )
+ );
}
-export default MyApp
+export default MyApp;
diff --git a/src/pages/_document.jsx b/src/pages/_document.jsx
index 3762c63b..fc9f2ee0 100644
--- a/src/pages/_document.jsx
+++ b/src/pages/_document.jsx
@@ -1,16 +1,24 @@
-import { Html, Head, Main, NextScript } from 'next/document'
-import Script from 'next/script'
+import { Html, Head, Main, NextScript } from 'next/document';
+import Script from 'next/script';
export default function MyDocument() {
- const env = process.env.NODE_ENV
+ const env = process.env.NODE_ENV;
return (
<Html>
<Head>
+ <link rel='preconnect' href='https://connect.facebook.net' />
+ <link rel='preconnect' href='https://googleads.g.doubleclick.net' />
+ <link rel='preconnect' href={process.env.NEXT_PUBLIC_ODOO_API_HOST} />
+ <link rel='prefetch' href='/images/logo-indoteknik-gear.png' />
+
<link rel='icon' href='/favicon.ico' />
<link rel='manifest' href='/manifest.json' />
<link rel='apple-touch-icon' href='/icon.jpg'></link>
- <link rel='apple-touch-startup-image' href='/images/splash/launch.png' />
+ <link
+ rel='apple-touch-startup-image'
+ href='/images/splash/launch.png'
+ />
<meta name='mobile-web-app-capable' content='yes' />
<meta name='apple-mobile-web-app-capable' content='yes' />
@@ -18,9 +26,11 @@ export default function MyDocument() {
<meta name='apple-mobile-web-app-title' content='Indoteknik.com' />
<meta name='theme-color' content='#fff' />
- <link rel='prefetch' href='/images/logo-indoteknik-gear.png' />
+ <meta
+ name='facebook-domain-verification'
+ content='328wmjs7hcnz74rwsqzxvq50rmbtm2'
+ />
- <meta name='facebook-domain-verification' content='328wmjs7hcnz74rwsqzxvq50rmbtm2' />
<Script
async
strategy='beforeInteractive'
@@ -28,6 +38,7 @@ export default function MyDocument() {
/>
<Script
+ async
id='google-analytics-ua'
strategy='beforeInteractive'
dangerouslySetInnerHTML={{
@@ -36,7 +47,7 @@ export default function MyDocument() {
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-10501937-1');
- `
+ `,
}}
/>
@@ -47,6 +58,7 @@ export default function MyDocument() {
/>
<Script
+ async
id='google-analytics-ga'
strategy='beforeInteractive'
dangerouslySetInnerHTML={{
@@ -55,11 +67,12 @@ export default function MyDocument() {
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-G1W8MNZ11P');
- `
+ `,
}}
/>
<Script
+ async
id='google-tag-manager'
strategy='afterInteractive'
dangerouslySetInnerHTML={{
@@ -68,7 +81,7 @@ export default function MyDocument() {
f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-PHRB7RP');
- `
+ `,
}}
/>
@@ -79,6 +92,7 @@ export default function MyDocument() {
/>
<Script
+ async
id='google-ads'
strategy='afterInteractive'
dangerouslySetInnerHTML={{
@@ -87,7 +101,7 @@ export default function MyDocument() {
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
- gtag('config', 'AW-954540379');`
+ gtag('config', 'AW-954540379');`,
}}
/>
@@ -119,5 +133,5 @@ export default function MyDocument() {
<NextScript />
</body>
</Html>
- )
+ );
}
diff --git a/src/pages/shop/cart.jsx b/src/pages/shop/cart.jsx
index 34cae86a..7475b23d 100644
--- a/src/pages/shop/cart.jsx
+++ b/src/pages/shop/cart.jsx
@@ -8,7 +8,7 @@ import dynamic from 'next/dynamic';
import Link from 'next/link';
const AppLayout = dynamic(() => import('@/core/components/layouts/AppLayout'));
-const CartDetail = dynamic(() => import('~/modules/cart/components/Detail'));
+const CartDetail = dynamic(() => import('~/pages/shop/cart'));
export default function Cart() {
return (
diff --git a/src/pages/shop/product/[slug].jsx b/src/pages/shop/product/[slug].jsx
index 667373b4..73e8987c 100644
--- a/src/pages/shop/product/[slug].jsx
+++ b/src/pages/shop/product/[slug].jsx
@@ -1,114 +1,6 @@
-import Seo from '@/core/components/Seo';
-import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner';
-import { getIdFromSlug } from '@/core/utils/slug';
-import productApi from '@/lib/product/api/productApi';
-import PageNotFound from '@/pages/404';
-import dynamic from 'next/dynamic';
-import { useRouter } from 'next/router';
-import cookie from 'cookie';
-import axios from 'axios';
-import { useProductContext } from '@/contexts/ProductContext';
-import { useEffect } from 'react';
-import { updateItemCart } from '@/core/utils/cart';
+import ProductDetailPage, {
+ getServerSideProps,
+} from '~/pages/shop/product/[slug]';
-const BasicLayout = dynamic(() =>
- import('@/core/components/layouts/BasicLayout')
-);
-const Product = dynamic(() =>
- import('@/lib/product/components/Product/Product')
-);
-
-export async function getServerSideProps(context) {
- const { slug } = context.query;
- const cookies = context.req.headers.cookie;
- const cookieObj = cookies ? cookie.parse(cookies) : {};
- const auth = cookieObj.auth ? JSON.parse(cookieObj.auth) : {};
- const tier = auth.pricelist ? auth.pricelist : false;
-
- let response = await axios(
- `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/product-detail?id=` +
- getIdFromSlug(slug) +
- '&auth=' +
- tier
- );
- let product = response.data;
- // let productSolr = await productApi({ id: getIdFromSlug(slug), headers: { Token: authToken } })
- // let productSolr = null
- if (product?.length == 1) {
- product = product[0];
- } else {
- product = null;
- }
-
- return {
- props: { product },
- };
-}
-
-export default function ProductDetail({ product }) {
- const router = useRouter();
- const { setProduct } = useProductContext();
-
- useEffect(() => {
- if (product) {
- setProduct(product);
- }
- }, [product, setProduct]);
-
- useEffect(() => {
- const { action, variantId, qty } = router.query;
- const addToCart = async () => {
- const data = {
- productId: variantId,
- quantity: qty,
- selected: true,
- programLineId: null,
- source: action,
- };
-
- await updateItemCart(data);
- const redirectURL =
- action === 'buy' ? '/shop/checkout?source=buy' : '/shop/cart';
- router.push(redirectURL);
- };
-
- if (action && variantId && qty) {
- addToCart();
- }
- }, [router]);
-
- if (!product) return <PageNotFound />;
-
- return (
- <BasicLayout>
- <Seo
- title={product?.name || '' + ' - Indoteknik.com' || ''}
- description='Temukan pilihan produk B2B Industri &amp; Alat Teknik untuk Perusahaan, UMKM &amp; Pemerintah dengan lengkap, mudah dan transparan.'
- openGraph={{
- url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath,
- images: [
- {
- url: product?.image,
- width: 800,
- height: 800,
- alt: product?.name,
- },
- ],
- type: 'product',
- }}
- additionalMetaTags={[
- {
- name: 'keywords',
- content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}`,
- },
- ]}
- />
- {!product && (
- <div className='container mx-auto flex justify-center pt-10'>
- <LogoSpinner width={36} height={36} />
- </div>
- )}
- {product && <Product product={product} />}
- </BasicLayout>
- );
-}
+export { getServerSideProps };
+export default ProductDetailPage;
diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js
index 41d24b53..7e887253 100644
--- a/src/utils/solrMapping.js
+++ b/src/utils/solrMapping.js
@@ -1,17 +1,18 @@
export const productMappingSolr = (products, pricelist) => {
return products.map((product) => {
- let price = product.price_tier1_v2_f || 0
- let priceDiscount = 0
- let discountPercentage = 0
+ let price = product.price_tier1_v2_f || 0;
+ let priceDiscount = price;
+ let discountPercentage = 0;
if (pricelist && product?.[`price_${pricelist}_f`] < price) {
- price = product?.[`price_${pricelist}_f`] || 0
+ price = product?.[`price_${pricelist}_f`] || 0;
+ priceDiscount = price;
}
- if (product?.flashsale_id_i > 0 ) {
- price = product?.flashsale_base_price_f || 0
- priceDiscount = product?.flashsale_price_f || 0
- discountPercentage = product?.flashsale_discount_f || 0
+ if (product?.flashsale_id_i > 0) {
+ price = product?.flashsale_base_price_f || 0;
+ priceDiscount = product?.flashsale_price_f || 0;
+ discountPercentage = product?.flashsale_discount_f || 0;
}
let productMapped = {
@@ -29,48 +30,48 @@ export const productMappingSolr = (products, pricelist) => {
categories: [],
flashSale: {
id: product?.flashsale_id_i,
- remainingTime: flashsaleTime(product?.flashsale_end_date_s)?.remainingTime,
+ remainingTime: flashsaleTime(product?.flashsale_end_date_s)
+ ?.remainingTime,
name: product?.product?.flashsale_name_s,
- tag: product?.flashsale_tag_s || 'FLASH SALE'
+ tag: product?.flashsale_tag_s || 'FLASH SALE',
},
- qtySold : product?.qty_sold_f || 0
- }
+ qtySold: product?.qty_sold_f || 0,
+ };
if (product.manufacture_id_i && product.manufacture_name_s) {
productMapped.manufacture = {
id: product.manufacture_id_i || '',
name: product.manufacture_name_s || '',
imagePromotion1: product.image_promotion_1_s || '',
- imagePromotion2: product.image_promotion_2_s || ''
- }
+ imagePromotion2: product.image_promotion_2_s || '',
+ };
}
productMapped.categories = [
{
id: product.category_id_i || '',
- name: product.category_name_s || ''
- }
- ]
- return productMapped
- })
-}
+ name: product.category_name_s || '',
+ },
+ ];
+ return productMapped;
+ });
+};
export const variantsMappingSolr = (parent, products, pricelist) => {
return products.map((product) => {
- let price = product.price_tier1_v2_f || 0
- let priceDiscount = 0
- let discountPercentage = 0
+ let price = product.price_tier1_v2_f || 0;
+ let priceDiscount = price;
+ let discountPercentage = 0;
- if (pricelist) {
- if (product?.[`price_${pricelist}_f`] < price) {
- price = product?.[`price_${pricelist}_f`] || 0
- }
+ if (pricelist && product?.[`price_${pricelist}_f`] < price) {
+ price = product?.[`price_${pricelist}_f`] || 0;
+ priceDiscount = price;
}
if (product?.flashsale_id_i > 0 && product?.flashsale_price_f < price) {
- price = product?.flashsale_base_price_f || 0
- priceDiscount = product?.flashsale_price_f || 0
- discountPercentage = product?.flashsale_discount_f || 0
+ price = product?.flashsale_base_price_f || 0;
+ priceDiscount = product?.flashsale_price_f || 0;
+ discountPercentage = product?.flashsale_discount_f || 0;
}
let productMapped = {
@@ -87,30 +88,33 @@ export const variantsMappingSolr = (parent, products, pricelist) => {
weight: product.weight_f || 0,
manufacture: {},
parent: {},
- qtySold : product?.qty_sold_f || 0
- }
+ qtySold: product?.qty_sold_f || 0,
+ };
if (product.manufacture_id_i && product.manufacture_name_s) {
productMapped.manufacture = {
id: product.manufacture_id_i || '',
- name: product.manufacture_name_s || ''
- }
+ name: product.manufacture_name_s || '',
+ };
}
productMapped.parent = {
id: parent.product_id_i || '',
image: parent.image_s || '',
- name: parent.name_s || ''
- }
- return productMapped
- })
-}
+ name: parent.name_s || '',
+ };
+ return productMapped;
+ });
+};
const flashsaleTime = (endDate) => {
- const flashsaleEndDate = new Date(endDate)
- const currentTime = new Date()
+ const flashsaleEndDate = new Date(endDate);
+ const currentTime = new Date();
- const timeDifferenceInMillis = flashsaleEndDate - currentTime
- const timeDifferenceInSeconds = timeDifferenceInMillis / 1000
+ const timeDifferenceInMillis = flashsaleEndDate - currentTime;
+ const timeDifferenceInSeconds = timeDifferenceInMillis / 1000;
- return { remainingTime: timeDifferenceInSeconds, isFlashSale: flashsaleEndDate > currentTime }
-}
+ return {
+ remainingTime: timeDifferenceInSeconds,
+ isFlashSale: flashsaleEndDate > currentTime,
+ };
+};