diff options
| -rw-r--r-- | src/lib/product/api/productSearchApi.js | 2 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 2 | ||||
| -rw-r--r-- | src/pages/api/shop/promo.js | 205 | ||||
| -rw-r--r-- | src/pages/shop/promo/[slug].tsx | 67 | ||||
| -rw-r--r-- | src/utils/solrMapping.js | 29 |
5 files changed, 285 insertions, 20 deletions
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<Category[]>([]); const [brandValues, setBrandValues] = useState<string[]>([]); const [categoryValues, setCategoryValues] = useState<string[]>([]); - 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<number>(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, }; }; + |
