summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/product/components/Product/ProductDesktopVariant.jsx13
-rw-r--r--src/lib/product/components/Product/ProductMobileVariant.jsx9
-rw-r--r--src/pages/api/shop/search.js116
-rw-r--r--src/utils/solrMapping.js3
4 files changed, 123 insertions, 18 deletions
diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx
index 6b4ab1e1..1584dc92 100644
--- a/src/lib/product/components/Product/ProductDesktopVariant.jsx
+++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx
@@ -314,6 +314,11 @@ const ProductDesktopVariant = ({
fetchData();
}, [product]);
+ const getImageVariant = (img) => {
+ if (!img || img.trim() === '') return '/images/noimage.jpeg';
+ return `${img}?variant=True`;
+ };
+
return (
<DesktopView>
<div className='relative'>
@@ -332,8 +337,8 @@ const ProductDesktopVariant = ({
<div className='w-full flex flex-wrap'>
<div className='w-5/12'>
<Image
- src={product.image + '?variant=True'}
- alt={product.name}
+ src={getImageVariant(product?.image)}
+ alt={product?.name}
className='w-full h-[350px]'
/>
</div>
@@ -345,9 +350,9 @@ const ProductDesktopVariant = ({
size={18}
className='text-red-600 shrink-0 mx-2'
/>
- <h1 className='text-red-600 font-normal text-h-sm'>
+ <div className='text-red-600 font-normal text-h-lg p-2'>
Maaf untuk saat ini Produk yang anda cari tidak tersedia
- </h1>
+ </div>
</div>
)}
<h1 className='text-title-md leading-10 font-medium'>
diff --git a/src/lib/product/components/Product/ProductMobileVariant.jsx b/src/lib/product/components/Product/ProductMobileVariant.jsx
index 0f4953df..9d375deb 100644
--- a/src/lib/product/components/Product/ProductMobileVariant.jsx
+++ b/src/lib/product/components/Product/ProductMobileVariant.jsx
@@ -181,6 +181,11 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
return Math.floor(Math.random() * 100) + 1;
});
+ const getImageVariant = (img) => {
+ if (!img || img.trim() === '') return '/images/noimage.jpeg';
+ return `${img}?variant=True`;
+ };
+
return (
<MobileView>
<div className='relative'>
@@ -338,7 +343,7 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
</div>
<Image
- src={product.image + '?variant=True'}
+ src={getImageVariant(product?.image)}
alt={product.name}
className='h-72 object-contain object-center w-full border-b border-gray_r-4'
/>
@@ -532,7 +537,7 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
<div className='flex mt-4'>
<div className='w-[15%]'>
<Image
- src={product.image + '?variant=True'}
+ src={getImageVariant(product?.image)}
alt={product.name}
className='h-20 object-contain object-center w-full border border-gray_r-4'
/>
diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js
index 7d4adfcb..5ea6a70a 100644
--- a/src/pages/api/shop/search.js
+++ b/src/pages/api/shop/search.js
@@ -2,6 +2,14 @@ import { productMappingSolr } from '@/utils/solrMapping';
import axios from 'axios';
import camelcaseObjectDeep from 'camelcase-object-deep';
+const escapeSolrQuery = (query) => {
+ if (query == '*') return query;
+ query = query.replace(/-/g, ' ');
+ const specialChars = /([\+\!\(\)\{\}\[\]\^"~\*\?:\\\/])/g;
+ const words = query.split(/\s+/);
+ return words.map((word) => specialChars.test(word) ? word.replace(specialChars, '\\$1') : word).join(' ');
+};
+
export default async function handler(req, res) {
const {
q = '*',
@@ -20,7 +28,99 @@ export default async function handler(req, res) {
let { stock = '' } = req.query;
// ============================================================
- // LOGIC KHUSUS UPSELL (Simple & Direct)
+ // [PERBAIKAN] 1. LOGIC KHUSUS COMPARE (PAKAI URLSearchParams)
+ // ============================================================
+ if (source === 'compare') {
+ try {
+ let qCompare = q === '*' ? '*:*' : q;
+
+ // Sanitasi Query
+ if (qCompare !== '*:*') {
+ // Kita escape, tapi biarkan stringnya bersih (jangan ditambah wildcard * manual)
+ // karena kita serahkan ke 'edismax' parser
+ qCompare = escapeSolrQuery(qCompare);
+ }
+
+ // [SOLUSI] Gunakan URLSearchParams untuk menyusun URL dengan aman
+ const params = new URLSearchParams();
+
+ params.append('q', qCompare);
+ params.append('rows', limit);
+ params.append('wt', 'json');
+ params.append('indent', 'true');
+
+ // Gunakan eDisMax parser (Otak Cerdas)
+ params.append('defType', 'edismax');
+
+ // Set Prioritas Pencarian (Boost ^)
+ // 1. default_code_s^20 : SKU persis (Prioritas Tertinggi)
+ // 2. search_keywords_t^10 : Field baru (Case insensitive)
+ // 3. display_name_s^1 : Cadangan
+ params.append('qf', 'default_code_s^20 search_keywords_t^10 display_name_s^1');
+
+ // Minimum Match 100% (Semua kata harus ada), ubah jika ingin lebih longgar
+ params.append('mm', '100%');
+
+ // Grouping
+ params.append('group', 'true');
+ params.append('group.field', 'template_id_i');
+ params.append('group.limit', '1');
+ params.append('group.main', 'true');
+
+ // Field List (fl)
+ params.append('fl', 'id,display_name_s,default_code_s,image_s,price_tier1_v2_f,attribute_set_id_i,attribute_set_name_s,template_id_i,product_id_i');
+
+ // Filter Query (fq) Dasar
+ params.append('fq', '-publish_b:false');
+ params.append('fq', 'price_tier1_v2_f:[1 TO *]');
+
+ // Logic Locking (Filter Attribute Set ID dari Frontend)
+ if (fq) {
+ if (Array.isArray(fq)) {
+ fq.forEach(f => params.append('fq', f));
+ } else {
+ params.append('fq', fq);
+ }
+ }
+
+ // Target Core: VARIANTS
+ // HAPUS parameter manual dari string URL, gunakan params object
+ const solrUrl = process.env.SOLR_HOST + '/solr/variants/select';
+
+ // Axios akan otomatis handle encoding % dan & dengan benar
+ const result = await axios.get(solrUrl, { params: params });
+
+ // Mapping Result
+ const mappedProducts = productMappingSolr(
+ result.data.response.docs,
+ false
+ );
+
+ const finalResponse = {
+ ...result.data,
+ response: {
+ ...result.data.response,
+ products: mappedProducts
+ }
+ };
+
+ delete finalResponse.response.docs;
+ const camelCasedData = camelcaseObjectDeep(finalResponse);
+
+ return res.status(200).json(camelCasedData);
+
+ } catch (e) {
+ console.error('[COMPARE SEARCH ERROR]', e.message);
+ if (e.response && e.response.data) {
+ // Log detail error dari Solr
+ console.error('[SOLR DETAILS]:', JSON.stringify(e.response.data, null, 2));
+ }
+ return res.status(200).json({ response: { products: [], numFound: 0 } });
+ }
+ }
+
+ // ============================================================
+ // LOGIC KHUSUS UPSELL (KODE LAMA ANDA)
// ============================================================
if (source === 'upsell') {
try {
@@ -88,7 +188,7 @@ export default async function handler(req, res) {
}
// ============================================================
- // SITEMAP (Biarkan tetap sama)
+ // SITEMAP (KODE LAMA ANDA)
// ============================================================
if (source === 'sitemap') {
try {
@@ -113,7 +213,7 @@ export default async function handler(req, res) {
}
// ============================================================
- // SEARCH NORMAL (LOGIKA LAMA)
+ // SEARCH NORMAL (KODE LAMA ANDA)
// ============================================================
let paramOrderBy = '';
@@ -219,12 +319,4 @@ export default async function handler(req, res) {
} 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+/);
- return words.map((word) => specialChars.test(word) ? word.replace(specialChars, '\\$1') : word).join(' ');
-}; \ No newline at end of file
+} \ No newline at end of file
diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js
index 33f0cbaf..8c0abcf1 100644
--- a/src/utils/solrMapping.js
+++ b/src/utils/solrMapping.js
@@ -127,6 +127,9 @@ export const variantsMappingSolr = (parent, products, pricelist) => {
variantTotal: product.variant_total_i || 0,
stockTotal: product.stock_total_f || 0,
weight: product.weight_f || 0,
+ attribute_set_id: product.attribute_set_id_i || 0,
+ attribute_set_name: product.attribute_set_name_s || '',
+ search_keywords: product.search_keywords_t || '',
manufacture: {},
parent: {},
qtySold: product?.qty_sold_f || 0,