From bf805f7da68891250a10d85d9206607de3cbfacf Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 2 Sep 2024 13:24:07 +0700 Subject: temp save update fetch data promotion program line --- src/lib/product/api/productSearchApi.js | 2 +- src/lib/product/components/ProductSearch.jsx | 2 +- src/pages/api/shop/promo.js | 205 +++++++++++++++++++++++++++ src/pages/shop/promo/[slug].tsx | 67 ++++++--- src/utils/solrMapping.js | 29 ++++ 5 files changed, 285 insertions(+), 20 deletions(-) create mode 100644 src/pages/api/shop/promo.js (limited to 'src') diff --git a/src/lib/product/api/productSearchApi.js b/src/lib/product/api/productSearchApi.js index 1626b7b7..2f792fd4 100644 --- a/src/lib/product/api/productSearchApi.js +++ b/src/lib/product/api/productSearchApi.js @@ -3,7 +3,7 @@ import axios from 'axios' const productSearchApi = async ({ query, operation = 'AND' }) => { const dataProductSearch = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?${query}&operation=${operation}` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/promo?${query}&operation=${operation}` ) return dataProductSearch.data } diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index eaeac71a..1f083722 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -87,7 +87,7 @@ const ProductSearch = ({ recurse(category); return ids; }; - + console.log("queryFinal",queryFinal) useEffect(() => { if(prefixUrl.includes('category')){ const ids = collectIds(dataCategoriesProduct); diff --git a/src/pages/api/shop/promo.js b/src/pages/api/shop/promo.js new file mode 100644 index 00000000..4b4c09b7 --- /dev/null +++ b/src/pages/api/shop/promo.js @@ -0,0 +1,205 @@ +import { map } from '@/utils/solrMapping'; +import axios from 'axios'; +import camelcaseObjectDeep from 'camelcase-object-deep'; + +export default async function handler(req, res) { + console.log("req_iman",req) + const { + q = '*', + page = 1, + brand = '', + category = '', + priceFrom = 0, + priceTo = 0, + orderBy = '', + operation = 'AND', + fq = '', + limit = 30, + } = req.query; + + let { stock = '' } = req.query; + + let paramOrderBy = ''; + switch (orderBy) { + case 'price-asc': + paramOrderBy += 'price_tier1_v2_f ASC'; + break; + case 'price-desc': + paramOrderBy += 'price_tier1_v2_f DESC'; + break; + case 'popular': + paramOrderBy += 'product_rating_f DESC, search_rank_i DESC,'; + break; + case 'popular-weekly': + paramOrderBy += 'search_rank_weekly_i DESC'; + break; + case 'stock': + paramOrderBy += 'product_rating_f DESC, stock_total_f DESC'; + break; + case 'flashsale-price-asc': + paramOrderBy += 'flashsale_price_f ASC'; + break; + default: + paramOrderBy += ''; + break; + } + + let checkQ = q.trim().split(/[\s\+\-\!\(\)\{\}\[\]\^"~\*\?:\\\/]+/); + let newQ = checkQ.length > 1 ? escapeSolrQuery(q) + '*' : escapeSolrQuery(q); + + let offset = (page - 1) * limit; + let parameter = [ + // 'facet.field=manufacture_name_s', + // 'facet.field=category_name', + 'facet=true', + 'indent=true', + // `facet.query=${escapeSolrQuery(q)}`, + `q.op=${operation}`, + `q=${newQ}`, + // 'qf=name_s', + `start=${parseInt(offset)}`, + `rows=${limit}`, + `sort=${paramOrderBy}`, + // `fq=-publish_b:false, product_rating_f:[8 TO *], price_tier1_v2_f:[1 TO *]`, + ]; + + if (priceFrom > 0 || priceTo > 0) { + parameter.push( + `fq=price_tier1_v2_f:[${priceFrom == '' ? '*' : priceFrom} TO ${ + priceTo == '' ? '*' : priceTo + }]` + ); + } + + let { auth } = req.cookies; + if (auth) { + auth = JSON.parse(auth); + if (auth.feature.onlyReadyStock) stock = true; + } + + if (brand) + parameter.push( + `fq=${brand + .split(',') + .map( + (manufacturer) => + `manufacture_name:"${encodeURIComponent(manufacturer)}"` + ) + .join(' OR ')}` + ); + if (category) + parameter.push( + `fq=${category + .split(',') + .map((cat) => `category_name:"${encodeURIComponent(cat)}"`) + .join(' OR ')}` + ); + // if (category) parameter.push(`fq=category_name:${capitalizeFirstLetter(category.replace(/,/g, ' OR '))}`) + if (stock) parameter.push(`fq=stock_total_f:{1 TO *}`); + + // Single fq in url params + if (typeof fq === 'string') parameter.push(`fq=${fq}`); + // Multi fq in url params + if (Array.isArray(fq)) + parameter = parameter.concat(fq.map((val) => `fq=${val}`)); + + console.log("parameter",parameter) + + let result = await axios( + process.env.SOLR_HOST + '/solr/promotion_program_lines/select?' + parameter.join('&') + ); + console.log("result",result) + try { + result.data.response.products = map( + result.data.response.docs, + auth?.pricelist || false + ); + result.data.responseHeader.params.start = parseInt( + result.data.responseHeader.params.start + ); + result.data.responseHeader.params.rows = parseInt( + result.data.responseHeader.params.rows + ); + delete result.data.response.docs; + result.data = camelcaseObjectDeep(result.data); + res.status(200).json(result.data); + } catch (error) { + res.status(400).json({ error: error.message }); + } +} + +const escapeSolrQuery = (query) => { + if (query == '*') return query; + + query = query.replace(/-/g, ' '); + + const specialChars = /([\+\!\(\)\{\}\[\]\^"~\*\?:\\\/])/g; + const words = query.split(/\s+/); + const escapedWords = words.map((word) => { + if (specialChars.test(word)) { + return word.replace(specialChars, '\\$1'); + } + return word; + }); + + return escapedWords.join(' '); +}; + + +/*const productResponseMap = (products, pricelist) => { + return products.map((product) => { + let price = product.price_tier1_v2_f || 0 + let priceDiscount = product.price_discount_f || 0 + let discountPercentage = product.discount_f || 0 + + if (pricelist) { + // const pricelistDiscount = product?.[`price_${pricelist}_f`] || false + // const pricelistDiscountPerc = product?.[`discount_${pricelist}_f`] || false + + // if (pricelistDiscount && pricelistDiscount > 0) priceDiscount = pricelistDiscount + // if (pricelistDiscountPerc && pricelistDiscountPerc > 0) + // discountPercentage = pricelistDiscountPerc + + price = product?.[`price_${pricelist}_f`] || 0 + } + + if (product?.flashsale_id_i > 0) { + price = product?.flashsale_base_price_f || 0 + priceDiscount = product?.flashsale_price_f || 0 + discountPercentage = product?.flashsale_discount_f || 0 + } + + let productMapped = { + id: product.product_id_i || '', + image: product.image_s || '', + code: product.default_code_s || '', + name: product.name_s || '', + lowestPrice: { price, priceDiscount, discountPercentage }, + variantTotal: product.variant_total_i || 0, + stockTotal: product.stock_total_f || 0, + weight: product.weight_f || 0, + manufacture: {}, + categories: [], + flashSale: { + id: product?.flashsale_id_i, + name: product?.product?.flashsale_name_s, + tag : product?.flashsale_tag_s || 'FLASH SALE' + } + } + + if (product.manufacture_id_i && product.manufacture_name_s) { + productMapped.manufacture = { + id: product.manufacture_id_i || '', + name: product.manufacture_name_s || '' + } + } + + productMapped.categories = [ + { + id: product.category_id_i || '', + name: product.category_name_s || '' + } + ] + return productMapped + }) +}*/ diff --git a/src/pages/shop/promo/[slug].tsx b/src/pages/shop/promo/[slug].tsx index aaee1249..92f5b7f8 100644 --- a/src/pages/shop/promo/[slug].tsx +++ b/src/pages/shop/promo/[slug].tsx @@ -2,30 +2,31 @@ import dynamic from 'next/dynamic' import NextImage from 'next/image'; import { useEffect, useState } from 'react' import { useRouter } from 'next/router' -import Seo from '../../../core/components/Seo' -import Promocrumb from '../../../lib/promo/components/Promocrumb' -import { fetchPromoItemsSolr, fetchVariantSolr } from '../../../api/promoApi' +import Seo from '../../../core/components/Seo.jsx' +import Promocrumb from '../../../lib/promo/components/Promocrumb.jsx' +import { fetchPromoItemsSolr, fetchVariantSolr } from '../../../api/promoApi.js' import LogoSpinner from '../../../core/components/elements/Spinner/LogoSpinner.jsx' -import ProductPromoCard from '../../../../src-migrate/modules/product-promo/components/Card' -import { IPromotion } from '../../../../src-migrate/types/promotion' +import ProductPromoCard from '../../../../src-migrate/modules/product-promo/components/Card.tsx' +import { IPromotion } from '../../../../src-migrate/types/promotion.ts' import React from 'react' import { SolrResponse } from "../../../../src-migrate/types/solr.ts"; -import DesktopView from '../../../core/components/views/DesktopView'; -import MobileView from '../../../core/components/views/MobileView'; +import DesktopView from '../../../core/components/views/DesktopView.jsx'; +import MobileView from '../../../core/components/views/MobileView.jsx'; import 'swiper/swiper-bundle.css'; -import useDevice from '../../../core/hooks/useDevice' -import ProductFilterDesktop from '../../../lib/product/components/ProductFilterDesktopPromotion'; -import ProductFilter from '../../../lib/product/components/ProductFilter'; +import useDevice from '../../../core/hooks/useDevice.js' +import ProductFilterDesktop from '../../../lib/product/components/ProductFilterDesktopPromotion.jsx'; +import ProductFilter from '../../../lib/product/components/ProductFilter.jsx'; import { HStack, Image, Tag, TagCloseButton, TagLabel } from '@chakra-ui/react'; -import { formatCurrency } from '../../../core/utils/formatValue'; -import Pagination from '../../../core/components/elements/Pagination/Pagination'; -import SideBanner from '../../../../src-migrate/modules/side-banner'; -import whatsappUrl from '../../../core/utils/whatsappUrl'; +import { formatCurrency } from '../../../core/utils/formatValue.js'; +import Pagination from '../../../core/components/elements/Pagination/Pagination.js'; +import SideBanner from '../../../../src-migrate/modules/side-banner/index.tsx'; +import whatsappUrl from '../../../core/utils/whatsappUrl.js'; import { cons, toQuery } from 'lodash-contrib'; import _ from 'lodash'; -import useActive from '../../../core/hooks/useActive'; +import useActive from '../../../core/hooks/useActive.js'; +import useProductSearch from '../../../lib/product/hooks/useProductSearch.js'; -const BasicLayout = dynamic(() => import('../../../core/components/layouts/BasicLayout')) +const BasicLayout = dynamic(() => import('../../../core/components/layouts/BasicLayout.jsx')) export default function PromoDetail() { const router = useRouter() @@ -40,9 +41,39 @@ export default function PromoDetail() { const [categories, setCategories] = useState([]); const [brandValues, setBrandValues] = useState([]); const [categoryValues, setCategoryValues] = useState([]); - const [orderBy, setOrderBy] = useState(router.query?.orderBy || 'popular'); + const [orderBy, setOrderBy] = useState(router.query?.orderBy); const popup = useActive(); const prefixUrl = `/shop/promo/${slug}` + const [queryFinal, setQueryFinal] = useState({}); + const [limit, setLimit] = useState(30); + const [q, setQ] = useState('**'); + const [finalQuery, setFinalQuery] = useState({fq: `type_value_s:${slug}`}); + const [products, setProducts] = useState(null); + useEffect(() => { + setQueryFinal({ ...finalQuery, q, limit, orderBy }); +}, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); + useEffect(() => { + setQueryFinal({ ...finalQuery, q, limit, orderBy }); + }, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); + + const { productSearch } = useProductSearch({ + query: queryFinal, + operation: 'OR', + }); + + console.log("productSearch",productSearch) + console.log("queryFinal",queryFinal) + + const pageCount = Math.ceil(productSearch.data?.response.numFound / limit); + const productStart = productSearch.data?.responseHeader.params.start; + const productRows = limit; + const productFound = productSearch.data?.response.numFound; + + useEffect(() => { + setProducts(productSearch.data?.response?.products); + }, [productSearch]); + + console.log("products",products) useEffect(() => { if (router.query.brand) { @@ -91,7 +122,7 @@ export default function PromoDetail() { setCurrentPage(pageNumber) try { - const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`,0,100); + const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`,0,10); setPromoItems(items); if (items.length === 0) { diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index fee474be..e93b0b54 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -1,3 +1,31 @@ +const map = (promotions) => { + const result = []; + for (const promotion of promotions) { + const data = { + id: promotion.id, + program_id: promotion.program_id_i, + name: promotion.name_s, + type: { + value: promotion.type_value_s, + label: promotion.type_label_s, + }, + limit: promotion.package_limit_i, + limit_user: promotion.package_limit_user_i, + limit_trx: promotion.package_limit_trx_i, + price: promotion.price_f, + sequence: promotion.sequence_i, + total_qty: promotion.total_qty_i, + products: JSON.parse(promotion.products_s), + product_id: promotion.product_ids[0], + qty_sold_f:promotion.total_qty_sold_f, + free_products: JSON.parse(promotion.free_products_s), + }; + result.push(data); + } + return result; +}; + + export const productMappingSolr = (products, pricelist) => { return products.map((product) => { let price = product.price_tier1_v2_f || 0; @@ -123,3 +151,4 @@ const flashsaleTime = (endDate) => { isFlashSale: flashsaleEndDate > currentTime, }; }; + -- cgit v1.2.3 From 3f384749fe51a2763e7e99351f36ce70954afb7a Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 2 Sep 2024 14:40:34 +0700 Subject: temp save --- src/lib/product/api/productSearchApi.js | 2 +- src/pages/api/shop/promo.js | 7 +- src/pages/shop/promo/[slug].jsx | 550 +++++++++++++++++++++++++++++++ src/pages/shop/promo/[slug].tsx | 554 -------------------------------- src/utils/solrMapping.js | 2 +- 5 files changed, 556 insertions(+), 559 deletions(-) create mode 100644 src/pages/shop/promo/[slug].jsx delete mode 100644 src/pages/shop/promo/[slug].tsx (limited to 'src') diff --git a/src/lib/product/api/productSearchApi.js b/src/lib/product/api/productSearchApi.js index 2f792fd4..670661aa 100644 --- a/src/lib/product/api/productSearchApi.js +++ b/src/lib/product/api/productSearchApi.js @@ -1,7 +1,7 @@ import _ from 'lodash-contrib' import axios from 'axios' -const productSearchApi = async ({ query, operation = 'AND' }) => { +const productSearchApi = async ({ query, operation = 'OR' }) => { const dataProductSearch = await axios( `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/promo?${query}&operation=${operation}` ) diff --git a/src/pages/api/shop/promo.js b/src/pages/api/shop/promo.js index 4b4c09b7..353ff3df 100644 --- a/src/pages/api/shop/promo.js +++ b/src/pages/api/shop/promo.js @@ -1,4 +1,4 @@ -import { map } from '@/utils/solrMapping'; +import { productMappingSolr, promoMappingSolr } from '@/utils/solrMapping'; import axios from 'axios'; import camelcaseObjectDeep from 'camelcase-object-deep'; @@ -55,7 +55,7 @@ export default async function handler(req, res) { 'indent=true', // `facet.query=${escapeSolrQuery(q)}`, `q.op=${operation}`, - `q=${newQ}`, + `q=${q}`, // 'qf=name_s', `start=${parseInt(offset)}`, `rows=${limit}`, @@ -110,7 +110,7 @@ export default async function handler(req, res) { ); console.log("result",result) try { - result.data.response.products = map( + result.data.response.products = promoMappingSolr( result.data.response.docs, auth?.pricelist || false ); @@ -121,6 +121,7 @@ export default async function handler(req, res) { result.data.responseHeader.params.rows ); delete result.data.response.docs; + console.log("result.data",result.data) result.data = camelcaseObjectDeep(result.data); res.status(200).json(result.data); } catch (error) { diff --git a/src/pages/shop/promo/[slug].jsx b/src/pages/shop/promo/[slug].jsx new file mode 100644 index 00000000..544286f9 --- /dev/null +++ b/src/pages/shop/promo/[slug].jsx @@ -0,0 +1,550 @@ +import dynamic from 'next/dynamic' +import NextImage from 'next/image'; +import { useEffect, useState } from 'react' +import { useRouter } from 'next/router' +import Seo from '../../../core/components/Seo.jsx' +import Promocrumb from '../../../lib/promo/components/Promocrumb.jsx' +import { fetchPromoItemsSolr, fetchVariantSolr } from '../../../api/promoApi.js' +import LogoSpinner from '../../../core/components/elements/Spinner/LogoSpinner.jsx' +import ProductPromoCard from '../../../../src-migrate/modules/product-promo/components/Card.tsx' +import { IPromotion } from '../../../../src-migrate/types/promotion.ts' +import React from 'react' +import { SolrResponse } from "../../../../src-migrate/types/solr.ts"; +import DesktopView from '../../../core/components/views/DesktopView.jsx'; +import MobileView from '../../../core/components/views/MobileView.jsx'; +import 'swiper/swiper-bundle.css'; +import useDevice from '../../../core/hooks/useDevice.js' +import ProductFilterDesktop from '../../../lib/product/components/ProductFilterDesktopPromotion.jsx'; +import ProductFilter from '../../../lib/product/components/ProductFilter.jsx'; +import { HStack, Image, Tag, TagCloseButton, TagLabel } from '@chakra-ui/react'; +import { formatCurrency } from '../../../core/utils/formatValue.js'; +import Pagination from '../../../core/components/elements/Pagination/Pagination.js'; +import SideBanner from '../../../../src-migrate/modules/side-banner/index.tsx'; +import whatsappUrl from '../../../core/utils/whatsappUrl.js'; +import { cons, toQuery } from 'lodash-contrib'; +import _ from 'lodash'; +import useActive from '../../../core/hooks/useActive.js'; +import useProductSearch from '../../../lib/product/hooks/useProductSearch.js'; + +const BasicLayout = dynamic(() => import('../../../core/components/layouts/BasicLayout.jsx')) + +export default function PromoDetail() { + const router = useRouter() + const { slug = '', brand ='', category='', priceFrom = '', priceTo = '', page = '1' } = router.query + const [promoItems, setPromoItems] = useState([]) + const [promoData, setPromoData] = useState(null) + const [currentPage, setCurrentPage] = useState(parseInt(10) || 1); + const itemsPerPage = 12; // Jumlah item yang ingin ditampilkan per halaman + const [loading, setLoading] = useState(true); + const { isMobile, isDesktop } = useDevice() + const [brands, setBrands] = useState([]); + const [categories, setCategories] = useState([]); + const [brandValues, setBrandValues] = useState([]); + const [categoryValues, setCategoryValues] = useState([]); + const [orderBy, setOrderBy] = useState(router.query?.orderBy); + const popup = useActive(); + const prefixUrl = `/shop/promo/${slug}` + const [queryFinal, setQueryFinal] = useState({}); + const [limit, setLimit] = useState(30); + const [q, setQ] = useState('*'); + const [finalQuery, setFinalQuery] = useState({fq: `type_value_s:${slug}`}); + const [products, setProducts] = useState(null); + useEffect(() => { + setQueryFinal({ ...finalQuery, q, limit, orderBy }); +}, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); + useEffect(() => { + setQueryFinal({ ...finalQuery, q, limit, orderBy }); + }, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); + + const { productSearch } = useProductSearch({ + query: queryFinal, + operation: 'OR', + }); + + console.log("productSearch",productSearch) + console.log("queryFinal",queryFinal) + + const pageCount = Math.ceil(productSearch.data?.response.numFound / limit); + const productStart = productSearch.data?.responseHeader.params.start; + const productRows = limit; + const productFound = productSearch.data?.response.numFound; + + useEffect(() => { + setProducts(productSearch.data?.response?.products); + }, [productSearch]); + + console.log("products",products) + + + + + + + + useEffect(() => { + if (router.query.brand) { + let brandsArray= []; + if (Array.isArray(router.query.brand)) { + brandsArray = router.query.brand; + } else if (typeof router.query.brand === 'string') { + brandsArray = router.query.brand.split(',').map((brand) => brand.trim()); + } + setBrandValues(brandsArray); + } else { + setBrandValues([]); + } + + if (router.query.category) { + let categoriesArray= []; + + if (Array.isArray(router.query.category)) { + categoriesArray = router.query.category; + } else if (typeof router.query.category === 'string') { + categoriesArray = router.query.category.split(',').map((category) => category.trim()); + } + setCategoryValues(categoriesArray); + } else { + setCategoryValues([]); + } + }, [router.query.brand, router.query.category]); + + useEffect(() => { + const loadPromo = async () => { + setLoading(true); + const brandsData = []; + const categoriesData= []; + + const pageNumber = Array.isArray(page) ? parseInt(page[0], 10) : parseInt(page, 10); + setCurrentPage(pageNumber) + + try { + const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`,0,10); + setPromoItems(items); + + if (items.length === 0) { + setPromoData([]) + setLoading(false); + return; + } + + const brandArray = Array.isArray(brand) ? brand : brand.split(','); + const categoryArray = Array.isArray(category) ? category : category.split(','); + + const promoDataPromises = items.map(async (item) => { + + try { + let brandQuery = ''; + if (brand) { + brandQuery = brandArray.map(b => `manufacture_name_s:${b}`).join(' OR '); + brandQuery = `(${brandQuery})`; + } + + let categoryQuery = ''; + if (category) { + categoryQuery = categoryArray.map(c => `category_name:${c}`).join(' OR '); + categoryQuery = `(${categoryQuery})`; + } + + let priceQuery = ''; + if (priceFrom && priceTo) { + priceQuery = `price_f:[${priceFrom} TO ${priceTo}]`; + } else if (priceFrom) { + priceQuery = `price_f:[${priceFrom} TO *]`; + } else if (priceTo) { + priceQuery = `price_f:[* TO ${priceTo}]`; + } + + let combinedQuery = ''; + let combinedQueryPrice = `${priceQuery}`; + if (brand && category && priceFrom || priceTo) { + combinedQuery = `${brandQuery} AND ${categoryQuery} `; + } else if (brand && category) { + combinedQuery = `${brandQuery} AND ${categoryQuery}`; + } else if (brand && priceFrom || priceTo) { + combinedQuery = `${brandQuery}`; + } else if (category && priceFrom || priceTo) { + combinedQuery = `${categoryQuery}`; + } else if (brand) { + combinedQuery = brandQuery; + } else if (category) { + combinedQuery = categoryQuery; + } + + if (combinedQuery && priceFrom || priceTo) { + const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); + const product = response.response.docs[0]; + const product_id = product.id; + const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} AND ${combinedQueryPrice}`,0,100); + return response2; + }else if(combinedQuery){ + const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); + const product = response.response.docs[0]; + const product_id = product.id; + const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} `,0,100); + return response2; + } else { + const response = await fetchPromoItemsSolr(`id:${item.id}`,0,100); + return response; + } + } catch (fetchError) { + return []; + } + }); + + const promoDataArray = await Promise.all(promoDataPromises); + const mergedPromoData = promoDataArray.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); + setPromoData(mergedPromoData); + + const dataBrandCategoryPromises = promoDataArray.map(async (promoData) => { + if (promoData) { + const dataBrandCategory = promoData.map(async (item) => { + let response; + if(category){ + const categoryQuery = categoryArray.map(c => `category_name:${c}`).join(' OR '); + response = await fetchVariantSolr(`id:${item.products[0].product_id} AND (${categoryQuery})`); + }else{ + response = await fetchVariantSolr(`id:${item.products[0].product_id}`) + } + + + if (response.response?.docs?.length > 0) { + const product = response.response.docs[0]; + const manufactureNameS = product.manufacture_name; + if (Array.isArray(manufactureNameS)) { + for (let i = 0; i < manufactureNameS.length; i += 2) { + const brand = manufactureNameS[i]; + const qty = 1; + const existingBrandIndex = brandsData.findIndex(b => b.brand === brand); + if (existingBrandIndex !== -1) { + brandsData[existingBrandIndex].qty += qty; + } else { + brandsData.push({ brand, qty }); + } + } + } + + const categoryNameS = product.category_name; + if (Array.isArray(categoryNameS)) { + for (let i = 0; i < categoryNameS.length; i += 2) { + const name = categoryNameS[i]; + const qty = 1; + const existingCategoryIndex = categoriesData.findIndex(c => c.name === name); + if (existingCategoryIndex !== -1) { + categoriesData[existingCategoryIndex].qty += qty; + } else { + categoriesData.push({ name, qty }); + } + } + } + } + }); + + return Promise.all(dataBrandCategory); + } + }); + + await Promise.all(dataBrandCategoryPromises); + setBrands(brandsData); + setCategories(categoriesData); + setLoading(false); + + } catch (loadError) { + // console.error("Error loading promo items:", loadError) + setLoading(false); + } + } + + if (slug) { + loadPromo() + } + },[slug, brand, category, priceFrom, priceTo, currentPage]); + + + function capitalizeFirstLetter(string) { + string = string.replace(/_/g, ' '); + return string.replace(/(^\w|\s\w)/g, function(match) { + return match.toUpperCase(); + }); + } + + const handleDeleteFilter = async (source, value) => { + let params = { + q: router.query.q, + orderBy: '', + brand: brandValues.join(','), + category: categoryValues.join(','), + priceFrom: priceFrom || '', + priceTo: priceTo || '', + }; + + let brands = brandValues; + let catagories = categoryValues; + switch (source) { + case 'brands': + brands = brandValues.filter((item) => item !== value); + params.brand = brands.join(','); + await setBrandValues(brands); + break; + case 'category': + catagories = categoryValues.filter((item) => item !== value); + params.category = catagories.join(','); + await setCategoryValues(catagories); + break; + case 'price': + params.priceFrom = ''; + params.priceTo = ''; + break; + case 'delete': + params = { + q: router.query.q, + orderBy: '', + brand: '', + category: '', + priceFrom: '', + priceTo: '', + }; + break; + } + + handleSubmitFilter(params); + }; + const handleSubmitFilter = (params) => { + params = _.pickBy(params, _.identity); + params = toQuery(params); + router.push(`${slug}?${params}`); + }; + + const visiblePromotions = promoData?.slice( (currentPage-1) * itemsPerPage, currentPage * 12) + + const toQuery = (obj) => { + const str = Object.keys(obj) + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`) + .join('&') + return str + } + + const whatPromo = capitalizeFirstLetter(slug) + const queryWithoutSlug = _.omit(router.query, ['slug']) + const queryString = toQuery(queryWithoutSlug) + + return ( + + + + +
+

Promo {whatPromo}

+ + + {promoItems.length >= 1 && ( +
+
+ +
+
+ )} + + {loading ? ( +
+ +
+ ) : promoData && promoItems.length >= 1 ? ( + <> +
+ {visiblePromotions?.map((promotion) => ( +
+ +
+ ))} +
+ + ) : ( +
+

Belum ada promo pada kategori ini

+
+ )} + + + +
+ +
+ +
+
+

Promo {whatPromo}

+
+ +
+ + +
+
+ + +
+
+ {loading ? ( +
+ +
+ ) : promoData && promoItems.length >= 1 ? ( + <> +
+ {products?.map((promotion) => ( +
+ +
+ ))} +
+ + ) : ( +
+

Belum ada promo pada kategori ini

+
+ )} +
+
+ +
+ + Barang yang anda cari tidak ada?{' '} + + Hubungi Kami + + +
+
+ + + + +
+ +
+
+
+
+ ) + } + +const FilterChoicesComponent = ({ + brandValues, + categoryValues, + priceFrom, + priceTo, + handleDeleteFilter, + }) => ( +
+ + {brandValues?.map((value, index) => ( + + {value} + handleDeleteFilter('brands', value)} /> + + ))} + + {categoryValues?.map((value, index) => ( + + {value} + handleDeleteFilter('category', value)} + /> + + ))} + {priceFrom && priceTo && ( + + + {formatCurrency(priceFrom) + '-' + formatCurrency(priceTo)} + + handleDeleteFilter('price', priceFrom)} + /> + + )} + {brandValues?.length > 0 || + categoryValues?.length > 0 || + priceFrom || + priceTo ? ( + + + + ) : ( + '' + )} + +
+); diff --git a/src/pages/shop/promo/[slug].tsx b/src/pages/shop/promo/[slug].tsx deleted file mode 100644 index 92f5b7f8..00000000 --- a/src/pages/shop/promo/[slug].tsx +++ /dev/null @@ -1,554 +0,0 @@ -import dynamic from 'next/dynamic' -import NextImage from 'next/image'; -import { useEffect, useState } from 'react' -import { useRouter } from 'next/router' -import Seo from '../../../core/components/Seo.jsx' -import Promocrumb from '../../../lib/promo/components/Promocrumb.jsx' -import { fetchPromoItemsSolr, fetchVariantSolr } from '../../../api/promoApi.js' -import LogoSpinner from '../../../core/components/elements/Spinner/LogoSpinner.jsx' -import ProductPromoCard from '../../../../src-migrate/modules/product-promo/components/Card.tsx' -import { IPromotion } from '../../../../src-migrate/types/promotion.ts' -import React from 'react' -import { SolrResponse } from "../../../../src-migrate/types/solr.ts"; -import DesktopView from '../../../core/components/views/DesktopView.jsx'; -import MobileView from '../../../core/components/views/MobileView.jsx'; -import 'swiper/swiper-bundle.css'; -import useDevice from '../../../core/hooks/useDevice.js' -import ProductFilterDesktop from '../../../lib/product/components/ProductFilterDesktopPromotion.jsx'; -import ProductFilter from '../../../lib/product/components/ProductFilter.jsx'; -import { HStack, Image, Tag, TagCloseButton, TagLabel } from '@chakra-ui/react'; -import { formatCurrency } from '../../../core/utils/formatValue.js'; -import Pagination from '../../../core/components/elements/Pagination/Pagination.js'; -import SideBanner from '../../../../src-migrate/modules/side-banner/index.tsx'; -import whatsappUrl from '../../../core/utils/whatsappUrl.js'; -import { cons, toQuery } from 'lodash-contrib'; -import _ from 'lodash'; -import useActive from '../../../core/hooks/useActive.js'; -import useProductSearch from '../../../lib/product/hooks/useProductSearch.js'; - -const BasicLayout = dynamic(() => import('../../../core/components/layouts/BasicLayout.jsx')) - -export default function PromoDetail() { - const router = useRouter() - const { slug = '', brand ='', category='', priceFrom = '', priceTo = '', page = '1' } = router.query - const [promoItems, setPromoItems] = useState([]) - const [promoData, setPromoData] = useState(null) - const [currentPage, setCurrentPage] = useState(parseInt(page as string, 10) || 1); - const itemsPerPage = 12; // Jumlah item yang ingin ditampilkan per halaman - const [loading, setLoading] = useState(true); - const { isMobile, isDesktop } = useDevice() - const [brands, setBrands] = useState([]); - const [categories, setCategories] = useState([]); - const [brandValues, setBrandValues] = useState([]); - const [categoryValues, setCategoryValues] = useState([]); - const [orderBy, setOrderBy] = useState(router.query?.orderBy); - const popup = useActive(); - const prefixUrl = `/shop/promo/${slug}` - const [queryFinal, setQueryFinal] = useState({}); - const [limit, setLimit] = useState(30); - const [q, setQ] = useState('**'); - const [finalQuery, setFinalQuery] = useState({fq: `type_value_s:${slug}`}); - const [products, setProducts] = useState(null); - useEffect(() => { - setQueryFinal({ ...finalQuery, q, limit, orderBy }); -}, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); - useEffect(() => { - setQueryFinal({ ...finalQuery, q, limit, orderBy }); - }, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); - - const { productSearch } = useProductSearch({ - query: queryFinal, - operation: 'OR', - }); - - console.log("productSearch",productSearch) - console.log("queryFinal",queryFinal) - - const pageCount = Math.ceil(productSearch.data?.response.numFound / limit); - const productStart = productSearch.data?.responseHeader.params.start; - const productRows = limit; - const productFound = productSearch.data?.response.numFound; - - useEffect(() => { - setProducts(productSearch.data?.response?.products); - }, [productSearch]); - - console.log("products",products) - - useEffect(() => { - if (router.query.brand) { - let brandsArray: string[] = []; - if (Array.isArray(router.query.brand)) { - brandsArray = router.query.brand; - } else if (typeof router.query.brand === 'string') { - brandsArray = router.query.brand.split(',').map((brand) => brand.trim()); - } - setBrandValues(brandsArray); - } else { - setBrandValues([]); - } - - if (router.query.category) { - let categoriesArray: string[] = []; - - if (Array.isArray(router.query.category)) { - categoriesArray = router.query.category; - } else if (typeof router.query.category === 'string') { - categoriesArray = router.query.category.split(',').map((category) => category.trim()); - } - setCategoryValues(categoriesArray); - } else { - setCategoryValues([]); - } - }, [router.query.brand, router.query.category]); - - interface Brand { - brand: string; - qty: number; - } - - interface Category { - name: string; - qty: number; - } - - useEffect(() => { - const loadPromo = async () => { - setLoading(true); - const brandsData: Brand[] = []; - const categoriesData: Category[] = []; - - const pageNumber = Array.isArray(page) ? parseInt(page[0], 10) : parseInt(page, 10); - setCurrentPage(pageNumber) - - try { - const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`,0,10); - setPromoItems(items); - - if (items.length === 0) { - setPromoData([]) - setLoading(false); - return; - } - - const brandArray = Array.isArray(brand) ? brand : brand.split(','); - const categoryArray = Array.isArray(category) ? category : category.split(','); - - const promoDataPromises = items.map(async (item) => { - - try { - let brandQuery = ''; - if (brand) { - brandQuery = brandArray.map(b => `manufacture_name_s:${b}`).join(' OR '); - brandQuery = `(${brandQuery})`; - } - - let categoryQuery = ''; - if (category) { - categoryQuery = categoryArray.map(c => `category_name:${c}`).join(' OR '); - categoryQuery = `(${categoryQuery})`; - } - - let priceQuery = ''; - if (priceFrom && priceTo) { - priceQuery = `price_f:[${priceFrom} TO ${priceTo}]`; - } else if (priceFrom) { - priceQuery = `price_f:[${priceFrom} TO *]`; - } else if (priceTo) { - priceQuery = `price_f:[* TO ${priceTo}]`; - } - - let combinedQuery = ''; - let combinedQueryPrice = `${priceQuery}`; - if (brand && category && priceFrom || priceTo) { - combinedQuery = `${brandQuery} AND ${categoryQuery} `; - } else if (brand && category) { - combinedQuery = `${brandQuery} AND ${categoryQuery}`; - } else if (brand && priceFrom || priceTo) { - combinedQuery = `${brandQuery}`; - } else if (category && priceFrom || priceTo) { - combinedQuery = `${categoryQuery}`; - } else if (brand) { - combinedQuery = brandQuery; - } else if (category) { - combinedQuery = categoryQuery; - } - - if (combinedQuery && priceFrom || priceTo) { - const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); - const product = response.response.docs[0]; - const product_id = product.id; - const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} AND ${combinedQueryPrice}`,0,100); - return response2; - }else if(combinedQuery){ - const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); - const product = response.response.docs[0]; - const product_id = product.id; - const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} `,0,100); - return response2; - } else { - const response = await fetchPromoItemsSolr(`id:${item.id}`,0,100); - return response; - } - } catch (fetchError) { - return []; - } - }); - - const promoDataArray = await Promise.all(promoDataPromises); - const mergedPromoData = promoDataArray.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); - setPromoData(mergedPromoData); - - const dataBrandCategoryPromises = promoDataArray.map(async (promoData) => { - if (promoData) { - const dataBrandCategory = promoData.map(async (item) => { - let response; - if(category){ - const categoryQuery = categoryArray.map(c => `category_name:${c}`).join(' OR '); - response = await fetchVariantSolr(`id:${item.products[0].product_id} AND (${categoryQuery})`); - }else{ - response = await fetchVariantSolr(`id:${item.products[0].product_id}`) - } - - - if (response.response?.docs?.length > 0) { - const product = response.response.docs[0]; - const manufactureNameS = product.manufacture_name; - if (Array.isArray(manufactureNameS)) { - for (let i = 0; i < manufactureNameS.length; i += 2) { - const brand = manufactureNameS[i]; - const qty = 1; - const existingBrandIndex = brandsData.findIndex(b => b.brand === brand); - if (existingBrandIndex !== -1) { - brandsData[existingBrandIndex].qty += qty; - } else { - brandsData.push({ brand, qty }); - } - } - } - - const categoryNameS = product.category_name; - if (Array.isArray(categoryNameS)) { - for (let i = 0; i < categoryNameS.length; i += 2) { - const name = categoryNameS[i]; - const qty = 1; - const existingCategoryIndex = categoriesData.findIndex(c => c.name === name); - if (existingCategoryIndex !== -1) { - categoriesData[existingCategoryIndex].qty += qty; - } else { - categoriesData.push({ name, qty }); - } - } - } - } - }); - - return Promise.all(dataBrandCategory); - } - }); - - await Promise.all(dataBrandCategoryPromises); - setBrands(brandsData); - setCategories(categoriesData); - setLoading(false); - - } catch (loadError) { - // console.error("Error loading promo items:", loadError) - setLoading(false); - } - } - - if (slug) { - loadPromo() - } - },[slug, brand, category, priceFrom, priceTo, currentPage]); - - - function capitalizeFirstLetter(string) { - string = string.replace(/_/g, ' '); - return string.replace(/(^\w|\s\w)/g, function(match) { - return match.toUpperCase(); - }); - } - - const handleDeleteFilter = async (source, value) => { - let params = { - q: router.query.q, - orderBy: '', - brand: brandValues.join(','), - category: categoryValues.join(','), - priceFrom: priceFrom || '', - priceTo: priceTo || '', - }; - - let brands = brandValues; - let catagories = categoryValues; - switch (source) { - case 'brands': - brands = brandValues.filter((item) => item !== value); - params.brand = brands.join(','); - await setBrandValues(brands); - break; - case 'category': - catagories = categoryValues.filter((item) => item !== value); - params.category = catagories.join(','); - await setCategoryValues(catagories); - break; - case 'price': - params.priceFrom = ''; - params.priceTo = ''; - break; - case 'delete': - params = { - q: router.query.q, - orderBy: '', - brand: '', - category: '', - priceFrom: '', - priceTo: '', - }; - break; - } - - handleSubmitFilter(params); - }; - const handleSubmitFilter = (params) => { - params = _.pickBy(params, _.identity); - params = toQuery(params); - router.push(`${slug}?${params}`); - }; - - const visiblePromotions = promoData?.slice( (currentPage-1) * itemsPerPage, currentPage * 12) - - const toQuery = (obj) => { - const str = Object.keys(obj) - .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`) - .join('&') - return str - } - - const whatPromo = capitalizeFirstLetter(slug) - const queryWithoutSlug = _.omit(router.query, ['slug']) - const queryString = toQuery(queryWithoutSlug) - - return ( - - - - -
-

Promo {whatPromo}

- - - {promoItems.length >= 1 && ( -
-
- -
-
- )} - - {loading ? ( -
- -
- ) : promoData && promoItems.length >= 1 ? ( - <> -
- {visiblePromotions?.map((promotion) => ( -
- -
- ))} -
- - ) : ( -
-

Belum ada promo pada kategori ini

-
- )} - - - -
- -
- -
-
-

Promo {whatPromo}

-
- -
- - -
-
- - -
-
- {loading ? ( -
- -
- ) : promoData && promoItems.length >= 1 ? ( - <> -
- {visiblePromotions?.map((promotion) => ( -
- -
- ))} -
- - ) : ( -
-

Belum ada promo pada kategori ini

-
- )} -
-
- -
- - Barang yang anda cari tidak ada?{' '} - - Hubungi Kami - - -
-
- - - - -
- -
-
-
-
- ) - } - -const FilterChoicesComponent = ({ - brandValues, - categoryValues, - priceFrom, - priceTo, - handleDeleteFilter, - }) => ( -
- - {brandValues?.map((value, index) => ( - - {value} - handleDeleteFilter('brands', value)} /> - - ))} - - {categoryValues?.map((value, index) => ( - - {value} - handleDeleteFilter('category', value)} - /> - - ))} - {priceFrom && priceTo && ( - - - {formatCurrency(priceFrom) + '-' + formatCurrency(priceTo)} - - handleDeleteFilter('price', priceFrom)} - /> - - )} - {brandValues?.length > 0 || - categoryValues?.length > 0 || - priceFrom || - priceTo ? ( - - - - ) : ( - '' - )} - -
-); diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index e93b0b54..7a115753 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -1,4 +1,4 @@ -const map = (promotions) => { +export const promoMappingSolr = (promotions, pricelist) => { const result = []; for (const promotion of promotions) { const data = { -- cgit v1.2.3 From b30e6a53d660f9ccbc0ded640c2a1dc5df673ff2 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 2 Sep 2024 16:43:25 +0700 Subject: update fetch data --- src/lib/product/api/productSearchApi.js | 2 +- src/lib/product/components/ProductSearch.jsx | 1 - src/lib/promo/api/productSearchApi.js | 12 ++++++++++++ src/lib/promo/hooks/usePromotionSearch.js | 15 +++++++++++++++ src/pages/api/shop/promo.js | 6 +++--- src/pages/shop/promo/[slug].jsx | 6 +++--- src/utils/solrMapping.js | 12 +++++------- 7 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 src/lib/promo/api/productSearchApi.js create mode 100644 src/lib/promo/hooks/usePromotionSearch.js (limited to 'src') diff --git a/src/lib/product/api/productSearchApi.js b/src/lib/product/api/productSearchApi.js index 670661aa..8ff8e57d 100644 --- a/src/lib/product/api/productSearchApi.js +++ b/src/lib/product/api/productSearchApi.js @@ -3,7 +3,7 @@ import axios from 'axios' const productSearchApi = async ({ query, operation = 'OR' }) => { const dataProductSearch = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/promo?${query}&operation=${operation}` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?${query}&operation=${operation}` ) return dataProductSearch.data } diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index 1f083722..ab55cae0 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -87,7 +87,6 @@ const ProductSearch = ({ recurse(category); return ids; }; - console.log("queryFinal",queryFinal) useEffect(() => { if(prefixUrl.includes('category')){ const ids = collectIds(dataCategoriesProduct); diff --git a/src/lib/promo/api/productSearchApi.js b/src/lib/promo/api/productSearchApi.js new file mode 100644 index 00000000..582b4486 --- /dev/null +++ b/src/lib/promo/api/productSearchApi.js @@ -0,0 +1,12 @@ +import _ from 'lodash-contrib' +import axios from 'axios' + +const productSearchApi = async ({ query, operation = 'AND' }) => { + const dataProductSearch = await axios( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/promo?${query}&operation=${operation}` + ) + console.log("dataProductSearch.data",dataProductSearch.data) + return dataProductSearch.data +} + +export default productSearchApi diff --git a/src/lib/promo/hooks/usePromotionSearch.js b/src/lib/promo/hooks/usePromotionSearch.js new file mode 100644 index 00000000..1a194646 --- /dev/null +++ b/src/lib/promo/hooks/usePromotionSearch.js @@ -0,0 +1,15 @@ +import { useQuery } from 'react-query' +import productSearchApi from '../api/productSearchApi' +import _ from 'lodash-contrib' + +const usePromotionSearch = ({ query, operation }) => { + const queryString = _.toQuery(query) + const fetchProductSearch = async () => await productSearchApi({ query: queryString , operation : operation}) + const productSearch = useQuery(`promoSearch-${queryString}`, fetchProductSearch) + + return { + productSearch + } +} + +export default usePromotionSearch diff --git a/src/pages/api/shop/promo.js b/src/pages/api/shop/promo.js index 353ff3df..e6867e89 100644 --- a/src/pages/api/shop/promo.js +++ b/src/pages/api/shop/promo.js @@ -108,12 +108,12 @@ export default async function handler(req, res) { let result = await axios( process.env.SOLR_HOST + '/solr/promotion_program_lines/select?' + parameter.join('&') ); - console.log("result",result) try { result.data.response.products = promoMappingSolr( - result.data.response.docs, - auth?.pricelist || false + result.data.response.docs ); + console.log("result.data.response.products",result.data.response.products) + result.data.responseHeader.params.start = parseInt( result.data.responseHeader.params.start ); diff --git a/src/pages/shop/promo/[slug].jsx b/src/pages/shop/promo/[slug].jsx index 544286f9..ce73ebd1 100644 --- a/src/pages/shop/promo/[slug].jsx +++ b/src/pages/shop/promo/[slug].jsx @@ -24,7 +24,7 @@ import whatsappUrl from '../../../core/utils/whatsappUrl.js'; import { cons, toQuery } from 'lodash-contrib'; import _ from 'lodash'; import useActive from '../../../core/hooks/useActive.js'; -import useProductSearch from '../../../lib/product/hooks/useProductSearch.js'; +import useProductSearch from '../../../lib/promo/hooks/usePromotionSearch.js'; const BasicLayout = dynamic(() => import('../../../core/components/layouts/BasicLayout.jsx')) @@ -45,7 +45,7 @@ export default function PromoDetail() { const popup = useActive(); const prefixUrl = `/shop/promo/${slug}` const [queryFinal, setQueryFinal] = useState({}); - const [limit, setLimit] = useState(30); + const [limit, setLimit] = useState(30); const [q, setQ] = useState('*'); const [finalQuery, setFinalQuery] = useState({fq: `type_value_s:${slug}`}); const [products, setProducts] = useState(null); @@ -365,7 +365,7 @@ export default function PromoDetail() { ) : promoData && promoItems.length >= 1 ? ( <>
- {visiblePromotions?.map((promotion) => ( + {products?.map((promotion) => (
diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index 7a115753..637d7c09 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -1,7 +1,6 @@ -export const promoMappingSolr = (promotions, pricelist) => { - const result = []; - for (const promotion of promotions) { - const data = { +export const promoMappingSolr = (promotions) => { + return promotions.map((promotion) =>{ + let productMapped = { id: promotion.id, program_id: promotion.program_id_i, name: promotion.name_s, @@ -20,9 +19,8 @@ export const promoMappingSolr = (promotions, pricelist) => { qty_sold_f:promotion.total_qty_sold_f, free_products: JSON.parse(promotion.free_products_s), }; - result.push(data); - } - return result; + return productMapped; + }) }; -- cgit v1.2.3 From 68ec9564aef424b86702512637c9cb738b44f215 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 2 Sep 2024 17:11:10 +0700 Subject: update search --- src/pages/api/shop/promo.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/pages/api/shop/promo.js b/src/pages/api/shop/promo.js index e6867e89..61fff4e5 100644 --- a/src/pages/api/shop/promo.js +++ b/src/pages/api/shop/promo.js @@ -122,7 +122,8 @@ export default async function handler(req, res) { ); delete result.data.response.docs; console.log("result.data",result.data) - result.data = camelcaseObjectDeep(result.data); + // result.data = camelcaseObjectDeep(result.data); + result.data = result.data; res.status(200).json(result.data); } catch (error) { res.status(400).json({ error: error.message }); -- cgit v1.2.3 From dab7f8a1ebd135d9335f1a53346c91d32a2be9f9 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 3 Sep 2024 08:46:46 +0700 Subject: update tempt --- src/pages/shop/promo/[slug].jsx | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pages/shop/promo/[slug].jsx b/src/pages/shop/promo/[slug].jsx index ce73ebd1..71ce8344 100644 --- a/src/pages/shop/promo/[slug].jsx +++ b/src/pages/shop/promo/[slug].jsx @@ -37,7 +37,7 @@ export default function PromoDetail() { const itemsPerPage = 12; // Jumlah item yang ingin ditampilkan per halaman const [loading, setLoading] = useState(true); const { isMobile, isDesktop } = useDevice() - const [brands, setBrands] = useState([]); + // const [brands, setBrands] = useState([]); const [categories, setCategories] = useState([]); const [brandValues, setBrandValues] = useState([]); const [categoryValues, setCategoryValues] = useState([]); @@ -75,7 +75,20 @@ export default function PromoDetail() { console.log("products",products) - + const brands = []; + for ( + let i = 0; + i < productSearch.data?.facetCounts?.facetFields?.manufactureNameS.length; + i += 2 + ) { + const brand = + productSearch.data?.facetCounts?.facetFields?.manufactureNameS[i]; + const qty = + productSearch.data?.facetCounts?.facetFields?.manufactureNameS[i + 1]; + if (qty > 0) { + brands.push({ brand, qty }); + } + } @@ -403,13 +416,13 @@ export default function PromoDetail() {
- + /> */}
@@ -470,8 +483,8 @@ export default function PromoDetail() { -- cgit v1.2.3 From 105ffc8cfa4d8efd51bb7654bfdd279df2a64779 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 3 Sep 2024 12:19:44 +0700 Subject: update tempt --- src/pages/api/shop/promo.js | 6 ++++-- src/pages/shop/promo/[slug].jsx | 27 ++++++++++++++++++++++----- src/utils/solrMapping.js | 6 ++++-- 3 files changed, 30 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/pages/api/shop/promo.js b/src/pages/api/shop/promo.js index 61fff4e5..57635958 100644 --- a/src/pages/api/shop/promo.js +++ b/src/pages/api/shop/promo.js @@ -49,8 +49,8 @@ export default async function handler(req, res) { let offset = (page - 1) * limit; let parameter = [ - // 'facet.field=manufacture_name_s', - // 'facet.field=category_name', + 'facet.field=manufacture_name_s', + 'facet.field=category_name', 'facet=true', 'indent=true', // `facet.query=${escapeSolrQuery(q)}`, @@ -108,7 +108,9 @@ export default async function handler(req, res) { let result = await axios( process.env.SOLR_HOST + '/solr/promotion_program_lines/select?' + parameter.join('&') ); + console.log("result",result.data) try { + console.log("result.data.response.docs",result.data.response.docs) result.data.response.products = promoMappingSolr( result.data.response.docs ); diff --git a/src/pages/shop/promo/[slug].jsx b/src/pages/shop/promo/[slug].jsx index 71ce8344..169ea0b5 100644 --- a/src/pages/shop/promo/[slug].jsx +++ b/src/pages/shop/promo/[slug].jsx @@ -38,14 +38,14 @@ export default function PromoDetail() { const [loading, setLoading] = useState(true); const { isMobile, isDesktop } = useDevice() // const [brands, setBrands] = useState([]); - const [categories, setCategories] = useState([]); + // const [categories, setCategories] = useState([]); const [brandValues, setBrandValues] = useState([]); const [categoryValues, setCategoryValues] = useState([]); const [orderBy, setOrderBy] = useState(router.query?.orderBy); const popup = useActive(); const prefixUrl = `/shop/promo/${slug}` const [queryFinal, setQueryFinal] = useState({}); - const [limit, setLimit] = useState(30); + const [limit, setLimit] = useState(22); const [q, setQ] = useState('*'); const [finalQuery, setFinalQuery] = useState({fq: `type_value_s:${slug}`}); const [products, setProducts] = useState(null); @@ -78,18 +78,35 @@ export default function PromoDetail() { const brands = []; for ( let i = 0; - i < productSearch.data?.facetCounts?.facetFields?.manufactureNameS.length; + i < productSearch.data?.facet_counts?.facet_fields?.manufacture_name_s.length; i += 2 ) { const brand = - productSearch.data?.facetCounts?.facetFields?.manufactureNameS[i]; + productSearch.data?.facet_counts?.facet_fields?.manufacture_name_s[i]; const qty = - productSearch.data?.facetCounts?.facetFields?.manufactureNameS[i + 1]; + productSearch.data?.facet_counts?.facet_fields?.manufacture_name_s[i + 1]; if (qty > 0) { brands.push({ brand, qty }); } } + console.log("brands",brands) + + const categories = []; + for ( + let i = 0; + i < productSearch.data?.facet_counts?.facet_fields?.category_name.length; + i += 2 + ) { + const name = productSearch.data?.facet_counts?.facet_fields?.category_name[i]; + const qty = + productSearch.data?.facet_counts?.facet_fields?.category_name[i + 1]; + if (qty > 0) { + categories.push({ name, qty }); + } + } + + diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index 637d7c09..15bf3afb 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -14,11 +14,13 @@ export const promoMappingSolr = (promotions) => { price: promotion.price_f, sequence: promotion.sequence_i, total_qty: promotion.total_qty_i, - products: JSON.parse(promotion.products_s), + products: JSON.parse(promotion.products_s) || '', product_id: promotion.product_ids[0], qty_sold_f:promotion.total_qty_sold_f, - free_products: JSON.parse(promotion.free_products_s), + free_products: JSON.parse(promotion.free_products_s) }; + console.log("productMapped",productMapped) + // console.log("promotions",promotions) return productMapped; }) }; -- cgit v1.2.3 From ad168bf46919b46f708625b7d2fdc56606ec9af6 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 3 Sep 2024 14:01:02 +0700 Subject: update all promo fix --- src/lib/promo/api/productSearchApi.js | 1 - src/pages/api/shop/promo.js | 15 +- src/pages/shop/promo/[slug].jsx | 272 ++++++---------------------------- src/utils/solrMapping.js | 2 - 4 files changed, 47 insertions(+), 243 deletions(-) (limited to 'src') diff --git a/src/lib/promo/api/productSearchApi.js b/src/lib/promo/api/productSearchApi.js index 582b4486..2f792fd4 100644 --- a/src/lib/promo/api/productSearchApi.js +++ b/src/lib/promo/api/productSearchApi.js @@ -5,7 +5,6 @@ const productSearchApi = async ({ query, operation = 'AND' }) => { const dataProductSearch = await axios( `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/promo?${query}&operation=${operation}` ) - console.log("dataProductSearch.data",dataProductSearch.data) return dataProductSearch.data } diff --git a/src/pages/api/shop/promo.js b/src/pages/api/shop/promo.js index 57635958..221a9adb 100644 --- a/src/pages/api/shop/promo.js +++ b/src/pages/api/shop/promo.js @@ -3,7 +3,6 @@ import axios from 'axios'; import camelcaseObjectDeep from 'camelcase-object-deep'; export default async function handler(req, res) { - console.log("req_iman",req) const { q = '*', page = 1, @@ -11,7 +10,7 @@ export default async function handler(req, res) { category = '', priceFrom = 0, priceTo = 0, - orderBy = '', + orderBy = 'if(exists(sequence_i),0,1) asc, sequence_i asc,', operation = 'AND', fq = '', limit = 30, @@ -19,7 +18,7 @@ export default async function handler(req, res) { let { stock = '' } = req.query; - let paramOrderBy = ''; + let paramOrderBy = 'if(exists(sequence_i),0,1) asc, sequence_i asc,'; switch (orderBy) { case 'price-asc': paramOrderBy += 'price_tier1_v2_f ASC'; @@ -60,7 +59,7 @@ export default async function handler(req, res) { `start=${parseInt(offset)}`, `rows=${limit}`, `sort=${paramOrderBy}`, - // `fq=-publish_b:false, product_rating_f:[8 TO *], price_tier1_v2_f:[1 TO *]`, + `fq=product_ids:[* TO *]`, ]; if (priceFrom > 0 || priceTo > 0) { @@ -83,7 +82,7 @@ export default async function handler(req, res) { .split(',') .map( (manufacturer) => - `manufacture_name:"${encodeURIComponent(manufacturer)}"` + `manufacture_name_s:"${encodeURIComponent(manufacturer)}"` ) .join(' OR ')}` ); @@ -102,19 +101,14 @@ export default async function handler(req, res) { // Multi fq in url params if (Array.isArray(fq)) parameter = parameter.concat(fq.map((val) => `fq=${val}`)); - - console.log("parameter",parameter) let result = await axios( process.env.SOLR_HOST + '/solr/promotion_program_lines/select?' + parameter.join('&') ); - console.log("result",result.data) try { - console.log("result.data.response.docs",result.data.response.docs) result.data.response.products = promoMappingSolr( result.data.response.docs ); - console.log("result.data.response.products",result.data.response.products) result.data.responseHeader.params.start = parseInt( result.data.responseHeader.params.start @@ -123,7 +117,6 @@ export default async function handler(req, res) { result.data.responseHeader.params.rows ); delete result.data.response.docs; - console.log("result.data",result.data) // result.data = camelcaseObjectDeep(result.data); result.data = result.data; res.status(200).json(result.data); diff --git a/src/pages/shop/promo/[slug].jsx b/src/pages/shop/promo/[slug].jsx index 169ea0b5..cfb2c841 100644 --- a/src/pages/shop/promo/[slug].jsx +++ b/src/pages/shop/promo/[slug].jsx @@ -4,12 +4,9 @@ import { useEffect, useState } from 'react' import { useRouter } from 'next/router' import Seo from '../../../core/components/Seo.jsx' import Promocrumb from '../../../lib/promo/components/Promocrumb.jsx' -import { fetchPromoItemsSolr, fetchVariantSolr } from '../../../api/promoApi.js' import LogoSpinner from '../../../core/components/elements/Spinner/LogoSpinner.jsx' import ProductPromoCard from '../../../../src-migrate/modules/product-promo/components/Card.tsx' -import { IPromotion } from '../../../../src-migrate/types/promotion.ts' import React from 'react' -import { SolrResponse } from "../../../../src-migrate/types/solr.ts"; import DesktopView from '../../../core/components/views/DesktopView.jsx'; import MobileView from '../../../core/components/views/MobileView.jsx'; import 'swiper/swiper-bundle.css'; @@ -19,9 +16,7 @@ import ProductFilter from '../../../lib/product/components/ProductFilter.jsx'; import { HStack, Image, Tag, TagCloseButton, TagLabel } from '@chakra-ui/react'; import { formatCurrency } from '../../../core/utils/formatValue.js'; import Pagination from '../../../core/components/elements/Pagination/Pagination.js'; -import SideBanner from '../../../../src-migrate/modules/side-banner/index.tsx'; import whatsappUrl from '../../../core/utils/whatsappUrl.js'; -import { cons, toQuery } from 'lodash-contrib'; import _ from 'lodash'; import useActive from '../../../core/hooks/useActive.js'; import useProductSearch from '../../../lib/promo/hooks/usePromotionSearch.js'; @@ -30,39 +25,54 @@ const BasicLayout = dynamic(() => import('../../../core/components/layouts/Basic export default function PromoDetail() { const router = useRouter() - const { slug = '', brand ='', category='', priceFrom = '', priceTo = '', page = '1' } = router.query - const [promoItems, setPromoItems] = useState([]) - const [promoData, setPromoData] = useState(null) + const { slug = '', brand ='', category='', page = '1' } = router.query const [currentPage, setCurrentPage] = useState(parseInt(10) || 1); - const itemsPerPage = 12; // Jumlah item yang ingin ditampilkan per halaman - const [loading, setLoading] = useState(true); - const { isMobile, isDesktop } = useDevice() - // const [brands, setBrands] = useState([]); - // const [categories, setCategories] = useState([]); - const [brandValues, setBrandValues] = useState([]); - const [categoryValues, setCategoryValues] = useState([]); const [orderBy, setOrderBy] = useState(router.query?.orderBy); const popup = useActive(); const prefixUrl = `/shop/promo/${slug}` const [queryFinal, setQueryFinal] = useState({}); - const [limit, setLimit] = useState(22); + const [limit, setLimit] = useState(30); const [q, setQ] = useState('*'); - const [finalQuery, setFinalQuery] = useState({fq: `type_value_s:${slug}`}); + const [finalQuery, setFinalQuery] = useState({}); const [products, setProducts] = useState(null); + const [brandValues, setBrand] = useState( + !router.pathname.includes('brands') + ? router.query.brand + ? router.query.brand.split(',') + : [] + : [] + ); + + const [categoryValues, setCategory] = useState( + router.query?.category?.split(',') || router.query?.category?.split(',') + ); + + const [priceFrom, setPriceFrom] = useState(router.query?.priceFrom || null); + const [priceTo, setPriceTo] = useState(router.query?.priceTo || null); + + useEffect(() => { - setQueryFinal({ ...finalQuery, q, limit, orderBy }); + const newQuery = { + fq: `type_value_s:${slug}`, + page : router.query.page? router.query.page : 1, + brand : router.query.brand? router.query.brand : '', + category : router.query.category? router.query.category : '', + priceFrom : router.query.priceFrom? router.query.priceFrom : '', + priceTo : router.query.priceTo? router.query.priceTo : '', + limit : router.query.limit? router.query.limit : '', + orderBy : router.query.orderBy? router.query.orderBy : '' + }; + setFinalQuery(newQuery); }, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); useEffect(() => { setQueryFinal({ ...finalQuery, q, limit, orderBy }); - }, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage]); + }, [router.query, prefixUrl, slug, brand, category, priceFrom, priceTo, currentPage, finalQuery]); const { productSearch } = useProductSearch({ query: queryFinal, operation: 'OR', }); - console.log("productSearch",productSearch) - console.log("queryFinal",queryFinal) const pageCount = Math.ceil(productSearch.data?.response.numFound / limit); const productStart = productSearch.data?.responseHeader.params.start; @@ -73,8 +83,6 @@ export default function PromoDetail() { setProducts(productSearch.data?.response?.products); }, [productSearch]); - console.log("products",products) - const brands = []; for ( let i = 0; @@ -90,8 +98,6 @@ export default function PromoDetail() { } } - console.log("brands",brands) - const categories = []; for ( let i = 0; @@ -105,190 +111,6 @@ export default function PromoDetail() { categories.push({ name, qty }); } } - - - - - - - useEffect(() => { - if (router.query.brand) { - let brandsArray= []; - if (Array.isArray(router.query.brand)) { - brandsArray = router.query.brand; - } else if (typeof router.query.brand === 'string') { - brandsArray = router.query.brand.split(',').map((brand) => brand.trim()); - } - setBrandValues(brandsArray); - } else { - setBrandValues([]); - } - - if (router.query.category) { - let categoriesArray= []; - - if (Array.isArray(router.query.category)) { - categoriesArray = router.query.category; - } else if (typeof router.query.category === 'string') { - categoriesArray = router.query.category.split(',').map((category) => category.trim()); - } - setCategoryValues(categoriesArray); - } else { - setCategoryValues([]); - } - }, [router.query.brand, router.query.category]); - - useEffect(() => { - const loadPromo = async () => { - setLoading(true); - const brandsData = []; - const categoriesData= []; - - const pageNumber = Array.isArray(page) ? parseInt(page[0], 10) : parseInt(page, 10); - setCurrentPage(pageNumber) - - try { - const items = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug}`,0,10); - setPromoItems(items); - - if (items.length === 0) { - setPromoData([]) - setLoading(false); - return; - } - - const brandArray = Array.isArray(brand) ? brand : brand.split(','); - const categoryArray = Array.isArray(category) ? category : category.split(','); - - const promoDataPromises = items.map(async (item) => { - - try { - let brandQuery = ''; - if (brand) { - brandQuery = brandArray.map(b => `manufacture_name_s:${b}`).join(' OR '); - brandQuery = `(${brandQuery})`; - } - - let categoryQuery = ''; - if (category) { - categoryQuery = categoryArray.map(c => `category_name:${c}`).join(' OR '); - categoryQuery = `(${categoryQuery})`; - } - - let priceQuery = ''; - if (priceFrom && priceTo) { - priceQuery = `price_f:[${priceFrom} TO ${priceTo}]`; - } else if (priceFrom) { - priceQuery = `price_f:[${priceFrom} TO *]`; - } else if (priceTo) { - priceQuery = `price_f:[* TO ${priceTo}]`; - } - - let combinedQuery = ''; - let combinedQueryPrice = `${priceQuery}`; - if (brand && category && priceFrom || priceTo) { - combinedQuery = `${brandQuery} AND ${categoryQuery} `; - } else if (brand && category) { - combinedQuery = `${brandQuery} AND ${categoryQuery}`; - } else if (brand && priceFrom || priceTo) { - combinedQuery = `${brandQuery}`; - } else if (category && priceFrom || priceTo) { - combinedQuery = `${categoryQuery}`; - } else if (brand) { - combinedQuery = brandQuery; - } else if (category) { - combinedQuery = categoryQuery; - } - - if (combinedQuery && priceFrom || priceTo) { - const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); - const product = response.response.docs[0]; - const product_id = product.id; - const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} AND ${combinedQueryPrice}`,0,100); - return response2; - }else if(combinedQuery){ - const response = await fetchVariantSolr(`id:${item.product_id} AND ${combinedQuery}`); - const product = response.response.docs[0]; - const product_id = product.id; - const response2 = await fetchPromoItemsSolr(`type_value_s:${Array.isArray(slug) ? slug[0] : slug} AND product_ids:${product_id} `,0,100); - return response2; - } else { - const response = await fetchPromoItemsSolr(`id:${item.id}`,0,100); - return response; - } - } catch (fetchError) { - return []; - } - }); - - const promoDataArray = await Promise.all(promoDataPromises); - const mergedPromoData = promoDataArray.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); - setPromoData(mergedPromoData); - - const dataBrandCategoryPromises = promoDataArray.map(async (promoData) => { - if (promoData) { - const dataBrandCategory = promoData.map(async (item) => { - let response; - if(category){ - const categoryQuery = categoryArray.map(c => `category_name:${c}`).join(' OR '); - response = await fetchVariantSolr(`id:${item.products[0].product_id} AND (${categoryQuery})`); - }else{ - response = await fetchVariantSolr(`id:${item.products[0].product_id}`) - } - - - if (response.response?.docs?.length > 0) { - const product = response.response.docs[0]; - const manufactureNameS = product.manufacture_name; - if (Array.isArray(manufactureNameS)) { - for (let i = 0; i < manufactureNameS.length; i += 2) { - const brand = manufactureNameS[i]; - const qty = 1; - const existingBrandIndex = brandsData.findIndex(b => b.brand === brand); - if (existingBrandIndex !== -1) { - brandsData[existingBrandIndex].qty += qty; - } else { - brandsData.push({ brand, qty }); - } - } - } - - const categoryNameS = product.category_name; - if (Array.isArray(categoryNameS)) { - for (let i = 0; i < categoryNameS.length; i += 2) { - const name = categoryNameS[i]; - const qty = 1; - const existingCategoryIndex = categoriesData.findIndex(c => c.name === name); - if (existingCategoryIndex !== -1) { - categoriesData[existingCategoryIndex].qty += qty; - } else { - categoriesData.push({ name, qty }); - } - } - } - } - }); - - return Promise.all(dataBrandCategory); - } - }); - - await Promise.all(dataBrandCategoryPromises); - setBrands(brandsData); - setCategories(categoriesData); - setLoading(false); - - } catch (loadError) { - // console.error("Error loading promo items:", loadError) - setLoading(false); - } - } - - if (slug) { - loadPromo() - } - },[slug, brand, category, priceFrom, priceTo, currentPage]); - function capitalizeFirstLetter(string) { string = string.replace(/_/g, ' '); @@ -302,7 +124,7 @@ export default function PromoDetail() { q: router.query.q, orderBy: '', brand: brandValues.join(','), - category: categoryValues.join(','), + category: categoryValues?.join(','), priceFrom: priceFrom || '', priceTo: priceTo || '', }; @@ -344,7 +166,6 @@ export default function PromoDetail() { router.push(`${slug}?${params}`); }; - const visiblePromotions = promoData?.slice( (currentPage-1) * itemsPerPage, currentPage * 12) const toQuery = (obj) => { const str = Object.keys(obj) @@ -355,7 +176,6 @@ export default function PromoDetail() { const whatPromo = capitalizeFirstLetter(slug) const queryWithoutSlug = _.omit(router.query, ['slug']) - const queryString = toQuery(queryWithoutSlug) return ( @@ -375,7 +195,7 @@ export default function PromoDetail() { priceTo={priceTo} handleDeleteFilter={handleDeleteFilter} /> - {promoItems.length >= 1 && ( + {products?.length >= 1 && (
)} - - {loading ? ( -
+ {productSearch.isLoading &&
-
- ) : promoData && promoItems.length >= 1 ? ( +
} + {products && ( <>
{products?.map((promotion) => ( @@ -402,15 +220,11 @@ export default function PromoDetail() { ))}
- ) : ( -
-

Belum ada promo pada kategori ini

-
- )} + ) } @@ -433,13 +247,13 @@ export default function PromoDetail() {
- {/* */} + />
@@ -451,11 +265,11 @@ export default function PromoDetail() { />
- {loading ? ( + {productSearch.isLoading ? (
- ) : promoData && promoItems.length >= 1 ? ( + ) : products && products.length >= 1 ? ( <>
{products?.map((promotion) => ( diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index 15bf3afb..0d50b99b 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -19,8 +19,6 @@ export const promoMappingSolr = (promotions) => { qty_sold_f:promotion.total_qty_sold_f, free_products: JSON.parse(promotion.free_products_s) }; - console.log("productMapped",productMapped) - // console.log("promotions",promotions) return productMapped; }) }; -- cgit v1.2.3