diff options
Diffstat (limited to 'src-migrate/modules/product-detail/components/Information.tsx')
| -rw-r--r-- | src-migrate/modules/product-detail/components/Information.tsx | 386 |
1 files changed, 312 insertions, 74 deletions
diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 813b6bf5..236a03af 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -9,19 +9,35 @@ 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'; import currencyFormat from '@/core/utils/currencyFormat'; -import { InputGroup, InputRightElement } from '@chakra-ui/react'; +import { + InputGroup, + InputRightElement, + SimpleGrid, + Flex, + Text, + Box, + Center, + Icon, +} from '@chakra-ui/react'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; -import Image from 'next/image'; +import ImageNext from 'next/image'; import { formatToShortText } from '~/libs/formatNumber'; import { createSlug } from '~/libs/slug'; import { IProductDetail } from '~/types/product'; import { useProductDetail } from '../stores/useProductDetail'; import useVariant from '../hook/useVariant'; +// Import View Components +import MobileView from '@/core/components/views/MobileView'; // Pastikan path import benar + +// Import Modal Compare +import ProductComparisonModal from './ProductComparisonModal'; + const Skeleton = dynamic(() => - import('@chakra-ui/react').then((mod) => mod.Skeleton) + import('@chakra-ui/react').then((mod) => mod.Skeleton), ); type Props = { @@ -37,14 +53,46 @@ const Information = ({ product }: Props) => { const inputRef = useRef<HTMLInputElement>(null); // source of truth - const variantOptions = product.variants; + // const variantOptions = product.variants; + const [variantOptions, setVariantOptions] = useState<any[]>( + product?.variants, + ); const variantId = selectedVariant?.id; const { slaVariant, isLoading } = useVariant({ variantId }); - /* ====================== - * Sync input text - * ====================== */ + const [warranties, setWarranties] = useState<Record<string, string>>({}); + const [loadingWarranty, setLoadingWarranty] = useState(false); + + // State untuk Modal Compare + const [isCompareOpen, setIsCompareOpen] = useState(false); + + useEffect(() => { + const fetchWarrantyDirectly = async () => { + if (!product?.variants || product.variants.length === 0) return; + + setLoadingWarranty(true); + try { + const skus = product.variants.map((v) => v.id).join(','); + const mainSku = product.variants[0].id; + + const res = await axios.get('/api/magento-product', { + params: { skus, main_sku: mainSku }, + }); + + if (res.data && res.data.warranties) { + setWarranties(res.data.warranties); + } + } catch (error) { + // console.error("Gagal ambil garansi:", error); + } finally { + setLoadingWarranty(false); + } + }; + + fetchWarrantyDirectly(); + }, [product]); + useEffect(() => { if (!selectedVariant) return; @@ -52,7 +100,7 @@ const Information = ({ product }: Props) => { selectedVariant.code + (selectedVariant.attributes?.[0] ? ` - ${selectedVariant.attributes[0]}` - : '') + : ''), ); }, [selectedVariant]); @@ -72,14 +120,19 @@ const Information = ({ product }: Props) => { /* ====================== * Handlers * ====================== */ - const handleOnChange = (value: string) => { + const handleOnChange = (vals: any) => { setDisableFilter(true); + let code = vals.replace(/\s-\s.*$/, '').trim(); + let variant = product?.variants.find((item) => item.code === code); - const variant = variantOptions.find((item) => String(item.id) === value); - - if (!variant) return; - - setSelectedVariant(variant); + if (variant) { + setSelectedVariant(variant); + setInputValue( + variant?.code + + (variant?.attributes[0] ? ' - ' + variant?.attributes[0] : ''), + ); + setVariantOptions(product?.variants); + } }; const handleOnKeyUp = (e: any) => { @@ -87,6 +140,14 @@ const Information = ({ product }: Props) => { setInputValue(e.target.value); }; + const rowStyle = { + backgroundColor: '#ffffff', + fontSize: '13px', + borderBottom: '1px dashed #e2e8f0', + padding: '8px 0', + marginBottom: '0px', + }; + return ( <div className={style['wrapper']}> {/* ===== Variant Selector ===== */} @@ -120,70 +181,133 @@ const Information = ({ product }: Props) => { </InputGroup> <AutoCompleteList> - {variantOptions.map((option) => ( - <AutoCompleteItem - key={option.id} - value={String(option.id)} - _selected={ - option.id === selectedVariant?.id - ? { bg: 'gray.300' } - : undefined - } - > - <div className='flex gap-x-2 w-full justify-between px-3 items-center p-2'> - <div className='text-small'> - {option.code} - {option.attributes?.[0] ? ` - ${option.attributes[0]}` : ''} - </div> - - <div - className={ - option.price?.discount_percentage - ? 'flex gap-x-4 items-center' - : '' - } - > - {option.price?.discount_percentage > 0 && ( - <> - <div className='badge-solid-red text-xs'> - {Math.floor(option.price.discount_percentage)}% - </div> - <div className='min-w-16 sm:min-w-24 text-gray_r-11 line-through text-[11px] sm:text-caption-2'> - {currencyFormat(option.price.price)} - </div> - </> - )} - <div className='min-w-20 sm:min-w-28 text-danger-500 font-semibold'> - {currencyFormat(option.price.price_discount)} + {variantOptions + .sort((a: any, b: any) => { + return a.code.localeCompare(b.code, undefined, { + numeric: true, + sensitivity: 'base', + }); + }) + .map((option, cid) => ( + <AutoCompleteItem + key={option.id} + // value={String(option.id)} + value={ + option.code + + (option.attributes?.[0] ? ` - ${option.attributes[0]}` : '') + } + _selected={ + option.id === selectedVariant?.id + ? { bg: 'gray.300' } + : undefined + } + > + <div className='flex gap-x-2 w-full justify-between px-3 items-center p-2'> + <div className='text-small'> + {option.code} + {option.attributes?.[0] + ? ` - ${option.attributes[0]}` + : ''} + </div> + <div + className={ + option?.price?.discount_percentage + ? 'flex gap-x-4 items-center justify-between' + : '' + } + > + {option?.price?.discount_percentage > 0 && ( + <> + <div className='badge-solid-red text-xs'> + {Math.floor(option.price.discount_percentage)}% + </div> + <div className='min-w-16 sm:min-w-24 text-gray_r-11 line-through text-[11px] sm:text-caption-2'> + {currencyFormat(option.price.price)} + </div> + </> + )} + <div className='min-w-20 sm:min-w-28 text-danger-500 font-semibold'> + {currencyFormat(option.price.price_discount)} + </div> </div> </div> - </div> - </AutoCompleteItem> - ))} + </AutoCompleteItem> + ))} </AutoCompleteList> </AutoComplete> + + {/* === TOMBOL BANDINGKAN PRODUK (HANYA MOBILE) === */} + <MobileView> + <div + className='w-full flex items-center justify-between py-3 px-1 mt-3 bg-white border-t border-b border-black-100 cursor-pointer hover:bg-gray-50 transition-colors group' + onClick={() => setIsCompareOpen(true)} + > + <div className='flex items-center gap-3'> + <div className='bg-red-50 p-2 rounded-full group-hover:bg-red-100 transition-colors'> + <ImageNext + src='/images/logo-bandingkan.svg' + width={15} + height={15} + alt='bandingkan' + /> + </div> + <div className='flex flex-col'> + <span className='text-sm font-bold text-gray-800'> + Bandingkan Produk + </span> + <span className='text-xs text-gray-500'> + Coba bandingkan dengan produk lainnya + </span> + </div> + </div> + <div className='flex items-center gap-2'> + <span className='bg-red-600 text-white text-[10px] font-bold px-2 py-0.5 rounded-full'> + Baru + </span> + <Icon + as={ChevronDownIcon} + className='w-4 h-4 text-gray-400 transform -rotate-90' + /> + </div> + </div> + </MobileView> + + {/* Render Modal (Logic open/close ada di dalam component) */} + {isCompareOpen && ( + <ProductComparisonModal + isOpen={isCompareOpen} + onClose={() => setIsCompareOpen(false)} + mainProduct={product} + selectedVariant={selectedVariant} + /> + )} </div> - {/* ===== Info Rows ===== */} - <div className={style['row']}> - <div className={style['label']}>Item Code</div> + {/* ITEM CODE */} + <div className={style['row']} style={rowStyle}> + <div className={style['label']} style={{ color: '#6b7280' }}> + Item Code + </div> <div className={style['value']}>{selectedVariant?.code}</div> </div> - <div className={style['row']}> - <div className={style['label']}>Manufacture</div> + {/* MANUFACTURE */} + <div className={style['row']} style={rowStyle}> + <div className={style['label']} style={{ color: '#6b7280' }}> + Manufacture + </div> <div className={style['value']}> {!!product.manufacture.name ? ( <Link href={createSlug( '/shop/brands/', product.manufacture.name, - product.manufacture.id.toString() + product.manufacture.id.toString(), )} > - {product.manufacture.logo ? ( - <Image - height={50} + {product?.manufacture.logo ? ( + <ImageNext + height={100} width={100} src={product.manufacture.logo} alt={product.manufacture.name} @@ -201,29 +325,143 @@ const Information = ({ product }: Props) => { </div> </div> - <div className={style['row']}> - <div className={style['label']}>Berat Barang</div> + {/* BERAT BARANG */} + <div className={style['row']} style={rowStyle}> + <div className={style['label']} style={{ color: '#6b7280' }}> + Berat Barang + </div> <div className={style['value']}> {selectedVariant?.weight > 0 ? `${selectedVariant.weight} Kg` : '-'} </div> </div> - <div className={style['row']}> - <div className={style['label']}>Terjual</div> + {/* TERJUAL */} + <div + className={style['row']} + style={{ ...rowStyle, borderBottom: 'none' }} + > + <div className={style['label']} style={{ color: '#6b7280' }}> + Terjual + </div> <div className={style['value']}> {product.qty_sold > 0 ? formatToShortText(product.qty_sold) : '-'} </div> </div> - <div className={style['row']}> - <div className={style['label']}>Persiapan Barang</div> - {isLoading ? ( - <div className={style['value']}> - <Skeleton height={5} width={100} /> - </div> - ) : ( - <div className={style['value']}>{sla?.sla_date}</div> - )} + {/* === DETAIL INFORMASI PRODUK === */} + <div className='mt-6 border-t pt-4'> + <h2 className='hidden md:block font-bold text-gray-800 text-sm mb-4'> + Detail Informasi Produk + </h2> + + <SimpleGrid columns={{ base: 3, md: 3 }} spacing={{ base: 2, md: 10 }}> + <Flex + direction={{ base: 'column', md: 'row' }} + align='center' + textAlign={{ base: 'center', md: 'left' }} + gap={{ base: 2, md: 3 }} + > + <img + src='/images/produk_asli.svg' + alt='Distributor Resmi' + className='w-8 h-8 md:w-10 md:h-10 shrink-0' + /> + <Box> + <Text + fontSize={{ base: '10px', md: '11px' }} + color='gray.500' + lineHeight='short' + mb='1px' + > + Distributor Resmi + </Text> + <Text + fontSize={{ base: '10px', md: '12px' }} + fontWeight='bold' + color='gray.800' + lineHeight='1.2' + > + Jaminan Produk Asli + </Text> + </Box> + </Flex> + + <Flex + direction={{ base: 'column', md: 'row' }} + align='center' + textAlign={{ base: 'center', md: 'left' }} + gap={{ base: 2, md: 3 }} + > + <img + src='/images/estimasi.svg' + alt='Estimasi Penyiapan' + className='w-8 h-8 md:w-9 md:h-9 shrink-0' + /> + <Box> + <Text + fontSize={{ base: '10px', md: '11px' }} + color='gray.500' + lineHeight='short' + mb='1px' + > + Estimasi Penyiapan + </Text> + {isLoading ? ( + <Center> + <Skeleton height='10px' width='50px' mt='2px' /> + </Center> + ) : ( + <Text + fontSize={{ base: '10px', md: '12px' }} + fontWeight='bold' + color='gray.800' + lineHeight='1.2' + > + {sla?.sla_date || '-'} + </Text> + )} + </Box> + </Flex> + + <Flex + direction={{ base: 'column', md: 'row' }} + align='center' + textAlign={{ base: 'center', md: 'left' }} + gap={{ base: 2, md: 3 }} + > + <img + src='/images/garansi.svg' + alt='Garansi Produk' + className='w-8 h-8 md:w-10 md:h-10 shrink-0' + /> + <Box> + <Text + fontSize={{ base: '10px', md: '11px' }} + color='gray.500' + lineHeight='short' + mb='1px' + > + Garansi Produk + </Text> + {loadingWarranty ? ( + <Center> + <Skeleton height='10px' width='50px' mt='2px' /> + </Center> + ) : ( + <Text + fontSize={{ base: '10px', md: '12px' }} + fontWeight='bold' + color='gray.800' + lineHeight='1.2' + > + {selectedVariant && warranties[selectedVariant.id] + ? warranties[selectedVariant.id] + : '-'} + </Text> + )} + </Box> + </Flex> + </SimpleGrid> </div> </div> ); |
