summaryrefslogtreecommitdiff
path: root/src-migrate
diff options
context:
space:
mode:
authorFIN-IT_AndriFP <andrifebriyadiputra@gmail.com>2025-12-03 11:49:18 +0700
committerFIN-IT_AndriFP <andrifebriyadiputra@gmail.com>2025-12-03 11:49:18 +0700
commit219c61c5c14e3a8dfed3d7158d59d11c476e3586 (patch)
treef640aab4055aea6997e8fa281476d05300187656 /src-migrate
parent825d86bb50f48f9a21d740d474c0dddee858dffb (diff)
(andri) fix search for similar product side dan bottom pada detail product
Diffstat (limited to 'src-migrate')
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx55
-rw-r--r--src-migrate/modules/product-detail/components/SimilarSide.tsx74
-rw-r--r--src-migrate/services/product.ts40
3 files changed, 121 insertions, 48 deletions
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index 5f930117..cfe73628 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -67,6 +67,8 @@ const ProductDetail = ({ product }: Props) => {
// State Data dari Magento
const [specs, setSpecs] = useState<{ code: string; label: string; value: string }[]>([]);
const [upsellIds, setUpsellIds] = useState<number[]>([]);
+ const [relatedIds, setRelatedIds] = useState<number[]>([]);
+
const [loadingSpecs, setLoadingSpecs] = useState(false);
const [errorSpecs, setErrorSpecs] = useState(false);
@@ -109,7 +111,7 @@ const ProductDetail = ({ product }: Props) => {
}, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl]);
// =========================================================================
- // LOGIC INISIALISASI VARIANT (HANDLE NAVIGASI)
+ // LOGIC INISIALISASI VARIANT (RESET SAAT NAVIGASI)
// =========================================================================
useEffect(() => {
if (typeof auth === 'object') {
@@ -121,28 +123,29 @@ const ProductDetail = ({ product }: Props) => {
setSelectedVariant(variantInit);
- // Reset data Magento saat produk berubah
+ // Reset data Magento
setSpecs([]);
setUpsellIds([]);
+ setRelatedIds([]);
}, [product, auth]);
// =========================================================================
- // LOGIC FETCH: SPECS & UPSELLS
+ // LOGIC FETCH: SPECS, UPSELLS, RELATED
// =========================================================================
useEffect(() => {
const fetchMagentoData = async () => {
- // Gunakan ID Variant (SKU Odoo)
- const idToFetch = selectedVariant?.id;
+ // Validasi kepemilikan varian
+ if (!selectedVariant?.id) return;
+ const isVariantOwner = product.variants.some(v => Number(v.id) === Number(selectedVariant.id));
+ if (!isVariantOwner) return;
- if (!idToFetch) return;
+ const idToFetch = selectedVariant.id;
setLoadingSpecs(true);
setErrorSpecs(false);
try {
- console.log("Fetching Magento data via Proxy for ID:", idToFetch);
-
const endpoint = `/api/magento-product?sku=${encodeURIComponent(String(idToFetch))}`;
const response = await fetch(endpoint, {
@@ -151,61 +154,75 @@ const ProductDetail = ({ product }: Props) => {
});
if (!response.ok) {
- console.warn(`Magento API status: ${response.status}`);
setSpecs([]);
setUpsellIds([]);
+ setRelatedIds([]);
return;
}
const data = await response.json();
- // 1. Simpan Data Spesifikasi
+ // Double Check
+ if (Number(idToFetch) !== Number(selectedVariant.id)) return;
+
+ // 1. Specs
if (data.specs && Array.isArray(data.specs)) {
setSpecs(data.specs);
} else {
setSpecs([]);
}
- // 2. Simpan Data Up-Sells (ID)
+ // 2. Upsell
if (data.upsell_ids && Array.isArray(data.upsell_ids)) {
setUpsellIds(data.upsell_ids);
} else {
setUpsellIds([]);
}
+ // 3. Related
+ if (data.related_ids && Array.isArray(data.related_ids)) {
+ setRelatedIds(data.related_ids);
+ } else {
+ setRelatedIds([]);
+ }
+
} catch (error) {
console.error("Gagal mengambil data Magento:", error);
setErrorSpecs(true);
setSpecs([]);
setUpsellIds([]);
+ setRelatedIds([]);
} finally {
setLoadingSpecs(false);
}
};
fetchMagentoData();
- }, [selectedVariant, product.id]);
+ }, [selectedVariant, product]);
// =========================================================================
- // HELPER: RENDER SPEC VALUE (SIMPLE TEXT/HTML ONLY)
+ // HELPER: RENDER SPEC VALUE (SIMPLE - NO LINK DETECT)
// =========================================================================
const renderSpecValue = (item: { code: string; label: string; value: string }) => {
const val = item.value;
if (!val) return '-';
- // Cek apakah mengandung tag HTML sederhana (<p>, <a>, <ul>, dll)
- if (val.includes('<') && val.includes('>')) {
+ const cleanVal = val.trim();
+
+ // 1. JIKA HTML (Legacy Data)
+ // Deteksi tag HTML sederhana
+ if (cleanVal.includes('<') && cleanVal.includes('>')) {
return (
<div
className="prose prose-sm text-gray-700"
- dangerouslySetInnerHTML={{ __html: val }}
+ dangerouslySetInnerHTML={{ __html: cleanVal }}
/>
);
}
- // Default: Teks Biasa
- return val;
+ // 2. TEKS BIASA
+ return cleanVal;
};
@@ -544,7 +561,7 @@ const ProductDetail = ({ product }: Props) => {
<div className='h-6' />
<div className={style['heading']}>Produk Serupa</div>
<div className='h-4' />
- <SimilarSide product={product} />
+ <SimilarSide product={product} relatedIds={relatedIds} />
</div>
)}
diff --git a/src-migrate/modules/product-detail/components/SimilarSide.tsx b/src-migrate/modules/product-detail/components/SimilarSide.tsx
index d70a314d..51d9eff7 100644
--- a/src-migrate/modules/product-detail/components/SimilarSide.tsx
+++ b/src-migrate/modules/product-detail/components/SimilarSide.tsx
@@ -1,33 +1,75 @@
import { Skeleton } from '@chakra-ui/react'
+import { useQuery } from 'react-query'
import ProductCard from '~/modules/product-card'
-import useProductSimilar from '~/modules/product-similar/hooks/useProductSimilar'
-import { IProductDetail } from '~/types/product'
+// Import service
+import { getProductSimilar, getProductsByIds } from '~/services/product'
+// TAMBAHKAN 'IProduct' DISINI
+import { IProduct, IProductDetail } from '~/types/product'
type Props = {
product: IProductDetail
+ relatedIds?: number[]
}
-const SimilarSide = ({ product }: Props) => {
- const productSimilar = useProductSimilar({
- name: product.name,
- except: { productId: product.id, manufactureId: product.manufacture.id },
- })
+const SimilarSide = ({ product, relatedIds = [] }: Props) => {
+
+ const hasRelated = relatedIds.length > 0;
- const products = productSimilar.data?.products || []
+ // 1. Fetch Related by ID
+ const relatedQuery = useQuery({
+ queryKey: ['product-related', relatedIds],
+ queryFn: () => getProductsByIds({ ids: relatedIds }),
+ enabled: hasRelated,
+ staleTime: 1000 * 60 * 5,
+ });
+
+ // 2. Fetch Similar Biasa
+ const similarQuery = useQuery({
+ queryKey: ['product-similar-side', product.name],
+ queryFn: () => getProductSimilar({
+ name: product.name,
+ except: {
+ productId: product.id,
+ manufactureId: product.manufacture?.id
+ }
+ }),
+ enabled: !hasRelated,
+ staleTime: 1000 * 60 * 5,
+ });
+
+ // ============================================================
+ // PERBAIKAN: Definisikan tipe array secara eksplisit (IProduct[])
+ // ============================================================
+ let products: IProduct[] = [];
+ let isLoading = false;
+
+ if (hasRelated) {
+ // Cast ke any dulu jika tipe return service belum sempurna terdeteksi, lalu ambil products
+ // Atau jika getProductsByIds me-return { products: IProduct[] }, ambil .products
+ // Sesuai kode service terakhir, getProductsByIds me-return GetProductSimilarRes yg punya .products
+ products = (relatedQuery.data as any)?.products || [];
+ isLoading = relatedQuery.isLoading;
+ } else {
+ products = similarQuery.data?.products || [];
+ isLoading = similarQuery.isLoading;
+ }
+
+ if (!isLoading && products.length === 0) return null;
return (
<Skeleton
- isLoaded={!productSimilar.isLoading}
- className="h-[500px] overflow-auto grid grid-cols-1 gap-y-4 divide-y divide-gray-300 border border-gray-300 rounded-lg"
+ isLoaded={!isLoading}
+ className="h-[500px] overflow-auto grid grid-cols-1 gap-y-4 divide-y divide-gray-300 border border-gray-300 rounded-lg p-2"
rounded='lg'
>
- {products.map((product) => (
- <ProductCard
- key={product.id}
- product={product}
- layout='horizontal'
- />
+ {products.map((item) => (
+ <div key={item.id} className="pt-2 first:pt-0">
+ <ProductCard
+ product={item}
+ layout='horizontal'
+ />
+ </div>
))}
</Skeleton>
)
diff --git a/src-migrate/services/product.ts b/src-migrate/services/product.ts
index f77fc3ec..fa9dae54 100644
--- a/src-migrate/services/product.ts
+++ b/src-migrate/services/product.ts
@@ -76,29 +76,43 @@ export interface GetProductsByIdsProps {
export const getProductsByIds = async ({
ids,
}: GetProductsByIdsProps): Promise<GetProductSimilarRes> => {
- // Jika array ID kosong, kembalikan object kosong agar tidak error
if (!ids || ids.length === 0) {
- return {
- products: [],
- num_found: 0,
- num_found_exact: true,
- start: 0
- };
+ return { products: [], num_found: 0, num_found_exact: true, start: 0 };
}
- // Buat query Solr format: product_id_i:(224102 OR 88019 OR ...)
const idQuery = ids.join(' OR ');
const query = [
- `q=*`, // Query wildcard (ambil semua)
- `fq=product_id_i:(${idQuery})`, // TAPI difilter hanya ID yang ada di list upsell
+ `q=*`,
+ `fq=(id:(${idQuery}) OR product_id_i:(${idQuery}))`,
'rows=20',
`source=upsell`,
];
const url = `${SELF_HOST}/api/shop/search?${query.join('&')}`;
- return await fetch(url)
- .then((res) => res.json())
- .then((res) => snakeCase(res.response));
+ // Request
+ const res = await fetch(url).then((res) => res.json());
+
+ // LOG 2: Hasil Pencarian SOLR
+ console.group("🔍 2. [Solr Search Result]");
+ console.log("Request URL:", url);
+ console.log("Requested IDs:", ids);
+
+ const foundDocs = res.response?.docs || [];
+ const foundIds = foundDocs.map((doc: any) => doc.id || doc.product_id_i);
+
+ console.log("Found Products Count:", res.response?.numFound);
+ console.log("Found IDs:", foundIds);
+
+ // Cek ID mana yang hilang
+ const missingIds = ids.filter((reqId) => !foundIds.includes(String(reqId)) && !foundIds.includes(Number(reqId)));
+ if (missingIds.length > 0) {
+ console.warn("⚠️ MISSING / NOT FOUND IDs:", missingIds);
+ } else {
+ console.log("✅ All IDs Found!");
+ }
+ console.groupEnd();
+
+ return snakeCase(res.response);
}; \ No newline at end of file