From fbbff6a25ed39b721927a79ce905f4d1e06e522b Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 23 Dec 2025 14:44:11 +0700 Subject: (andri) fix sticky kolom pertama specs --- .../product-detail/components/ProductDetail.tsx | 198 ++++++++++++--------- 1 file changed, 118 insertions(+), 80 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 3beb75b4..1c9c39e0 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -460,88 +460,126 @@ const ProductDetail = ({ product }: Props) => { - {/* SPESIFIKASI (LOGIKA GROUPING + RATA TENGAH) */} + {/* SPESIFIKASI (LOGIKA GROUPING + RATA TENGAH + STICKY FIRST COLUMN) */} - {loadingSpecs ? ( -
- ) : specsMatrix.length > 0 ? ( - (() => { - const isSingleVariant = product.variants.length === 1; - const globalAlign = isSingleVariant ? "left" : "center"; - - return ( - - - - - {product.variants.map(v => ( - - ))} - - - - {specsMatrix.map((row, i) => { - - // CASE 1: GROUPING (Label punya ' : ') - if (row.type === 'group') { - return ( - - {/* Header Group Kiri */} - - - {/* Content Group Kanan */} - {product.variants.map(v => ( - - ))} - - ); - } - - // CASE 2: SINGLE ITEM - return ( - - - {product.variants.map(v => { - const rawValue = row.values[v.id] || '-'; - return ( - - ); - })} - - ); - })} - -
- Spesifikasi - - {isSingleVariant ? 'Detail' : (v.attributes && v.attributes.length > 0 ? v.attributes.join(' - ') : v.code)} -
{row.label} -
-
- {row.children.map((child: any, idx: number) => { - const rawVal = child.values[v.id]; - if (!rawVal || rawVal === '-') return null; - - return ( -
- - {child.label}: - - {renderSpecValue(rawVal)} -
- ); - })} -
-
-
{row.label} - {renderSpecValue(rawValue)} -
- ); - })() - ) : ( - Spesifikasi teknis belum tersedia. - )} + {loadingSpecs ? ( +
+ ) : specsMatrix.length > 0 ? ( + (() => { + const isSingleVariant = product.variants.length === 1; + const globalAlign = isSingleVariant ? "left" : "center"; + + return ( + + + + {/* 1. STICKY HEADER KOLOM PERTAMA */} + + {product.variants.map(v => ( + + ))} + + + + {specsMatrix.map((row, i) => { + + // CASE 1: GROUPING + if (row.type === 'group') { + return ( + + {/* 2. STICKY BODY KOLOM PERTAMA (GROUP) */} + + + {product.variants.map(v => ( + + ))} + + ); + } + + // CASE 2: SINGLE ITEM + return ( + + {/* 3. STICKY BODY KOLOM PERTAMA (SINGLE) */} + + + {product.variants.map(v => { + const rawValue = row.values[v.id] || '-'; + return ( + + ); + })} + + ); + })} + +
+ Spesifikasi + + {isSingleVariant ? 'Detail' : (v.attributes && v.attributes.length > 0 ? v.attributes.join(' - ') : v.code)} +
+ {row.label} + +
+
+ {row.children.map((child: any, idx: number) => { + const rawVal = child.values[v.id]; + if (!rawVal || rawVal === '-') return null; + + return ( +
+ + {child.label}: + + {renderSpecValue(rawVal)} +
+ ); + })} +
+
+
+ {row.label} + + {renderSpecValue(rawValue)} +
+ ); + })() + ) : ( + Spesifikasi teknis belum tersedia. + )}
-- cgit v1.2.3 From ddc308aa4e5da482763f97e16ab92fe777e8dfeb Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 23 Dec 2025 14:48:48 +0700 Subject: (andri) fix scrolltoindex --- src-migrate/modules/product-detail/components/ProductDetail.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 1c9c39e0..82b99697 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -312,10 +312,9 @@ const ProductDetail = ({ product }: Props) => { }; const scrollToIndex = (i: number) => { - const el = sliderRef.current; - if (!el) return; - const elRef = sliderRef.current; - elRef.scrollTo({ left: i * elRef.clientWidth, behavior: 'smooth' }); + const el = sliderRef.current; + if (!el) return; + el.scrollTo({ left: i * el.clientWidth, behavior: 'smooth' }); setCurrentIdx(i); setMainImage(allImages[i] || ''); }; -- cgit v1.2.3 From fd50180ac301916c6feb5e93598f3fc59ecb78a8 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 5 Jan 2026 10:41:39 +0700 Subject: (andri) get desc variant product from magento --- .../modules/product-detail/components/ProductDetail.tsx | 11 ++++++++++- src/pages/api/magento-product.ts | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 82b99697..4cc082d8 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -71,6 +71,7 @@ const ProductDetail = ({ product }: Props) => { const [specsMatrix, setSpecsMatrix] = useState([]); const [upsellIds, setUpsellIds] = useState([]); const [relatedIds, setRelatedIds] = useState([]); + const [descriptionMap, setDescriptionMap] = useState>({}); const [loadingSpecs, setLoadingSpecs] = useState(false); @@ -174,6 +175,10 @@ const ProductDetail = ({ product }: Props) => { setSpecsMatrix([]); } + if (data.descriptions){ + setDescriptionMap(data.descriptions); + } + // 2. Upsell & Related if (data.upsell_ids && Array.isArray(data.upsell_ids)) setUpsellIds(data.upsell_ids); else setUpsellIds([]); @@ -319,6 +324,10 @@ const ProductDetail = ({ product }: Props) => { setMainImage(allImages[i] || ''); }; + const activeMagentoDesc = selectedVariant?.id ? descriptionMap[String(selectedVariant.id)] : ''; + const finalDescription = activeMagentoDesc || product.description || 'Deskripsi produk tidak tersedia.'; + const cleanDescription = finalDescription === '


' ? 'Deskripsi produk tidak tersedia.' : finalDescription; + return ( <>
@@ -455,7 +464,7 @@ const ProductDetail = ({ product }: Props) => { {/* DESKRIPSI */}
-

' ? '

Lorem ipsum dolor sit amet.

' : product.description, }} /> +
diff --git a/src/pages/api/magento-product.ts b/src/pages/api/magento-product.ts index 297f0ebc..ef5fd3bc 100644 --- a/src/pages/api/magento-product.ts +++ b/src/pages/api/magento-product.ts @@ -103,6 +103,15 @@ export default async function handler( matrix.push(row); }); + // Deskripsi produk per varian + const descriptions:Record = {}; + items.forEach((p: any) => { + const descAttr = p.custom_attributes.find((a: any) => a.attribute_code === 'description' || a.attribute_code === 'short_description'); + descriptions[p.sku] = descAttr ? descAttr.value : ''; + }); + + + // ===================================================================== // 3. AMBIL LINKS (UPSELL & RELATED) DARI MAIN VARIANT SAJA // ===================================================================== @@ -126,7 +135,8 @@ export default async function handler( res.status(200).json({ specsMatrix: matrix, upsell_ids: upsellIds, - related_ids: relatedIds + related_ids: relatedIds, + descriptions: descriptions }); } catch (error) { -- cgit v1.2.3 From 4c9f6b642871f180de70f38e6c05c88c234b7d32 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 5 Jan 2026 11:01:11 +0700 Subject: (andri) add garansi --- src-migrate/modules/product-detail/components/Information.tsx | 9 +++++++-- src/pages/api/magento-product.ts | 9 ++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index b7d3401e..4d54c03c 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -273,7 +273,7 @@ const Information = ({ product }: Props) => { - {/* 3. Garansi Produk */} + {/* 3. Garansi Produk - UPDATED DYNAMIC VALUE */} { /> Garansi Produk - 24 Bulan + + {/* Mengambil dari selectedVariant.z_warranty */} + {selectedVariant && (selectedVariant as any).z_warranty + ? (selectedVariant as any).z_warranty + : '-'} + diff --git a/src/pages/api/magento-product.ts b/src/pages/api/magento-product.ts index ef5fd3bc..4e963844 100644 --- a/src/pages/api/magento-product.ts +++ b/src/pages/api/magento-product.ts @@ -109,6 +109,12 @@ export default async function handler( const descAttr = p.custom_attributes.find((a: any) => a.attribute_code === 'description' || a.attribute_code === 'short_description'); descriptions[p.sku] = descAttr ? descAttr.value : ''; }); + + const warranties: Record = {}; + items.forEach((p: any) => { + const warAttr = p.custom_attributes.find((a: any) => a.attribute_code === 'z_warranty'); + warranties[p.sku] = warAttr ? warAttr.value : ''; + }); @@ -136,7 +142,8 @@ export default async function handler( specsMatrix: matrix, upsell_ids: upsellIds, related_ids: relatedIds, - descriptions: descriptions + descriptions: descriptions, + warranties: warranties, }); } catch (error) { -- cgit v1.2.3 From c0c09e2d948e04f487641dc0141cc38523fe0b69 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 5 Jan 2026 13:43:48 +0700 Subject: (andri) fix garansi --- .../product-detail/components/Information.tsx | 59 +++++++++++++++++++--- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 4d54c03c..25ca3709 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -9,6 +9,7 @@ import style from '../styles/information.module.css'; import dynamic from 'next/dynamic'; import Link from 'next/link'; import { useEffect, useRef, useState } from 'react'; +import axios from 'axios'; // <--- 1. TAMBAHAN IMPORT AXIOS import currencyFormat from '@/core/utils/currencyFormat'; import { InputGroup, InputRightElement, SimpleGrid, Flex, Text, Box } from '@chakra-ui/react'; @@ -44,6 +45,44 @@ const Information = ({ product }: Props) => { const variantId = selectedVariant?.id; const { slaVariant, isLoading } = useVariant({ variantId }); + const [warranties, setWarranties] = useState>({}); + const [loadingWarranty, setLoadingWarranty] = useState(false); + + useEffect(() => { + const fetchWarrantyDirectly = async () => { + if (!product?.variants || product.variants.length === 0) return; + + setLoadingWarranty(true); + try { + // Ambil semua SKU untuk dikirim ke API + const skus = product.variants.map((v) => v.id).join(','); + const mainSku = product.variants[0].id; + console.log("Fetching warranties for SKUs:", skus); + console.log("Main SKU:", mainSku); + + // Panggil API magento-product + const res = await axios.get('/api/magento-product', { + params: { skus, main_sku: mainSku } + }); + + // Simpan hasil ke state lokal + if (res.data && res.data.warranties) { + setWarranties(res.data.warranties); + } + console.log("Warranties API Response:", res); + console.log("Warranties fetched:", res.data.warranties); + } catch (error) { + console.error("Gagal ambil garansi:", error); + } finally { + setLoadingWarranty(false); + } + }; + + fetchWarrantyDirectly(); + }, [product]); + // ====================================================== + + useEffect(() => { if (selectedVariant) { setInputValue( @@ -239,7 +278,6 @@ const Information = ({ product }: Props) => {

Detail Informasi Produk

- {/* Perubahan: Spacing diperbesar menjadi 10 agar estimasi bergeser ke kanan */} {/* 1. Distributor Resmi */} @@ -273,7 +311,7 @@ const Information = ({ product }: Props) => { - {/* 3. Garansi Produk - UPDATED DYNAMIC VALUE */} + {/* 3. Garansi Produk */} { /> Garansi Produk - - {/* Mengambil dari selectedVariant.z_warranty */} - {selectedVariant && (selectedVariant as any).z_warranty - ? (selectedVariant as any).z_warranty - : '-'} - + + {/* Menggunakan Loading State & Data dari 'warranties' */} + {loadingWarranty ? ( + + ) : ( + + {selectedVariant && warranties[selectedVariant.id] + ? warranties[selectedVariant.id] + : '-'} + + )} -- cgit v1.2.3 From 44afa589c9ced88aa4dcd93181d9e068d79603ea Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 6 Jan 2026 11:21:14 +0700 Subject: (andri) fix sort pilih variant --- .../product-detail/components/Information.tsx | 35 +++++++++++----------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 25ca3709..c565682f 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -103,23 +103,20 @@ const Information = ({ product }: Props) => { } }, [slaVariant, isLoading, setSla]); - const handleOnChange = (vals: any) => { - setDisableFilter(true); - let code = vals.replace(/\s-\s.*$/, '').trim(); - let variant = variantOptions.find((item) => item.code === code); - setSelectedVariant(variant); - setInputValue( - variant?.code + - (variant?.attributes[0] ? ' - ' + variant?.attributes[0] : '') - ); +const handleOnChange = (vals: any) => { +    setDisableFilter(true); +    let code = vals.replace(/\s-\s.*$/, '').trim(); +    let variant = product?.variants.find((item) => item.code === code); +    if (variant) { - const filteredOptions = product?.variants.filter( - (item) => item !== variant - ); - const newOptions = [variant, ...filteredOptions]; - setVariantOptions(newOptions); - } - }; +    setSelectedVariant(variant); +    setInputValue( +      variant?.code + +        (variant?.attributes[0] ? ' - ' + variant?.attributes[0] : '') +    ); + setVariantOptions(product?.variants); +    } +  }; const handleOnKeyUp = (e: any) => { setDisableFilter(false); @@ -166,7 +163,11 @@ const Information = ({ product }: Props) => { - {variantOptions.map((option, cid) => ( + {variantOptions + .sort((a: any, b: any) => { + return a.code.localeCompare(b.code, undefined, { numeric: true, sensitivity: 'base' }); + }) + .map((option, cid) => ( Date: Thu, 8 Jan 2026 10:08:55 +0700 Subject: (andri) fix table spec desktop --- .../product-detail/components/ProductDetail.tsx | 204 ++++++++++++--------- 1 file changed, 116 insertions(+), 88 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 4cc082d8..3a4d9c7e 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -468,119 +468,147 @@ const ProductDetail = ({ product }: Props) => {
- {/* SPESIFIKASI (LOGIKA GROUPING + RATA TENGAH + STICKY FIRST COLUMN) */} + {/* SPESIFIKASI */} - + {loadingSpecs ? (
) : specsMatrix.length > 0 ? ( (() => { - const isSingleVariant = product.variants.length === 1; - const globalAlign = isSingleVariant ? "left" : "center"; + const topHeaders: any[] = []; + const subHeaders: any[] = []; + const flatSpecs: any[] = []; + + specsMatrix.forEach(row => { + if (row.type === 'group') { + topHeaders.push({ + label: row.label, + type: 'group', + colSpan: row.children.length, + rowSpan: 1 + }); + row.children.forEach((child: any) => { + subHeaders.push(child); + flatSpecs.push(child); + }); + } else { + topHeaders.push({ + label: row.label, + type: 'single', + colSpan: 1, + rowSpan: 2 + }); + flatSpecs.push(row); + } + }); return ( - + - {/* 1. STICKY HEADER KOLOM PERTAMA */} + + {topHeaders.map((th, idx) => ( + - {product.variants.map(v => ( - + ))} + + + {subHeaders.map((sub, idx) => ( + ))} + - {specsMatrix.map((row, i) => { - - // CASE 1: GROUPING - if (row.type === 'group') { - return ( - - {/* 2. STICKY BODY KOLOM PERTAMA (GROUP) */} + {product.variants.map((v, vIdx) => ( + + + + {flatSpecs.map((spec, sIdx) => { + const rawValue = spec.values[v.id] || '-'; + return ( - - {product.variants.map(v => ( - - ))} - - ); - } - - // CASE 2: SINGLE ITEM - return ( - - {/* 3. STICKY BODY KOLOM PERTAMA (SINGLE) */} - - - {product.variants.map(v => { - const rawValue = row.values[v.id] || '-'; - return ( - - ); - })} - - ); - })} + ); + })} + + ))}
+ Tipe / Variant + - Spesifikasi - - {isSingleVariant ? 'Detail' : (v.attributes && v.attributes.length > 0 ? v.attributes.join(' - ') : v.code)} + > + {th.label} +
+ {sub.label}
+ {v.attributes && v.attributes.length > 0 ? v.attributes.join(' - ') : v.code} + - {row.label} + {renderSpecValue(rawValue)} -
-
- {row.children.map((child: any, idx: number) => { - const rawVal = child.values[v.id]; - if (!rawVal || rawVal === '-') return null; - - return ( -
- - {child.label}: - - {renderSpecValue(rawVal)} -
- ); - })} -
-
-
- {row.label} - - {renderSpecValue(rawValue)} -
); -- cgit v1.2.3 From 8004947c70f9e963f942aed75e6e99c7c6e82b65 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 8 Jan 2026 13:16:35 +0700 Subject: (andri) add minmax width td body --- .../modules/product-detail/components/ProductDetail.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 3a4d9c7e..a18e70bb 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -535,6 +535,7 @@ const ProductDetail = ({ product }: Props) => { position="sticky" left={0} top={0} zIndex={21} bg="red.600" rowSpan={2} width="25%" + minW="100px" borderColor="whiteAlpha.300" color="white" fontSize="sm" textTransform="none" verticalAlign="middle" boxShadow="2px 0 5px -2px rgba(0,0,0,0.1)" @@ -584,15 +585,20 @@ const ProductDetail = ({ product }: Props) => { {product.variants.map((v, vIdx) => ( + {/* 1. KOLOM JUDUL VARIANT (Sticky Kiri) */} {v.attributes && v.attributes.length > 0 ? v.attributes.join(' - ') : v.code} - + {/* 2. KOLOM DATA SPESIFIKASI */} {flatSpecs.map((spec, sIdx) => { const rawValue = spec.values[v.id] || '-'; return ( @@ -602,6 +608,12 @@ const ProductDetail = ({ product }: Props) => { textAlign="center" fontSize="sm" verticalAlign="middle" + px={2} + py={3} + minW="100px" + maxW="150px" + whiteSpace="normal" + overflowWrap="break-word" > {renderSpecValue(rawValue)} -- cgit v1.2.3 From 3a93af1298a725f4f399659be88bac15b8a7ca82 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 8 Jan 2026 13:20:21 +0700 Subject: fix --- src-migrate/modules/product-detail/components/ProductDetail.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index a18e70bb..2652106c 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -538,9 +538,10 @@ const ProductDetail = ({ product }: Props) => { minW="100px" borderColor="whiteAlpha.300" color="white" fontSize="sm" textTransform="none" verticalAlign="middle" + textAlign="center" boxShadow="2px 0 5px -2px rgba(0,0,0,0.1)" > - Tipe / Variant + Variant {topHeaders.map((th, idx) => ( @@ -593,6 +594,7 @@ const ProductDetail = ({ product }: Props) => { verticalAlign="middle" boxShadow="2px 0 5px -2px rgba(0,0,0,0.05)" minW="100px" + textAlign="center" whiteSpace="normal" py={4} > -- cgit v1.2.3 From e7faa6bb5dc33f576cb53cd1f4716bf2c6b03571 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 8 Jan 2026 13:27:25 +0700 Subject: (andri) hide brand in specs --- src-migrate/modules/product-detail/components/ProductDetail.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 2652106c..a98cb0a4 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -169,8 +169,13 @@ const ProductDetail = ({ product }: Props) => { // 1. Specs Matrix (Processed Grouping) if (data.specsMatrix && Array.isArray(data.specsMatrix)) { - const processed = processMatrixData(data.specsMatrix); - setSpecsMatrix(processed); + const filteredMatrix = data.specsMatrix.filter((item: any) => { + const code = item.code || ''; + return !code.includes('z_brand'); + }); + + const processed = processMatrixData(filteredMatrix); + setSpecsMatrix(processed); } else { setSpecsMatrix([]); } -- cgit v1.2.3 From cc12fccc972dc4f5533a05742090bd0f1c5a3d4b Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Thu, 8 Jan 2026 14:42:37 +0700 Subject: (andri) fix sort table spec --- .../product-detail/components/ProductDetail.tsx | 37 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index a98cb0a4..387a7e5f 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -2,7 +2,8 @@ import style from '../styles/product-detail.module.css'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { useEffect, useRef, useState, UIEvent } from 'react'; +// Import useMemo +import { useEffect, useRef, useState, UIEvent, useMemo } from 'react'; // Import komponen Chakra UI import { @@ -329,6 +330,37 @@ const ProductDetail = ({ product }: Props) => { setMainImage(allImages[i] || ''); }; + const sortedVariants = useMemo(() => { + if (!product?.variants) return []; + + return [...product.variants].sort((a, b) => { + const labelA = a.attributes && a.attributes.length > 0 + ? a.attributes.join(' - ') + : a.code || ''; + + const labelB = b.attributes && b.attributes.length > 0 + ? b.attributes.join(' - ') + : b.code || ''; + + const getNumber = (str: string) => { + const match = String(str).match(/(\d+(\.\d+)?)/); + return match ? parseFloat(match[0]) : null; + }; + + const numA = getNumber(labelA); + const numB = getNumber(labelB); + + if (numA !== null && numB !== null && numA !== numB) { + return numA - numB; + } + + return String(labelA).localeCompare(String(labelB), undefined, { + numeric: true, + sensitivity: 'base' + }); + }); + }, [product.variants]); + const activeMagentoDesc = selectedVariant?.id ? descriptionMap[String(selectedVariant.id)] : ''; const finalDescription = activeMagentoDesc || product.description || 'Deskripsi produk tidak tersedia.'; const cleanDescription = finalDescription === '


' ? 'Deskripsi produk tidak tersedia.' : finalDescription; @@ -589,7 +621,8 @@ const ProductDetail = ({ product }: Props) => { - {product.variants.map((v, vIdx) => ( + {/* MENGGUNAKAN sortedVariants HASIL REVISI */} + {sortedVariants.map((v, vIdx) => ( {/* 1. KOLOM JUDUL VARIANT (Sticky Kiri) */} Date: Fri, 9 Jan 2026 10:33:14 +0700 Subject: (andri) ganti sticky variant kolom pertama menjadi attribute code pertama dari magento --- .../product-detail/components/ProductDetail.tsx | 58 ++++++++++------------ 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 387a7e5f..983cb843 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -567,27 +567,20 @@ const ProductDetail = ({ product }: Props) => { return ( + {/* Baris 1: Header Utama */} - - {topHeaders.map((th, idx) => ( ))} + + {/* Baris 2: Sub Header */} {subHeaders.map((sub, idx) => ( - {/* MENGGUNAKAN sortedVariants HASIL REVISI */} {sortedVariants.map((v, vIdx) => ( - {/* 1. KOLOM JUDUL VARIANT (Sticky Kiri) */} - - {/* 2. KOLOM DATA SPESIFIKASI */} {flatSpecs.map((spec, sIdx) => { const rawValue = spec.values[v.id] || '-'; + const isFirstCol = sIdx === 0; return ( -- cgit v1.2.3 From bab9bfaeeab2ba13bba1d7ed02e25b5384919bf9 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 9 Jan 2026 10:57:14 +0700 Subject: (andri) fix sticky header --- .../product-detail/components/ProductDetail.tsx | 46 ++++++++++++---------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 983cb843..5a2ad093 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -572,10 +572,9 @@ const ProductDetail = ({ product }: Props) => { {topHeaders.map((th, idx) => ( - {subHeaders.map((sub, idx) => ( - - ))} + {subHeaders.map((sub, idx) => { + const isFirstHeaderGroup = topHeaders[0]?.type === 'group'; + const shouldSticky = idx === 0 && isFirstHeaderGroup; + return ( + + ); + })} -- cgit v1.2.3 From 6c2de1aa1cc655b1da525015ac0280fd4c72731f Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Fri, 9 Jan 2026 14:29:26 +0700 Subject: border bottom none --- src-migrate/modules/product-detail/components/ProductDetail.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 5a2ad093..7ca2f9a7 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -587,6 +587,7 @@ const ProductDetail = ({ product }: Props) => { fontWeight="800" letterSpacing="wide" verticalAlign="middle" + borderBottom="none" > {th.label} -- cgit v1.2.3
- Variant - {
{
- {v.attributes && v.attributes.length > 0 ? v.attributes.join(' - ') : v.code} - {renderSpecValue(rawValue)} { {/* Baris 2: Sub Header */}
- {sub.label} - + {sub.label} +