summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/home/api/CategoryPilihanApi.js8
-rw-r--r--src/lib/home/components/CategoryDynamic.jsx50
-rw-r--r--src/lib/home/components/CategoryPilihan.jsx14
-rw-r--r--src/lib/home/hooks/useCategoryPilihan.js13
-rw-r--r--src/lib/lob/components/Breadcrumb.jsx55
-rw-r--r--src/lib/product/components/CategorySection.jsx11
-rw-r--r--src/lib/product/components/LobSectionCategory.jsx82
-rw-r--r--src/lib/product/components/ProductSearch.jsx123
8 files changed, 314 insertions, 42 deletions
diff --git a/src/lib/home/api/CategoryPilihanApi.js b/src/lib/home/api/CategoryPilihanApi.js
new file mode 100644
index 00000000..8a0b38d3
--- /dev/null
+++ b/src/lib/home/api/CategoryPilihanApi.js
@@ -0,0 +1,8 @@
+import odooApi from '@/core/api/odooApi'
+
+const categoryPilihanApi = async () => {
+ const dataCategoryPilihan = await odooApi('GET', '/api/v1/lob_homepage')
+ return dataCategoryPilihan
+}
+
+export default categoryPilihanApi
diff --git a/src/lib/home/components/CategoryDynamic.jsx b/src/lib/home/components/CategoryDynamic.jsx
index fa1df286..6ab03ec3 100644
--- a/src/lib/home/components/CategoryDynamic.jsx
+++ b/src/lib/home/components/CategoryDynamic.jsx
@@ -2,38 +2,58 @@ import React, { useEffect, useState } from 'react';
import useCategoryManagement from '../hooks/useCategoryManagement';
import NextImage from 'next/image';
import Link from "next/link";
-import router from 'next/router';
import { createSlug } from '@/core/utils/slug';
+import odooApi from '@/core/api/odooApi';
const CategoryDynamic = () => {
const { categoryManagement } = useCategoryManagement();
+ const [categoryData, setCategoryData] = useState({});
+ const [subCategoryData, setSubCategoryData] = useState({});
+
+ useEffect(() => {
+ const fetchCategoryData = async () => {
+ if (categoryManagement && categoryManagement.data) {
+ const updatedCategoryData = {};
+ const updatedSubCategoryData = {};
+
+ for (const category of categoryManagement.data) {
+ // Calculate level 1 products
+ const countLevel1 = await odooApi('GET', `/api/v1/category/numFound?parent_id=${category.categoryIdI}`);
+ // console.log("countLevel1.child",countLevel1)
+
+ updatedCategoryData[category.categoryIdI] = countLevel1?.numFound;
+
+
+ // Calculate level 2 products for each sub-category
+ for (const subCategory of countLevel1.children) {
+ updatedSubCategoryData[subCategory.id] = subCategory.numFound;
+ }
+ }
- const calculateLevel3Products = (category) => {
- return category.childFrontendIdI.reduce((total, child) => total + (child.numFound || 0), 0);
- };
+ setCategoryData(updatedCategoryData);
+ setSubCategoryData(updatedSubCategoryData);
+ }
+ };
- const calculateLevel2Products = (category) => {
- return category.categories.reduce((total, subCategory) => {
- const level3Products = calculateLevel3Products(subCategory);
- return total + (subCategory.numFound || 0) + level3Products;
- }, 0);
- };
+ fetchCategoryData();
+ }, [categoryManagement, categoryData]);
+
return (
<div>
{categoryManagement && categoryManagement.data?.map((category) => {
- const countLevel2 = calculateLevel2Products(category);
-
+ const countLevel1 = categoryData[category.categoryIdI] || 0;
+
return (
<div key={category.id}>
<div className='bagian-judul flex flex-row justify-start items-center gap-3 mb-4 mt-4'>
<div className='font-semibold sm:text-h-lg mr-2'>{category.name}</div>
- <p className='text-gray_r-10 text-sm'>{countLevel2} Produk tersedia</p>
+ <p className='text-gray_r-10 text-sm'>{countLevel1} Produk tersedia</p>
<Link href={createSlug('/shop/category/', category?.name, category?.categoryIdI)} className="!text-red-500 font-semibold">Lihat Semua</Link>
</div>
<div className='grid grid-cols-3 gap-2'>
{category.categories.map((subCategory) => {
- const countLevel3 = calculateLevel3Products(subCategory);
+ const countLevel2 = subCategoryData[subCategory.idLevel2] || 0;
return (
<div key={subCategory.id} className='border rounded justify-start items-start'>
@@ -48,7 +68,7 @@ const CategoryDynamic = () => {
/>
<div className='bagian-judul flex flex-col justify-center items-start gap-2 ml-2'>
<div className='font-semibold text-lg mr-2'>{subCategory.name}</div>
- <p className='text-gray_r-10 text-sm'>{(subCategory.numFound || 0) + countLevel3} Produk</p>
+ <p className='text-gray_r-10 text-sm'>{countLevel2} Produk tersedia</p>
<Link href={createSlug('/shop/category/', subCategory?.name, subCategory?.idLevel2)} className="!text-red-500 font-semibold">Lihat Semua</Link>
</div>
</div>
diff --git a/src/lib/home/components/CategoryPilihan.jsx b/src/lib/home/components/CategoryPilihan.jsx
index 5aa1fbcc..6efc1070 100644
--- a/src/lib/home/components/CategoryPilihan.jsx
+++ b/src/lib/home/components/CategoryPilihan.jsx
@@ -6,11 +6,11 @@ import { useEffect, useState } from 'react';
import { bannerApi } from '../../../api/bannerApi';
const { useQuery } = require('react-query')
import { HeroBannerSkeleton } from '../../../components/skeleton/BannerSkeleton';
-
+import useCategoryPilihan from '../hooks/useCategoryPilihan';
const CategoryPilihan = ({ id, categories }) => {
+ const { categoryPilihan } = useCategoryPilihan();
const heroBanner = useQuery('categoryPilihan', bannerApi({ type: 'banner-category-list' }));
-
return (
<section>
<div className='flex flex-row items-center mb-4'>
@@ -38,19 +38,19 @@ const CategoryPilihan = ({ id, categories }) => {
)}
</div>
<div className="group/item grid grid-cols-6 gap-y-2 w-full h-full col-span-2 ">
- {categories.map((category) => (
- <div key={category.id} className="KartuInti h-48 w-60 max-w-sm lg:max-w-full flex flex-col border-[1px] border-gray-200 relative group">
+ {categoryPilihan?.data?.map((category) => (
+ <div className="KartuInti h-48 w-60 max-w-sm lg:max-w-full flex flex-col border-[1px] border-gray-200 relative group">
<div className='KartuB absolute h-48 w-60 inset-0 flex items-center justify-center '>
<div className="group/edit flex items-center justify-end h-48 w-60 flex-col group-hover/item:visible">
<div className=' h-36 flex justify-end items-end'>
- <Image className='group-hover:scale-105 transition-transform duration-300 ' src={category?.image1920? category?.image1920 : '/images/noimage.jpeg'} width={120} height={120} alt={category?.name} />
+ <Image className='group-hover:scale-105 transition-transform duration-300 ' src={category?.image? category?.image : '/images/noimage.jpeg'} width={120} height={120} alt={category?.name} />
</div>
- <h2 className="text-gray-700 content-center h-12 border-t-[1px] px-1 w-60 border-gray-200 font-normal text-sm text-center">{category?.name}</h2>
+ <h2 className="text-gray-700 content-center h-12 border-t-[1px] px-1 w-60 border-gray-200 font-normal text-sm text-center">{category?.industries}</h2>
</div>
</div>
<div className='KartuA relative inset-0 flex h-36 w-60 items-center justify-center opacity-0 group-hover:opacity-75 group-hover:bg-[#E20613] transition-opacity '>
<Link
- href={createSlug('/shop/category/', category?.name, category?.id)}
+ href={createSlug('/shop/lob/', category?.industries, category?.id)}
className='category-mega-box__parent text-white rounded-lg'
>
Lihat semua
diff --git a/src/lib/home/hooks/useCategoryPilihan.js b/src/lib/home/hooks/useCategoryPilihan.js
new file mode 100644
index 00000000..12a86f7e
--- /dev/null
+++ b/src/lib/home/hooks/useCategoryPilihan.js
@@ -0,0 +1,13 @@
+import categoryPilihanApi from '../api/CategoryPilihanApi'
+import { useQuery } from 'react-query'
+
+const useCategoryPilihan = () => {
+ const fetchCategoryPilihan = async () => await categoryPilihanApi()
+ const { isLoading, data } = useQuery('categoryPilihanApi', fetchCategoryPilihan)
+
+ return {
+ categoryPilihan: { data, isLoading }
+ }
+}
+
+export default useCategoryPilihan \ No newline at end of file
diff --git a/src/lib/lob/components/Breadcrumb.jsx b/src/lib/lob/components/Breadcrumb.jsx
new file mode 100644
index 00000000..5722fd39
--- /dev/null
+++ b/src/lib/lob/components/Breadcrumb.jsx
@@ -0,0 +1,55 @@
+import odooApi from '@/core/api/odooApi'
+import { createSlug } from '@/core/utils/slug'
+import {
+ Breadcrumb as ChakraBreadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ Skeleton
+} from '@chakra-ui/react'
+import Link from 'next/link'
+import React from 'react'
+import { useQuery } from 'react-query'
+
+/**
+ * Render a breadcrumb component.
+ *
+ * @param {object} categoryId - The ID of the category.
+ * @return {JSX.Element} The breadcrumb component.
+ */
+const Breadcrumb = ({ categoryId }) => {
+ const breadcrumbs = useQuery(
+ `lob-breadcrumbs/${categoryId}`,
+ async () => await odooApi('GET', `/api/v1/lob_homepage/${categoryId}/category_id`)
+ )
+ return (
+ <div className='container mx-auto py-4 md:py-6'>
+ <Skeleton isLoaded={!breadcrumbs.isLoading} className='w-2/3'>
+ <ChakraBreadcrumb>
+ <BreadcrumbItem>
+ <BreadcrumbLink as={Link} href='/' className='!text-danger-500 whitespace-nowrap'>
+ Home
+ </BreadcrumbLink>
+ </BreadcrumbItem>
+
+ {breadcrumbs?.data?.map((category, index) => (
+ <BreadcrumbItem key={index} isCurrentPage={index === breadcrumbs.data.length - 1}>
+ {index === breadcrumbs.data.length - 1 ? (
+ <BreadcrumbLink className='whitespace-nowrap'>{category.industries}</BreadcrumbLink>
+ ) : (
+ <BreadcrumbLink
+ as={Link}
+ href={createSlug('/shop/lob/', category.industries, category.id)}
+ className='!text-danger-500 whitespace-nowrap'
+ >
+ {category.industries}
+ </BreadcrumbLink>
+ )}
+ </BreadcrumbItem>
+ ))}
+ </ChakraBreadcrumb>
+ </Skeleton>
+ </div>
+ )
+}
+
+export default Breadcrumb
diff --git a/src/lib/product/components/CategorySection.jsx b/src/lib/product/components/CategorySection.jsx
index 14a39e7e..2af3db10 100644
--- a/src/lib/product/components/CategorySection.jsx
+++ b/src/lib/product/components/CategorySection.jsx
@@ -13,23 +13,16 @@ import {
HeartIcon,
} from '@heroicons/react/24/outline';
import { useState } from 'react'; // Import useState
+import { getIdFromSlug } from '@/core/utils/slug'
const CategorySection = ({ categories }) => {
const { isDesktop, isMobile } = useDevice();
- const router = useRouter();
const [isOpenCategory, setIsOpenCategory] = useState(false); // State to manage category visibility
- let teks = router.query.slug;
- let hasil = teks?.match(/(\d+)$/)[0];
-
- const breadcrumbs = useQuery(
- `category-breadcrumbs/${hasil}`,
- async () => await odooApi('GET', `/api/v1/category/${hasil}/category-breadcrumb`)
- );
-
const handleToggleCategories = () => {
setIsOpenCategory(!isOpenCategory);
};
+
const displayedCategories = isOpenCategory ? categories : categories.slice(0, 10);
diff --git a/src/lib/product/components/LobSectionCategory.jsx b/src/lib/product/components/LobSectionCategory.jsx
new file mode 100644
index 00000000..34a09e46
--- /dev/null
+++ b/src/lib/product/components/LobSectionCategory.jsx
@@ -0,0 +1,82 @@
+import Image from "next/image";
+import Link from 'next/link';
+import { createSlug } from '@/core/utils/slug';
+import useDevice from '@/core/hooks/useDevice';
+import { Swiper, SwiperSlide } from 'swiper/react';
+import 'swiper/css';
+import { useQuery } from 'react-query';
+import { useRouter } from 'next/router';
+import {
+ ChevronDownIcon,
+ ChevronUpIcon, // Import ChevronUpIcon for toggling
+ DocumentCheckIcon,
+ HeartIcon,
+} from '@heroicons/react/24/outline';
+import { useState } from 'react'; // Import useState
+import { getIdFromSlug } from '@/core/utils/slug'
+
+const LobSectionCategory = ({ categories }) => {
+ const { isDesktop, isMobile } = useDevice();
+ const [isOpenCategory, setIsOpenCategory] = useState(false); // State to manage category visibility
+
+ const handleToggleCategories = () => {
+ setIsOpenCategory(!isOpenCategory);
+ };
+ console.log("categories",categories[0]?.categoryIds)
+
+ const displayedCategories = categories[0]?.categoryIds;
+
+ return (
+ <section>
+ {isDesktop && (
+ <div className="group/item grid grid-flow-col gap-y-2 gap-x-4 w-full h-full">
+ {displayedCategories?.map((category) => (
+ <Link
+ href={createSlug('/shop/category/', category?.name, category?.id)}
+ key={category?.id}
+ passHref
+ className="block hover:scale-105 transition-transform duration-300 bg-cover bg-center h-[144px]"
+ style={{
+ backgroundImage: `url('${category?.image ? category?.image : 'https://erp.indoteknik.com/web/image?model=x_banner.banner&id=5&field=x_banner_image&unique=09202023100557'}')`,
+ }}
+ >
+ </Link>
+ ))}
+ </div>
+ )}
+
+ {isMobile && (
+ <div className="py-4">
+ <Swiper slidesPerView={1.2} spaceBetween={10}>
+ {displayedCategories?.map((category) => (
+ <SwiperSlide key={category?.id}>
+ <Link
+ href={createSlug('/shop/category/', category?.name, category?.id)}
+ key={category?.id}
+ passHref
+ className="block bg-cover bg-center h-[144px]"
+ style={{
+ backgroundImage: `url('${category?.image ? category?.image : 'https://erp.indoteknik.com/web/image?model=x_banner.banner&id=5&field=x_banner_image&unique=09202023100557'}')`,
+ }}
+ >
+ </Link>
+ </SwiperSlide>
+ ))}
+ </Swiper>
+ {categories.length > 10 && (
+ <div className="w-full flex justify-end mt-4">
+ <button
+ onClick={handleToggleCategories}
+ className="flex justify-end mt-4 bg-red-500 text-white text-sm px-4 py-2 rounded"
+ >
+ {isOpenCategory ? 'Sembunyikan Semua' : 'Lihat Semua'}
+ </button>
+ </div>
+ )}
+ </div>
+ )}
+ </section>
+ )
+}
+
+export default LobSectionCategory
diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx
index 34018ffe..4d510c5b 100644
--- a/src/lib/product/components/ProductSearch.jsx
+++ b/src/lib/product/components/ProductSearch.jsx
@@ -27,7 +27,9 @@ import ProductSearchSkeleton from './Skeleton/ProductSearchSkeleton';
import SideBanner from '~/modules/side-banner';
import FooterBanner from '~/modules/footer-banner';
import CategorySection from './CategorySection';
+import LobSectionCategory from './LobSectionCategory';
import { getIdFromSlug } from '@/core/utils/slug'
+import { data } from 'autoprefixer';
const ProductSearch = ({
query,
@@ -36,14 +38,105 @@ const ProductSearch = ({
brand = null,
}) => {
const router = useRouter();
+
const { page = 1 } = query;
const [q, setQ] = useState(query?.q || '*');
const [search, setSearch] = useState(query?.q || '*');
const [limit, setLimit] = useState(query?.limit || 30);
const [orderBy, setOrderBy] = useState(router.query?.orderBy || 'popular');
+ const [finalQuery, setFinalQuery] = useState({});
+ const [queryFinal, setQueryFinal] = useState({});
+ const [dataCategoriesProduct, setDataCategoriesProduct] = useState([])
+ const [dataCategoriesLob, setDataCategoriesLob] = useState([])
+ const categoryId = getIdFromSlug(prefixUrl)
+ const [data, setData] = useState([])
+ const [dataLob, setDataLob] = useState([]);
if (defaultBrand) query.brand = defaultBrand.toLowerCase();
+ const dataIdCategories = []
+ useEffect(() => {
+ if(prefixUrl.includes('category')){
+ const loadProduct = async () => {
+ const getCategoriesId = await odooApi('GET', `/api/v1/category/numFound?parent_id=${categoryId}`);
+ if (getCategoriesId) {
+ setDataCategoriesProduct(getCategoriesId);
+ }
+ };
+ loadProduct();
+ }else if(prefixUrl.includes('lob')){
+ const loadProduct = async () => {
+ const lobData = await odooApi('GET', `/api/v1/lob_homepage/${categoryId}/category_id`);
+
+ if (lobData) {
+ setDataLob(lobData);
+ }
+ };
+ loadProduct();
+
+ }
+ }, [categoryId]);
+
+ const collectIds = (category) => {
+ const ids = [];
+ function recurse(cat) {
+ if (cat && cat.id) {
+ ids.push(cat.id);
+ }
+ if (Array.isArray(cat.children)) {
+ cat.children.forEach(recurse);
+ }
+ }
+ recurse(category);
+ return ids;
+ };
+
+ useEffect(() => {
+ if(prefixUrl.includes('category')){
+ const ids = collectIds(dataCategoriesProduct);
+ const newQuery = {
+ fq: `category_id_ids:(${ids.join(' OR ')})`,
+ page,
+ brand : router.query.brand? router.query.brand : '',
+ category : router.query.category? router.query.category : '',
+ };
+ setFinalQuery(newQuery);
+ } else if (prefixUrl.includes('lob')){
+
+ const fetchCategoryData = async () => {
+ if (dataLob[0]?.categoryIds) {
+
+ for (const cate of dataLob[0].categoryIds) {
+
+ dataIdCategories.push(cate.childId)
+ }
+
+
+ const mergedArray = dataIdCategories.flat();
+
+ const newQuery = {
+ fq: `category_id_ids:(${mergedArray.join(' OR ')})`,
+ page,
+ brand : router.query.brand? router.query.brand : '',
+ category : router.query.category? router.query.category : '',
+ };
+
+ setFinalQuery(newQuery);
+
+ }
+ };
+ fetchCategoryData();
+ }
+ }, [dataCategoriesProduct, dataLob]);
+
+ useEffect(() => {
+ if (prefixUrl.includes('category') || prefixUrl.includes('lob')) {
+ setQueryFinal({ ...finalQuery, q, limit, orderBy });
+ } else {
+ setQueryFinal({ ...query, q, limit, orderBy });
+ }
+ }, [prefixUrl,dataCategoriesProduct, query, finalQuery]);
+
const { productSearch } = useProductSearch({
- query: { ...query, q, limit, orderBy },
+ query: queryFinal,
operation: 'AND',
});
const [products, setProducts] = useState(null);
@@ -63,6 +156,7 @@ const ProductSearch = ({
const [categoryValues, setCategory] = useState(
query?.category?.split(',') || []
);
+
const [priceFrom, setPriceFrom] = useState(query?.priceFrom || null);
const [priceTo, setPriceTo] = useState(query?.priceTo || null);
@@ -71,9 +165,7 @@ const ProductSearch = ({
const productRows = limit;
const productFound = productSearch.data?.response.numFound;
const [dataCategories, setDataCategories] = useState([])
-
- const categoryId = getIdFromSlug(prefixUrl)
-
+
useEffect(() => {
if (productFound == 0 && query.q && !spellings) {
searchSpellApi({ query: query.q }).then((response) => {
@@ -103,7 +195,7 @@ const ProductSearch = ({
});
}
}, [productFound, query, spellings]);
-
+ let id = []
useEffect(() => {
const checkIfBrand = async () => {
const brand = await axios(
@@ -122,15 +214,20 @@ const ProductSearch = ({
}, [q]);
useEffect(() => {
- const loadCategories = async () => {
- const getCategories = await odooApi('GET', `/api/v1/category/child?parent_id=${categoryId}`)
- if(getCategories){
- setDataCategories(getCategories)
- }
+ if(prefixUrl.includes('category')){
+ const loadCategories = async () => {
+ const getCategories = await odooApi('GET', `/api/v1/category/child?parent_id=${categoryId}`)
+ if(getCategories){
+ setDataCategories(getCategories)
+ }
+ }
+ loadCategories()
}
- loadCategories()
}, [])
+
+
+
const brands = [];
for (
let i = 0;
@@ -277,6 +374,8 @@ const ProductSearch = ({
};
const isNotReadyStockPage = router.asPath !== '/shop/search?orderBy=stock';
+
+ console.log("finalQuery",finalQuery)
return (
<>
@@ -338,6 +437,7 @@ const ProductSearch = ({
SpellingComponent
)}
</div>
+ <LobSectionCategory categories={dataLob}/>
<CategorySection categories={dataCategories}/>
{productFound > 0 && (
@@ -429,6 +529,7 @@ const ProductSearch = ({
</div>
<div className='w-9/12 pl-6'>
+ <LobSectionCategory categories={dataLob}/>
<CategorySection categories={dataCategories}/>
{bannerPromotionHeader && bannerPromotionHeader?.image && (
<div className='mb-3'>