diff options
| author | trisusilo48 <tri.susilo@altama.co.id> | 2024-09-12 10:33:12 +0700 |
|---|---|---|
| committer | trisusilo48 <tri.susilo@altama.co.id> | 2024-09-12 10:33:12 +0700 |
| commit | 05328fd8972bfd6f4a14a036603b70ba35386a14 (patch) | |
| tree | b991a49a38fd364e749086830fdd89aa1f209f8a /src | |
| parent | 914729a0d6ba9a9dd32d308954642439fa88c1d2 (diff) | |
| parent | 375365d46144e6f20bf7d4ffee1f52e6400cf214 (diff) | |
Merge branch 'release' into CR/product_detail
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/api/odooApi.js | 2 | ||||
| -rw-r--r-- | src/lib/home/api/categoryManagementApi.js | 48 | ||||
| -rw-r--r-- | src/lib/home/components/CategoryDynamic.jsx | 31 | ||||
| -rw-r--r-- | src/lib/home/components/CategoryDynamicMobile.jsx | 45 | ||||
| -rw-r--r-- | src/lib/home/hooks/useCategoryManagement.js | 13 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 4 | ||||
| -rw-r--r-- | src/pages/api/shop/promo.js | 2 | ||||
| -rw-r--r-- | src/pages/api/shop/search.js | 57 | ||||
| -rw-r--r-- | src/pages/index.jsx | 8 | ||||
| -rw-r--r-- | src/pages/shop/brands/[slug].jsx | 7 |
10 files changed, 150 insertions, 67 deletions
diff --git a/src/core/api/odooApi.js b/src/core/api/odooApi.js index 3349ff4b..504d097a 100644 --- a/src/core/api/odooApi.js +++ b/src/core/api/odooApi.js @@ -64,7 +64,7 @@ const odooApi = async (method, url, data = {}, headers = {}) => { } return camelcaseObjectDeep(res.data.result) || []; } catch (error) { - console.log(error); + // console.log(error); } }; diff --git a/src/lib/home/api/categoryManagementApi.js b/src/lib/home/api/categoryManagementApi.js index b70d60ce..0aeb2aac 100644 --- a/src/lib/home/api/categoryManagementApi.js +++ b/src/lib/home/api/categoryManagementApi.js @@ -1,8 +1,40 @@ -import odooApi from '@/core/api/odooApi' - -const categoryManagementApi = async () => { - const dataCategoryManagement = await odooApi('GET', '/api/v1/categories_management') - return dataCategoryManagement -} - -export default categoryManagementApi +export const fetchCategoryManagementSolr = async () => { + let sort ='sort=sequence_i asc'; + try { + const response = await fetch(`/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 + }; + return productMapped; + }) + };
\ No newline at end of file diff --git a/src/lib/home/components/CategoryDynamic.jsx b/src/lib/home/components/CategoryDynamic.jsx index 14015e28..ca104ada 100644 --- a/src/lib/home/components/CategoryDynamic.jsx +++ b/src/lib/home/components/CategoryDynamic.jsx @@ -1,6 +1,5 @@ import React, { useEffect, useState, useCallback } from 'react'; -import useCategoryManagement from '../hooks/useCategoryManagement'; -import {fetchPopulerProductSolr} from '../api/categoryManagementApi' +import {fetchCategoryManagementSolr} from '../api/categoryManagementApi' import NextImage from 'next/image'; import Link from "next/link"; import { createSlug } from '@/core/utils/slug'; @@ -14,7 +13,20 @@ import { Navigation, Pagination, Autoplay } from 'swiper'; const CategoryDynamic = () => { - const { categoryManagement } = useCategoryManagement(); + const [categoryManagement, setCategoryManagement] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const loadBrand = useCallback(async () => { + setIsLoading(true) + const items = await fetchCategoryManagementSolr(); + + setIsLoading(false) + setCategoryManagement(items) + }, []) + + useEffect(() => { + loadBrand() + }, [loadBrand]) + // const [categoryData, setCategoryData] = useState({}); // const [subCategoryData, setSubCategoryData] = useState({}); @@ -55,18 +67,17 @@ const CategoryDynamic = () => { return ( <div> - {categoryManagement && categoryManagement.data?.map((category) => { + {categoryManagement && categoryManagement?.map((category) => { // const countLevel1 = categoryData[category.categoryIdI] || 0; - return ( - <Skeleton key={category.id} isLoaded={categoryManagement}> + <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'> <div className='font-semibold sm:text-h-lg mr-2'>{category.name}</div> {/* <Skeleton isLoaded={countLevel1 != 0}> <p className={`text-gray_r-10 text-sm`}>{countLevel1} Produk tersedia</p> </Skeleton> */} - <Link href={createSlug('/shop/category/', category?.name, category?.categoryIdI)} className="!text-red-500 font-semibold">Lihat Semua</Link> + <Link href={createSlug('/shop/category/', category?.name, category?.id)} className="!text-red-500 font-semibold">Lihat Semua</Link> </div> {/* Swiper for SubCategories */} @@ -94,13 +105,13 @@ const CategoryDynamic = () => { {countLevel2} Produk tersedia </p> </Skeleton> */} - <Link href={createSlug('/shop/category/', subCategory?.name, subCategory?.idLevel2)} className="!text-red-500 font-semibold">Lihat Semua</Link> + <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.childFrontendIdI.map((childCategory) => ( + {subCategory.child_frontend_id_i.map((childCategory) => ( <div key={childCategory.id} className=''> - <Link href={createSlug('/shop/category/', childCategory?.name, childCategory?.idLevel3)} className="flex flex-row gap-2 border rounded group hover:border-red-500"> + <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} diff --git a/src/lib/home/components/CategoryDynamicMobile.jsx b/src/lib/home/components/CategoryDynamicMobile.jsx index 2877a5a7..1061f3e4 100644 --- a/src/lib/home/components/CategoryDynamicMobile.jsx +++ b/src/lib/home/components/CategoryDynamicMobile.jsx @@ -1,22 +1,35 @@ -import React, { useEffect, useState } from 'react'; -import useCategoryManagement from '../hooks/useCategoryManagement'; +import React, { useEffect, useState, useCallback } from 'react'; import NextImage from 'next/image'; import Link from "next/link"; import { createSlug } from '@/core/utils/slug'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; +import {fetchCategoryManagementSolr} from '../api/categoryManagementApi' const CategoryDynamicMobile = () => { - const { categoryManagement } = useCategoryManagement() const [selectedCategory, setSelectedCategory] = useState({}); + const [categoryManagement, setCategoryManagement] = useState([]) + const [isLoading, setIsLoading] = useState(false) + + const loadBrand = useCallback(async () => { + setIsLoading(true) + const items = await fetchCategoryManagementSolr(); + + setIsLoading(false) + setCategoryManagement(items) + }, []) + + useEffect(() => { + loadBrand() + }, [loadBrand]) useEffect(() => { const loadPromo = async () => { try { - if (categoryManagement.data?.length > 0) { - const initialSelections = categoryManagement.data.reduce((acc, category) => { + if (categoryManagement?.length > 0) { + const initialSelections = categoryManagement.reduce((acc, category) => { if (category.categories.length > 0) { - acc[category.id] = category.categories[0].idLevel2; + acc[category.id] = category.categories[0].id_level_2; } return acc; }, {}); @@ -28,7 +41,7 @@ const CategoryDynamicMobile = () => { }; loadPromo(); - }, [categoryManagement.data]); + }, [categoryManagement]); const handleCategoryLevel2Click = (categoryIdI, idLevel2) => { setSelectedCategory(prev => ({ @@ -39,18 +52,18 @@ const CategoryDynamicMobile = () => { return ( <div className='p-4'> - {categoryManagement.data && categoryManagement.data.map((category) => ( + {categoryManagement && categoryManagement?.map((category) => ( <div key={category.id}> <div className='bagian-judul flex flex-row justify-between items-center gap-3 mb-4 mt-4'> - <div className='font-semibold sm:text-h-sm mr-2'>{category.name}</div> - <Link href={createSlug('/shop/category/', category?.name, category?.categoryIdI)} className="!text-red-500 font-semibold text-sm">Lihat Semua</Link> + <div className='font-semibold sm:text-h-sm mr-2'>{category?.name}</div> + <Link href={createSlug('/shop/category/', category?.name, category?.id)} className="!text-red-500 font-semibold text-sm">Lihat Semua</Link> </div> <Swiper slidesPerView={2.3} spaceBetween={10}> {category.categories.map((index) => ( <SwiperSlide key={index.id}> <div - onClick={() => handleCategoryLevel2Click(category.id, index?.idLevel2)} - className={`border flex justify-start items-center max-w-48 max-h-16 rounded ${selectedCategory[category.id] === index?.idLevel2 ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-200 text-gray-900'}`} + onClick={() => handleCategoryLevel2Click(category.id, index?.id_level_2)} + className={`border flex justify-start items-center max-w-48 max-h-16 rounded ${selectedCategory[category.id] === index?.id_level_2 ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-200 text-gray-900'}`} > <div className='p-1 flex justify-start items-center'> <div className='flex flex-row justify-center items-center'> @@ -62,7 +75,7 @@ const CategoryDynamicMobile = () => { className='' /> <div className='bagian-judul flex flex-col justify-center items-start gap-1 ml-2'> - <div className='font-semibold text-[10px] line-clamp-1'>{index.name}</div> + <div className='font-semibold text-[10px] line-clamp-1'>{index?.name}</div> </div> </div> </div> @@ -73,9 +86,9 @@ const CategoryDynamicMobile = () => { <div className='p-3 mt-2 border'> <div className='grid grid-cols-2 gap-2 overflow-y-auto max-h-[240px]'> {category.categories.map((index) => ( - selectedCategory[category.id] === index?.idLevel2 && index.childFrontendIdI.map((x) => ( + selectedCategory[category.id] === index?.id_level_2 && index?.child_frontend_id_i.map((x) => ( <div key={x.id}> - <Link href={createSlug('/shop/category/', x?.name, x?.idLevel3)} className="flex flex-row gap-1 border rounded group hover:border-red-500"> + <Link href={createSlug('/shop/category/', x?.name, x?.id_level_3)} className="flex flex-row gap-1 border rounded group hover:border-red-500"> <NextImage src={x.image ? x.image : "/images/noimage.jpeg"} alt={x.name} @@ -84,7 +97,7 @@ const CategoryDynamicMobile = () => { className='p-2' /> <div className='bagian-judul flex flex-col justify-center items-start gap-1 break-words line-clamp-2 group-hover:text-red-500'> - <div className='font-semibold line-clamp-2 group-hover:text-red-500 text-[10px]'>{x.name}</div> + <div className='font-semibold line-clamp-2 group-hover:text-red-500 text-[10px]'>{x?.name}</div> </div> </Link> </div> diff --git a/src/lib/home/hooks/useCategoryManagement.js b/src/lib/home/hooks/useCategoryManagement.js deleted file mode 100644 index c1dda585..00000000 --- a/src/lib/home/hooks/useCategoryManagement.js +++ /dev/null @@ -1,13 +0,0 @@ -import categoryManagementApi from '../api/categoryManagementApi' -import { useQuery } from 'react-query' - -const useCategoryManagement = () => { - const fetchCategoryManagement = async () => await categoryManagementApi() - const { isLoading, data } = useQuery('categoryManagementApi', fetchCategoryManagement) - - return { - categoryManagement: { data, isLoading } - } -} - -export default useCategoryManagement
\ No newline at end of file diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index ab55cae0..1edc31c9 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -381,6 +381,8 @@ const ProductSearch = ({ }; const isNotReadyStockPage = router.asPath !== '/shop/search?orderBy=stock'; + + console.log('is spelling', spellings); return ( <> <MobileView> @@ -574,7 +576,7 @@ const ProductSearch = ({ /> <div className='flex justify-between items-center mb-5'> <div className='leading-6 text-gray_r-11'> - {!spellings ? ( + {spellings?.length < 1 || !spellings ? ( <> Menampilkan {pageCount > 1 ? ( diff --git a/src/pages/api/shop/promo.js b/src/pages/api/shop/promo.js index 221a9adb..f90c8559 100644 --- a/src/pages/api/shop/promo.js +++ b/src/pages/api/shop/promo.js @@ -52,6 +52,7 @@ export default async function handler(req, res) { 'facet.field=category_name', 'facet=true', 'indent=true', + `facet.limit=-1`, // `facet.query=${escapeSolrQuery(q)}`, `q.op=${operation}`, `q=${q}`, @@ -60,6 +61,7 @@ export default async function handler(req, res) { `rows=${limit}`, `sort=${paramOrderBy}`, `fq=product_ids:[* TO *]`, + `fq=active_b:true`, ]; if (priceFrom > 0 || priceTo > 0) { diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js index 6f98efcb..6269d3ed 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -1,6 +1,8 @@ import { productMappingSolr } from '@/utils/solrMapping'; import axios from 'axios'; import camelcaseObjectDeep from 'camelcase-object-deep'; +import searchSuggestApi from '@/core/api/searchSuggestApi'; +import { ECDH } from 'crypto'; export default async function handler(req, res) { const { @@ -14,6 +16,7 @@ export default async function handler(req, res) { operation = 'AND', fq = '', limit = 30, + source = '', } = req.query; let { stock = '' } = req.query; @@ -42,10 +45,40 @@ export default async function handler(req, res) { paramOrderBy += ''; break; } - + + // let suggestWord = null; + // let keywords = q; + // let checkQ = null; + + // if (q != '*') { + // checkQ = q.trim().split(/[\s\+\-\!\(\)\{\}\[\]\^"~\*\?:\\\/]+/); + // if (checkQ.length > 1) { + // const dataSearchSuggest = await axios( + // `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/suggest?q=${checkQ[1]}` + // ); + // suggestWord = dataSearchSuggest.data.suggestions[0]; + // } + // if (suggestWord && suggestWord?.term.split(' ').length <= 1) { + // keywords = `"${escapeSolrQuery(checkQ[0] + ' ' + suggestWord?.term)}"`; + // } + // } + + // let newQ = keywords; + let checkQ = q.trim().split(/[\s\+\-\!\(\)\{\}\[\]\^"~\*\?:\\\/]+/); - let newQ = checkQ.length > 1 ? escapeSolrQuery(q) + '*' : escapeSolrQuery(q); + let newQ = escapeSolrQuery(q); + + const formattedQuery = `(${newQ.split(' ').map(term => `${term}*`).join(' ') })`; + const mm = checkQ.length > 2 ? checkQ.length > 5 ? '55%' : '85%' : `${checkQ.length}`; + + const filterQueries = [ + '-publish_b:false', + 'product_rating_f:[8 TO *]', + 'price_tier1_v2_f:[1 TO *]' + ]; + const fq_ = filterQueries.join('AND '); + let offset = (page - 1) * limit; let parameter = [ 'facet.field=manufacture_name_s', @@ -53,13 +86,15 @@ export default async function handler(req, res) { 'facet=true', 'indent=true', `facet.query=${escapeSolrQuery(q)}`, - `q.op=${operation}`, - `q=${newQ}`, - 'qf=name_s', + `q.op=OR`, + `q=${source == 'similar' || checkQ.length < 3 ? checkQ.length < 2 ? newQ : newQ + '*' : formattedQuery }`, + `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', `start=${parseInt(offset)}`, `rows=${limit}`, `sort=${paramOrderBy}`, - `fq=-publish_b:false, product_rating_f:[8 TO *], price_tier1_v2_f:[1 TO *]`, + `fq=${encodeURIComponent(fq_)}`, + `mm=${encodeURIComponent(mm)}`, ]; if (priceFrom > 0 || priceTo > 0) { @@ -97,14 +132,15 @@ 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=${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=${val}`)); - + parameter = parameter.concat(fq.map((val) => `fq=${encodeURIComponent(val)}`)); + let result = await axios( process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&') ); + try { result.data.response.products = productMappingSolr( result.data.response.docs, @@ -126,7 +162,7 @@ export default async function handler(req, res) { const escapeSolrQuery = (query) => { if (query == '*') return query; - + query = query.replace(/-/g, ' '); const specialChars = /([\+\!\(\)\{\}\[\]\^"~\*\?:\\\/])/g; @@ -141,7 +177,6 @@ const escapeSolrQuery = (query) => { return escapedWords.join(' '); }; - /*const productResponseMap = (products, pricelist) => { return products.map((product) => { let price = product.price_tier1_v2_f || 0 diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 613950a6..0e87205e 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -10,7 +10,7 @@ import { FlashSaleSkeleton } from '@/lib/flashSale/skeleton/FlashSaleSkeleton'; import PreferredBrandSkeleton from '@/lib/home/components/Skeleton/PreferredBrandSkeleton'; import BannerPromoSkeleton from '@/lib/home/components/Skeleton/BannerPromoSkeleton'; import PromotinProgram from '@/lib/promotinProgram/components/HomePage'; -import PagePopupIformation from '~/modules/popup-information'; +import PagePopupIformation from '~/modules/popup-information'; // need change to dynamic and ssr : false import CategoryPilihan from '../lib/home/components/CategoryPilihan'; import odooApi from '@/core/api/odooApi'; import { getAuth } from '~/libs/auth'; @@ -56,7 +56,7 @@ const ProgramPromotion = dynamic(() => const BannerSection = dynamic(() => import('@/lib/home/components/BannerSection') -); +); const CategoryHomeId = dynamic(() => import('@/lib/home/components/CategoryHomeId') ); @@ -71,8 +71,8 @@ import('@/lib/home/components/CategoryDynamicMobile') const CustomerReviews = dynamic(() => import('@/lib/review/components/CustomerReviews') -); -const ServiceList = dynamic(() => import('@/lib/home/components/ServiceList')); +); // need to ssr:false +const ServiceList = dynamic(() => import('@/lib/home/components/ServiceList')); // need to ssr: false diff --git a/src/pages/shop/brands/[slug].jsx b/src/pages/shop/brands/[slug].jsx index 88e9b302..ed6724ea 100644 --- a/src/pages/shop/brands/[slug].jsx +++ b/src/pages/shop/brands/[slug].jsx @@ -18,9 +18,10 @@ export default function BrandDetail() { const brandName = getNameFromSlug(slug) const id = getIdFromSlug(slug) const {brand} = useBrand({id}) - if ( !brand.isLoading && _.isEmpty(brand.data)) { - return <PageNotFound />; - } + // if ( !brand.isLoading && _.isEmpty(brand.data)) { + // console.log('ini masuk pak') + // return <PageNotFound />; + // } return ( <BasicLayout> <Seo |
