From ab4819716063f4d7eb4578cbcb0d66077145bdab Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 12 Jan 2026 13:33:39 +0700 Subject: (andri) fix index table spek & fix view detail information di mobile --- .../product-detail/components/Information.tsx | 88 +++++++++-------- .../product-detail/components/ProductDetail.tsx | 106 +++++++++++++-------- 2 files changed, 114 insertions(+), 80 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index c565682f..e07537fb 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -9,10 +9,10 @@ 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 axios from 'axios'; import currencyFormat from '@/core/utils/currencyFormat'; -import { InputGroup, InputRightElement, SimpleGrid, Flex, Text, Box } from '@chakra-ui/react'; +import { InputGroup, InputRightElement, SimpleGrid, Flex, Text, Box, Center } from '@chakra-ui/react'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; import Image from 'next/image'; import { formatToShortText } from '~/libs/formatNumber'; @@ -54,23 +54,16 @@ const Information = ({ product }: Props) => { 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 { @@ -80,8 +73,6 @@ const Information = ({ product }: Props) => { fetchWarrantyDirectly(); }, [product]); - // ====================================================== - useEffect(() => { if (selectedVariant) { @@ -103,27 +94,26 @@ const Information = ({ product }: Props) => { } }, [slaVariant, isLoading, setSla]); -const handleOnChange = (vals: any) => { -    setDisableFilter(true); -    let code = vals.replace(/\s-\s.*$/, '').trim(); -    let variant = product?.variants.find((item) => item.code === code); -    + const handleOnChange = (vals: any) => { + setDisableFilter(true); + let code = vals.replace(/\s-\s.*$/, '').trim(); + let variant = product?.variants.find((item) => item.code === code); + if (variant) { -    setSelectedVariant(variant); -    setInputValue( -      variant?.code + -        (variant?.attributes[0] ? ' - ' + variant?.attributes[0] : '') -    ); + setSelectedVariant(variant); + setInputValue( + variant?.code + + (variant?.attributes[0] ? ' - ' + variant?.attributes[0] : '') + ); setVariantOptions(product?.variants); -    } -  }; + } + }; const handleOnKeyUp = (e: any) => { setDisableFilter(false); setInputValue(e.target.value); }; - // STYLE CUSTOM UNTUK BARIS (Item Code, dll) const rowStyle = { backgroundColor: '#ffffff', fontSize: '13px', @@ -275,37 +265,49 @@ const handleOnChange = (vals: any) => { - {/* === DETAIL INFORMASI PRODUK (Updated Layout) === */} + {/* === DETAIL INFORMASI PRODUK (Horizontal Minimalis) === */}

Detail Informasi Produk

- + {/* Mobile: 3 Kolom, Spacing Kecil. Desktop: 3 Kolom, Spacing Besar */} + + {/* 1. Distributor Resmi */} - + Distributor Resmi - Distributor Resmi - Jaminan Produk Asli + Distributor Resmi + Jaminan Produk Asli {/* 2. Estimasi Penyiapan */} - + Estimasi Penyiapan - Estimasi Penyiapan + Estimasi Penyiapan {isLoading ? ( - +
) : ( - + {sla?.sla_date || '3 - 7 Hari'} )} @@ -313,20 +315,24 @@ const handleOnChange = (vals: any) => {
{/* 3. Garansi Produk */} - + Garansi Produk - Garansi Produk + Garansi Produk - {/* Menggunakan Loading State & Data dari 'warranties' */} {loadingWarranty ? ( - +
) : ( - + {selectedVariant && warranties[selectedVariant.id] ? warranties[selectedVariant.id] : '-'} diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 7ca2f9a7..04bf5533 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -60,6 +60,45 @@ const RWebShare = dynamic( () => import('react-web-share').then((m) => m.RWebShare), { ssr: false } ); +// 1. STYLE DESKTOP (Tebal, Jelas, dengan Border/Padding) +const cssScrollbarDesktop = { + '&::-webkit-scrollbar': { + width: '10px', + height: '10px', + }, + '&::-webkit-scrollbar-track': { + background: '#f1f1f1', + borderRadius: '4px', + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: '#9ca3af', // Gray-400 + borderRadius: '6px', + border: '2px solid #f1f1f1', // Efek padding + }, + '&::-webkit-scrollbar-thumb:hover': { + backgroundColor: '#6b7280', + }, +}; + +// 2. STYLE MOBILE (Tipis, Minimalis, Tanpa Border) +const cssScrollbarMobile = { + '&::-webkit-scrollbar': { + width: '8px', // Sangat tipis vertikal + height: '8px', // Sangat tipis horizontal + }, + '&::-webkit-scrollbar-track': { + background: '#f1f1f1', + borderRadius: '4px', + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: '#9ca3af', // Gray-400 + borderRadius: '6px', + border: '2px solid #f1f1f1', // Efek padding + }, + '&::-webkit-scrollbar-thumb:hover': { + backgroundColor: '#6b7280', + }, +}; const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST; @@ -494,7 +533,7 @@ const ProductDetail = ({ product }: Props) => { Deskripsi Spesifikasi - Detail Lainnya + {/* Detail Lainnya */} @@ -514,29 +553,13 @@ const ProductDetail = ({ product }: Props) => { overflowX="auto" overflowY="auto" maxHeight="500px" - css={{ - '&::-webkit-scrollbar': { - width: '12px', - height: '12px', - }, - '&::-webkit-scrollbar-track': { - background: '#f1f1f1', - }, - '&::-webkit-scrollbar-thumb': { - backgroundColor: '#a0aec0', - borderRadius: '8px', - border: '4px solid transparent', - backgroundClip: 'content-box', - }, - '&::-webkit-scrollbar-thumb:hover': { - backgroundColor: '#718096', - }, - }} + css={isMobile ? cssScrollbarMobile : cssScrollbarDesktop} > {loadingSpecs ? (
) : specsMatrix.length > 0 ? ( (() => { + // --- 1. PROSES DATA --- const topHeaders: any[] = []; const subHeaders: any[] = []; const flatSpecs: any[] = []; @@ -564,17 +587,19 @@ const ProductDetail = ({ product }: Props) => { } }); + // --- 2. RENDER TABLE (Z-INDEX DITURUNKAN) --- return ( - - - {/* Baris 1: Header Utama */} +
+ {/* Turun dari 20 ke 3 */} + {/* Row 1: Header Utama */} {topHeaders.map((th, idx) => ( ))} - - {/* Baris 2: Sub Header */} + + {/* Row 2: Sub Header */} - {subHeaders.map((sub, idx) => { + {subHeaders.map((sub, idx) => { const isFirstHeaderGroup = topHeaders[0]?.type === 'group'; const shouldSticky = idx === 0 && isFirstHeaderGroup; return ( @@ -622,35 +651,34 @@ const ProductDetail = ({ product }: Props) => { })} - + {sortedVariants.map((v, vIdx) => ( {flatSpecs.map((spec, sIdx) => { const rawValue = spec.values[v.id] || '-'; - const isFirstCol = sIdx === 0; + const isFirstCol = sIdx === 0; return ( @@ -669,7 +697,7 @@ const ProductDetail = ({ product }: Props) => { {/* DETAIL LAINNYA */} -

Informasi tambahan belum tersedia.

+ {/*

Informasi tambahan belum tersedia.

*/} -- cgit v1.2.3 From 0befdef251386bd026d53721e5cb2173aebd41f2 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 12 Jan 2026 15:28:52 +0700 Subject: (andri) fix detail informasi mobile --- src-migrate/modules/product-detail/components/Information.tsx | 4 +++- src-migrate/modules/product-detail/components/ProductDetail.tsx | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index e07537fb..6e2c930e 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -267,7 +267,9 @@ const Information = ({ product }: Props) => { {/* === DETAIL INFORMASI PRODUK (Horizontal Minimalis) === */}
-

Detail Informasi Produk

+

+ Detail Informasi Produk +

{/* Mobile: 3 Kolom, Spacing Kecil. Desktop: 3 Kolom, Spacing Besar */} diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 04bf5533..fbfe262d 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -508,19 +508,19 @@ const ProductDetail = ({ product }: Props) => {

{product.name}

-
+
)}
{isMobile && ( -
+
)} -
+
{!!activeVariantId && !isApproval && ( )} -- cgit v1.2.3 From 8033f86a4c87241a6cdc24f84772754ee3719145 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 12 Jan 2026 15:37:11 +0700 Subject: (andri) fix table specs jika variannya hanya ada q --- .../product-detail/components/ProductDetail.tsx | 106 +++++++++++++-------- 1 file changed, 65 insertions(+), 41 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index fbfe262d..7b166942 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -2,7 +2,6 @@ import style from '../styles/product-detail.module.css'; import Link from 'next/link'; import { useRouter } from 'next/router'; -// Import useMemo import { useEffect, useRef, useState, UIEvent, useMemo } from 'react'; // Import komponen Chakra UI @@ -60,6 +59,7 @@ const RWebShare = dynamic( () => import('react-web-share').then((m) => m.RWebShare), { ssr: false } ); + // 1. STYLE DESKTOP (Tebal, Jelas, dengan Border/Padding) const cssScrollbarDesktop = { '&::-webkit-scrollbar': { @@ -83,20 +83,15 @@ const cssScrollbarDesktop = { // 2. STYLE MOBILE (Tipis, Minimalis, Tanpa Border) const cssScrollbarMobile = { '&::-webkit-scrollbar': { - width: '8px', // Sangat tipis vertikal - height: '8px', // Sangat tipis horizontal + width: '3px', // Sangat tipis vertikal + height: '3px', // Sangat tipis horizontal }, - '&::-webkit-scrollbar-track': { - background: '#f1f1f1', - borderRadius: '4px', + '&::-webkit-scrollbar-track': { + background: 'transparent', }, '&::-webkit-scrollbar-thumb': { - backgroundColor: '#9ca3af', // Gray-400 - borderRadius: '6px', - border: '2px solid #f1f1f1', // Efek padding - }, - '&::-webkit-scrollbar-thumb:hover': { - backgroundColor: '#6b7280', + backgroundColor: '#cbd5e1', // Gray-300 + borderRadius: '3px', }, }; @@ -244,14 +239,13 @@ const ProductDetail = ({ product }: Props) => { }, [product.id]); // ========================================================================= - // HELPER 1: GROUPING DATA BY LABEL (Separator ':') + // HELPER 1: GROUPING DATA BY LABEL // ========================================================================= const processMatrixData = (rawMatrix: any[]) => { const groups: any = {}; const result: any[] = []; rawMatrix.forEach(item => { - // Cek Label: "Group Name : Sub Label" if (item.label && item.label.includes(' : ')) { const parts = item.label.split(' : '); const groupName = parts[0].trim(); @@ -268,7 +262,7 @@ const ProductDetail = ({ product }: Props) => { groups[groupName].children.push({ ...item, - label: childLabel // Override label jadi pendek + label: childLabel }); } else { @@ -287,7 +281,6 @@ const ProductDetail = ({ product }: Props) => { if (!val) return '-'; const strVal = String(val).trim(); - // URL Link const isUrl = !strVal.includes(' ') && ( strVal.startsWith('http') || strVal.startsWith('www.') @@ -306,7 +299,6 @@ const ProductDetail = ({ product }: Props) => { ); } - // HTML if (strVal.includes('<') && strVal.includes('>')) { return (
{ ); } - // Teks Biasa return strVal; }; @@ -533,7 +524,6 @@ const ProductDetail = ({ product }: Props) => { Deskripsi Spesifikasi - {/* Detail Lainnya */} @@ -559,7 +549,55 @@ const ProductDetail = ({ product }: Props) => {
) : specsMatrix.length > 0 ? ( (() => { - // --- 1. PROSES DATA --- + const variantCount = sortedVariants.length; + const isSingleVariant = variantCount === 1; + + // === LOGIC 1: SINGLE VARIANT (VERTICAL TABLE) === + if (isSingleVariant) { + const singleVariantId = sortedVariants[0].id; + // Flatten data untuk list vertical + const rows: any[] = []; + specsMatrix.forEach(row => { + if (row.type === 'group') { + row.children.forEach((child: any) => rows.push(child)); + } else { + rows.push(row); + } + }); + + return ( +
4, dan 20 -> 3 boxShadow={idx === 0 ? "2px 0 5px -2px rgba(0,0,0,0.2)" : "none"} bg="red.600" @@ -582,29 +607,31 @@ const ProductDetail = ({ product }: Props) => { rowSpan={th.rowSpan} color="white" textAlign="center" - fontSize="sm" + fontSize={isMobile ? "xs" : "sm"} textTransform="none" fontWeight="800" letterSpacing="wide" verticalAlign="middle" borderBottom="none" + px={isMobile ? 2 : 4} > {th.label}
4 boxShadow={shouldSticky ? "2px 0 5px -2px rgba(0,0,0,0.2)" : "none"} color="white" @@ -614,7 +641,9 @@ const ProductDetail = ({ product }: Props) => { verticalAlign="middle" whiteSpace="nowrap" bg="red.600" + borderBottom="none" pt={1} pb={1} + px={isMobile ? 2 : 4} > {sub.label}
2 bg={vIdx % 2 === 0 ? 'white' : 'gray.50'} boxShadow={isFirstCol ? "2px 0 5px -2px rgba(0,0,0,0.1)" : "none"} - // ================================= borderColor="gray.200" textAlign="center" - fontSize="sm" + fontSize={isMobile ? "xs" : "sm"} verticalAlign="middle" - px={2} + px={isMobile ? 1 : 2} py={3} - minW="100px" - maxW="150px" + minW={isMobile ? "100px" : "120px"} + maxW="200px" whiteSpace="normal" overflowWrap="break-word" - fontWeight={isFirstCol ? "bold" : "normal"} + fontWeight={isFirstCol ? "bold" : "normal"} > {renderSpecValue(rawValue)}
+ + {rows.map((row, idx) => ( + + {/* Kolom Label (Kiri) */} + + {/* Kolom Value (Kanan) */} + + + ))} + +
+ {row.label} + + {renderSpecValue(row.values[singleVariantId])} +
+ ); + } + + // === LOGIC 2: MULTIPLE VARIANTS (MATRIX TABLE HORIZONTAL) === const topHeaders: any[] = []; const subHeaders: any[] = []; const flatSpecs: any[] = []; @@ -587,21 +625,17 @@ const ProductDetail = ({ product }: Props) => { } }); - // --- 2. RENDER TABLE (Z-INDEX DITURUNKAN) --- return ( - {/* Turun dari 20 ke 3 */} - {/* Row 1: Header Utama */} + {topHeaders.map((th, idx) => ( ))} - - {/* Row 2: Sub Header */} {subHeaders.map((sub, idx) => { - const isFirstHeaderGroup = topHeaders[0]?.type === 'group'; - const shouldSticky = idx === 0 && isFirstHeaderGroup; - return ( + const isFirstHeaderGroup = topHeaders[0]?.type === 'group'; + const shouldSticky = idx === 0 && isFirstHeaderGroup; + return (
4, dan 20 -> 3 + zIndex={idx === 0 ? 4 : 3} boxShadow={idx === 0 ? "2px 0 5px -2px rgba(0,0,0,0.2)" : "none"} - bg="red.600" colSpan={th.colSpan} rowSpan={th.rowSpan} @@ -619,21 +653,17 @@ const ProductDetail = ({ product }: Props) => {
4 + zIndex={shouldSticky ? 4 : 1} boxShadow={shouldSticky ? "2px 0 5px -2px rgba(0,0,0,0.2)" : "none"} - color="white" textAlign="center" fontSize="xs" @@ -641,7 +671,6 @@ const ProductDetail = ({ product }: Props) => { verticalAlign="middle" whiteSpace="nowrap" bg="red.600" - borderBottom="none" pt={1} pb={1} px={isMobile ? 2 : 4} > @@ -661,13 +690,11 @@ const ProductDetail = ({ product }: Props) => { return ( 2 + zIndex={isFirstCol ? 2 : 1} bg={vIdx % 2 === 0 ? 'white' : 'gray.50'} boxShadow={isFirstCol ? "2px 0 5px -2px rgba(0,0,0,0.1)" : "none"} - borderColor="gray.200" textAlign="center" fontSize={isMobile ? "xs" : "sm"} @@ -695,9 +722,6 @@ const ProductDetail = ({ product }: Props) => { )} - - {/* DETAIL LAINNYA */} - {/*

Informasi tambahan belum tersedia.

*/} -- cgit v1.2.3 From bdc571812ce174ff218c77bed0b5e06bf31e3196 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Mon, 12 Jan 2026 15:54:42 +0700 Subject: (andri) check nilai suatu attribute + bersihkan tanda petik --- src/pages/api/magento-product.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/pages/api/magento-product.ts b/src/pages/api/magento-product.ts index 4e963844..f61daf69 100644 --- a/src/pages/api/magento-product.ts +++ b/src/pages/api/magento-product.ts @@ -87,20 +87,31 @@ export default async function handler( // Susun Matrix // Struktur: { code, label, values: { [sku]: value } } const matrix: any[] = []; + Array.from(allAttributeCodes).forEach((code) => { const row: any = { code: code, label: labelsMap[code], - values: {} // Key = SKU/ID Variant, Value = Isi Atribut + values: {} }; + let hasData = false; + items.forEach((p: any) => { const attr = p.custom_attributes.find((a: any) => a.attribute_code === code); - // Simpan value berdasarkan SKU (ID Variant dari Odoo) - row.values[p.sku] = attr ? attr.value : '-'; + let rawVal = attr && attr.value !== null ? String(attr.value).trim() : ''; + if (rawVal.length >= 2 && rawVal.startsWith('"') && rawVal.endsWith('"')) { + rawVal = rawVal.slice(1, -1).trim(); + } + if (rawVal !== '' && rawVal !== '-') { + hasData = true; + } + row.values[p.sku] = rawVal; }); - matrix.push(row); + if (hasData) { + matrix.push(row); + } }); // Deskripsi produk per varian -- cgit v1.2.3 From 2843980c8bfe25d67bd70e176c546b5e2fe66975 Mon Sep 17 00:00:00 2001 From: FIN-IT_AndriFP Date: Tue, 13 Jan 2026 16:29:58 +0700 Subject: on brand --- .../modules/product-detail/components/ProductDetail.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 7b166942..05b84260 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -204,13 +204,14 @@ const ProductDetail = ({ product }: Props) => { // 1. Specs Matrix (Processed Grouping) if (data.specsMatrix && Array.isArray(data.specsMatrix)) { - const filteredMatrix = data.specsMatrix.filter((item: any) => { - const code = item.code || ''; - return !code.includes('z_brand'); - }); - - const processed = processMatrixData(filteredMatrix); + // const filteredMatrix = data.specsMatrix.filter((item: any) => { + // const code = item.code || ''; + // return !code.includes('z_brand'); + // }); + const processed = processMatrixData(data.specsMatrix); setSpecsMatrix(processed); + // const processed = processMatrixData(filteredMatrix); + // setSpecsMatrix(processed); } else { setSpecsMatrix([]); } -- cgit v1.2.3