From 60497cfbdf29df106d3d4270bfd480f270f16098 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Mon, 25 Mar 2024 16:52:42 +0700 Subject: generate recomendation --- src/pages/api/shop/generate-recomendation.js | 60 +++++ src/pages/my/recomendation/api/recomendation.js | 21 +- .../components/products-recomendatison.jsx | 278 ++++++++++++++++++--- 3 files changed, 322 insertions(+), 37 deletions(-) create mode 100644 src/pages/api/shop/generate-recomendation.js (limited to 'src') diff --git a/src/pages/api/shop/generate-recomendation.js b/src/pages/api/shop/generate-recomendation.js new file mode 100644 index 00000000..a2a9ee40 --- /dev/null +++ b/src/pages/api/shop/generate-recomendation.js @@ -0,0 +1,60 @@ +import { productMappingSolr } from '@/utils/solrMapping' +import axios from 'axios'; +import camelcaseObjectDeep from 'camelcase-object-deep'; + +export default async function handler(req, res) { + const { q = null } = req.query + + if (!q) { + return res.status(422).json({ error: 'parameter missing' }) + } + + let parameter = [ + `q=${escapeSolrQuery(q)}`, + `q.op=AND`, + `indent=true`, + `fq=-publish_b:false`, + `qf=name_s^2 description_s`, + `facetch=true`, + `fq=price_tier1_v2_f:[1 TO *]`, + `rows=1`, + `sort=product_rating_f DESC, price_discount_f DESC`, + ]; + let result = await axios( + process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&') + ); + try { + let { auth } = req.cookies; + if (auth) auth = JSON.parse(auth); + result.data.response.products = productMappingSolr( + 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; + + 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(' '); +}; diff --git a/src/pages/my/recomendation/api/recomendation.js b/src/pages/my/recomendation/api/recomendation.js index 47ed743a..8ff760d0 100644 --- a/src/pages/my/recomendation/api/recomendation.js +++ b/src/pages/my/recomendation/api/recomendation.js @@ -1,8 +1,17 @@ -import axios from "axios" +import axios from 'axios'; +import { useQuery } from 'react-query'; -const GenerateRecomendations = async ({query}) => { - const GenerateRecomendationProducts = await axios(`${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/recomendation?${query}`) +const GenerateRecomendations = ({ query }) => { + const queryString = _.toQuery(query); + const GenerateRecomendationProducts = async () => + await axios( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/recomendation?${queryString}` + ); + const productSearch = useQuery( + `generateRecomendation-${ququeryStringery}`, + GenerateRecomendationProducts + ); - return GenerateRecomendationProducts -} -export default GenerateRecomendations \ No newline at end of file + return productSearch; +}; +export default GenerateRecomendations; diff --git a/src/pages/my/recomendation/components/products-recomendatison.jsx b/src/pages/my/recomendation/components/products-recomendatison.jsx index c20aaa5b..72b858e8 100644 --- a/src/pages/my/recomendation/components/products-recomendatison.jsx +++ b/src/pages/my/recomendation/components/products-recomendatison.jsx @@ -1,19 +1,79 @@ import Menu from '@/lib/auth/components/Menu'; -import { useState } from 'react'; -import * as XLSX from 'xlsx'; +import { useEffect, useState } from 'react'; +import * as XLSX from 'xlsx'; +import GenerateRecomendations from '../api/recomendation'; +import axios from 'axios'; +import { Button, Link } from '@chakra-ui/react'; +import Image from 'next/image'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import formatCurrency from '~/libs/formatCurrency'; const ProductsRecomendation = ({ id }) => { const [excelData, setExcelData] = useState(null); + const [products, setProducts] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const [variantsOpen, setVariantsOpen] = useState([]); - const handleSubmit = (e) => { + const mappingProducts = async ({ index, product, result, variants }) => { + const resultMapping = { + index : index, + product: product, + result: { + id: result?.id || '-', + name: result?.displayName || '-', + code: result?.code || '-', + image: result?.image || '-', + price: result?.lowestPrice.priceDiscount || '-', + manufacture: result?.manufacture.name || '-', + variantTotal: result?.variantTotal || '-', + variants: variants || '-', + }, + }; + + return resultMapping; + }; + + const searchRecomendation = async ({ product, index }) => { + let variants = []; + const searchProduct = await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/generate-recomendation?q=${product}` + ); + + const result = + searchProduct.data.response.numFound > 0 + ? searchProduct.data.response.products[0] + : null; + + if (result?.variantTotal > 1) { + const searchVariants = await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/product-detail?id=${result.id}` + ); + variants = searchVariants.data[0].variants; + } + + const resultMapping = await mappingProducts({ index, product, result, variants }); + + return resultMapping; + }; + + const handleSubmit = async (e) => { + setIsLoading(true); e.preventDefault(); if (excelData) { - // Lakukan operasi pencarian atau operasi lainnya di sini - console.log('ini data excel',excelData); // Contoh: Menampilkan data excel ke konsol + const results = await Promise.all( + excelData.map(async (row, i) => { + const index = i + 1; + const product = row[0]; + return await searchRecomendation({ product, index }); + }) + ).then((results) => setProducts(results)); + setIsLoading(false); } else { console.log('No excel data available'); } }; + const handleFileChange = (e) => { const file = e.target.files[0]; const reader = new FileReader(); @@ -27,36 +87,107 @@ const ProductsRecomendation = ({ id }) => { }; reader.readAsArrayBuffer(file); }; + + const handleVariantsOpen = ({ variants }) => { + setVariantsOpen(variants); + setIsOpen(true); + }; + const hadnliChooseVariants = ({ id, variant }) => { + let foundIndex = products.findIndex((item) => item.result.id === id); + if (foundIndex !== -1) { + products[foundIndex].result.code = variant?.code; + products[foundIndex].result.name = variant?.name; + } else { + console.log('Data not found.'); + } + setIsOpen(false); + }; return ( -
-
- -
-
-
-

- Generate Recomendation -

-
-
-

Contoh Excel

+ <> + setIsOpen(false)} + className='w-full md:!w-[60%]' + title='List Variants' + > +
- - + + + + - - - - + {variantsOpen?.map((variant, index) => ( + + + + + + + ))}
ProductQtyPart NumberVariants Harga
Tekiro Long Nose Pliers Tang Lancip10
{variant.code}{variant.attributes.join(', ') || '-'} + {variant.price.discountPercentage > 0 && ( +
+
+ {Math.floor(variant.price.discountPercentage)}% +
+
+ Rp {formatCurrency(variant.price.price)} +
+
+ )} + {variant.price.priceDiscount > 0 && + `Rp ${formatCurrency(variant.price.priceDiscount)}`} + {variant.price.priceDiscount === 0 && '-'} +
+ +
-
-
+ +
+
+ +
+
+
+

+ Generate Recomendation +

+
+
+

Contoh Excel

+ + + + + + + + + + + + + +
ProductQty
Tekiro Long Nose Pliers Tang Lancip10
+
+
- - + +
+
+ {products && products.length > 0 && ( +
+ + + + + + + + + + + + + + {products.map((product, index) => ( + + + + + + + + + + ))} + +
ProductItem codeKeteranganBrandHargaImagelainnya
{product?.product} + {product?.result?.code === '-' && + product.result.variantTotal > 1 && ( + + )} + {product?.result.code !== '-' && + product?.result.variantTotal > 1 ? ( + <> + {product?.result.code} + + + ) : ( + <>{product?.result.code} + )} + {product?.result.name}{product?.result.manufacture} + {product?.result.price !== '-' + ? `Rp ${formatCurrency(product?.result.price)}` + : '-'} + + {product?.result.image !== '-' ? ( + {product?.result.name} + ) : ( + '-' + )} +
+
+ )} +
-
+ ); }; -- cgit v1.2.3