summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafi Zadanly <zadanlyr@gmail.com>2024-01-16 16:08:43 +0700
committerRafi Zadanly <zadanlyr@gmail.com>2024-01-16 16:08:43 +0700
commita70fd5b6d9c7a769ac1aaa22a7d037ba3be27a05 (patch)
tree825d6b5de089bb22003bb2a517d371dc291f1962
parentd9dafa74857959974e9d379dc1a3abfbaf2af83d (diff)
Update improve product detail performance
-rw-r--r--package.json1
-rw-r--r--src-migrate/components/ui/image.tsx2
-rw-r--r--src-migrate/libs/whatsappUrl.ts4
-rw-r--r--src-migrate/modules/product-detail/components/AddToWishlist.tsx2
-rw-r--r--src-migrate/modules/product-detail/components/Breadcrumb.tsx40
-rw-r--r--src-migrate/modules/product-detail/components/Image.tsx12
-rw-r--r--src-migrate/modules/product-detail/components/Information.tsx34
-rw-r--r--src-migrate/modules/product-detail/components/PriceAction.tsx10
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx68
-rw-r--r--src-migrate/modules/product-detail/components/VariantList.tsx10
-rw-r--r--src-migrate/pages/shop/product/[slug].tsx13
-rw-r--r--src-migrate/pages/shop/product/product.module.css0
-rw-r--r--src-migrate/services/product.ts8
-rw-r--r--src-migrate/types/auth.ts4
-rw-r--r--src-migrate/types/category.ts4
-rw-r--r--src-migrate/types/odoo.ts5
-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/fonts/Inter/inter.css85
-rw-r--r--src/pages/_app.jsx15
-rw-r--r--src/pages/_document.jsx31
22 files changed, 288 insertions, 277 deletions
diff --git a/package.json b/package.json
index 9b9a002a..7f855b5f 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
"react-loading-skeleton": "^3.3.1",
"react-query": "^3.39.3",
"react-select": "^5.7.0",
+ "react-web-share": "^2.0.2",
"snakecase-keys": "^5.5.0",
"swiper": "^8.4.4",
"tw-merge": "^0.0.1-alpha.3",
diff --git a/src-migrate/components/ui/image.tsx b/src-migrate/components/ui/image.tsx
index a91b2a9d..c7ed0b0e 100644
--- a/src-migrate/components/ui/image.tsx
+++ b/src-migrate/components/ui/image.tsx
@@ -26,7 +26,7 @@ const Image = (props: ImageProps) => {
>
<NextImage
className={clsxm(
- 'duration-700 ease-in-out',
+ 'duration-500 ease-in-out',
isLoading
? 'scale-[1.02] blur-xl grayscale'
: 'scale-100 blur-0 grayscale-0',
diff --git a/src-migrate/libs/whatsappUrl.ts b/src-migrate/libs/whatsappUrl.ts
index 02c36cef..66879585 100644
--- a/src-migrate/libs/whatsappUrl.ts
+++ b/src-migrate/libs/whatsappUrl.ts
@@ -26,7 +26,9 @@ export const whatsappUrl = ({
let greetingText = '';
if (needLogin && !auth) {
- return fallbackUrl ? `/login=next?${fallbackUrl}` : '/login';
+ return fallbackUrl
+ ? `/login?next=${encodeURIComponent(fallbackUrl)}`
+ : '/login';
}
let result = TEMPLATES[template].replace(
diff --git a/src-migrate/modules/product-detail/components/AddToWishlist.tsx b/src-migrate/modules/product-detail/components/AddToWishlist.tsx
index eab3c7be..fccbdcf5 100644
--- a/src-migrate/modules/product-detail/components/AddToWishlist.tsx
+++ b/src-migrate/modules/product-detail/components/AddToWishlist.tsx
@@ -6,7 +6,7 @@ const AddToWishlist = () => {
return (
<Button
variant='link'
- className='!text-gray-500 !font-medium'
+ colorScheme='gray'
leftIcon={<HeartIcon size={18} />}
>
Wishlist
diff --git a/src-migrate/modules/product-detail/components/Breadcrumb.tsx b/src-migrate/modules/product-detail/components/Breadcrumb.tsx
new file mode 100644
index 00000000..0ee5b115
--- /dev/null
+++ b/src-migrate/modules/product-detail/components/Breadcrumb.tsx
@@ -0,0 +1,40 @@
+import React, { Fragment } from 'react'
+import { useQuery } from 'react-query'
+import { getProductCategoryBreadcrumb } from '~/services/product'
+import Link from 'next/link'
+import { createSlug } from '~/libs/slug'
+
+type Props = {
+ id: number,
+ name: string
+}
+
+const Breadcrumb = ({ id, name }: Props) => {
+ const query = useQuery({
+ queryKey: ['product-category-breadcrumb'],
+ queryFn: () => getProductCategoryBreadcrumb(id)
+ })
+
+ const breadcrumbs = query.data || []
+
+ return (
+ <div className='line-clamp-3 md:line-clamp-1 leading-7'>
+ <Link href='/' className='text-danger-500'>Home</Link>
+ <span className='mx-2'>/</span>
+ {breadcrumbs.map((category, index) => (
+ <Fragment key={index}>
+ <Link
+ href={createSlug('/shop/category/', category.name, category.id.toString())}
+ className='text-danger-500'
+ >
+ {category.name}
+ </Link>
+ <span className='mx-2'>/</span>
+ </Fragment>
+ ))}
+ <span>{name}</span>
+ </div>
+ )
+}
+
+export default Breadcrumb \ No newline at end of file
diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx
index 361580ea..2c5e989b 100644
--- a/src-migrate/modules/product-detail/components/Image.tsx
+++ b/src-migrate/modules/product-detail/components/Image.tsx
@@ -11,14 +11,16 @@ type Props = {
const Image = ({ product }: Props) => {
return (
- <div className='h-[340px] border border-gray-200 rounded-lg p-2 relative'>
+ <div className='h-[250px] md:h-[340px] flex items-center justify-center border border-gray-200 rounded-lg p-4 relative'>
<ImageUI
src={product.image || '/images/noimage.jpeg'}
alt={product.name}
- width={512}
- height={512}
- className='object-contain object-center h-full'
- classNames={{ wrapper: 'h-full' }}
+ width={256}
+ height={256}
+ className='object-contain object-center h-full w-full'
+ classNames={{ wrapper: 'h-full w-full' }}
+ loading='eager'
+ priority
/>
<div className='absolute hidden md:block top-4 right-4'>
<Tooltip
diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx
index fd0e0b3c..52eb6b88 100644
--- a/src-migrate/modules/product-detail/components/Information.tsx
+++ b/src-migrate/modules/product-detail/components/Information.tsx
@@ -9,6 +9,7 @@ import { IProductDetail } from '~/types/product'
import { IProductVariantSLA } from '~/types/productVariant'
import { createSlug } from '~/libs/slug'
import { getVariantSLA } from '~/services/productVariant'
+import { formatToShortText } from '~/libs/formatNumber'
const Skeleton = dynamic(() => import('@chakra-ui/react').then((mod) => mod.Skeleton))
@@ -31,10 +32,6 @@ const Information = ({ product }: Props) => {
<div className={style['label']}>SKU Number</div>
<div className={style['value']}>SKU-{product.id}</div>
</div>
- {/* <div className={style['row']}>
- <div className={style['label']}>Part Number</div>
- <div className={style['value']}>{product.code || '-'}</div>
- </div> */}
<div className={style['row']}>
<div className={style['label']}>Manufacture</div>
<div className={style['value']}>
@@ -48,35 +45,10 @@ const Information = ({ product }: Props) => {
) : '-'}
</div>
</div>
- {/* <div className={style['row']}>
- <div className={style['label']}>Preparation Time</div>
- <div className={style['value']}>
- {product.variant_total > 1 && 'Lihat Variant'}
- {product.variant_total === 1 && (
- <Skeleton isLoaded={querySLA.isSuccess} w={querySLA.isSuccess ? '100%' : '40px'} h='100%'>
- {sla?.sla_date}
- </Skeleton>
- )}
- </div>
- </div>
<div className={style['row']}>
- <div className={style['label']}>Stock</div>
- <div className={style['value']}>
- {product.variant_total > 1 && 'Lihat Variant'}
- {product.variant_total === 1 && (
- <Skeleton isLoaded={querySLA.isSuccess} w={querySLA.isSuccess ? '100%' : '10px'} h='100%'>
- {sla?.qty && sla.qty > 0 ? sla?.qty : '-'}
- </Skeleton>
- )}
- </div>
+ <div className={style['label']}>Terjual</div>
+ <div className={style['value']}>{product.qty_sold > 0 ? formatToShortText(product.qty_sold) : '-'}</div>
</div>
- <div className={style['row']}>
- <div className={style['label']}>Weight</div>
- <div className={style['value']}>
- {product.variant_total > 1 && 'Lihat Variant'}
- {product.variant_total === 1 && (product.weight > 0 ? `${product.weight} kg` : '-')}
- </div>
- </div> */}
</div>
)
}
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx
index cfb596fa..dd211f6f 100644
--- a/src-migrate/modules/product-detail/components/PriceAction.tsx
+++ b/src-migrate/modules/product-detail/components/PriceAction.tsx
@@ -28,13 +28,6 @@ const PriceAction = ({ product }: Props) => {
return (
<div className='block md:sticky top-[150px] bg-white py-0 md:py-6 z-10'>
- {product.qty_sold > 0 && (
- <div className={style['secondary-text']}>
- {formatToShortText(product.qty_sold)} Terjual
- </div>
- )}
- <div className='h-2' />
-
{!!activePrice && activePrice.price > 0 && (
<>
<div className={style['main-price']}>
@@ -60,7 +53,8 @@ const PriceAction = ({ product }: Props) => {
<div className='h-4' />
<div className={style['action-wrapper']}>
- <input type='number' value={quantityInput} onChange={(e) => setQuantityInput(e.target.value)} className={style['quantity-input']} />
+ <label htmlFor="quantity" className='hidden'>Quantity</label>
+ <input type='number' id='quantity' value={quantityInput} onChange={(e) => setQuantityInput(e.target.value)} className={style['quantity-input']} />
<AddToCart variantId={activeVariantId} quantity={Number(quantityInput)} />
<AddToCart source='buy' variantId={activeVariantId} quantity={Number(quantityInput)} />
</div>
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index 08ad7d51..93fa7118 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -2,28 +2,34 @@ import style from '../styles/product-detail.module.css'
import React, { useEffect } from 'react'
import Link from 'next/link'
-import { MessageCircleIcon } from 'lucide-react'
+import { useRouter } from 'next/router'
+
+import { MessageCircleIcon, Share2Icon } from 'lucide-react'
import { Button } from '@chakra-ui/react'
import { IProductDetail } from '~/types/product'
+import useDevice from '@/core/hooks/useDevice'
+import { whatsappUrl } from '~/libs/whatsappUrl'
+import { useProductDetail } from '../stores/useProductDetail'
+
+import { RWebShare } from 'react-web-share'
import ProductImage from './Image'
import Information from './Information'
import AddToWishlist from './AddToWishlist'
import VariantList from './VariantList'
import SimilarSide from './SimilarSide'
import SimilarBottom from './SimilarBottom'
-import useDevice from '@/core/hooks/useDevice'
import PriceAction from './PriceAction'
-import { whatsappUrl } from '~/libs/whatsappUrl'
-import { useRouter } from 'next/router'
-import { useProductDetail } from '../stores/useProductDetail'
import ProductPromoSection from '~/modules/product-promo/components/Section'
+import Breadcrumb from './Breadcrumb'
type Props = {
product: IProductDetail
}
+const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST
+
const ProductDetail = ({ product }: Props) => {
const { isDesktop, isMobile } = useDevice()
const router = useRouter()
@@ -46,6 +52,9 @@ const ProductDetail = ({ product }: Props) => {
return (
<>
<div className='md:flex md:flex-wrap'>
+ <div className="w-full mb-4 md:mb-6 px-4 md:px-0">
+ <Breadcrumb id={product.id} name={product.name} />
+ </div>
<div className='md:w-9/12 md:flex md:flex-col md:pr-4'>
<div className='md:flex md:flex-wrap'>
<div className="md:w-4/12">
@@ -63,18 +72,39 @@ const ProductDetail = ({ product }: Props) => {
<Information product={product} />
- <div className='h-4' />
+ <div className='h-6' />
+
+ <div className="flex gap-x-5">
+ <Button
+ as={Link}
+ href={askAdminUrl}
+ variant='link'
+ target='_blank'
+ colorScheme='gray'
+ leftIcon={<MessageCircleIcon size={18} />}
+ >
+ Ask Admin
+ </Button>
+
+ <AddToWishlist />
+
+ <RWebShare
+ data={{
+ text: 'Check out this product',
+ title: `${product.name} - Indoteknik.com`,
+ url: SELF_HOST + router.asPath
+ }}
+ >
+ <Button
+ variant='link'
+ colorScheme='gray'
+ leftIcon={<Share2Icon size={18} />}
+ >
+ Share
+ </Button>
+ </RWebShare>
+ </div>
- <Button
- as={Link}
- href={askAdminUrl}
- variant='link'
- target='_blank'
- colorScheme='red'
- leftIcon={<MessageCircleIcon size={18} />}
- >
- Ask Admin
- </Button>
</div>
</div>
@@ -117,9 +147,7 @@ const ProductDetail = ({ product }: Props) => {
<div className="md:w-3/12">
<PriceAction product={product} />
- <AddToWishlist />
-
- <div className='h-8' />
+ <div className='h-6' />
<div className={style['heading']}>
Produk Serupa
@@ -131,7 +159,7 @@ const ProductDetail = ({ product }: Props) => {
</div>
)}
- <div className='md:w-full py-0 md:py-10 px-4 md:px-0'>
+ <div className='md:w-full pt-4 md:py-10 px-4 md:px-0'>
<div className={style['heading']}>
Kamu Mungkin Juga Suka
</div>
diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx
index 96b7486b..e8c18921 100644
--- a/src-migrate/modules/product-detail/components/VariantList.tsx
+++ b/src-migrate/modules/product-detail/components/VariantList.tsx
@@ -22,10 +22,10 @@ const VariantList = ({ variants }: Props) => {
<div className={style['header']}>
<div className="w-2/12 sticky left-0 bg-gray-200">Part Number</div>
<div className="w-2/12">Variant</div>
- <div className="w-2/12">Stock</div>
+ <div className="w-1/12">Stock</div>
<div className="w-2/12">Masa Persiapan</div>
<div className="w-1/12">Berat</div>
- <div className="w-2/12">Harga</div>
+ <div className="w-3/12">Harga</div>
</div>
{variants.map((variant) => (
<LazyLoadComponent key={variant.id}>
@@ -49,8 +49,8 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => {
return (
<div className={style['row']}>
<div className='w-2/12 sticky left-0 bg-white md:bg-transparent'>{variant.code}</div>
- <div className='w-2/12'>{variant.attributes.join(', ')}</div>
- <div className='w-2/12'>
+ <div className='w-2/12'>{variant.attributes.join(', ') || '-'}</div>
+ <div className='w-1/12'>
<Skeleton isLoaded={querySLA.isSuccess} h='21px' w={16}>
{sla?.qty !== undefined && (
<>
@@ -68,7 +68,7 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => {
<div className='w-1/12'>
{variant.weight > 0 ? `${variant.weight} Kg` : '-'}
</div>
- <div className='w-2/12'>
+ <div className='w-3/12'>
{variant.price.price > 0 && `Rp ${formatCurrency(variant.price.price)}`}
{variant.price.price === 0 && '-'}
</div>
diff --git a/src-migrate/pages/shop/product/[slug].tsx b/src-migrate/pages/shop/product/[slug].tsx
index 883532ed..733e10d9 100644
--- a/src-migrate/pages/shop/product/[slug].tsx
+++ b/src-migrate/pages/shop/product/[slug].tsx
@@ -1,5 +1,3 @@
-import style from './product.module.css'
-
import { GetServerSideProps, NextPage } from 'next'
import React from 'react'
import dynamic from 'next/dynamic'
@@ -10,6 +8,7 @@ import { getIdFromSlug } from '~/libs/slug'
import { IProductDetail } from '~/types/product'
import { Seo } from '~/components/seo'
+import { useRouter } from 'next/router'
const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout'), { ssr: false })
const ProductDetail = dynamic(() => import('~/modules/product-detail'), { ssr: false })
@@ -36,14 +35,18 @@ export const getServerSideProps: GetServerSideProps<PageProps> = (async (context
}
})
+const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST
+
const ProductDetailPage: NextPage<PageProps> = ({ product }) => {
+ const router = useRouter();
+
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,
+ url: SELF_HOST + router.asPath,
images: [
{
url: product?.image,
@@ -60,10 +63,10 @@ const ProductDetailPage: NextPage<PageProps> = ({ product }) => {
content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}`,
}
]}
- // canonical=''
+ canonical={SELF_HOST + router.asPath}
/>
- <div className='md:container pt-10'>
+ <div className='md:container pt-4 md:pt-6'>
<ProductDetail product={product} />
</div>
</BasicLayout>
diff --git a/src-migrate/pages/shop/product/product.module.css b/src-migrate/pages/shop/product/product.module.css
deleted file mode 100644
index e69de29b..00000000
--- a/src-migrate/pages/shop/product/product.module.css
+++ /dev/null
diff --git a/src-migrate/services/product.ts b/src-migrate/services/product.ts
index c9e93396..4ef027e1 100644
--- a/src-migrate/services/product.ts
+++ b/src-migrate/services/product.ts
@@ -1,5 +1,7 @@
import { IProduct, IProductDetail } from '~/types/product';
import snakeCase from 'snakecase-keys';
+import odooApi from '~/libs/odooApi';
+import { ICategoryBreadcrumb } from '~/types/category';
const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST;
@@ -57,3 +59,9 @@ export const getProductSimilar = async ({
.then((res) => res.json())
.then((res) => snakeCase(res.response));
};
+
+export const getProductCategoryBreadcrumb = async (
+ id: number
+): Promise<ICategoryBreadcrumb[]> => {
+ return await odooApi('GET', `/api/v1/product/${id}/category-breadcrumb`);
+};
diff --git a/src-migrate/types/auth.ts b/src-migrate/types/auth.ts
index 4f69bb96..02e3623d 100644
--- a/src-migrate/types/auth.ts
+++ b/src-migrate/types/auth.ts
@@ -1,5 +1,5 @@
import { registerSchema } from '~/validations/auth';
-import { OdooApiProps } from './odoo';
+import { OdooApiRes } from './odoo';
import { z } from 'zod';
export type AuthProps = {
@@ -17,7 +17,7 @@ export type AuthProps = {
token: string;
};
-export type AuthApiProps = OdooApiProps & { result: AuthProps };
+export type AuthApiProps = OdooApiRes<AuthProps>;
export type RegisterProps = z.infer<typeof registerSchema>;
diff --git a/src-migrate/types/category.ts b/src-migrate/types/category.ts
new file mode 100644
index 00000000..1037b5f9
--- /dev/null
+++ b/src-migrate/types/category.ts
@@ -0,0 +1,4 @@
+export interface ICategoryBreadcrumb {
+ id: number;
+ name: string;
+}
diff --git a/src-migrate/types/odoo.ts b/src-migrate/types/odoo.ts
index b34bc667..73a029e9 100644
--- a/src-migrate/types/odoo.ts
+++ b/src-migrate/types/odoo.ts
@@ -1,6 +1,7 @@
-export type OdooApiProps = {
+export interface OdooApiRes<T> {
status: {
code: number;
description: string;
};
-};
+ result: T;
+}
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/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/pages/_app.jsx b/src/pages/_app.jsx
index aa545863..9067fd03 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -1,6 +1,6 @@
import '@/fonts/Inter/inter.css';
import '@/styles/globals.css';
-import 'react-loading-skeleton/dist/skeleton.css';
+// import 'react-loading-skeleton/dist/skeleton.css';
import { useEffect, useState } from 'react';
import dynamic from 'next/dynamic';
@@ -9,8 +9,6 @@ import { AnimatePresence, motion } from 'framer-motion';
import { QueryClient, QueryClientProvider } from 'react-query';
import useDevice from '@/core/hooks/useDevice';
-import { ProductProvider } from '@/contexts/ProductContext';
-import { ProductCartProvider } from '@/contexts/ProductCartContext';
import theme from '../../chakra.theme';
const NextProgress = dynamic(() => import('next-progress'), { ssr: false });
@@ -18,6 +16,17 @@ 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 }
diff --git a/src/pages/_document.jsx b/src/pages/_document.jsx
index 3762c63b..8eb3f15e 100644
--- a/src/pages/_document.jsx
+++ b/src/pages/_document.jsx
@@ -1,16 +1,23 @@
-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='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' />
@@ -20,7 +27,11 @@ export default function MyDocument() {
<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'
@@ -36,7 +47,7 @@ export default function MyDocument() {
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-10501937-1');
- `
+ `,
}}
/>
@@ -55,7 +66,7 @@ export default function MyDocument() {
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-G1W8MNZ11P');
- `
+ `,
}}
/>
@@ -68,7 +79,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');
- `
+ `,
}}
/>
@@ -87,7 +98,7 @@ export default function MyDocument() {
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
- gtag('config', 'AW-954540379');`
+ gtag('config', 'AW-954540379');`,
}}
/>
@@ -119,5 +130,5 @@ export default function MyDocument() {
<NextScript />
</body>
</Html>
- )
+ );
}