diff options
| author | FIN-IT_AndriFP <andrifebriyadiputra@gmail.com> | 2025-12-16 15:57:30 +0700 |
|---|---|---|
| committer | FIN-IT_AndriFP <andrifebriyadiputra@gmail.com> | 2025-12-16 15:57:30 +0700 |
| commit | 0c745c513765b23ee200eb61ebfe13086a1ea700 (patch) | |
| tree | 787805484d2c0fbbebf626cb8357988a95a9c677 | |
| parent | 31853dc731c6e4105c9cf9bd373c63e6e989caa4 (diff) | |
(andri) try search with q* solr
| -rw-r--r-- | src-migrate/modules/product-detail/components/ProductComparisonModal.tsx | 133 |
1 files changed, 50 insertions, 83 deletions
diff --git a/src-migrate/modules/product-detail/components/ProductComparisonModal.tsx b/src-migrate/modules/product-detail/components/ProductComparisonModal.tsx index a58ad5b2..3ee61c9d 100644 --- a/src-migrate/modules/product-detail/components/ProductComparisonModal.tsx +++ b/src-migrate/modules/product-detail/components/ProductComparisonModal.tsx @@ -80,6 +80,7 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant } const targetId = activeItem.id; + // Ambil code dari properti yang tersedia const displayCode = activeItem.default_code || activeItem.code || activeItem.sku || mainProduct.default_code || mainProduct.code; const variantOptions = mainProduct.variants?.map((v: any) => ({ @@ -102,8 +103,8 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant const productSlot1 = { id: targetId, - sku: targetId, // ID untuk API - realCode: displayCode, + sku: targetId, // ID untuk API & Key + realCode: displayCode, // String untuk Tampilan name: mainProduct.name, price: activeItem.price?.price || activeItem.price || mainProduct.lowest_price?.price || 0, image: activeItem.image || mainProduct.image, @@ -130,6 +131,7 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant const fetchSpecs = async () => { setIsLoadingMatrix(true); try { + // Kirim ID ke API const allSkus = validProducts.map(p => p.sku).join(','); const mainSku = validProducts[0]?.sku; @@ -151,7 +153,7 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant }, [products, isOpen]); // =========================================================================== - // 3. SEARCH LOGIC + // 3. SEARCH LOGIC (MATCHED WITH API JSON) // =========================================================================== useEffect(() => { const delayDebounceFn = setTimeout(async () => { @@ -163,16 +165,21 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant setIsSearching(true); try { const attrSetId = selectedVariant?.attribute_set_id || mainProduct?.attribute_set_id; + const params = new URLSearchParams({ source: 'compare', - q: searchQuery, + q: '*', limit: '5', + // Filter kategori aktif agar Apple-to-Apple fq: attrSetId ? `attribute_set_id_i:${attrSetId}` : '' }); - const res = await fetch(`/api/search?${params.toString()}`); + // Gunakan endpoint yang benar: /api/shop/search + const res = await fetch(`/api/shop/search?${params.toString()}`); + if (res.ok) { const data = await res.json(); + // Data sudah CamelCase dari Backend setSearchResults(data.response?.products || []); } else { setSearchResults([]); @@ -210,12 +217,19 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant } }; - const handleAddProduct = (solrProduct: any, slotIndex: number) => { + const handleAddProduct = (searchItem: any, slotIndex: number) => { const newProducts = [...products]; - const idToAdd = solrProduct.product_id_i || solrProduct.id; - const codeToAdd = solrProduct.default_code_s || solrProduct.sku; + // [FIX] MAPPING DARI JSON API ANDA (CAMELCASE) + // JSON: { id: 88019, code: "RX-SP0006", displayName: "...", lowestPrice: { price: ... } } + + const idToAdd = searchItem.id; + const codeToAdd = searchItem.code; // Langsung 'code', bukan 'default_code_s' + const nameToAdd = searchItem.displayName || searchItem.name; + const imageToAdd = searchItem.image; + const priceToAdd = searchItem.lowestPrice?.price || 0; + // Cek Duplikat if (newProducts.find(p => p && String(p.id) === String(idToAdd))) { toast({ title: "Produk sudah ada", status: "warning", position: "top" }); return; @@ -223,17 +237,17 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant newProducts[slotIndex] = { id: idToAdd, - sku: idToAdd, - realCode: codeToAdd, - name: solrProduct.display_name_s || solrProduct.name_s, - price: solrProduct.price_tier1_v2_f || 0, - image: solrProduct.image_s, + sku: idToAdd, // ID untuk API + realCode: codeToAdd, // Code String untuk Tampilan + name: nameToAdd, + price: priceToAdd, + image: imageToAdd, variants: [{ id: idToAdd, code: codeToAdd, - name: solrProduct.name_s, - price: solrProduct.price_tier1_v2_f, - image: solrProduct.image_s + name: nameToAdd, + price: priceToAdd, + image: imageToAdd }] }; @@ -269,17 +283,11 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant <ModalBody p={6} bg="white"> <Grid templateColumns="200px repeat(4, 1fr)" gap={4}> - - {/* Cell 1: Kosong */} <GridItem /> - - {/* Loop Slot Produk */} {products.map((product, index) => ( <GridItem key={index} position="relative" minW="0"> {product ? ( <VStack align="stretch" spacing={3} h="100%"> - - {/* Tombol Hapus */} {index !== 0 && ( <IconButton aria-label="Hapus" icon={<Trash2 size={16}/>} @@ -287,46 +295,33 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant colorScheme="red" onClick={() => handleRemoveProduct(index)} zIndex={2} /> )} - - {/* Gambar */} <Box h="160px" display="flex" alignItems="center" justifyContent="center" bg="gray.50" borderRadius="md" p={2}> <Image src={product.image || '/images/noimage.jpeg'} - alt={product.name} - maxH="100%" objectFit="contain" - onError={(e) => { (e.target as HTMLImageElement).src = '/images/noimage.jpeg'; }} + alt={product.name} maxH="100%" objectFit="contain" + onError={(e) => { (e.target as HTMLImageElement).src = '/images/no-image-compare.svg'; }} /> </Box> - - {/* Info Harga & Nama */} <Box> <Text color="red.600" fontWeight="bold" fontSize="md"> {product.price > 0 ? formatPrice(product.price) : 'Hubungi Admin'} </Text> - {/* Margin Bottom agar tidak tertutup dropdown */} <Text fontSize="xs" fontWeight="bold" noOfLines={2} h="35px" title={product.name} mb={2}> {product.name} </Text> </Box> - - {/* Dropdown Varian */} + <Select - size="sm" - borderRadius="md" - fontSize="xs" + size="sm" borderRadius="md" fontSize="xs" value={product.id} onChange={(e) => handleVariantChange(index, e.target.value)} - isDisabled={false} - bg="white" + isDisabled={false} bg="white" > {product.variants && product.variants.map((v: any) => ( - <option key={v.id} value={v.id}> - {v.code} - </option> + <option key={v.id} value={v.id}>{v.code}</option> ))} </Select> - {/* Tombol */} <HStack spacing={2}> <IconButton aria-label="Cart" icon={<Icon as={ShoppingCart} />} variant="outline" colorScheme="red" size="sm" /> <Button as="a" href={`/product/${product.id}`} target="_blank" colorScheme="red" size="sm" flex={1} fontSize="xs"> @@ -335,7 +330,6 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant </HStack> </VStack> ) : ( - // SLOT KOSONG <VStack align="stretch" spacing={3} h="100%" position="relative"> <InputGroup size="sm"> <InputLeftElement pointerEvents="none"><Icon as={Search} color="gray.300" /></InputLeftElement> @@ -347,6 +341,7 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant /> </InputGroup> + {/* --- HASIL SEARCH (MAPPING FIX) --- */} {activeSearchSlot === index && searchQuery.length > 0 && ( <Box position="absolute" top="35px" left={0} right={0} bg="white" boxShadow="lg" zIndex={10} borderRadius="md" border="1px solid" borderColor="gray.200" maxH="250px" overflowY="auto"> {isSearching ? ( @@ -355,16 +350,20 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant <List spacing={0}> {searchResults.map((res) => ( <ListItem - key={res.product_id_i || res.id} + key={res.id} p={2} borderBottom="1px solid #f0f0f0" _hover={{ bg: 'red.50', cursor: 'pointer' }} onClick={() => handleAddProduct(res, index)} > <Flex align="center" gap={2}> - <Image src={res.image_s || '/images/noimage.jpeg'} boxSize="30px" objectFit="contain" onError={(e) => { (e.target as HTMLImageElement).src = '/images/noimage.jpeg'; }} /> + <Image src={res.image || '/images/noimage.jpeg'} boxSize="30px" objectFit="contain" onError={(e) => { (e.target as HTMLImageElement).src = '/images/noimage.jpeg'; }} /> <Box> - <Text fontSize="xs" fontWeight="bold" noOfLines={1}>{res.display_name_s || res.name_s}</Text> - <Text fontSize="xs" color="red.500">{formatPrice(res.price_tier1_v2_f || 0)}</Text> + {/* [FIX] Gunakan displayName atau name */} + <Text fontSize="xs" fontWeight="bold" noOfLines={1}>{res.displayName || res.name}</Text> + {/* [FIX] Gunakan lowestPrice.price */} + <Text fontSize="xs" color="red.500"> + {formatPrice(res.lowestPrice?.price || 0)} + </Text> </Box> </Flex> </ListItem> @@ -385,51 +384,22 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant </GridItem> ))} - {/* --- BAGIAN SPESIFIKASI --- */} <GridItem colSpan={5} py={6}> - <Box borderBottom="2px solid" borderColor="gray.100" pb={2}> - <Text fontSize="lg" fontWeight="bold">Spesifikasi Teknis</Text> - </Box> + <Box borderBottom="2px solid" borderColor="gray.100" pb={2}><Text fontSize="lg" fontWeight="bold">Spesifikasi Teknis</Text></Box> </GridItem> {isLoadingMatrix ? ( - <GridItem colSpan={5} textAlign="center" py={10}> - <Spinner color="red.500" thickness="4px" size="xl" /> - <Text mt={2} color="gray.500">Memuat data...</Text> - </GridItem> + <GridItem colSpan={5} textAlign="center" py={10}><Spinner color="red.500" thickness="4px" size="xl" /><Text mt={2} color="gray.500">Memuat data...</Text></GridItem> ) : specsMatrix.length > 0 ? ( specsMatrix.map((row, rowIndex) => ( <React.Fragment key={row.code || rowIndex}> - {/* Label (Kiri) */} - <GridItem - py={3} - px={2} - borderBottom="1px solid" - borderColor="gray.100" - bg={rowIndex % 2 !== 0 ? "white" : "gray.50"} - display="flex" - alignItems="center" - > + <GridItem py={3} px={2} borderBottom="1px solid" borderColor="gray.100" bg={rowIndex % 2 !== 0 ? "white" : "gray.50"} display="flex" alignItems="center"> <Text fontWeight="bold" fontSize="sm" color="gray.700">{row.label}</Text> </GridItem> - - {/* Value (Rata Tengah) */} {products.map((product, colIndex) => { const val = product ? (row.values[String(product.sku)] || '-') : ''; - return ( - <GridItem - key={`${row.code}-${colIndex}`} - py={3} - px={2} - borderBottom="1px solid" - borderColor="gray.100" - bg={rowIndex % 2 !== 0 ? "white" : "gray.50"} - display="flex" // [FIX] Flex untuk centering - alignItems="center" // [FIX] Vertical Center - justifyContent="center" // [FIX] Horizontal Center - textAlign="center" // [FIX] Text Align Center - > + <GridItem key={`${row.code}-${colIndex}`} py={3} px={2} borderBottom="1px solid" borderColor="gray.100" bg={rowIndex % 2 !== 0 ? "white" : "gray.50"} display="flex" alignItems="center" justifyContent="center" textAlign="center"> <Text fontSize="sm" color="gray.600">{renderSpecValue(val)}</Text> </GridItem> ); @@ -437,11 +407,8 @@ const ProductComparisonModal = ({ isOpen, onClose, mainProduct, selectedVariant </React.Fragment> )) ) : ( - <GridItem colSpan={5} py={10} textAlign="center" color="gray.500" bg="gray.50"> - <Text>Data spesifikasi belum tersedia untuk produk ini.</Text> - </GridItem> + <GridItem colSpan={5} py={10} textAlign="center" color="gray.500" bg="gray.50"><Text>Data spesifikasi belum tersedia untuk produk ini.</Text></GridItem> )} - </Grid> </ModalBody> </ModalContent> |
