import React, { useEffect, useState, useRef } from 'react'; // Tambah useRef import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, Button, Text, Box, Badge, Grid, GridItem, Image, Input, InputGroup, InputLeftElement, VStack, HStack, IconButton, Flex, Icon, Spinner, List, ListItem, useToast, Select, useOutsideClick // Tambah import ini } from '@chakra-ui/react'; import { Search, Trash2 } from 'lucide-react'; // --- HELPER FORMATTING --- const formatPrice = (price: number) => { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0, }).format(price); }; const renderSpecValue = (val: any) => { if (!val || val === '-') return '-'; return String(val).replace(/<[^>]*>?/gm, ''); }; type Props = { isOpen: boolean; onClose: () => void; mainProduct: any; selectedVariant: any; }; const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant }: Props) => { const toast = useToast(); // --- STATE --- const [products, setProducts] = useState<(any | null)[]>([null, null, null, null]); const [specsMatrix, setSpecsMatrix] = useState([]); const [isLoadingMatrix, setIsLoadingMatrix] = useState(false); // Search State const [activeSearchSlot, setActiveSearchSlot] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [isSearching, setIsSearching] = useState(false); // --- REF & OUTSIDE CLICK --- const searchWrapperRef = useRef(null); // Ref untuk mendeteksi klik useOutsideClick({ ref: searchWrapperRef, handler: () => { // Jika user klik di luar area search yang aktif, tutup search if (activeSearchSlot !== null) { setActiveSearchSlot(null); setSearchResults([]); // Opsional: bersihkan hasil juga } }, }); // =========================================================================== // 1. LOGIC UTAMA: ISI SLOT 1 // =========================================================================== useEffect(() => { if (isOpen && mainProduct) { let activeItem = selectedVariant; if (!activeItem && mainProduct.variants && mainProduct.variants.length > 0) { activeItem = mainProduct.variants[0]; } if (!activeItem) { activeItem = mainProduct; } const targetId = activeItem.id; const displayCode = activeItem.default_code || activeItem.code || activeItem.sku || mainProduct.default_code || mainProduct.code; const variantOptions = mainProduct.variants?.map((v: any) => ({ id: v.id, code: v.default_code || v.code || v.sku, name: v.name, price: v.price?.price || v.price || 0, image: v.image })) || []; if (variantOptions.length === 0) { variantOptions.push({ id: targetId, code: displayCode, name: mainProduct.name, price: activeItem.price?.price || activeItem.price || 0, image: activeItem.image || mainProduct.image }); } const productSlot1 = { id: targetId, sku: targetId, realCode: displayCode, name: mainProduct.name, price: activeItem.price?.price || activeItem.price || mainProduct.lowest_price?.price || 0, image: activeItem.image || mainProduct.image, variants: variantOptions }; setProducts((prev) => { const newSlots = [...prev]; if (!newSlots[0] || String(newSlots[0].id) !== String(targetId)) { newSlots[0] = productSlot1; } return newSlots; }); } }, [isOpen, mainProduct, selectedVariant]); // =========================================================================== // 2. FETCH SPECS // =========================================================================== useEffect(() => { const validProducts = products.filter(p => p !== null); if (!isOpen || validProducts.length === 0) return; const fetchSpecs = async () => { setIsLoadingMatrix(true); try { const allSkus = validProducts.map(p => p.sku).join(','); const mainSku = validProducts[0]?.sku; const res = await fetch(`/api/magento-product?skus=${allSkus}&main_sku=${mainSku}`); if (!res.ok) return; const data = await res.json(); if (data.specsMatrix) { setSpecsMatrix(data.specsMatrix); } } catch (err) { console.error(err); } finally { setIsLoadingMatrix(false); } }; fetchSpecs(); }, [products, isOpen]); // =========================================================================== // 3. SEARCH LOGIC // =========================================================================== useEffect(() => { const delayDebounceFn = setTimeout(async () => { if (searchQuery.length > 0 && searchQuery.length < 3) { setSearchResults([]); return; } if (activeSearchSlot === null) return; const attrSetId = selectedVariant?.attribute_set_id || mainProduct?.attribute_set_id; if (!attrSetId) { console.warn("Search dibatalkan: Produk utama tidak memiliki attribute_set_id"); setSearchResults([]); setIsSearching(false); return; } setIsSearching(true); try { const queryParam = searchQuery === '' ? '*' : searchQuery; const params = new URLSearchParams({ source: 'compare', q: queryParam, limit: '20', fq: `attribute_set_id_i:${attrSetId}` }); const res = await fetch(`/api/shop/search?${params.toString()}`); if (res.ok) { const data = await res.json(); setSearchResults(data.response?.products || []); } else { setSearchResults([]); } } catch (e) { setSearchResults([]); } finally { setIsSearching(false); } }, 500); return () => clearTimeout(delayDebounceFn); }, [searchQuery, mainProduct, selectedVariant, activeSearchSlot]); // =========================================================================== // 4. HANDLERS // =========================================================================== const handleVariantChange = (slotIndex: number, newId: string) => { const currentProduct = products[slotIndex]; if (!currentProduct || !currentProduct.variants) return; const selectedVar = currentProduct.variants.find((v: any) => String(v.id) === String(newId)); if (selectedVar) { const newProducts = [...products]; newProducts[slotIndex] = { ...currentProduct, id: selectedVar.id, sku: selectedVar.id, realCode: selectedVar.code, price: selectedVar.price, image: selectedVar.image }; setProducts(newProducts); } }; const handleAddProduct = (searchItem: any, slotIndex: number) => { const newProducts = [...products]; const idToAdd = searchItem.id; const codeToAdd = searchItem.code; const nameToAdd = searchItem.displayName || searchItem.name; const imageToAdd = searchItem.image; const priceToAdd = searchItem.lowestPrice?.price || 0; if (newProducts.find(p => p && String(p.id) === String(idToAdd))) { toast({ title: "Produk sudah ada", status: "warning", position: "top" }); return; } newProducts[slotIndex] = { id: idToAdd, sku: idToAdd, realCode: codeToAdd, name: nameToAdd, price: priceToAdd, image: imageToAdd, variants: [{ id: idToAdd, code: codeToAdd, name: nameToAdd, price: priceToAdd, image: imageToAdd }] }; setProducts(newProducts); setActiveSearchSlot(null); setSearchQuery(''); setSearchResults([]); }; const handleRemoveProduct = (index: number) => { const newProducts = [...products]; newProducts[index] = null; setProducts(newProducts); if (newProducts.every(p => p === null)) setSpecsMatrix([]); }; return ( Bandingkan Produk {products.filter(p => p !== null).length} Item Detail Spesifikasi Produk yang kamu pilih {products.map((product, index) => ( {product ? ( {index !== 0 && ( } size="xs" position="absolute" top={-2} right={-2} colorScheme="red" onClick={() => handleRemoveProduct(index)} zIndex={2} /> )} {product.name} { (e.target as HTMLImageElement).src = '/images/no-image-compare.svg'; }} /> {product.price > 0 ? formatPrice(product.price) : 'Hubungi Admin'} {product.name} } variant="outline" colorScheme="red" size="sm" /> ) : ( {/* WRAPPER SEARCH DENGAN REF */} {/* Hanya berikan ref jika ini adalah slot yang sedang aktif dicari */} { setActiveSearchSlot(index); setSearchQuery(''); }} onChange={(e) => setSearchQuery(e.target.value)} /> {/* HASIL SEARCH */} {activeSearchSlot === index && ( {/* CEK ATTRIBUTE SET ID */} {!selectedVariant?.attribute_set_id && !mainProduct?.attribute_set_id ? ( Perbandingan Tidak Tersedia Produk utama tidak memiliki data kategori yang valid untuk dibandingkan. ) : ( <> {isSearching ? ( ) : searchResults.length > 0 ? ( {searchResults.map((res) => ( handleAddProduct(res, index)} > { (e.target as HTMLImageElement).src = '/images/no-image-compare.svg'; }} /> {res.displayName || res.name} {formatPrice(res.lowestPrice?.price || 0)} ))} ) : ( {searchQuery === '' ? 'Menampilkan rekomendasi...' : 'Produk tidak ditemukan.'} )} )} )} {/* SLOT KOSONG */} Empty Slot Produk Belum Ditambahkan )} ))} {/* --- HEADER SPESIFIKASI --- */} Spesifikasi Teknis {isLoadingMatrix && specsMatrix.length > 0 && ( Updating... )} {/* --- MATRIX SPEK --- */} {isLoadingMatrix && specsMatrix.length === 0 ? ( Memuat data... ) : specsMatrix.length > 0 ? ( specsMatrix.map((row, rowIndex) => ( {row.label} {products.map((product, colIndex) => { const val = product ? (row.values[String(product.sku)] || '-') : ''; return ( {isLoadingMatrix && product && !row.values[String(product.sku)] ? ( ) : ( {renderSpecValue(val)} )} ); })} )) ) : ( Data spesifikasi belum tersedia untuk produk ini. )} ); }; export default ProductComparisonModal;