summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortrisusilo48 <tri.susilo@altama.co.id>2024-11-15 14:55:57 +0700
committertrisusilo48 <tri.susilo@altama.co.id>2024-11-15 14:55:57 +0700
commit36c6e0361762cb0faff10996e30737a58cf14284 (patch)
tree4cb225b8f106c5ea2ce1d929fc8af0b9757ea0e4 /src
parentc69b71c16ff7cc7a347394710d069ef711db08bb (diff)
parent7ca8fce4ac97ffc31042dd4d016460a5e61284a5 (diff)
Merge branch 'new-release' of https://bitbucket.org/altafixco/next-indoteknik into new-release
Diffstat (limited to 'src')
-rw-r--r--src/components/ui/HeroBanner.jsx37
-rw-r--r--src/components/ui/HeroBannerSecondary.jsx69
-rw-r--r--src/core/components/elements/Navbar/TopBanner.jsx53
-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/PromotionProgram.jsx96
-rw-r--r--src/lib/product/components/ProductCard.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.js44
-rw-r--r--src/pages/api/shop/brands.js39
-rw-r--r--src/utils/solrMapping.js1
14 files changed, 543 insertions, 331 deletions
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/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/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/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/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx
index 936d4ee0..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:
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..3cb8a237
--- /dev/null
+++ b/src/pages/api/page-content.js
@@ -0,0 +1,44 @@
+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 });
+ console.log('items', items);
+ 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..219f2cb0 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,20 @@ 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 cacheKey = `brands_home`;
+ let cachedData = await client.get(cacheKey);
+
+ if (cachedData) {
+ return res.status(200).json(JSON.parse(cachedData));
+ }
+
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);
+
+ await client.set(cacheKey, JSON.stringify(dataBrands), 'EX', 259200);
res.status(200).json(dataBrands);
} catch (error) {
@@ -39,13 +60,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/utils/solrMapping.js b/src/utils/solrMapping.js
index f896a6a8..01a8587c 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 || '',