summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json1
-rw-r--r--src-migrate/modules/page-content/index.tsx33
-rw-r--r--src-migrate/modules/product-card/components/ProductCard.tsx176
-rw-r--r--src-migrate/modules/product-detail/components/Image.tsx118
-rw-r--r--src-migrate/modules/product-detail/components/Information.tsx22
-rw-r--r--src-migrate/modules/product-detail/components/PriceAction.tsx2
-rw-r--r--src-migrate/pages/shop/cart/index.tsx2
-rw-r--r--src-migrate/types/product.ts1
-rw-r--r--src-migrate/types/productVariant.ts1
-rw-r--r--src/components/ui/HeroBanner.jsx37
-rw-r--r--src/components/ui/HeroBannerSecondary.jsx69
-rw-r--r--src/core/components/elements/Footer/BasicFooter.jsx2
-rw-r--r--src/core/components/elements/Footer/SimpleFooter.jsx2
-rw-r--r--src/core/components/elements/Navbar/NavbarDesktop.jsx12
-rw-r--r--src/core/components/elements/Navbar/TopBanner.jsx53
-rw-r--r--src/core/utils/whatsappUrl.js21
-rw-r--r--src/lib/checkout/components/Checkout.jsx40
-rw-r--r--src/lib/flashSale/components/FlashSale.jsx2
-rw-r--r--src/lib/flashSale/components/FlashSaleNonDisplay.jsx2
-rw-r--r--src/lib/home/components/BannerSection.jsx38
-rw-r--r--src/lib/home/components/CategoryDynamic.jsx247
-rw-r--r--src/lib/home/components/CategoryDynamicMobile.jsx63
-rw-r--r--src/lib/home/components/PreferredBrand.jsx81
-rw-r--r--src/lib/home/components/PromotionProgram.jsx96
-rw-r--r--src/lib/product/components/Product/ProductDesktopVariant.jsx18
-rw-r--r--src/lib/product/components/Product/ProductMobileVariant.jsx154
-rw-r--r--src/lib/product/components/ProductCard.jsx15
-rw-r--r--src/lib/treckingAwb/component/Manifest.jsx124
-rw-r--r--src/pages/_document.jsx13
-rw-r--r--src/pages/api/banner-section.js44
-rw-r--r--src/pages/api/category-management.js85
-rw-r--r--src/pages/api/hero-banner.js45
-rw-r--r--src/pages/api/page-content.js43
-rw-r--r--src/pages/api/shop/brands.js30
-rw-r--r--src/pages/api/shop/preferredBrand.js61
-rw-r--r--src/pages/api/shop/product-detail.js2
-rw-r--r--src/pages/api/shop/search.js31
-rw-r--r--src/pages/index.jsx237
-rw-r--r--src/utils/solrMapping.js6
39 files changed, 1186 insertions, 843 deletions
diff --git a/package.json b/package.json
index 3990e5cd..5e981c72 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"react-query": "^3.39.3",
"react-select": "^5.8.0",
"react-web-share": "^2.0.2",
+ "redis": "^4.7.0",
"snakecase-keys": "^5.5.0",
"swiper": "^8.4.4",
"tw-merge": "^0.0.1-alpha.3",
diff --git a/src-migrate/modules/page-content/index.tsx b/src-migrate/modules/page-content/index.tsx
index 3423ca8b..54ee0a04 100644
--- a/src-migrate/modules/page-content/index.tsx
+++ b/src-migrate/modules/page-content/index.tsx
@@ -10,28 +10,21 @@ type Props = {
const PageContent = ({ path }: Props) => {
const [localData, setData] = useState<PageContentProps>();
const [shouldFetch, setShouldFetch] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
- const localData = localStorage.getItem(`page-content:${path}`);
- if (localData) {
- setData(JSON.parse(localData));
- }else{
- setShouldFetch(true);
- }
- },[])
-
- const { data, isLoading } = useQuery<PageContentProps>(
- `page-content:${path}`,
- async () => await getPageContent({ path }), {
- enabled: shouldFetch,
- onSuccess: (data) => {
- if (data) {
- localStorage.setItem(`page-content:${path}`, JSON.stringify(data));
- setData(data);
- }
- },
- }
- );
+ const fetchData = async () => {
+ setIsLoading(true);
+ const res = await fetch(`/api/page-content?path=${path}`);
+ const { data } = await res.json();
+ if (data) {
+ setData(data);
+ }
+ setIsLoading(false);
+ };
+
+ fetchData();
+ }, []);
const parsedContent = useMemo<string>(() => {
if (!localData) return '';
diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx
index 0febfadb..a439cdc8 100644
--- a/src-migrate/modules/product-card/components/ProductCard.tsx
+++ b/src-migrate/modules/product-card/components/ProductCard.tsx
@@ -1,95 +1,108 @@
-import style from '../styles/product-card.module.css'
+import style from '../styles/product-card.module.css';
import ImageNext from 'next/image';
-import clsx from 'clsx'
-import Link from 'next/link'
-import React, { useEffect, useMemo, useState } from 'react'
-import Image from '~/components/ui/image'
-import useUtmSource from '~/hooks/useUtmSource'
-import clsxm from '~/libs/clsxm'
-import formatCurrency from '~/libs/formatCurrency'
-import { formatToShortText } from '~/libs/formatNumber'
-import { createSlug } from '~/libs/slug'
-import { IProduct } from '~/types/product'
-
+import clsx from 'clsx';
+import Link from 'next/link';
+import React, { useEffect, useMemo, useState } from 'react';
+import Image from '~/components/ui/image';
+import useUtmSource from '~/hooks/useUtmSource';
+import clsxm from '~/libs/clsxm';
+import formatCurrency from '~/libs/formatCurrency';
+import { formatToShortText } from '~/libs/formatNumber';
+import { createSlug } from '~/libs/slug';
+import { IProduct } from '~/types/product';
+import useDevice from '@/core/hooks/useDevice';
type Props = {
- product: IProduct
- layout?: 'vertical' | 'horizontal'
-}
+ product: IProduct;
+ layout?: 'vertical' | 'horizontal';
+};
const ProductCard = ({ product, layout = 'vertical' }: Props) => {
- const utmSource = useUtmSource()
-
+ const utmSource = useUtmSource();
+ const { isDesktop, isMobile } = useDevice();
const URL = {
- product: createSlug('/shop/product/', product.name, product.id.toString()) + `?utm_source=${utmSource}`,
- manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString()),
- }
+ product:
+ createSlug('/shop/product/', product.name, product.id.toString()) +
+ `?utm_source=${utmSource}`,
+ manufacture: createSlug(
+ '/shop/brands/',
+ product.manufacture.name,
+ product.manufacture.id.toString()
+ ),
+ };
const image = useMemo(() => {
- if (product.image) return product.image + '?ratio=square'
- return '/images/noimage.jpeg'
- }, [product.image])
+ if (!isDesktop && product.image_mobile) {
+ return product.image_mobile + '?ratio=square';
+ } else {
+ if (product.image) return product.image + '?ratio=square';
+ return '/images/noimage.jpeg';
+ }
+ }, [product.image, product.image_mobile]);
return (
- <div className={clsxm(style['wrapper'], {
- [style['wrapper-v']]: layout === 'vertical',
- [style['wrapper-h']]: layout === 'horizontal',
- })}
+ <div
+ className={clsxm(style['wrapper'], {
+ [style['wrapper-v']]: layout === 'vertical',
+ [style['wrapper-h']]: layout === 'horizontal',
+ })}
>
- <div className={clsxm('relative', {
- [style['image-v']]: layout === 'vertical',
- [style['image-h']]: layout === 'horizontal',
- })}>
+ <div
+ className={clsxm('relative', {
+ [style['image-v']]: layout === 'vertical',
+ [style['image-h']]: layout === 'horizontal',
+ })}
+ >
<Link href={URL.product}>
-
- <div className="relative">
- <Image
- src={image}
- alt={product.name}
- width={128}
- height={128}
- className='object-contain object-center h-full w-full'
- />
- <div className="absolute top-0 right-0 flex mt-2">
- <div className="gambarB ">
- {product.isSni && (
- <ImageNext
- src="/images/sni-logo.png"
- alt="SNI Logo"
- className="w-3 h-4 object-contain object-top sm:h-4"
- width={50}
- height={50}
- />
- )}
- </div>
- <div className="gambarC ">
- {product.isTkdn && (
- <ImageNext
- src="/images/TKDN.png"
- alt="TKDN"
- className="w-5 h-4 object-contain object-top ml-1 mr-1 sm:h-6"
- width={50}
- height={50}
- />
- )}
+ <div className='relative'>
+ <Image
+ src={image}
+ alt={product.name}
+ width={128}
+ height={128}
+ className='object-contain object-center h-full w-full'
+ />
+ <div className='absolute top-0 right-0 flex mt-2'>
+ <div className='gambarB '>
+ {product.isSni && (
+ <ImageNext
+ src='/images/sni-logo.png'
+ alt='SNI Logo'
+ className='w-3 h-4 object-contain object-top sm:h-4'
+ width={50}
+ height={50}
+ />
+ )}
+ </div>
+ <div className='gambarC '>
+ {product.isTkdn && (
+ <ImageNext
+ src='/images/TKDN.png'
+ alt='TKDN'
+ className='w-5 h-4 object-contain object-top ml-1 mr-1 sm:h-6'
+ width={50}
+ height={50}
+ />
+ )}
+ </div>
</div>
</div>
- </div>
{product.variant_total > 1 && (
- <div className={style['variant-badge']}>{product.variant_total} Varian</div>
+ <div className={style['variant-badge']}>
+ {product.variant_total} Varian
+ </div>
)}
</Link>
</div>
- <div className={clsxm({
- [style['content-v']]: layout === 'vertical',
- [style['content-h']]: layout === 'horizontal',
- })}>
- <Link
- href={URL.manufacture}
- className={style['brand']}
- >
+ <div
+ className={clsxm({
+ [style['content-v']]: layout === 'vertical',
+ [style['content-h']]: layout === 'horizontal',
+ })}
+ >
+ <Link href={URL.manufacture} className={style['brand']}>
{product.manufacture.name}
</Link>
@@ -113,17 +126,15 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => {
<div className='h-1.5' />
<div className={style['price-inc']}>
- Inc PPN:
- Rp {formatCurrency(Math.round(product.lowest_price.price * 1.11))}
+ Inc PPN: Rp{' '}
+ {formatCurrency(Math.round(product.lowest_price.price * 1.11))}
</div>
<div className='h-1' />
<div className='flex items-center gap-x-2.5'>
{product.stock_total > 0 && (
- <div className={style['ready-stock']}>
- Ready Stock
- </div>
+ <div className={style['ready-stock']}>Ready Stock</div>
)}
{product.qty_sold > 0 && (
<div className={style['sold']}>
@@ -131,14 +142,11 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => {
</div>
)}
</div>
-
</div>
</div>
- )
-}
-
-const classPrefix = ({ layout }: Props) => {
+ );
+};
-}
+const classPrefix = ({ layout }: Props) => {};
-export default ProductCard \ No newline at end of file
+export default ProductCard;
diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx
index 30ca0d34..96ae2027 100644
--- a/src-migrate/modules/product-detail/components/Image.tsx
+++ b/src-migrate/modules/product-detail/components/Image.tsx
@@ -1,22 +1,22 @@
import style from '../styles/image.module.css';
import ImageNext from 'next/image';
-import React, { useEffect, useMemo, useState } from 'react'
-import { InfoIcon } from 'lucide-react'
-import { Tooltip } from '@chakra-ui/react'
+import React, { useEffect, useMemo, useState } from 'react';
+import { InfoIcon } from 'lucide-react';
+import { Tooltip } from '@chakra-ui/react';
-import { IProductDetail } from '~/types/product'
-import ImageUI from '~/components/ui/image'
+import { IProductDetail } from '~/types/product';
+import ImageUI from '~/components/ui/image';
import moment from 'moment';
-
+import useDevice from '@/core/hooks/useDevice';
type Props = {
- product: IProductDetail
-}
+ product: IProductDetail;
+};
const Image = ({ product }: Props) => {
- const flashSale = product.flash_sale
+ const flashSale = product.flash_sale;
const [count, setCount] = useState(flashSale?.remaining_time || 0);
-
+ const { isDesktop, isMobile } = useDevice();
useEffect(() => {
let interval: NodeJS.Timeout;
@@ -34,59 +34,60 @@ const Image = ({ product }: Props) => {
};
}, [flashSale?.remaining_time]);
- const duration = moment.duration(count, 'seconds')
-
+ const duration = moment.duration(count, 'seconds');
const image = useMemo(() => {
- if (product.image) return product.image + '?ratio=square'
- return '/images/noimage.jpeg'
- }, [product.image])
+ if (!isDesktop && product.image_mobile) {
+ return product.image_mobile + '?ratio=square';
+ } else {
+ if (product.image) return product.image + '?ratio=square';
+ return '/images/noimage.jpeg';
+ }
+ }, [product.image, product.image_mobile]);
return (
<div className={style['wrapper']}>
{/* <div className="relative"> */}
- <ImageUI
- src={image}
- alt={product.name}
- width={256}
- height={256}
- className={style['image']}
- loading='eager'
- priority
- />
- <div className="absolute top-4 right-10 flex ">
- <div className="gambarB ">
- {product.isSni && (
- <ImageNext
- src="/images/sni-logo.png"
- alt="SNI Logo"
- className="w-12 h-8 object-contain object-top sm:h-6"
- width={50}
- height={50}
- />
- )}
- </div>
- <div className="gambarC ">
- {product.isTkdn && (
- <ImageNext
- src="/images/TKDN.png"
- alt="TKDN"
- className="w-16 h-8 object-contain object-top ml-1 mr-1 sm:h-6"
- width={50}
- height={50}
- />
- )}
- </div>
- </div>
- {/* </div> */}
-
-
+ <ImageUI
+ src={image}
+ alt={product.name}
+ width={256}
+ height={256}
+ className={style['image']}
+ loading='eager'
+ priority
+ />
+ <div className='absolute top-4 right-10 flex '>
+ <div className='gambarB '>
+ {product.isSni && (
+ <ImageNext
+ src='/images/sni-logo.png'
+ alt='SNI Logo'
+ className='w-12 h-8 object-contain object-top sm:h-6'
+ width={50}
+ height={50}
+ />
+ )}
+ </div>
+ <div className='gambarC '>
+ {product.isTkdn && (
+ <ImageNext
+ src='/images/TKDN.png'
+ alt='TKDN'
+ className='w-16 h-8 object-contain object-top ml-1 mr-1 sm:h-6'
+ width={50}
+ height={50}
+ />
+ )}
+ </div>
+ </div>
+ {/* </div> */}
<div className={style['absolute-info']}>
<Tooltip
placement='bottom-end'
label='Gambar atau foto berperan sebagai ilustrasi produk. Kadang tidak sesuai dengan kondisi terbaru dengan berbagai perubahan dan perbaikan. Hubungi admin kami untuk informasi yang lebih baik perihal gambar.'
>
- <div className="text-gray-600">
+ <div className='text-gray-600'>
<InfoIcon size={20} />
</div>
</Tooltip>
@@ -94,7 +95,7 @@ const Image = ({ product }: Props) => {
{flashSale.remaining_time > 0 && (
<div className='absolute bottom-0 w-full h-14'>
- <div className="relative w-full h-full">
+ <div className='relative w-full h-full'>
<ImageUI
src='/images/BG-FLASH-SALE.jpg'
alt='Flash Sale Indoteknik'
@@ -105,7 +106,9 @@ const Image = ({ product }: Props) => {
<div className={style['flashsale']}>
<div className='flex items-center gap-x-3'>
- <div className={style['disc-badge']}>{Math.floor(product.lowest_price.discount_percentage)}%</div>
+ <div className={style['disc-badge']}>
+ {Math.floor(product.lowest_price.discount_percentage)}%
+ </div>
<div className={style['flashsale-text']}>
<ImageUI
src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg'
@@ -122,12 +125,11 @@ const Image = ({ product }: Props) => {
<span>{duration.seconds().toString().padStart(2, '0')}</span>
</div>
</div>
-
</div>
</div>
)}
</div>
- )
-}
+ );
+};
-export default Image \ No newline at end of file
+export default Image;
diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx
index b9f4be91..5e1ea186 100644
--- a/src-migrate/modules/product-detail/components/Information.tsx
+++ b/src-migrate/modules/product-detail/components/Information.tsx
@@ -96,7 +96,7 @@ const Information = ({ product }: Props) => {
<label className='form-label mb-2 text-lg text-red-600'>
Pilih Variant * :{' '}
<span className='text-gray_r-9 text-sm'>
- {product?.variant_total} Variants
+ {product?.variants?.length} Variants
</span>{' '}
</label>
<AutoComplete
@@ -190,13 +190,19 @@ const Information = ({ product }: Props) => {
product.manufacture.id.toString()
)}
>
- <Image
- height={50}
- width={100}
- src={product.manufacture.logo}
- alt={product.manufacture.name}
- className='h-8 object-fit'
- />
+ {product?.manufacture.logo ? (
+ <Image
+ height={50}
+ width={100}
+ src={product.manufacture.logo}
+ alt={product.manufacture.name}
+ className='h-8 object-fit'
+ />
+ ) : (
+ <p className='font-bold text-red-500'>
+ {product.manufacture.name}
+ </p>
+ )}
</Link>
) : (
'-'
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx
index 413c643a..0b27b1b3 100644
--- a/src-migrate/modules/product-detail/components/PriceAction.tsx
+++ b/src-migrate/modules/product-detail/components/PriceAction.tsx
@@ -162,7 +162,7 @@ const PriceAction = ({ product }: Props) => {
</span> */}
</div>
<div>
- {product?.is_in_bu && (
+ {selectedVariant?.is_in_bu && (
<Link href='/panduan-pick-up-service' className='group'>
<Image
src='/images/PICKUP-NOW.png'
diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx
index 70a28073..24baa933 100644
--- a/src-migrate/pages/shop/cart/index.tsx
+++ b/src-migrate/pages/shop/cart/index.tsx
@@ -35,8 +35,6 @@ const CartPage = () => {
const [hasChanged, setHasChanged] = useState(false);
const prevCartRef = useRef<CartItem[] | null>(null);
- console.log('ini cart', cart);
-
useEffect(() => {
const handleScroll = () => {
setIsTop(window.scrollY < 200);
diff --git a/src-migrate/types/product.ts b/src-migrate/types/product.ts
index fb9c888c..85ea702a 100644
--- a/src-migrate/types/product.ts
+++ b/src-migrate/types/product.ts
@@ -3,6 +3,7 @@ import { IProductVariantDetail } from './productVariant';
export interface IProduct {
id: number;
image: string;
+ image_mobile: string;
code: string;
display_name: string;
name: string;
diff --git a/src-migrate/types/productVariant.ts b/src-migrate/types/productVariant.ts
index 861b216a..5144e7c1 100644
--- a/src-migrate/types/productVariant.ts
+++ b/src-migrate/types/productVariant.ts
@@ -4,6 +4,7 @@ export interface IProductVariantDetail {
code: string;
name: string;
weight: number;
+ is_in_bu: boolean;
is_flashsale: {
remaining_time: number;
is_flashsale: boolean;
diff --git a/src/components/ui/HeroBanner.jsx b/src/components/ui/HeroBanner.jsx
index 64838b85..2eea5915 100644
--- a/src/components/ui/HeroBanner.jsx
+++ b/src/components/ui/HeroBanner.jsx
@@ -6,7 +6,7 @@ import 'swiper/css/pagination';
import { Swiper, SwiperSlide } from 'swiper/react';
import Image from 'next/image';
-import { useMemo } from 'react';
+import { useEffect, useMemo, useState } from 'react';
import { useQuery } from 'react-query';
import { bannerApi } from '@/api/bannerApi';
@@ -27,7 +27,20 @@ const swiperBanner = {
};
const HeroBanner = () => {
- const heroBanner = useQuery('heroBanner', bannerApi({ type: 'index-a-1' }));
+ // const heroBanner = useQuery('heroBanner', bannerApi({ type: 'index-a-1' }));
+ const [data, setData] = useState(null);
+ useEffect(() => {
+ const fetchData = async () => {
+ const res = await fetch(`/api/hero-banner?type=index-a-1`);
+ const { data } = await res.json();
+ if (data) {
+ setData(data);
+ }
+ };
+
+ fetchData();
+ }, []);
+ const heroBanner = data;
const swiperBannerMobile = {
...swiperBanner,
@@ -44,9 +57,9 @@ const HeroBanner = () => {
};
const BannerComponent = useMemo(() => {
- if (!heroBanner.data) return null;
+ if (!heroBanner) return null;
- return heroBanner.data.map((banner, index) => (
+ return heroBanner.map((banner, index) => (
<SwiperSlide key={index}>
<Link href={banner.url} className='w-full h-auto'>
<Image
@@ -56,22 +69,22 @@ const HeroBanner = () => {
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={index === 0}
+ loading={index === 0 ? 'eager' : 'lazy'}
+ placeholder='blur'
+ blurDataURL='/images/indoteknik-placeholder.png'
+ sizes='(max-width: 768px) 100vw, 50vw'
/>
</Link>
</SwiperSlide>
));
- }, [heroBanner.data]);
+ }, [heroBanner]);
return (
<>
<MobileView>
<SmoothRender
- isLoaded={heroBanner.data?.length > 0}
+ isLoaded={heroBanner?.length > 0}
height='68vw'
duration='750ms'
delay='100ms'
@@ -81,7 +94,7 @@ const HeroBanner = () => {
</MobileView>
<DesktopView>
- {heroBanner.data?.length > 0 && (
+ {heroBanner?.length > 0 && (
<Swiper {...swiperBannerDesktop}>{BannerComponent}</Swiper>
)}
</DesktopView>
diff --git a/src/components/ui/HeroBannerSecondary.jsx b/src/components/ui/HeroBannerSecondary.jsx
index a7b32a4a..6074c9a6 100644
--- a/src/components/ui/HeroBannerSecondary.jsx
+++ b/src/components/ui/HeroBannerSecondary.jsx
@@ -1,39 +1,58 @@
-import Link from '@/core/components/elements/Link/Link'
-import { getRandomInt } from '@/utils/getRandomInt'
-import Image from 'next/image'
-import { useMemo } from 'react'
-import { useQuery } from 'react-query'
-import { HeroBannerSkeleton } from '../skeleton/BannerSkeleton'
-import { bannerApi } from '@/api/bannerApi'
+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 { HeroBannerSkeleton } from '../skeleton/BannerSkeleton';
+import { bannerApi } from '@/api/bannerApi';
const HeroBannerSecondary = () => {
- const heroBannerSecondary = useQuery('heroBannerSecondary', bannerApi({ type: 'index-a-2' }))
+ const [heroBannerSecondary, setHeroBannerSecondary] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+ // const heroBannerSecondary = useQuery(
+ // 'heroBannerSecondary',
+ // bannerApi({ type: 'index-a-2' })
+ // );
- const randomIndex = useMemo(() => {
- if (!heroBannerSecondary.data) return null
- const length = heroBannerSecondary.data?.length
- return getRandomInt(length)
- }, [heroBannerSecondary.data])
+ useEffect(() => {
+ const fetchData = async () => {
+ setIsLoading(true);
+ const res = await fetch(`/api/hero-banner?type=index-a-2`);
+ const { data } = await res.json();
+ if (data) {
+ setHeroBannerSecondary(data);
+ }
+ setIsLoading(false);
+ };
+
+ fetchData();
+ }, []);
- if (heroBannerSecondary.isLoading) return <HeroBannerSkeleton />
+ const randomIndex = useMemo(() => {
+ if (!heroBannerSecondary) return null;
+ const length = heroBannerSecondary?.length;
+ return getRandomInt(length);
+ }, [heroBannerSecondary]);
+ if (isLoading) return <HeroBannerSkeleton />;
return (
- heroBannerSecondary.data && randomIndex !== null && (
- <Link href={heroBannerSecondary.data[randomIndex].url} className="h-full">
+ heroBannerSecondary &&
+ randomIndex !== null && (
+ <Link href={heroBannerSecondary[randomIndex]?.url} className='h-full'>
<Image
- src={heroBannerSecondary.data[randomIndex].image}
+ src={heroBannerSecondary[randomIndex]?.image}
width={512}
height={1024}
- alt={heroBannerSecondary.data[randomIndex].name}
- className="object-cover object-center h-full"
- loading="lazy"
- placeholder="blur"
- blurDataURL="/images/indoteknik-placeholder.png"
- sizes="(max-width: 768px) 100vw, 50vw"
+ alt={heroBannerSecondary[randomIndex]?.name}
+ className='object-cover object-center h-full'
+ loading='lazy'
+ placeholder='blur'
+ blurDataURL='/images/indoteknik-placeholder.png'
+ sizes='(max-width: 768px) 100vw, 50vw'
/>
</Link>
)
);
-}
+};
-export default HeroBannerSecondary
+export default HeroBannerSecondary;
diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx
index b46d25b5..d804bd24 100644
--- a/src/core/components/elements/Footer/BasicFooter.jsx
+++ b/src/core/components/elements/Footer/BasicFooter.jsx
@@ -269,7 +269,7 @@ const InformationCenter = () => (
<li className='text-gray_r-12/80 flex items-center'>
<PhoneArrowUpRightIcon className='w-[18px] mr-2' />
<a href='tel:02129338828' target='_blank' rel='noreferrer'>
- (021) 2933-8828
+ (021) 29338828
</a>
</li>
<li className='text-gray_r-12/80 flex items-center'>
diff --git a/src/core/components/elements/Footer/SimpleFooter.jsx b/src/core/components/elements/Footer/SimpleFooter.jsx
index 371b1652..1f5e13e7 100644
--- a/src/core/components/elements/Footer/SimpleFooter.jsx
+++ b/src/core/components/elements/Footer/SimpleFooter.jsx
@@ -13,7 +13,7 @@ const SimpleFooter = () => (
<ul className='flex flex-col gap-y-2'>
<li className='text-gray_r-12/80 flex items-center'>
<PhoneArrowUpRightIcon className='w-[18px] mr-2' />
- <a href='tel:02129338828'>(021) 2933-8828 / 29</a>
+ <a href='tel:02129338828'>(021) 29338828</a>
</li>
<li className='text-gray_r-12/80 flex items-center'>
<EnvelopeIcon className='w-[18px] mr-2' />
diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx
index 04cf76d1..fa3df5bf 100644
--- a/src/core/components/elements/Navbar/NavbarDesktop.jsx
+++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx
@@ -391,7 +391,7 @@ const SocialMedias = () => (
>
<NextImage
src='/images/socials/youtube.webp'
- alt='Youtube - Indoteknik.com'
+ // alt='Youtube - Indoteknik.com'
width={24}
height={24}
/>
@@ -403,7 +403,7 @@ const SocialMedias = () => (
>
<NextImage
src='/images/socials/tiktok.png'
- alt='TikTok - Indoteknik.com'
+ // alt='TikTok - Indoteknik.com'
width={24}
height={24}
/>
@@ -423,7 +423,7 @@ const SocialMedias = () => (
>
<NextImage
src='/images/socials/Facebook.png'
- alt='Facebook - Indoteknik.com'
+ // alt='Facebook - Indoteknik.com'
width={24}
height={24}
/>
@@ -435,7 +435,7 @@ const SocialMedias = () => (
>
<NextImage
src='/images/socials/Instagram.png'
- alt='Instagram - Indoteknik.com'
+ // alt='Instagram - Indoteknik.com'
width={24}
height={24}
/>
@@ -447,7 +447,7 @@ const SocialMedias = () => (
>
<NextImage
src='/images/socials/Linkedin.png'
- alt='Linkedin - Indoteknik.com'
+ // alt='Linkedin - Indoteknik.com'
width={24}
height={24}
/>
@@ -459,7 +459,7 @@ const SocialMedias = () => (
>
<NextImage
src='/images/socials/g_maps.png'
- alt='Maps - Indoteknik.com'
+ // alt='Maps - Indoteknik.com'
width={24}
height={24}
/>
diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx
index f438ae67..709495ce 100644
--- a/src/core/components/elements/Navbar/TopBanner.jsx
+++ b/src/core/components/elements/Navbar/TopBanner.jsx
@@ -1,22 +1,37 @@
import Image from 'next/image';
-import { useQuery } from 'react-query';import useDevice from '@/core/hooks/useDevice'
+import { useQuery } from 'react-query';
+import useDevice from '@/core/hooks/useDevice';
import odooApi from '@/core/api/odooApi';
import SmoothRender from '~/components/ui/smooth-render';
import Link from '../Link/Link';
import { background } from '@chakra-ui/react';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
const TopBanner = ({ onLoad = () => {} }) => {
- const { isDesktop, isMobile } = useDevice()
- const topBanner = useQuery({
- queryKey: 'topBanner',
- queryFn: async () => await odooApi('GET', '/api/v1/banner?type=top-banner'),
- refetchOnWindowFocus: false,
- });
+ const [topBanner, setTopBanner] = useState([]);
+ const { isDesktop, isMobile } = useDevice();
+
+ useEffect(() => {
+ const fetchData = async () => {
+ const res = await fetch(`/api/hero-banner?type=top-banner`);
+ const { data } = await res.json();
+ if (data) {
+ setTopBanner(data);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ // const topBanner = useQuery({
+ // queryKey: 'topBanner',
+ // queryFn: async () => await odooApi('GET', '/api/v1/banner?type=top-banner'),
+ // refetchOnWindowFocus: false,
+ // });
// const backgroundColor = topBanner.data?.[0]?.backgroundColor || 'transparent';
- const hasData = topBanner.data?.length > 0;
- const data = topBanner.data?.[0] || null;
+ const hasData = topBanner?.length > 0;
+ const data = topBanner?.[0] || null;
useEffect(() => {
if (hasData) {
@@ -31,17 +46,15 @@ const TopBanner = ({ onLoad = () => {} }) => {
duration='700ms'
delay='300ms'
className='h-auto'
- >
+ >
<Link
- href={data?.url}
- className="block bg-cover bg-center h-3 md:h-6 lg:h-[36px]"
- style={{
- backgroundImage: `url('${data?.image}')`,
- }}
- >
- </Link>
-
- </SmoothRender>
+ href={data?.url}
+ className='block bg-cover bg-center h-3 md:h-6 lg:h-[36px]'
+ style={{
+ backgroundImage: `url('${data?.image}')`,
+ }}
+ ></Link>
+ </SmoothRender>
);
};
diff --git a/src/core/utils/whatsappUrl.js b/src/core/utils/whatsappUrl.js
index 7a129aa6..c840e105 100644
--- a/src/core/utils/whatsappUrl.js
+++ b/src/core/utils/whatsappUrl.js
@@ -2,28 +2,31 @@ import { getAuth } from "./auth"
const whatsappUrl = (template = 'default', payload, urlPath = null) => {
let user = getAuth()
- if(!user){
- if(urlPath) return `/login?next=${urlPath}`
- if(!urlPath) return '/login'
- }
+ // if(!user){
+ // if(urlPath) return `/login?next=${urlPath}`
+ // if(!urlPath) return '/login'
+ // }
let parentName = user.parentName || '-'
let url = 'https://wa.me/6281717181922'
let text = 'Hallo Indoteknik.com,'
+ if(user){
+ text += `Saya ${user.name}, Saya dari ${parentName}`
+ }
switch (template) {
case 'product':
- text += ` Saya ${user.name} , Saya dari ${parentName} Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}`
+ text += ` Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}`
break
case 'productWeight':
- text += ` Saya ${user.name} , Saya dari ${parentName} Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}`
+ text += ` Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}`
break
case 'productSearch':
- text += `Saya lagi cari-cari produk ${payload?.name}, bisa bantu saya cari produknya?`
+ text += ` Saya lagi cari-cari produk ${payload?.name}, bisa bantu saya cari produknya?`
break
case null:
- text += `Saya ${user.name}, Saya dari ${parentName} Bisa tolong bantu kebutuhan saya?`
+ text += ` Bisa tolong bantu kebutuhan saya?`
break;
default:
- text += `Saya ${user.name}, Saya dari ${parentName} Bisa tolong bantu kebutuhan saya?`
+ text += ` Bisa tolong bantu kebutuhan saya?`
break
}
if (text) url += `?text=${encodeURI(text)}`
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx
index e83e719c..6fb5cdb4 100644
--- a/src/lib/checkout/components/Checkout.jsx
+++ b/src/lib/checkout/components/Checkout.jsx
@@ -37,6 +37,18 @@ const SELF_PICKUP_ID = 32;
const { checkoutApi } = require('../api/checkoutApi');
const { getProductsCheckout } = require('../api/checkoutApi');
+function convertToInternational(number) {
+ if (typeof number !== 'string') {
+ throw new Error("Input harus berupa string");
+ }
+
+ if (number.startsWith('08')) {
+ return '+62' + number.slice(2);
+ }
+
+ return number;
+}
+
const Checkout = () => {
const router = useRouter();
const query = router.query.source ?? null;
@@ -413,7 +425,12 @@ const Checkout = () => {
Math.round(parseInt(finalShippingAmt * 1.1) / 1000) * 1000;
const finalGT = GT < 0 ? 0 : GT;
setGrandTotal(finalGT);
- }, [biayaKirim, cartCheckout?.grandTotal, activeVoucher, activeVoucherShipping]);
+ }, [
+ biayaKirim,
+ cartCheckout?.grandTotal,
+ activeVoucher,
+ activeVoucherShipping,
+ ]);
const checkout = async () => {
const file = poFile.current.files[0];
@@ -484,6 +501,13 @@ const Checkout = () => {
transaction_id: isCheckouted.id,
});
+ gtag('set', 'user_data', {
+ email: auth.email,
+ phone_number: convertToInternational(auth.mobile) ?? convertToInternational(auth.phone),
+ });
+
+ gtag('config', 'AW-954540379', { ' allow_enhanced_conversions':true } ) ;
+
for (const product of products) deleteItemCart({ productId: product.id });
if (grandTotal > 0) {
const payment = await axios.post(
@@ -501,7 +525,7 @@ const Checkout = () => {
}
}
- /* const midtrans = async () => {
+ /* const midtrans = async () => {
for (const product of products) deleteItemCart({ productId: product.id });
if (grandTotal > 0) {
const payment = await axios.post(
@@ -1193,7 +1217,11 @@ const Checkout = () => {
<div className='text-gray_r-11'>
Biaya Kirim <p className='text-xs mt-1'>{etdFix}</p>
</div>
- <div>{currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000)}</div>
+ <div>
+ {currencyFormat(
+ Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000
+ )}
+ </div>
</div>
{activeVoucherShipping && voucherShippingAmt && (
<div className='flex gap-x-2 justify-between'>
@@ -1494,7 +1522,11 @@ const Checkout = () => {
Biaya Kirim
<p className='text-xs mt-1'>{etdFix}</p>
</div>
- <div>{currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000) }</div>
+ <div>
+ {currencyFormat(
+ Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000
+ )}
+ </div>
</div>
{activeVoucherShipping && voucherShippingAmt && (
<div className='flex gap-x-2 justify-between'>
diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx
index 89c46de4..8be1d7a6 100644
--- a/src/lib/flashSale/components/FlashSale.jsx
+++ b/src/lib/flashSale/components/FlashSale.jsx
@@ -69,7 +69,7 @@ const FlashSaleProduct = ({ flashSaleId }) => {
useEffect(() => {
const loadProducts = async () => {
const dataProducts = await productSearchApi({
- query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=500&orderBy=flashsale-price-asc`,
+ query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=500&orderBy=flashsale-price-asc&source=similar`,
operation: 'AND',
});
setProducts(dataProducts.response);
diff --git a/src/lib/flashSale/components/FlashSaleNonDisplay.jsx b/src/lib/flashSale/components/FlashSaleNonDisplay.jsx
index c91de2be..4b420fac 100644
--- a/src/lib/flashSale/components/FlashSaleNonDisplay.jsx
+++ b/src/lib/flashSale/components/FlashSaleNonDisplay.jsx
@@ -56,7 +56,7 @@ const FlashSaleProduct = ({ flashSaleId }) => {
useEffect(() => {
const loadProducts = async () => {
const dataProducts = await productSearchApi({
- query: `fq=-flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=25&orderBy=flashsale-discount-desc`,
+ query: `fq=-flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=25&orderBy=flashsale-discount-desc&source=similar`,
operation: 'AND',
});
setProducts(dataProducts.response);
diff --git a/src/lib/home/components/BannerSection.jsx b/src/lib/home/components/BannerSection.jsx
index 60d38f8f..303b5c4b 100644
--- a/src/lib/home/components/BannerSection.jsx
+++ b/src/lib/home/components/BannerSection.jsx
@@ -11,27 +11,33 @@ const BannerSection = () => {
const [shouldFetch, setShouldFetch] = useState(false);
useEffect(() => {
- const localData = localStorage.getItem('Homepage_bannerSection');
- if (localData) {
- setData(JSON.parse(localData));
- }else{
- setShouldFetch(true);
- }
- }, []);
-
- // const fetchBannerSection = async () => await bannerSectionApi();
- const getBannerSection = useQuery('bannerSection', bannerApi({ type: 'home-banner' }), {
- enabled: shouldFetch,
- onSuccess: (data) => {
+ const fetchCategoryData = async () => {
+ const res = await fetch('/api/banner-section');
+ const { data } = await res.json();
if (data) {
- localStorage.setItem('Homepage_bannerSection', JSON.stringify(data));
setData(data);
}
- },
- });
+ };
- const bannerSection = data;
+ fetchCategoryData();
+ }, []);
+ // const fetchBannerSection = async () => await bannerSectionApi();
+ const getBannerSection = useQuery(
+ 'bannerSection',
+ bannerApi({ type: 'home-banner' }),
+ {
+ enabled: shouldFetch,
+ onSuccess: (data) => {
+ if (data) {
+ localStorage.setItem('Homepage_bannerSection', JSON.stringify(data));
+ setData(data);
+ }
+ },
+ }
+ );
+
+ const bannerSection = data;
return (
bannerSection &&
bannerSection?.length > 0 && (
diff --git a/src/lib/home/components/CategoryDynamic.jsx b/src/lib/home/components/CategoryDynamic.jsx
index e62575f7..cc4f42b7 100644
--- a/src/lib/home/components/CategoryDynamic.jsx
+++ b/src/lib/home/components/CategoryDynamic.jsx
@@ -1,81 +1,33 @@
-import React, { useEffect, useState, useCallback } from 'react';
-import {
- fetchCategoryManagementSolr,
- fetchCategoryManagementVersion,
-} from '../api/categoryManagementApi';
+import React, { useEffect, useState } from 'react';
+import { fetchCategoryManagementSolr } from '../api/categoryManagementApi';
+import { Skeleton } from '@chakra-ui/react';
import NextImage from 'next/image';
import Link from 'next/link';
import { createSlug } from '@/core/utils/slug';
-import { Skeleton } from '@chakra-ui/react';
import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import { Pagination } from 'swiper';
-const saveToLocalStorage = (key, data, version) => {
- const now = new Date();
- const item = {
- value: data,
- version: version,
- lastFetchedTime: now.getTime(),
- };
- localStorage.setItem(key, JSON.stringify(item));
-};
-
-const getFromLocalStorage = (key) => {
- const itemStr = localStorage.getItem(key);
- if (!itemStr) return null;
-
- const item = JSON.parse(itemStr);
- return item;
-};
-
-const getElapsedTime = (lastFetchedTime) => {
- const now = new Date();
- return now.getTime() - lastFetchedTime;
-};
-
const CategoryDynamic = () => {
const [categoryManagement, setCategoryManagement] = useState([]);
- const [isLoading, setIsLoading] = useState(false);
-
- const loadBrand = useCallback(async () => {
- const cachedData = getFromLocalStorage('homepage_categoryDynamic');
-
- if (cachedData) {
- // Hitung selisih waktu antara saat ini dengan waktu terakhir data di-fetch
- const elapsedTime = getElapsedTime(cachedData.lastFetchedTime);
-
- if (elapsedTime < 24 * 60 * 60 * 1000) {
- setCategoryManagement(cachedData.value);
- return;
- }
- }
+ const [isLoading, setIsLoading] = useState(true);
- const latestVersion = await fetchCategoryManagementVersion();
- if (cachedData && cachedData.version === latestVersion) {
- // perbarui waktu
- saveToLocalStorage(
- 'homepage_categoryDynamic',
- cachedData.value,
- latestVersion
- );
- setCategoryManagement(cachedData.value);
- } else {
+ useEffect(() => {
+ const fetchCategoryData = async () => {
setIsLoading(true);
- const items = await fetchCategoryManagementSolr();
+ const res = await fetch('/api/category-management');
+ const { data } = await res.json();
+ if (data) {
+ setCategoryManagement(data);
+ }
setIsLoading(false);
+ };
- saveToLocalStorage('homepage_categoryDynamic', items, latestVersion);
- setCategoryManagement(items);
- }
+ fetchCategoryData();
}, []);
- useEffect(() => {
- loadBrand();
- }, [loadBrand]);
-
const swiperBanner = {
modules: [Pagination],
classNames: 'mySwiper',
@@ -90,104 +42,99 @@ const CategoryDynamic = () => {
return (
<div>
{categoryManagement &&
- categoryManagement?.map((category) => {
- return (
- <Skeleton key={category.id} isLoaded={!isLoading}>
- <div key={category.id}>
- <div className='bagian-judul flex flex-row justify-start items-center gap-3 mb-4 mt-4'>
- <h1 className='font-semibold text-[14px] sm:text-h-lg mr-2'>
- {category.name}
- </h1>
- <Link
- href={createSlug(
- '/shop/category/',
- category?.name,
- category?.category_id
- )}
- className='!text-red-500 font-semibold'
- >
- Lihat Semua
- </Link>
- </div>
+ categoryManagement.map((category) => (
+ <Skeleton key={category.id} isLoaded={!isLoading}>
+ <div key={category.id}>
+ <div className='bagian-judul flex flex-row justify-start items-center gap-3 mb-4 mt-4'>
+ <h1 className='font-semibold text-[14px] sm:text-h-lg mr-2'>
+ {category.name}
+ </h1>
+ <Link
+ href={createSlug(
+ '/shop/category/',
+ category?.name,
+ category?.category_id
+ )}
+ className='!text-red-500 font-semibold'
+ >
+ Lihat Semua
+ </Link>
+ </div>
- {/* Swiper for SubCategories */}
- <Swiper {...swiperBanner}>
- {category.categories.map((subCategory) => {
- return (
- <SwiperSlide key={subCategory.id}>
- <div className='border rounded justify-start items-start '>
- <div className='p-3'>
- <div className='flex flex-row border rounded mb-2 justify-start items-center'>
- <NextImage
- src={
- subCategory.image
- ? subCategory.image
- : '/images/noimage.jpeg'
- }
- alt={subCategory.name}
- width={90}
- height={30}
- className='object-fit p-4'
- />
- <div className='bagian-judul flex flex-col justify-center items-start gap-2 ml-2'>
- <h2 className='font-semibold text-lg mr-2'>
- {subCategory?.name}
- </h2>
+ <Swiper {...swiperBanner}>
+ {category?.categories?.map((subCategory) => (
+ <SwiperSlide key={subCategory.id}>
+ <div className='border rounded justify-start items-start '>
+ <div className='p-3'>
+ <div className='flex flex-row border rounded mb-2 justify-start items-center'>
+ <NextImage
+ src={
+ subCategory.image
+ ? subCategory.image
+ : '/images/noimage.jpeg'
+ }
+ alt={subCategory.name}
+ width={90}
+ height={30}
+ className='object-fit p-4'
+ />
+ <div className='bagian-judul flex flex-col justify-center items-start gap-2 ml-2'>
+ <h2 className='font-semibold text-lg mr-2'>
+ {subCategory?.name}
+ </h2>
+ <Link
+ href={createSlug(
+ '/shop/category/',
+ subCategory?.name,
+ subCategory?.id_level_2
+ )}
+ className='!text-red-500 font-semibold'
+ >
+ Lihat Semua
+ </Link>
+ </div>
+ </div>
+ <div className='grid grid-cols-2 gap-2 overflow-y-auto max-h-[240px] min-h-[240px] content-start'>
+ {subCategory.child_frontend_id_i.map(
+ (childCategory) => (
+ <div key={childCategory.id} className=''>
<Link
href={createSlug(
'/shop/category/',
- subCategory?.name,
- subCategory?.id_level_2
+ childCategory?.name,
+ childCategory?.id_level_3
)}
- className='!text-red-500 font-semibold'
+ className='flex flex-row gap-2 border rounded group hover:border-red-500'
>
- Lihat Semua
+ <NextImage
+ src={
+ childCategory.image
+ ? childCategory.image
+ : '/images/noimage.jpeg'
+ }
+ alt={childCategory.name}
+ className='p-2 ml-1'
+ width={40}
+ height={40}
+ />
+ <div className='bagian-judul flex flex-col justify-center items-center gap-2 break-words line-clamp-2 group-hover:text-red-500'>
+ <h3 className='font-semibold line-clamp-2 group-hover:text-red-500 text-sm mr-2'>
+ {childCategory.name}
+ </h3>
+ </div>
</Link>
</div>
- </div>
- <div className='grid grid-cols-2 gap-2 overflow-y-auto max-h-[240px] min-h-[240px] content-start'>
- {subCategory.child_frontend_id_i.map(
- (childCategory) => (
- <div key={childCategory.id} className=''>
- <Link
- href={createSlug(
- '/shop/category/',
- childCategory?.name,
- childCategory?.id_level_3
- )}
- className='flex flex-row gap-2 border rounded group hover:border-red-500'
- >
- <NextImage
- src={
- childCategory.image
- ? childCategory.image
- : '/images/noimage.jpeg'
- }
- alt={childCategory.name}
- className='p-2 ml-1'
- width={40}
- height={40}
- />
- <div className='bagian-judul flex flex-col justify-center items-center gap-2 break-words line-clamp-2 group-hover:text-red-500'>
- <h3 className='font-semibold line-clamp-2 group-hover:text-red-500 text-sm mr-2'>
- {childCategory.name}
- </h3>
- </div>
- </Link>
- </div>
- )
- )}
- </div>
- </div>
+ )
+ )}
</div>
- </SwiperSlide>
- );
- })}
- </Swiper>
- </div>
- </Skeleton>
- );
- })}
+ </div>
+ </div>
+ </SwiperSlide>
+ ))}
+ </Swiper>
+ </div>
+ </Skeleton>
+ ))}
</div>
);
};
diff --git a/src/lib/home/components/CategoryDynamicMobile.jsx b/src/lib/home/components/CategoryDynamicMobile.jsx
index 55654b0e..67ae6f5f 100644
--- a/src/lib/home/components/CategoryDynamicMobile.jsx
+++ b/src/lib/home/components/CategoryDynamicMobile.jsx
@@ -9,71 +9,26 @@ import {
fetchCategoryManagementVersion,
} from '../api/categoryManagementApi';
-const saveToLocalStorage = (key, data, version) => {
- const now = new Date();
- const item = {
- value: data,
- version: version,
- lastFetchedTime: now.getTime(),
- };
- localStorage.setItem(key, JSON.stringify(item));
-};
-
-const getFromLocalStorage = (key) => {
- const itemStr = localStorage.getItem(key);
- if (!itemStr) return null;
-
- const item = JSON.parse(itemStr);
- return item;
-};
-
-const getElapsedTime = (lastFetchedTime) => {
- const now = new Date();
- return now.getTime() - lastFetchedTime;
-};
-
const CategoryDynamicMobile = () => {
const [selectedCategory, setSelectedCategory] = useState({});
const [categoryManagement, setCategoryManagement] = useState([]);
const [isLoading, setIsLoading] = useState(false);
- const loadCategoryManagement = useCallback(async () => {
- const cachedData = getFromLocalStorage('homepage_categoryDynamic');
-
- if (cachedData) {
- // Hitung selisih waktu antara saat ini dengan waktu terakhir data di-fetch
- const elapsedTime = getElapsedTime(cachedData.lastFetchedTime);
-
- if (elapsedTime < 24 * 60 * 60 * 1000) {
- setCategoryManagement(cachedData.value);
- return;
- }
- }
-
- const latestVersion = await fetchCategoryManagementVersion();
- if (cachedData && cachedData.version === latestVersion) {
- // perbarui waktu
- saveToLocalStorage(
- 'homepage_categoryDynamic',
- cachedData.value,
- latestVersion
- );
- setCategoryManagement(cachedData.value);
- } else {
+ useEffect(() => {
+ const fetchCategoryData = async () => {
setIsLoading(true);
- const items = await fetchCategoryManagementSolr();
+ const res = await fetch('/api/category-management');
+ const { data } = await res.json();
+ if (data) {
+ setCategoryManagement(data);
+ }
setIsLoading(false);
+ };
- saveToLocalStorage('homepage_categoryDynamic', items, latestVersion);
- setCategoryManagement(items);
- }
+ fetchCategoryData();
}, []);
useEffect(() => {
- loadCategoryManagement();
- }, [loadCategoryManagement]);
-
- useEffect(() => {
if (categoryManagement?.length > 0) {
const initialSelections = categoryManagement.reduce((acc, category) => {
if (category.categories.length > 0) {
diff --git a/src/lib/home/components/PreferredBrand.jsx b/src/lib/home/components/PreferredBrand.jsx
index eefced60..b7a30503 100644
--- a/src/lib/home/components/PreferredBrand.jsx
+++ b/src/lib/home/components/PreferredBrand.jsx
@@ -1,49 +1,50 @@
-import { Swiper, SwiperSlide } from 'swiper/react'
-import { Navigation, Pagination, Autoplay } from 'swiper';
-import { useCallback, useEffect, useState } from 'react'
-import usePreferredBrand from '../hooks/usePreferredBrand'
-import PreferredBrandSkeleton from './Skeleton/PreferredBrandSkeleton'
-import BrandCard from '@/lib/brand/components/BrandCard'
-import useDevice from '@/core/hooks/useDevice'
-import Link from '@/core/components/elements/Link/Link'
-import axios from 'axios'
+import { Swiper, SwiperSlide } from 'swiper/react';
+import { Navigation, Pagination, Autoplay } from 'swiper';
+import { useCallback, useEffect, useState } from 'react';
+import usePreferredBrand from '../hooks/usePreferredBrand';
+import PreferredBrandSkeleton from './Skeleton/PreferredBrandSkeleton';
+import BrandCard from '@/lib/brand/components/BrandCard';
+import useDevice from '@/core/hooks/useDevice';
+import Link from '@/core/components/elements/Link/Link';
+import axios from 'axios';
const PreferredBrand = () => {
- let query = ''
- let params = 'prioritas'
- const [isLoading, setIsLoading] = useState(true)
- const [startWith, setStartWith] = useState(null)
- const [manufactures, setManufactures] = useState([])
+ let query = '';
+ let params = 'prioritas';
+ const [isLoading, setIsLoading] = useState(true);
+ const [startWith, setStartWith] = useState(null);
+ const [manufactures, setManufactures] = useState([]);
const loadBrand = useCallback(async () => {
- setIsLoading(true)
- const name = startWith ? `${startWith}*` : ''
- const result = await axios(`${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/brands?rows=20`)
-
- setIsLoading(false)
- setManufactures((manufactures) => [...result.data])
- }, [startWith])
+ setIsLoading(true);
+ const name = startWith ? `${startWith}*` : '';
+ const result = await axios(
+ `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/preferredBrand?rows=20`
+ );
+ setIsLoading(false);
+ setManufactures((manufactures) => [...result.data]);
+ }, [startWith]);
const toggleStartWith = (alphabet) => {
- setManufactures([])
+ setManufactures([]);
if (alphabet == startWith) {
- setStartWith(null)
- return
+ setStartWith(null);
+ return;
}
- setStartWith(alphabet)
- }
+ setStartWith(alphabet);
+ };
useEffect(() => {
- loadBrand()
- }, [])
+ loadBrand();
+ }, []);
// const { preferredBrands } = usePreferredBrand(query)
- const { isMobile, isDesktop } = useDevice()
+ const { isMobile, isDesktop } = useDevice();
const swiperBanner = {
- modules:[Navigation, Pagination, Autoplay],
+ modules: [Navigation, Pagination, Autoplay],
autoplay: {
delay: 4000,
- disableOnInteraction: false
+ disableOnInteraction: false,
},
loop: true,
className: 'h-[70px] md:h-[100px] w-full',
@@ -53,13 +54,17 @@ const PreferredBrand = () => {
dynamicBullets: true,
dynamicMainBullets: isMobile ? 6 : 8,
clickable: true,
- }
- }
- const preferredBrandsData = manufactures ? manufactures.slice(0, 20) : []
+ },
+ };
+ const preferredBrandsData = manufactures ? manufactures.slice(0, 20) : [];
return (
<div className='px-4 sm:px-0'>
<div className='flex justify-between items-center mb-4'>
- <h1 className='font-semibold text-[14px] sm:text-h-lg'><Link href='/shop/brands' className='!text-black font-semibold'>Brand Pilihan</Link></h1>
+ <h1 className='font-semibold text-[14px] sm:text-h-lg'>
+ <Link href='/shop/brands' className='!text-black font-semibold'>
+ Brand Pilihan
+ </Link>
+ </h1>
{isDesktop && (
<Link href='/shop/brands' className='!text-red-500 font-semibold'>
Lihat Semua
@@ -79,7 +84,7 @@ const PreferredBrand = () => {
)}
</div>
</div>
- )
-}
+ );
+};
-export default PreferredBrand \ No newline at end of file
+export default PreferredBrand;
diff --git a/src/lib/home/components/PromotionProgram.jsx b/src/lib/home/components/PromotionProgram.jsx
index 7433e7f0..562fa138 100644
--- a/src/lib/home/components/PromotionProgram.jsx
+++ b/src/lib/home/components/PromotionProgram.jsx
@@ -10,32 +10,50 @@ const BannerSection = () => {
const { isMobile, isDesktop } = useDevice();
const [data, setData] = useState(null);
const [shouldFetch, setShouldFetch] = useState(false);
-
useEffect(() => {
- const localData = localStorage.getItem('Homepage_promotionProgram');
- if (localData) {
- setData(JSON.parse(localData));
- }else{
- setShouldFetch(true);
- }
- },[])
-
- const getPromotionProgram = useQuery(
- 'promotionProgram',
- bannerApi({ type: 'banner-promotion' }),{
- enabled: shouldFetch,
- onSuccess: (data) => {
- if (data) {
- localStorage.setItem('Homepage_promotionProgram', JSON.stringify(data));
- setData(data);
- }
+ const fetchData = async () => {
+ const res = await fetch(`/api/hero-banner?type=banner-promotion`);
+ const { data } = await res.json();
+ if (data) {
+ setData(data);
}
- }
- );
+ };
+
+ fetchData();
+ }, []);
+
+ // useEffect(() => {
+ // const localData = localStorage.getItem('Homepage_promotionProgram');
+ // if (localData) {
+ // setData(JSON.parse(localData));
+ // } else {
+ // setShouldFetch(true);
+ // }
+ // }, []);
- const promotionProgram = data
+ // const getPromotionProgram = useQuery(
+ // 'promotionProgram',
+ // bannerApi({ type: 'banner-promotion' }),
+ // {
+ // enabled: shouldFetch,
+ // onSuccess: (data) => {
+ // if (data) {
+ // localStorage.setItem(
+ // 'Homepage_promotionProgram',
+ // JSON.stringify(data)
+ // );
+ // setData(data);
+ // }
+ // },
+ // }
+ // );
- if (getPromotionProgram?.isLoading && !data) {
+ const promotionProgram = data;
+
+ // if (getPromotionProgram?.isLoading && !data) {
+ // return <BannerPromoSkeleton />;
+ // }
+ if (!data) {
return <BannerPromoSkeleton />;
}
@@ -62,24 +80,22 @@ const BannerSection = () => {
</Link>
)}
</div>
- {isDesktop &&
- promotionProgram &&
- promotionProgram?.length > 0 && (
- <div className='grid grid-cols-3 sm:grid-cols-3 gap-4 rounded-md'>
- {promotionProgram?.map((banner) => (
- <Link key={banner.id} href={banner.url}>
- <Image
- width={439}
- height={150}
- quality={85}
- src={banner.image}
- alt={banner.name}
- className='h-auto w-full rounded hover:scale-105 transition duration-500 ease-in-out'
- />
- </Link>
- ))}
- </div>
- )}
+ {isDesktop && promotionProgram && promotionProgram?.length > 0 && (
+ <div className='grid grid-cols-3 sm:grid-cols-3 gap-4 rounded-md'>
+ {promotionProgram?.map((banner) => (
+ <Link key={banner.id} href={banner.url}>
+ <Image
+ width={439}
+ height={150}
+ quality={85}
+ src={banner.image}
+ alt={banner.name}
+ className='h-auto w-full rounded hover:scale-105 transition duration-500 ease-in-out'
+ />
+ </Link>
+ ))}
+ </div>
+ )}
{isMobile && (
<Swiper slidesPerView={1.1} spaceBetween={8} freeMode>
diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx
index f4569574..5dfd452b 100644
--- a/src/lib/product/components/Product/ProductDesktopVariant.jsx
+++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx
@@ -264,11 +264,17 @@ const ProductDesktopVariant = ({
product.manufacture.id.toString()
)}
>
- <Image
- width={100}
- src={product.manufacture.logo}
- alt={product.manufacture.name}
- />
+ {product?.manufacture.logo ? (
+ <Image
+ width={100}
+ src={product.manufacture.logo}
+ alt={product.manufacture.name}
+ />
+ ) : (
+ <p className='font-bold text-red-500'>
+ {product.manufacture.name}
+ </p>
+ )}
</Link>
</div>
</div>
@@ -434,7 +440,7 @@ const ProductDesktopVariant = ({
</div>
<div>
<Skeleton
- isLoaded={sla}
+ isLoaded={!isLoadingSLA}
h='21px'
// w={16}
className={
diff --git a/src/lib/product/components/Product/ProductMobileVariant.jsx b/src/lib/product/components/Product/ProductMobileVariant.jsx
index b87bcbc8..de5c3f10 100644
--- a/src/lib/product/components/Product/ProductMobileVariant.jsx
+++ b/src/lib/product/components/Product/ProductMobileVariant.jsx
@@ -168,44 +168,14 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
return (
<MobileView>
- <Image
- src={product.image + '?variant=True'}
- alt={product.name}
- className='h-72 object-contain object-center w-full border-b border-gray_r-4'
- />
-
- <div className='p-4'>
- <div className='flex items-end mb-2'>
- {product.manufacture?.name ? (
- <Link
- href={createSlug(
- '/shop/brands/',
- product.manufacture?.name,
- product.manufacture?.id
- )}
- >
- {product.manufacture?.name}
- </Link>
- ) : (
- <div>-</div>
- )}
- <button type='button' className='ml-auto' onClick={toggleWishlist}>
- {wishlist.data?.productTotal > 0 ? (
- <HeartIcon className='w-6 fill-danger-500 text-danger-500' />
- ) : (
- <HeartIcon className='w-6' />
- )}
- </button>
- </div>
- <h1 className='font-medium text-h-lg leading-8 md:text-title-md md:leading-10 mb-3'>
- {activeVariant?.name}
- </h1>
-
+ <div
+ className={`px-4 block md:sticky md:top-[150px] md:py-6 fixed bottom-0 left-0 right-0 bg-white p-2 z-10 pb-6 pt-6 rounded-lg shadow-[rgba(0,0,4,0.1)_0px_-4px_4px_0px] `}
+ >
{activeVariant.isFlashSale &&
activeVariant?.price?.discountPercentage > 0 ? (
<>
<div className='flex gap-x-1 items-center'>
- <div className='badge-solid-red'>
+ <div className='bg-danger-500 px-2 py-1.5 rounded text-white text-caption-2'>
{activeVariant?.price?.discountPercentage}%
</div>
<div className='text-gray_r-11 line-through text-caption-1'>
@@ -223,7 +193,7 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
</div>
</>
) : (
- <h3 className='text-danger-500 font-semibold mt-1'>
+ <div className='text-danger-500 font-semibold mt-1 text-3xl'>
{activeVariant?.price?.price > 0 ? (
<>
{currencyFormat(activeVariant?.price?.price)}
@@ -253,54 +223,84 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
</a>
</span>
)}
- </h3>
+ </div>
)}
+ <div className=''>
+ <div className='mt-4 mb-2'>Jumlah</div>
+ <div className='flex gap-x-3'>
+ <div className='w-2/12'>
+ <input
+ name='quantity'
+ type='number'
+ className='form-input'
+ value={quantity}
+ onChange={(e) => setQuantity(e.target.value)}
+ />
+ </div>
+ <button
+ type='button'
+ className='btn-yellow flex-1'
+ onClick={handleClickCart}
+ >
+ Keranjang
+ </button>
+ <button
+ type='button'
+ className='btn-solid-red flex-1'
+ onClick={handleClickBuy}
+ >
+ Beli
+ </button>
+ </div>
+ <Button
+ onClick={() => handleButton(product.id)}
+ color={'red'}
+ colorScheme='white'
+ className='w-full border-2 p-2 gap-1 mt-2 hover:bg-slate-100 flex items-center'
+ >
+ <ImageNext
+ src='/images/writing.png'
+ alt='penawaran instan'
+ className=''
+ width={25}
+ height={25}
+ />
+ Penawaran Harga Instan
+ </Button>
+ </div>
</div>
-
- <Divider />
+ <Image
+ src={product.image + '?variant=True'}
+ alt={product.name}
+ className='h-72 object-contain object-center w-full border-b border-gray_r-4'
+ />
<div className='p-4'>
- <div className='mt-4 mb-2'>Jumlah</div>
- <div className='flex gap-x-3'>
- <div className='w-2/12'>
- <input
- name='quantity'
- type='number'
- className='form-input'
- value={quantity}
- onChange={(e) => setQuantity(e.target.value)}
- />
- </div>
- <button
- type='button'
- className='btn-yellow flex-1'
- onClick={handleClickCart}
- >
- Keranjang
- </button>
- <button
- type='button'
- className='btn-solid-red flex-1'
- onClick={handleClickBuy}
- >
- Beli
+ <div className='flex items-end mb-2'>
+ {product.manufacture?.name ? (
+ <Link
+ href={createSlug(
+ '/shop/brands/',
+ product.manufacture?.name,
+ product.manufacture?.id
+ )}
+ >
+ {product.manufacture?.name}
+ </Link>
+ ) : (
+ <div>-</div>
+ )}
+ <button type='button' className='ml-auto' onClick={toggleWishlist}>
+ {wishlist.data?.productTotal > 0 ? (
+ <HeartIcon className='w-6 fill-danger-500 text-danger-500' />
+ ) : (
+ <HeartIcon className='w-6' />
+ )}
</button>
</div>
- <Button
- onClick={() => handleButton(product.id)}
- color={'red'}
- colorScheme='white'
- className='w-full border-2 p-2 gap-1 mt-2 hover:bg-slate-100 flex items-center'
- >
- <ImageNext
- src='/images/writing.png'
- alt='penawaran instan'
- className=''
- width={25}
- height={25}
- />
- Penawaran Harga Instan
- </Button>
+ <h1 className='font-medium text-h-lg leading-8 md:text-title-md md:leading-10 mb-3'>
+ {activeVariant?.name}
+ </h1>
</div>
<Divider />
diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx
index d3b50302..3e6a6913 100644
--- a/src/lib/product/components/ProductCard.jsx
+++ b/src/lib/product/components/ProductCard.jsx
@@ -10,12 +10,13 @@ import { sellingProductFormat } from '@/core/utils/formatValue';
import { createSlug } from '@/core/utils/slug';
import whatsappUrl from '@/core/utils/whatsappUrl';
import useUtmSource from '~/hooks/useUtmSource';
+import useDevice from '@/core/hooks/useDevice';
const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
const router = useRouter();
const utmSource = useUtmSource();
const [discount, setDiscount] = useState(0);
-
+ const { isDesktop, isMobile } = useDevice();
let voucherPastiHemat = 0;
voucherPastiHemat = product?.newVoucherPastiHemat[0];
@@ -26,9 +27,13 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
});
const image = useMemo(() => {
- if (product.image) return product.image + '?ratio=square';
- return '/images/noimage.jpeg';
- }, [product.image]);
+ if (!isDesktop && product.image_mobile) {
+ return product.image_mobile + '?ratio=square';
+ } else {
+ if (product.image) return product.image + '?ratio=square';
+ return '/images/noimage.jpeg';
+ }
+ }, [product.image, product.image_mobile]);
const URL = {
product:
@@ -143,7 +148,7 @@ 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'>
+ <Link href={URL.manufacture} className='mb-1 mt-1 truncate'>
{product.manufacture.name}
</Link>
) : (
diff --git a/src/lib/treckingAwb/component/Manifest.jsx b/src/lib/treckingAwb/component/Manifest.jsx
index fbc95702..02d0bc7a 100644
--- a/src/lib/treckingAwb/component/Manifest.jsx
+++ b/src/lib/treckingAwb/component/Manifest.jsx
@@ -1,16 +1,16 @@
-import odooApi from '@/core/api/odooApi'
-import BottomPopup from '@/core/components/elements/Popup/BottomPopup'
-import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner'
-import { getAuth } from '@/core/utils/auth'
-import { useEffect, useState } from 'react'
-import { toast } from 'react-hot-toast'
-import ImageNext from 'next/image'
-import { list } from 'postcss'
+import odooApi from '@/core/api/odooApi';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner';
+import { getAuth } from '@/core/utils/auth';
+import { useEffect, useState } from 'react';
+import { toast } from 'react-hot-toast';
+import ImageNext from 'next/image';
+import { list } from 'postcss';
const Manifest = ({ idAWB, closePopup }) => {
- const [manifests, setManifests] = useState(null)
- const [isLoading, setIsLoading] = useState(false)
-
+ const [manifests, setManifests] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ console.log('manifests', manifests);
const formatCustomDate = (date) => {
const months = [
'Jan',
@@ -24,61 +24,60 @@ const Manifest = ({ idAWB, closePopup }) => {
'Sep',
'Oct',
'Nov',
- 'Dec'
- ]
+ 'Dec',
+ ];
- const parts = date.split(' ') // Pisahkan tanggal dan waktu
- const [datePart, timePart] = parts
- const [yyyy, mm, dd] = datePart.split('-')
- const [hh, min] = timePart.split(':')
+ const parts = date.split(' '); // Pisahkan tanggal dan waktu
+ const [datePart, timePart] = parts;
+ const [yyyy, mm, dd] = datePart.split('-');
+ const [hh, min] = timePart.split(':');
- const monthAbbreviation = months[parseInt(mm, 10) - 1]
+ const monthAbbreviation = months[parseInt(mm, 10) - 1];
- return `${dd} ${monthAbbreviation} ${hh}:${min}`
- }
+ return `${dd} ${monthAbbreviation} ${hh}:${min}`;
+ };
const getManifest = async () => {
- setIsLoading(true)
- const auth = getAuth()
- let list
- if(auth){
+ setIsLoading(true);
+ const auth = getAuth();
+ let list;
+ if (auth) {
list = await odooApi(
'GET',
`/api/v1/partner/${auth.partnerId}/stock-picking/${idAWB}/tracking`
- )
- }else{
- list = await odooApi(
- 'GET',
- `/api/v1/stock-picking/${idAWB}/tracking`
- )
+ );
+ } else {
+ list = await odooApi('GET', `/api/v1/stock-picking/${idAWB}/tracking`);
}
- setManifests(list)
- setIsLoading(false)
- }
+ setManifests(list);
+ setIsLoading(false);
+ };
useEffect(() => {
if (idAWB) {
- getManifest()
+ getManifest();
} else {
- setManifests(null)
+ setManifests(null);
}
- }, [idAWB])
+ }, [idAWB]);
- const [copied, setCopied] = useState(false)
+ const [copied, setCopied] = useState(false);
const handleCopyClick = () => {
- const textToCopy = manifests?.waybillNumber
- navigator.clipboard.writeText(textToCopy)
- setCopied(true)
- toast.success('No Resi Berhasil di Copy')
- setTimeout(() => setCopied(false), 2000) // Reset copied state after 2 seconds
- }
+ const textToCopy = manifests?.waybillNumber;
+ navigator.clipboard.writeText(textToCopy);
+ setCopied(true);
+ toast.success('No Resi Berhasil di Copy');
+ setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds
+ };
return (
<>
{isLoading && (
<BottomPopup active={true} close=''>
- <div className='leading-7 text-gray_r-12/80 flex justify-center'>Mohon Tunggu</div>
+ <div className='leading-7 text-gray_r-12/80 flex justify-center'>
+ Mohon Tunggu
+ </div>
<div className='container flex justify-center my-4'>
<LogoSpinner width={48} height={48} />
</div>
@@ -111,11 +110,14 @@ const Manifest = ({ idAWB, closePopup }) => {
</div>
<div className=''>
<h1 className='text-body-1'>
- Estimasi tiba pada <span className='text-gray_r-11 text-sm'>({manifests?.eta})</span>
+ Estimasi tiba pada{' '}
+ <span className='text-gray_r-11 text-sm'>({manifests?.eta})</span>
</h1>
<h1 className='text-sm mt-2 mb-3'>
Dikirim Menggunakan{' '}
- <span className='text-red-500 font-semibold'>{manifests?.deliveryOrder.carrier}</span>
+ <span className='text-red-500 font-semibold'>
+ {manifests?.deliveryOrder.carrier}
+ </span>
</h1>
{manifests?.waybillNumber && (
<div className='flex justify-between items-center'>
@@ -154,10 +156,16 @@ const Manifest = ({ idAWB, closePopup }) => {
{manifests.delivered == true && index == 0 ? (
<div
class={`absolute w-6 h-6 rounded-full mt-1.5 -left-3 border ${
- index == 0 ? 'bg-green-100 border-green-100' : 'bg-gray_r-7 border-white'
+ index == 0
+ ? 'bg-green-100 border-green-100'
+ : 'bg-gray_r-7 border-white'
}`}
>
- <ImageNext src='/images/open-box(1).svg' width={30} height={20} />
+ <ImageNext
+ src='/images/open-box(1).svg'
+ width={30}
+ height={20}
+ />
</div>
) : (
<div
@@ -167,7 +175,9 @@ const Manifest = ({ idAWB, closePopup }) => {
{manifests.delivered != true && (
<div
class={`absolute w-3 h-3 rounded-full mt-1.5 -left-1.5 border ${
- index == 0 ? 'bg-green-600 border-green-600' : 'bg-gray_r-7 border-white'
+ index == 0
+ ? 'bg-green-600 border-green-600'
+ : 'bg-gray_r-7 border-white'
} `}
/>
)}
@@ -176,9 +186,15 @@ const Manifest = ({ idAWB, closePopup }) => {
{formatCustomDate(manifest.datetime)}
</time>
{manifests.delivered == true && index == 0 && (
- <p class={`leading-6 font-semibold text-sm text-green-600 `}>Sudah Sampai</p>
+ <p
+ class={`leading-6 font-semibold text-sm text-green-600 `}
+ >
+ Sudah Sampai
+ </p>
)}
- <p class={`leading-6 text-[12px] text-gray_r-11`}>{manifest.description}</p>
+ <p class={`leading-6 text-[12px] text-gray_r-11`}>
+ {manifest.description}
+ </p>
</li>
</>
))}
@@ -187,7 +203,7 @@ const Manifest = ({ idAWB, closePopup }) => {
</BottomPopup>
)}
</>
- )
-}
+ );
+};
-export default Manifest
+export default Manifest;
diff --git a/src/pages/_document.jsx b/src/pages/_document.jsx
index 6af6294f..4b67c3f9 100644
--- a/src/pages/_document.jsx
+++ b/src/pages/_document.jsx
@@ -115,6 +115,19 @@ export default function MyDocument() {
}}
/>
+ <Script
+ async
+ id='gtag-config'
+ strategy='afterInteractive'
+ dangerouslySetInnerHTML={{
+ __html: `
+ gtag('config', 'AW-954540379/fCU8CI3Y8OoZENvClMcD', {
+ 'phone_conversion_number': '(021) 29338828'
+ });
+ `,
+ }}
+ />
+
{/* <Script
id='tawk-script-tag'
strategy='afterInteractive'
diff --git a/src/pages/api/banner-section.js b/src/pages/api/banner-section.js
new file mode 100644
index 00000000..7d7040c0
--- /dev/null
+++ b/src/pages/api/banner-section.js
@@ -0,0 +1,44 @@
+import odooApi from '@/core/api/odooApi';
+import { createClient } from 'redis';
+
+const client = createClient();
+
+client.on('error', (err) => console.error('Redis Client Error', err));
+
+const connectRedis = async () => {
+ if (!client.isOpen) {
+ await client.connect();
+ }
+};
+
+export default async function handler(req, res) {
+ try {
+ await connectRedis();
+ const cacheKey = 'hero-banner';
+ // await client.del(cacheKey);
+ let cachedData = await client.get(cacheKey);
+
+ if (cachedData) {
+ const data = JSON.parse(cachedData);
+ return res.status(200).json({ data });
+ } else {
+ const dataBannerSections = await odooApi(
+ 'GET',
+ '/api/v1/banner?type=home-banner'
+ );
+
+ // Simpan hasil fetch ke Redis dengan masa kadaluarsa 3 hari (259200 detik)
+ await client.set(
+ cacheKey,
+ JSON.stringify(dataBannerSections),
+ 'EX',
+ 259200
+ );
+
+ return res.status(200).json({ data: dataBannerSections });
+ }
+ } catch (error) {
+ console.error('Error interacting with Redis or fetching data:', error);
+ return res.status(500).json({ error: 'Internal Server Error' });
+ }
+}
diff --git a/src/pages/api/category-management.js b/src/pages/api/category-management.js
new file mode 100644
index 00000000..f05d8644
--- /dev/null
+++ b/src/pages/api/category-management.js
@@ -0,0 +1,85 @@
+import { createClient } from 'redis';
+// import { fetchCategoryManagementSolr } from '../../lib/home/api/categoryManagementApi';
+const client = createClient();
+client.on('error', (err) => console.error('Redis Client Error', err));
+
+const connectRedis = async () => {
+ if (!client.isOpen) {
+ await client.connect();
+ }
+};
+
+export default async function handler(req, res) {
+ try {
+ await connectRedis();
+ // await client.del('homepage_categoryDynamic');
+
+ let cachedData;
+ if (req.method === 'GET') {
+ cachedData = await client.get('homepage_categoryDynamic');
+
+ if (!cachedData) {
+ const items = await fetchCategoryManagementSolr();
+ await client.set(
+ 'homepage_categoryDynamic',
+ JSON.stringify(items),
+ 'EX',
+ 259200 // Expiry 3 hari
+ );
+ cachedData = await client.get('homepage_categoryDynamic');
+ }
+ const data = cachedData ? JSON.parse(cachedData) : null;
+ res.status(200).json({ data });
+ } else {
+ res.setHeader('Allow', ['GET']);
+ res.status(405).end(`Method ${req.method} Not Allowed`);
+ }
+ } catch (error) {
+ console.error('Error interacting with Redis:', error);
+ res.status(500).json({ error: 'Error interacting with Redis' });
+ }
+}
+
+const fetchCategoryManagementSolr = async () => {
+ let sort = 'sort=sequence_i asc';
+ try {
+ const response = await fetch(
+ `http://34.101.189.218:8983/solr/category_management/query?q=*:*&q.op=OR&indent=true&${sort}&&rows=20`
+ );
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ const data = await response.json();
+ const promotions = await map(data.response.docs);
+ return promotions;
+ } catch (error) {
+ console.error('Error fetching promotion data:', error);
+ return [];
+ }
+};
+const map = async (promotions) => {
+ return promotions.map((promotion) => {
+ let parsedCategories = promotion.categories.map((category) => {
+ // Parse string JSON utama
+ let parsedCategory = JSON.parse(category);
+
+ // Parse setiap elemen di child_frontend_id_i jika ada
+ if (parsedCategory.child_frontend_id_i) {
+ parsedCategory.child_frontend_id_i =
+ parsedCategory.child_frontend_id_i.map((child) => JSON.parse(child));
+ }
+
+ return parsedCategory;
+ });
+ let productMapped = {
+ id: promotion.id,
+ name: promotion.name_s,
+ image: promotion.image_s,
+ sequence: promotion.sequence_i,
+ numFound: promotion.numFound_i,
+ categories: parsedCategories,
+ category_id: promotion.category_id_i,
+ };
+ return productMapped;
+ });
+};
diff --git a/src/pages/api/hero-banner.js b/src/pages/api/hero-banner.js
new file mode 100644
index 00000000..7a348cfa
--- /dev/null
+++ b/src/pages/api/hero-banner.js
@@ -0,0 +1,45 @@
+import odooApi from '@/core/api/odooApi';
+import { createClient } from 'redis';
+
+const client = createClient();
+
+client.on('error', (err) => console.error('Redis Client Error', err));
+
+const connectRedis = async () => {
+ if (!client.isOpen) {
+ await client.connect();
+ }
+};
+
+export default async function handler(req, res) {
+ const { type } = req.query;
+ try {
+ await connectRedis();
+ const cacheKey = `homepage_bannerSection_${type}`;
+ // await client.del(cacheKey);
+ let cachedData = await client.get(cacheKey);
+
+ if (cachedData) {
+ const data = JSON.parse(cachedData);
+ return res.status(200).json({ data });
+ } else {
+ const dataBannerSections = await odooApi(
+ 'GET',
+ `/api/v1/banner?type=${type}`
+ );
+
+ // Simpan hasil fetch ke Redis dengan masa kadaluarsa 3 hari (259200 detik)
+ await client.set(
+ cacheKey,
+ JSON.stringify(dataBannerSections),
+ 'EX',
+ 259200
+ );
+ cachedData = await client.get(cacheKey);
+ return res.status(200).json({ data: cachedData });
+ }
+ } catch (error) {
+ console.error('Error interacting with Redis or fetching data:', error);
+ return res.status(500).json({ error: 'Internal Server Error' });
+ }
+}
diff --git a/src/pages/api/page-content.js b/src/pages/api/page-content.js
new file mode 100644
index 00000000..a6514505
--- /dev/null
+++ b/src/pages/api/page-content.js
@@ -0,0 +1,43 @@
+import { createClient } from 'redis';
+import { getPageContent } from '~/services/pageContent';
+// import { fetchCategoryManagementSolr } from '../../lib/home/api/categoryManagementApi';
+const client = createClient();
+client.on('error', (err) => console.error('Redis Client Error', err));
+
+const connectRedis = async () => {
+ if (!client.isOpen) {
+ await client.connect();
+ }
+};
+
+export default async function handler(req, res) {
+ const { path } = req.query;
+ try {
+ await connectRedis();
+ // await client.del('onbording-popup');
+
+ let cachedData;
+ if (req.method === 'GET') {
+ cachedData = await client.get(`page-content:${path}`);
+
+ if (!cachedData) {
+ const items = await getPageContent({ path });
+ await client.set(
+ `page-content:${path}`,
+ JSON.stringify(items),
+ 'EX',
+ 604800 // Expiry 1 minggu
+ );
+ cachedData = await client.get(`page-content:${path}`);
+ }
+ const data = cachedData ? JSON.parse(cachedData) : null;
+ res.status(200).json({ data });
+ } else {
+ res.setHeader('Allow', ['GET']);
+ res.status(405).end(`Method ${req.method} Not Allowed`);
+ }
+ } catch (error) {
+ console.error('Error interacting with Redis:', error);
+ res.status(500).json({ error: 'Error interacting with Redis' });
+ }
+}
diff --git a/src/pages/api/shop/brands.js b/src/pages/api/shop/brands.js
index 9c2824b3..d56e4b13 100644
--- a/src/pages/api/shop/brands.js
+++ b/src/pages/api/shop/brands.js
@@ -1,8 +1,20 @@
import axios from 'axios';
+import { createClient } from 'redis';
const SOLR_HOST = process.env.SOLR_HOST;
+const client = createClient();
+
+client.on('error', (err) => console.error('Redis Client Error', err));
+
+const connectRedis = async () => {
+ if (!client.isOpen) {
+ await client.connect();
+ }
+};
export default async function handler(req, res) {
+ await connectRedis();
+
try {
let params = '*:*';
let sort =
@@ -11,12 +23,12 @@ export default async function handler(req, res) {
if (req.query.params) {
rows = 100;
- switch (req?.query?.params) {
+ switch (req.query.params) {
case 'level_s':
params = 'level_s:prioritas';
break;
case 'search':
- params = `name_s:"${req?.query?.q.toLowerCase()}"`;
+ params = `name_s:"${req.query.q.toLowerCase()}"`;
sort = '';
rows = 1;
break;
@@ -24,11 +36,11 @@ export default async function handler(req, res) {
params = `name_s:${req.query.params}`.toLowerCase();
}
}
- if(req.query.rows) rows = req.query.rows;
-
+ if (req.query.rows) rows = req.query.rows;
+
const url = `${SOLR_HOST}/solr/brands/select?q=${params}&q.op=OR&indent=true&rows=${rows}&${sort}`;
- let brands = await axios(url);
- let dataBrands = responseMap(brands.data.response.docs);
+ const brands = await axios(url);
+ const dataBrands = responseMap(brands.data.response.docs);
res.status(200).json(dataBrands);
} catch (error) {
@@ -39,13 +51,11 @@ export default async function handler(req, res) {
const responseMap = (brands) => {
return brands.map((brand) => {
- let brandMapping = {
+ return {
id: brand.id,
name: brand.display_name_s,
logo: brand.image_s || '',
- sequance: brand.sequence_i || '',
+ sequence: brand.sequence_i || '',
};
-
- return brandMapping;
});
};
diff --git a/src/pages/api/shop/preferredBrand.js b/src/pages/api/shop/preferredBrand.js
new file mode 100644
index 00000000..4cb35c84
--- /dev/null
+++ b/src/pages/api/shop/preferredBrand.js
@@ -0,0 +1,61 @@
+import axios from 'axios';
+import { createClient } from 'redis';
+
+const SOLR_HOST = process.env.SOLR_HOST;
+const client = createClient();
+
+client.on('error', (err) => console.error('Redis Client Error', err));
+
+const connectRedis = async () => {
+ if (!client.isOpen) {
+ await client.connect();
+ }
+};
+
+export default async function handler(req, res) {
+ await connectRedis();
+
+ try {
+ let params = '*:*';
+ let sort =
+ 'sort=if(exists(sequence_i),0,1) asc,sequence_i asc, if(exists(image_s),0,1) asc ';
+ let rows = 20;
+
+ if (req.query.params) {
+ rows = 20;
+ switch (req.query.params) {
+ case 'level_s':
+ params = 'level_s:prioritas';
+ break;
+ case 'search':
+ params = `name_s:"${req.query.q.toLowerCase()}"`;
+ sort = '';
+ rows = 1;
+ break;
+ default:
+ params = `name_s:${req.query.params}`.toLowerCase();
+ }
+ }
+ if (req.query.rows) rows = req.query.rows;
+
+ const url = `${SOLR_HOST}/solr/brands/select?q=${params}&q.op=OR&indent=true&rows=${rows}&${sort}`;
+ const brands = await axios(url);
+ const dataBrands = responseMap(brands.data.response.docs);
+
+ res.status(200).json(dataBrands);
+ } catch (error) {
+ console.error('Error fetching data from Solr:', error);
+ res.status(500).json({ error: 'Internal Server Error' });
+ }
+}
+
+const responseMap = (brands) => {
+ return brands.map((brand) => {
+ return {
+ id: brand.id,
+ name: brand.display_name_s,
+ logo: brand.image_s || '',
+ sequence: brand.sequence_i || '',
+ };
+ });
+};
diff --git a/src/pages/api/shop/product-detail.js b/src/pages/api/shop/product-detail.js
index 247f2a04..faa96028 100644
--- a/src/pages/api/shop/product-detail.js
+++ b/src/pages/api/shop/product-detail.js
@@ -8,7 +8,7 @@ export default async function handler(req, res) {
)
let productVariants = await axios(
process.env.SOLR_HOST +
- `/solr/variants/select?q=template_id_i:${req.query.id}&q.op=OR&indent=true&rows=100&fq=-publish_b:false`
+ `/solr/variants/select?q=template_id_i:${req.query.id}&q.op=OR&indent=true&rows=100&fq=-publish_b:false AND price_tier1_v2_f:[1 TO *]`
)
let auth = req.query.auth === 'false' ? JSON.parse(req.query.auth) : req.query.auth
let result = productMappingSolr(productTemplate.data.response.docs, auth || false)
diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js
index ace281f7..63ec7ca0 100644
--- a/src/pages/api/shop/search.js
+++ b/src/pages/api/shop/search.js
@@ -20,7 +20,6 @@ export default async function handler(req, res) {
} = req.query;
let { stock = '' } = req.query;
-
let paramOrderBy = '';
switch (orderBy) {
case 'flashsale-discount-desc':
@@ -73,8 +72,9 @@ export default async function handler(req, res) {
const formattedQuery = `(${newQ
.split(' ')
- .map((term) => `${term}*`)
+ .map((term) => (term.length < 2 ? term : `${term}*`)) // Tambahkan '*' hanya jika panjang kata >= 2
.join(' ')})`;
+
const mm =
checkQ.length > 2
? checkQ.length > 5
@@ -88,11 +88,23 @@ export default async function handler(req, res) {
'price_tier1_v2_f:[1 TO *]',
];
- if (fq && source != 'similar') {
- filterQueries.push(fq);
+ if (fq && source != 'similar' && typeof fq != 'string') {
+ // filterQueries.push(fq);
+ fq.push(...filterQueries);
}
const fq_ = filterQueries.join(' AND ');
+ let keywords = newQ;
+ if (source === 'similar' || checkQ.length < 3) {
+ if (checkQ.length < 2 || checkQ[1].length < 2) {
+ keywords = newQ;
+ } else {
+ keywords = newQ + '*';
+ }
+ } else {
+ keywords = formattedQuery;
+ }
+
let offset = (page - 1) * limit;
let parameter = [
'facet.field=manufacture_name_s',
@@ -101,13 +113,7 @@ export default async function handler(req, res) {
'indent=true',
`facet.query=${escapeSolrQuery(q)}`,
`q.op=OR`,
- `q=${
- source == 'similar' || checkQ.length < 3
- ? checkQ.length < 2
- ? newQ
- : newQ + '*'
- : formattedQuery
- }`,
+ `q=${keywords}`,
`defType=edismax`,
'qf=name_s description_clean_t category_name manufacture_name_s variants_code_t variants_name_t category_id_ids default_code_s manufacture_id_i category_id_i ',
`start=${parseInt(offset)}`,
@@ -152,13 +158,12 @@ export default async function handler(req, res) {
if (stock) parameter.push(`fq=stock_total_f:{1 TO *}`);
// Single fq in url params
- // if (typeof fq === 'string') parameter.push(`fq=${encodeURIComponent(fq)}`);
+ if (typeof fq === 'string') parameter.push(`fq=${encodeURIComponent(fq)}`);
// Multi fq in url params
if (Array.isArray(fq))
parameter = parameter.concat(
fq.map((val) => `fq=${encodeURIComponent(val)}`)
);
-
let result = await axios(
process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&')
);
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index cc4d68db..2ec1231a 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -45,18 +45,19 @@ const FlashSale = dynamic(
}
);
-const ProgramPromotion = dynamic(() =>
- import('@/lib/home/components/PromotionProgram'),
-{
- loading: () => <BannerPromoSkeleton />,
-}
+const ProgramPromotion = dynamic(
+ () => import('@/lib/home/components/PromotionProgram'),
+ {
+ loading: () => <BannerPromoSkeleton />,
+ }
);
const BannerSection = dynamic(() =>
import('@/lib/home/components/BannerSection')
-);
-const CategoryHomeId = dynamic(() =>
- import('@/lib/home/components/CategoryHomeId'), {ssr: false}
+);
+const CategoryHomeId = dynamic(
+ () => import('@/lib/home/components/CategoryHomeId'),
+ { ssr: false }
);
const CategoryDynamic = dynamic(() =>
@@ -64,17 +65,18 @@ const CategoryDynamic = dynamic(() =>
);
const CategoryDynamicMobile = dynamic(() =>
-import('@/lib/home/components/CategoryDynamicMobile')
+ import('@/lib/home/components/CategoryDynamicMobile')
);
-const CustomerReviews = dynamic(() =>
- import('@/lib/review/components/CustomerReviews'), {ssr: false}
+const CustomerReviews = dynamic(
+ () => import('@/lib/review/components/CustomerReviews'),
+ { ssr: false }
); // need to ssr:false
-const ServiceList = dynamic(() => import('@/lib/home/components/ServiceList'), {ssr: false}); // need to ssr: false
+const ServiceList = dynamic(() => import('@/lib/home/components/ServiceList'), {
+ ssr: false,
+}); // need to ssr: false
-
-
-export default function Home({categoryId}) {
+export default function Home({ categoryId }) {
const bannerRef = useRef(null);
const wrapperRef = useRef(null);
@@ -85,123 +87,110 @@ export default function Home({categoryId}) {
bannerRef.current?.querySelector(':first-child')?.clientHeight + 'px';
};
- useEffect(() => {
- const loadCategories = async () => {
- const getCategories = await odooApi('GET', '/api/v1/category/child?partner_id='+{categoryId})
- if(getCategories){
- setDataCategories(getCategories)
- }
- }
- loadCategories()
- }, [])
-
- const [dataCategories, setDataCategories] = useState([])
return (
<>
- <BasicLayout>
- <Seo
- title='Indoteknik.com: B2B Industrial Supply & Solution'
- description='Temukan pilihan produk B2B Industri &amp; Alat Teknik untuk Perusahaan, UMKM &amp; Pemerintah dengan lengkap, mudah dan transparan.'
- additionalMetaTags={[
- {
- name: 'keywords',
- content: 'indoteknik, indoteknik.com, toko teknik, toko perkakas, jual genset, jual fogging, jual krisbow, harga krisbow, harga alat safety, harga pompa air',
- },
- ]} />
-
- <PagePopupIformation />
-
- <DesktopView>
- <div className='container mx-auto'>
- <div
- className='flex min-h-[400px] h-[460px]'
- ref={wrapperRef}
- onLoad={handleOnLoad}
- >
- <div className='w-2/12'>
- <HeroBannerSecondary />
- </div>
- <div className='w-7/12 px-1' ref={bannerRef}>
- <HeroBanner />
+ <BasicLayout>
+ <Seo
+ title='Indoteknik.com: B2B Industrial Supply & Solution'
+ description='Temukan pilihan produk B2B Industri &amp; Alat Teknik untuk Perusahaan, UMKM &amp; Pemerintah dengan lengkap, mudah dan transparan.'
+ additionalMetaTags={[
+ {
+ name: 'keywords',
+ content:
+ 'indoteknik, indoteknik.com, toko teknik, toko perkakas, jual genset, jual fogging, jual krisbow, harga krisbow, harga alat safety, harga pompa air',
+ },
+ ]}
+ />
+
+ <PagePopupIformation />
+
+ <DesktopView>
+ <div className='container mx-auto'>
+ <div
+ className='flex min-h-[400px] h-[460px]'
+ ref={wrapperRef}
+ onLoad={handleOnLoad}
+ >
+ <div className='w-2/12'>
+ <HeroBannerSecondary />
+ </div>
+ <div className='w-7/12 px-1' ref={bannerRef}>
+ <HeroBanner />
+ </div>
+ <div className='w-3/12'>
+ <DelayRender renderAfter={200}>
+ <PopularProduct />
+ </DelayRender>
+ </div>
</div>
- <div className='w-3/12'>
- <DelayRender renderAfter={200}>
- <PopularProduct />
- </DelayRender>
- </div>
- </div>
- <div className='my-16 flex flex-col gap-y-8'>
- <ServiceList />
- <div id='flashsale'>
- <PreferredBrand />
+ <div className='my-16 flex flex-col gap-y-8'>
+ <ServiceList />
+ <div id='flashsale'>
+ <PreferredBrand />
+ </div>
+ {!auth?.feature?.soApproval && (
+ <>
+ <DelayRender renderAfter={200}>
+ <ProgramPromotion />
+ </DelayRender>
+ <DelayRender renderAfter={200}>
+ <FlashSale />
+ </DelayRender>
+ </>
+ )}
+ {/* <PromotinProgram /> */}
+ <CategoryPilihan />
+ <CategoryDynamic />
+ <CategoryHomeId />
+ <BannerSection />
+ <CustomerReviews />
</div>
+ </div>
+ </DesktopView>
+ <MobileView>
+ <DelayRender renderAfter={200}>
+ <HeroBanner />
+ </DelayRender>
+ <div className='flex flex-col gap-y-4 my-6'>
+ <DelayRender renderAfter={400}>
+ <ServiceList />
+ </DelayRender>
+ <DelayRender renderAfter={400}>
+ <div id='flashsale'>
+ <PreferredBrand />
+ </div>
+ </DelayRender>
{!auth?.feature?.soApproval && (
<>
- <DelayRender renderAfter={200}>
- <ProgramPromotion />
- </DelayRender>
- <DelayRender renderAfter={200}>
- <FlashSale />
- </DelayRender>
+ <DelayRender renderAfter={400}>
+ <ProgramPromotion />
+ </DelayRender>
+ <DelayRender renderAfter={600}>
+ <FlashSale />
+ </DelayRender>
</>
)}
- {/* <PromotinProgram /> */}
- {dataCategories &&(
- <CategoryPilihan categories={dataCategories} />
- )}
- <CategoryDynamic />
- <CategoryHomeId />
- <BannerSection />
- <CustomerReviews />
+ <DelayRender renderAfter={600}>
+ {/* <PromotinProgram /> */}
+ </DelayRender>
+ <DelayRender renderAfter={600}>
+ <CategoryPilihan />
+ <CategoryDynamicMobile />
+ </DelayRender>
+ <DelayRender renderAfter={800}>
+ <PopularProduct />
+ </DelayRender>
+ <DelayRender renderAfter={1000}>
+ <CategoryHomeId />
+ <BannerSection />
+ </DelayRender>
+ <DelayRender renderAfter={1200}>
+ <CustomerReviews />
+ </DelayRender>
</div>
- </div>
- </DesktopView>
- <MobileView>
- <DelayRender renderAfter={200}>
- <HeroBanner />
- </DelayRender>
- <div className='flex flex-col gap-y-4 my-6'>
- <DelayRender renderAfter={400}>
- <ServiceList />
- </DelayRender>
- <DelayRender renderAfter={400}>
- <div id='flashsale'>
- <PreferredBrand />
- </div>
- </DelayRender>
- {!auth?.feature?.soApproval && (
- <>
- <DelayRender renderAfter={400}>
- <ProgramPromotion />
- </DelayRender>
- <DelayRender renderAfter={600}>
- <FlashSale />
- </DelayRender>
- </>
- )}
- <DelayRender renderAfter={600}>
- {/* <PromotinProgram /> */}
- </DelayRender>
- <DelayRender renderAfter={600}>
- {dataCategories &&(
- <CategoryPilihan categories={dataCategories} />
- )}
- <CategoryDynamicMobile />
- </DelayRender>
- <DelayRender renderAfter={800}>
- <PopularProduct />
- </DelayRender>
- <DelayRender renderAfter={1000}>
- <CategoryHomeId />
- <BannerSection />
- </DelayRender>
- <DelayRender renderAfter={1200}>
- <CustomerReviews />
- </DelayRender>
- </div>
- </MobileView>
- </BasicLayout>
- </>
+ </MobileView>
+ </BasicLayout>
+ </>
);
-} \ No newline at end of file
+}
diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js
index f896a6a8..ecd62be2 100644
--- a/src/utils/solrMapping.js
+++ b/src/utils/solrMapping.js
@@ -43,6 +43,7 @@ export const productMappingSolr = (products, pricelist) => {
let productMapped = {
id: product.product_id_i || '',
image: product.image_s || '',
+ imageMobile: product.image_mobile_s || '',
code: product.default_code_s || '',
description: product.description_t || '',
displayName: product.display_name_s || '',
@@ -74,7 +75,7 @@ export const productMappingSolr = (products, pricelist) => {
name: product.manufacture_name_s || '',
imagePromotion1: product.image_promotion_1_s || '',
imagePromotion2: product.image_promotion_2_s || '',
- logo : product.x_logo_manufacture_s || '',
+ logo: product.x_logo_manufacture_s || '',
};
}
@@ -128,13 +129,14 @@ export const variantsMappingSolr = (parent, products, pricelist) => {
manufacture: {},
parent: {},
qtySold: product?.qty_sold_f || 0,
+ is_in_bu: product?.is_in_bu_b || false,
};
if (product.manufacture_id_i && product.manufacture_name_s) {
productMapped.manufacture = {
id: product.manufacture_id_i || '',
name: product.manufacture_name_s || '',
- logo : parent[0]?.x_logo_manufacture_s
+ logo: parent[0]?.x_logo_manufacture_s,
};
}
productMapped.parent = {