summaryrefslogtreecommitdiff
path: root/src-migrate/modules/product-detail/components
diff options
context:
space:
mode:
Diffstat (limited to 'src-migrate/modules/product-detail/components')
-rw-r--r--src-migrate/modules/product-detail/components/Information.tsx393
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx741
2 files changed, 692 insertions, 442 deletions
diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx
index ce848267..757cd473 100644
--- a/src-migrate/modules/product-detail/components/Information.tsx
+++ b/src-migrate/modules/product-detail/components/Information.tsx
@@ -12,7 +12,16 @@ import { useEffect, useRef, useState } from 'react';
import axios from 'axios';
import currencyFormat from '@/core/utils/currencyFormat';
-import { InputGroup, InputRightElement, SimpleGrid, Flex, Text, Box, Center, Icon } 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 ImageNext from 'next/image';
import { formatToShortText } from '~/libs/formatNumber';
@@ -28,7 +37,7 @@ import MobileView from '@/core/components/views/MobileView'; // Pastikan path im
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 = {
@@ -36,7 +45,8 @@ type Props = {
};
const Information = ({ product }: Props) => {
- const { selectedVariant, setSelectedVariant, setSla, sla } = useProductDetail();
+ const { selectedVariant, setSelectedVariant, setSla, sla } =
+ useProductDetail();
const [inputValue, setInputValue] = useState<string>('');
const [disableFilter, setDisableFilter] = useState<boolean>(false);
@@ -45,7 +55,7 @@ const Information = ({ product }: Props) => {
// source of truth
// const variantOptions = product.variants;
const [variantOptions, setVariantOptions] = useState<any[]>(
- product?.variants
+ product?.variants,
);
const variantId = selectedVariant?.id;
@@ -59,25 +69,25 @@ const Information = ({ product }: Props) => {
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);
- }
+ 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();
@@ -90,7 +100,7 @@ const Information = ({ product }: Props) => {
selectedVariant.code +
(selectedVariant.attributes?.[0]
? ` - ${selectedVariant.attributes[0]}`
- : '')
+ : ''),
);
}, [selectedVariant]);
@@ -114,14 +124,14 @@ const Information = ({ product }: Props) => {
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] : '')
+ (variant?.attributes[0] ? ' - ' + variant?.attributes[0] : ''),
);
- setVariantOptions(product?.variants);
+ setVariantOptions(product?.variants);
}
};
@@ -135,7 +145,7 @@ const Information = ({ product }: Props) => {
fontSize: '13px',
borderBottom: '1px dashed #e2e8f0',
padding: '8px 0',
- marginBottom: '0px'
+ marginBottom: '0px',
};
return (
@@ -173,96 +183,126 @@ const Information = ({ product }: Props) => {
<AutoCompleteList>
{variantOptions
.sort((a: any, b: any) => {
- return a.code.localeCompare(b.code, undefined, { numeric: true, sensitivity: 'base' });
+ 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)}
+ <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
+ 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}
- />
+ <ProductComparisonModal
+ isOpen={isCompareOpen}
+ onClose={() => setIsCompareOpen(false)}
+ mainProduct={product}
+ selectedVariant={selectedVariant}
+ />
)}
</div>
{/* ITEM CODE */}
<div className={style['row']} style={rowStyle}>
- <div className={style['label']} style={{ color: '#6b7280' }}>Item Code</div>
+ <div className={style['label']} style={{ color: '#6b7280' }}>
+ Item Code
+ </div>
<div className={style['value']}>{selectedVariant?.code}</div>
</div>
{/* MANUFACTURE */}
<div className={style['row']} style={rowStyle}>
- <div className={style['label']} style={{ color: '#6b7280' }}>Manufacture</div>
+ <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 ? (
@@ -287,81 +327,144 @@ const Information = ({ product }: Props) => {
{/* BERAT BARANG */}
<div className={style['row']} style={rowStyle}>
- <div className={style['label']} style={{ color: '#6b7280' }}>Berat Barang</div>
+ <div className={style['label']} style={{ color: '#6b7280' }}>
+ Berat Barang
+ </div>
<div className={style['value']}>
{selectedVariant?.weight > 0 ? `${selectedVariant.weight} Kg` : '-'}
</div>
</div>
{/* TERJUAL */}
- <div className={style['row']} style={{ ...rowStyle, borderBottom: 'none' }}>
- <div className={style['label']} style={{ color: '#6b7280' }}>Terjual</div>
+ <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>
{/* === 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">
+ <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>
+ <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>
);
};
-export default Information; \ No newline at end of file
+export default Information;
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index 35726437..de205c41 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -5,24 +5,24 @@ import { useRouter } from 'next/router';
import { useEffect, useRef, useState, UIEvent, useMemo } from 'react';
// Import komponen Chakra UI
-import {
- Button,
- Tabs,
- TabList,
- TabPanels,
- Tab,
- TabPanel,
- Table,
- Tbody,
- Tr,
+import {
+ Button,
+ Tabs,
+ TabList,
+ TabPanels,
+ Tab,
+ TabPanel,
+ Table,
+ Tbody,
+ Tr,
Td,
- Th,
+ Th,
Thead,
Box,
Spinner,
Center,
Text,
- Stack
+ Stack,
} from '@chakra-ui/react';
// Import Icons
@@ -30,7 +30,7 @@ import {
AlertTriangle,
MessageCircleIcon,
Share2Icon,
- ExternalLink
+ ExternalLink,
} from 'lucide-react';
import { LazyLoadComponent } from 'react-lazy-load-image-component';
@@ -62,7 +62,7 @@ type Props = {
const RWebShare = dynamic(
() => import('react-web-share').then((m) => m.RWebShare),
- { ssr: false }
+ { ssr: false },
);
// 1. STYLE DESKTOP (Tebal, Jelas, dengan Border/Padding)
@@ -88,8 +88,8 @@ const cssScrollbarDesktop = {
// 2. STYLE MOBILE (Tipis, Minimalis, Tanpa Border)
const cssScrollbarMobile = {
'&::-webkit-scrollbar': {
- width: '3px', // Sangat tipis vertikal
- height: '3px', // Sangat tipis horizontal
+ width: '3px', // Sangat tipis vertikal
+ height: '3px', // Sangat tipis horizontal
},
'&::-webkit-scrollbar-track': {
background: 'transparent',
@@ -107,14 +107,16 @@ const ProductDetail = ({ product }: Props) => {
const router = useRouter();
const [auth, setAuth] = useState<any>(null);
- console.log('Render ProductDetail for product ID:', product);
-
+ // console.log('Render ProductDetail for product ID:', product);
+
// State Data dari Magento
- const [specsMatrix, setSpecsMatrix] = useState<any[]>([]);
+ const [specsMatrix, setSpecsMatrix] = useState<any[]>([]);
const [upsellIds, setUpsellIds] = useState<number[]>([]);
- const [relatedIds, setRelatedIds] = useState<number[]>([]);
- const [descriptionMap, setDescriptionMap] = useState<Record<string, string>>({});
-
+ const [relatedIds, setRelatedIds] = useState<number[]>([]);
+ const [descriptionMap, setDescriptionMap] = useState<Record<string, string>>(
+ {},
+ );
+
const [loadingSpecs, setLoadingSpecs] = useState(false);
// 2. STATE MODAL COMPARE (Baru)
@@ -136,7 +138,7 @@ const ProductDetail = ({ product }: Props) => {
activeVariantId,
setIsApproval,
isApproval,
- selectedVariant,
+ selectedVariant,
setSelectedVariant,
} = useProductDetail();
@@ -178,75 +180,75 @@ const ProductDetail = ({ product }: Props) => {
const variantInit =
product?.variants?.find((variant) => variant.is_in_bu) ||
product?.variants?.[0];
-
+
setSelectedVariant(variantInit);
setSpecsMatrix([]);
setUpsellIds([]);
setRelatedIds([]);
-
- }, [product, auth]);
+ }, [product, auth]);
// 2. LOGIC FETCH DATA
useEffect(() => {
const fetchMagentoData = async () => {
- const allVariantIds = product.variants.map(v => v.id);
-
+ const allVariantIds = product.variants.map((v) => v.id);
+
if (allVariantIds.length === 0) return;
const mainId = allVariantIds[0];
setLoadingSpecs(true);
-
+
try {
const params = new URLSearchParams({
- skus: allVariantIds.join(','),
- main_sku: String(mainId)
+ skus: allVariantIds.join(','),
+ main_sku: String(mainId),
});
const endpoint = `/api/magento-product?${params.toString()}`;
const response = await fetch(endpoint, {
- method: 'GET',
- headers: { 'Content-Type': 'application/json' }
+ method: 'GET',
+ headers: { 'Content-Type': 'application/json' },
});
if (!response.ok) {
- setSpecsMatrix([]);
- setUpsellIds([]);
- setRelatedIds([]);
- return;
+ setSpecsMatrix([]);
+ setUpsellIds([]);
+ setRelatedIds([]);
+ return;
}
const data = await response.json();
// 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(data.specsMatrix);
- setSpecsMatrix(processed);
- // const processed = processMatrixData(filteredMatrix);
- // setSpecsMatrix(processed);
+ // 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([]);
+ setSpecsMatrix([]);
}
- if (data.descriptions){
+ if (data.descriptions) {
setDescriptionMap(data.descriptions);
}
// 2. Upsell & Related
- if (data.upsell_ids && Array.isArray(data.upsell_ids)) setUpsellIds(data.upsell_ids);
+ if (data.upsell_ids && Array.isArray(data.upsell_ids))
+ setUpsellIds(data.upsell_ids);
else setUpsellIds([]);
- if (data.related_ids && Array.isArray(data.related_ids)) setRelatedIds(data.related_ids);
+ if (data.related_ids && Array.isArray(data.related_ids))
+ setRelatedIds(data.related_ids);
else setRelatedIds([]);
-
} catch (error) {
- console.error("Gagal mengambil data Magento:", error);
+ console.error('Gagal mengambil data Magento:', error);
setSpecsMatrix([]);
} finally {
setLoadingSpecs(false);
@@ -254,60 +256,56 @@ const ProductDetail = ({ product }: Props) => {
};
fetchMagentoData();
-
- }, [product.id]);
+ }, [product.id]);
// HELPER 1: GROUPING DATA BY LABEL
const processMatrixData = (rawMatrix: any[]) => {
- const groups: any = {};
- const result: any[] = [];
-
- rawMatrix.forEach(item => {
- if (item.label && item.label.includes(' : ')) {
- const parts = item.label.split(' : ');
- const groupName = parts[0].trim();
- const childLabel = parts.slice(1).join(' : ').trim();
-
- if (!groups[groupName]) {
- groups[groupName] = {
- type: 'group',
- label: groupName,
- children: []
- };
- result.push(groups[groupName]);
- }
-
- groups[groupName].children.push({
- ...item,
- label: childLabel
- });
-
- } else {
- result.push({ ...item, type: 'single' });
- }
- });
+ const groups: any = {};
+ const result: any[] = [];
+
+ rawMatrix.forEach((item) => {
+ if (item.label && item.label.includes(' : ')) {
+ const parts = item.label.split(' : ');
+ const groupName = parts[0].trim();
+ const childLabel = parts.slice(1).join(' : ').trim();
+
+ if (!groups[groupName]) {
+ groups[groupName] = {
+ type: 'group',
+ label: groupName,
+ children: [],
+ };
+ result.push(groups[groupName]);
+ }
- return result;
- };
+ groups[groupName].children.push({
+ ...item,
+ label: childLabel,
+ });
+ } else {
+ result.push({ ...item, type: 'single' });
+ }
+ });
+ return result;
+ };
// HELPER 2: RENDER SPEC VALUE
const renderSpecValue = (val: any) => {
if (!val) return '-';
const strVal = String(val).trim();
- const isUrl = !strVal.includes(' ') && (
- strVal.startsWith('http') ||
- strVal.startsWith('www.')
- );
+ const isUrl =
+ !strVal.includes(' ') &&
+ (strVal.startsWith('http') || strVal.startsWith('www.'));
if (isUrl) {
const href = strVal.startsWith('http') ? strVal : `https://${strVal}`;
return (
- <a
- href={href}
- target="_blank"
- rel="noopener noreferrer"
- className="text-red-600 hover:underline inline-flex items-center gap-1"
+ <a
+ href={href}
+ target='_blank'
+ rel='noopener noreferrer'
+ className='text-red-600 hover:underline inline-flex items-center gap-1'
>
<ExternalLink size={14} /> Link
</a>
@@ -315,39 +313,38 @@ const ProductDetail = ({ product }: Props) => {
}
if (strVal.includes('<') && strVal.includes('>')) {
- return (
- <Box
- className="prose prose-sm text-gray-700"
- sx={{
- '& ul, & ol': {
- paddingLeft: '1.2rem',
- margin: 0,
- textAlign: 'left'
- },
- '& li': {
- fontWeight: 'normal',
- marginBottom: '4px',
- textAlign: 'left'
- },
- '& strong': {
- display: 'block',
- marginBottom: '2px',
- fontWeight: 'bold'
- },
- '& p': {
- margin: 0,
- textAlign: 'left'
- }
- }}
- dangerouslySetInnerHTML={{ __html: strVal }}
- />
- );
+ return (
+ <Box
+ className='prose prose-sm text-gray-700'
+ sx={{
+ '& ul, & ol': {
+ paddingLeft: '1.2rem',
+ margin: 0,
+ textAlign: 'left',
+ },
+ '& li': {
+ fontWeight: 'normal',
+ marginBottom: '4px',
+ textAlign: 'left',
+ },
+ '& strong': {
+ display: 'block',
+ marginBottom: '2px',
+ fontWeight: 'bold',
+ },
+ '& p': {
+ margin: 0,
+ textAlign: 'left',
+ },
+ }}
+ dangerouslySetInnerHTML={{ __html: strVal }}
+ />
+ );
}
return strVal;
};
-
const allImages = (() => {
const arr: string[] = [];
if (product?.image) arr.push(product.image);
@@ -389,8 +386,8 @@ const ProductDetail = ({ product }: Props) => {
};
const scrollToIndex = (i: number) => {
- const el = sliderRef.current;
- if (!el) return;
+ const el = sliderRef.current;
+ if (!el) return;
el.scrollTo({ left: i * el.clientWidth, behavior: 'smooth' });
setCurrentIdx(i);
setMainImage(allImages[i] || '');
@@ -398,15 +395,17 @@ const ProductDetail = ({ product }: Props) => {
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 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+)?)/);
@@ -420,23 +419,31 @@ const ProductDetail = ({ product }: Props) => {
return numA - numB;
}
- return String(labelA).localeCompare(String(labelB), undefined, {
- numeric: true,
- sensitivity: 'base'
+ 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 === '<p><br></p>' ? 'Deskripsi produk tidak tersedia.' : finalDescription;
+ const activeMagentoDesc = selectedVariant?.id
+ ? descriptionMap[String(selectedVariant.id)]
+ : '';
+ const finalDescription =
+ activeMagentoDesc ||
+ product.description ||
+ 'Deskripsi produk tidak tersedia.';
+ const cleanDescription =
+ finalDescription === '<p><br></p>'
+ ? 'Deskripsi produk tidak tersedia.'
+ : finalDescription;
return (
<>
{/* 3. MODAL POPUP DIRENDER DISINI */}
{/* Render di luar layout utama agar tidak tertutup elemen lain */}
- <ProductComparisonModal
- isOpen={isCompareOpen}
+ <ProductComparisonModal
+ isOpen={isCompareOpen}
onClose={() => setCompareOpen(false)}
mainProduct={product}
selectedVariant={selectedVariant}
@@ -478,23 +485,52 @@ const ProductDetail = ({ product }: Props) => {
{/* ... Image Slider ... */}
{isMobile ? (
<div className='relative'>
- <div ref={sliderRef} onScroll={handleMobileScroll} className='flex overflow-x-auto snap-x snap-mandatory scroll-smooth no-scrollbar' style={{ scrollBehavior: 'smooth', msOverflowStyle: 'none', scrollbarWidth: 'none' }}>
+ <div
+ ref={sliderRef}
+ onScroll={handleMobileScroll}
+ className='flex overflow-x-auto snap-x snap-mandatory scroll-smooth no-scrollbar'
+ style={{
+ scrollBehavior: 'smooth',
+ msOverflowStyle: 'none',
+ scrollbarWidth: 'none',
+ }}
+ >
{allImages.length > 0 ? (
allImages.map((img, i) => (
- <div key={i} className='w-full flex-shrink-0 snap-center flex justify-center items-center'>
- <img src={img} alt={`Gambar ${i + 1}`} className='w-[85%] aspect-square object-contain' onError={(e) => { (e.target as HTMLImageElement).src = '/images/noimage.jpeg'; }} />
+ <div
+ key={i}
+ className='w-full flex-shrink-0 snap-center flex justify-center items-center'
+ >
+ <img
+ src={img}
+ alt={`Gambar ${i + 1}`}
+ className='w-[85%] aspect-square object-contain'
+ onError={(e) => {
+ (e.target as HTMLImageElement).src =
+ '/images/noimage.jpeg';
+ }}
+ />
</div>
))
) : (
<div className='w-full flex-shrink-0 snap-center flex justify-center items-center'>
- <img src={mainImage || '/images/noimage.jpeg'} alt='Gambar produk' className='w-[85%] aspect-square object-contain' />
+ <img
+ src={mainImage || '/images/noimage.jpeg'}
+ alt='Gambar produk'
+ className='w-[85%] aspect-square object-contain'
+ />
</div>
)}
</div>
{allImages.length > 1 && (
<div className='absolute bottom-2 left-0 right-0 flex justify-center gap-2'>
{allImages.map((_, i) => (
- <button key={i} aria-label={`Ke slide ${i + 1}`} className={`w-2 h-2 rounded-full ${currentIdx === i ? 'bg-gray-800' : 'bg-gray-300'}`} onClick={() => scrollToIndex(i)} />
+ <button
+ key={i}
+ aria-label={`Ke slide ${i + 1}`}
+ className={`w-2 h-2 rounded-full ${currentIdx === i ? 'bg-gray-800' : 'bg-gray-300'}`}
+ onClick={() => scrollToIndex(i)}
+ />
))}
</div>
)}
@@ -506,8 +542,21 @@ const ProductDetail = ({ product }: Props) => {
<div className='mt-4 overflow-x-auto'>
<div className='flex space-x-3 pb-3'>
{allImages.map((img, index) => (
- <div key={index} className={`flex-shrink-0 w-16 h-16 cursor-pointer border-2 rounded-md transition-colors ${mainImage === img ? 'border-red-500 ring-2 ring-red-200' : 'border-gray-200 hover:border-gray-300'}`} onClick={() => setMainImage(img)}>
- <img src={img} alt={`Thumbnail ${index + 1}`} className='w-full h-full object-cover rounded-sm' loading='lazy' onError={(e) => { (e.target as HTMLImageElement).src = '/images/noimage.jpeg'; }} />
+ <div
+ key={index}
+ className={`flex-shrink-0 w-16 h-16 cursor-pointer border-2 rounded-md transition-colors ${mainImage === img ? 'border-red-500 ring-2 ring-red-200' : 'border-gray-200 hover:border-gray-300'}`}
+ onClick={() => setMainImage(img)}
+ >
+ <img
+ src={img}
+ alt={`Thumbnail ${index + 1}`}
+ className='w-full h-full object-cover rounded-sm'
+ loading='lazy'
+ onError={(e) => {
+ (e.target as HTMLImageElement).src =
+ '/images/noimage.jpeg';
+ }}
+ />
</div>
))}
</div>
@@ -578,17 +627,37 @@ const ProductDetail = ({ product }: Props) => {
{/* === SECTION TABS: DESKRIPSI & SPESIFIKASI === */}
<div className={style['section-card']}>
- <Tabs variant="unstyled">
- <TabList borderBottom="1px solid" borderColor="gray.200">
- <Tab
- _selected={{ color: 'red.600', borderColor: 'red.600', borderBottomWidth: '3px', fontWeight: 'bold', marginBottom: '-1.5px' }}
- color="gray.500" fontWeight="medium" fontSize="sm" px={4} py={3}
+ <Tabs variant='unstyled'>
+ <TabList borderBottom='1px solid' borderColor='gray.200'>
+ <Tab
+ _selected={{
+ color: 'red.600',
+ borderColor: 'red.600',
+ borderBottomWidth: '3px',
+ fontWeight: 'bold',
+ marginBottom: '-1.5px',
+ }}
+ color='gray.500'
+ fontWeight='medium'
+ fontSize='sm'
+ px={4}
+ py={3}
>
Deskripsi
</Tab>
- <Tab
- _selected={{ color: 'red.600', borderColor: 'red.600', borderBottomWidth: '3px', fontWeight: 'bold', marginBottom: '-1.5px' }}
- color="gray.500" fontWeight="medium" fontSize="sm" px={4} py={3}
+ <Tab
+ _selected={{
+ color: 'red.600',
+ borderColor: 'red.600',
+ borderBottomWidth: '3px',
+ fontWeight: 'bold',
+ marginBottom: '-1.5px',
+ }}
+ color='gray.500'
+ fontWeight='medium'
+ fontSize='sm'
+ px={4}
+ py={3}
>
Spesifikasi
</Tab>
@@ -603,53 +672,54 @@ const ProductDetail = ({ product }: Props) => {
<TabPanels>
{/* DESKRIPSI */}
<TabPanel px={0} py={6}>
- <div className='overflow-x-auto text-sm text-gray-700'>
+ <div className='overflow-x-auto text-sm text-gray-700'>
{loadingSpecs ? (
<Stack spacing={4}>
- <Skeleton height='20px' width="100%" />
- <Skeleton height='20px' width="90%" />
- <Skeleton height='20px' width="95%" />
- <Skeleton height='20px' width="70%" />
+ <Skeleton height='20px' width='100%' />
+ <Skeleton height='20px' width='90%' />
+ <Skeleton height='20px' width='95%' />
+ <Skeleton height='20px' width='70%' />
</Stack>
) : (
- <Box
- className={style['description']}
+ <Box
+ className={style['description']}
sx={{
- 'ul, ol': {
- marginTop: '0.5em !important',
- marginBottom: '1em !important',
- marginLeft: '0 !important',
- listStylePosition: 'outside !important',
- paddingLeft: '1.5em !important'
- },
- 'ul': { listStyleType: 'disc !important' },
- 'ol': { listStyleType: 'decimal !important' },
- 'li': {
- marginBottom: '0.4em !important',
- paddingLeft: '0.3em !important',
- lineHeight: '1.6 !important'
- }
+ 'ul, ol': {
+ marginTop: '0.5em !important',
+ marginBottom: '1em !important',
+ marginLeft: '0 !important',
+ listStylePosition: 'outside !important',
+ paddingLeft: '1.5em !important',
+ },
+ ul: { listStyleType: 'disc !important' },
+ ol: { listStyleType: 'decimal !important' },
+ li: {
+ marginBottom: '0.4em !important',
+ paddingLeft: '0.3em !important',
+ lineHeight: '1.6 !important',
+ },
}}
- dangerouslySetInnerHTML={{ __html: cleanDescription }}
+ dangerouslySetInnerHTML={{ __html: cleanDescription }}
/>
)}
-
</div>
</TabPanel>
{/* SPESIFIKASI */}
<TabPanel px={0} py={2}>
- <Box
- border="1px solid"
- borderColor="gray.200"
- borderRadius="sm"
- overflowX="auto"
- overflowY="auto"
- maxHeight="500px"
+ <Box
+ border='1px solid'
+ borderColor='gray.200'
+ borderRadius='sm'
+ overflowX='auto'
+ overflowY='auto'
+ maxHeight='500px'
css={isMobile ? cssScrollbarMobile : cssScrollbarDesktop}
>
{loadingSpecs ? (
- <Center py={6}><Spinner color='red.500' /></Center>
+ <Center py={6}>
+ <Spinner color='red.500' />
+ </Center>
) : specsMatrix.length > 0 ? (
(() => {
const variantCount = sortedVariants.length;
@@ -657,47 +727,57 @@ const ProductDetail = ({ product }: Props) => {
// === 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 (
- <Table variant="simple" size={isMobile ? "sm" : "md"}>
- <Tbody>
- {rows.map((row, idx) => (
- <Tr key={idx} bg={idx % 2 === 0 ? 'white' : 'gray.50'}>
- {/* Kolom Label (Kiri) */}
- <Td
- width="40%"
- fontWeight="bold"
- color="gray.600"
- borderColor="gray.200"
- verticalAlign="top"
- py={3}
- >
- {row.label}
- </Td>
- {/* Kolom Value (Kanan) */}
- <Td
- color="gray.800"
- borderColor="gray.200"
- verticalAlign="top"
- py={3}
- >
- {renderSpecValue(row.values[singleVariantId])}
- </Td>
- </Tr>
- ))}
- </Tbody>
- </Table>
- );
+ 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 (
+ <Table
+ variant='simple'
+ size={isMobile ? 'sm' : 'md'}
+ >
+ <Tbody>
+ {rows.map((row, idx) => (
+ <Tr
+ key={idx}
+ bg={idx % 2 === 0 ? 'white' : 'gray.50'}
+ >
+ {/* Kolom Label (Kiri) */}
+ <Td
+ width='40%'
+ fontWeight='bold'
+ color='gray.600'
+ borderColor='gray.200'
+ verticalAlign='top'
+ py={3}
+ >
+ {row.label}
+ </Td>
+ {/* Kolom Value (Kanan) */}
+ <Td
+ color='gray.800'
+ borderColor='gray.200'
+ verticalAlign='top'
+ py={3}
+ >
+ {renderSpecValue(
+ row.values[singleVariantId],
+ )}
+ </Td>
+ </Tr>
+ ))}
+ </Tbody>
+ </Table>
+ );
}
// === LOGIC 2: MULTIPLE VARIANTS (MATRIX TABLE HORIZONTAL) ===
@@ -705,51 +785,63 @@ const ProductDetail = ({ product }: Props) => {
const subHeaders: any[] = [];
const flatSpecs: any[] = [];
- specsMatrix.forEach(row => {
+ 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);
- });
+ 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);
+ topHeaders.push({
+ label: row.label,
+ type: 'single',
+ colSpan: 1,
+ rowSpan: 2,
+ });
+ flatSpecs.push(row);
}
});
return (
- <Table variant="simple" size={isMobile ? "sm" : "md"}>
- <Thead bg="red.600" position="sticky" top={0} zIndex={3}>
+ <Table
+ variant='simple'
+ size={isMobile ? 'sm' : 'md'}
+ >
+ <Thead
+ bg='red.600'
+ position='sticky'
+ top={0}
+ zIndex={3}
+ >
<Tr>
{topHeaders.map((th, idx) => (
- <Th
+ <Th
key={`top-${idx}`}
- position={idx === 0 ? "sticky" : "static"}
+ position={idx === 0 ? 'sticky' : 'static'}
left={idx === 0 ? 0 : undefined}
zIndex={idx === 0 ? 4 : 3}
- boxShadow={idx === 0 ? "2px 0 5px -2px rgba(0,0,0,0.2)" : "none"}
- bg="red.600"
+ boxShadow={
+ idx === 0
+ ? '2px 0 5px -2px rgba(0,0,0,0.2)'
+ : 'none'
+ }
+ bg='red.600'
colSpan={th.colSpan}
rowSpan={th.rowSpan}
- color="white"
- textAlign="center"
- fontSize={isMobile ? "xs" : "sm"}
- textTransform="none"
- fontWeight="800"
- letterSpacing="wide"
- verticalAlign="middle"
- borderBottom="none"
+ color='white'
+ textAlign='center'
+ fontSize={isMobile ? 'xs' : 'sm'}
+ textTransform='none'
+ fontWeight='800'
+ letterSpacing='wide'
+ verticalAlign='middle'
+ borderBottom='none'
px={isMobile ? 2 : 4}
>
{th.label}
@@ -758,23 +850,32 @@ const ProductDetail = ({ product }: Props) => {
</Tr>
<Tr>
{subHeaders.map((sub, idx) => {
- const isFirstHeaderGroup = topHeaders[0]?.type === 'group';
- const shouldSticky = idx === 0 && isFirstHeaderGroup;
- return (
- <Th
+ const isFirstHeaderGroup =
+ topHeaders[0]?.type === 'group';
+ const shouldSticky =
+ idx === 0 && isFirstHeaderGroup;
+ return (
+ <Th
key={`sub-${idx}`}
- position={shouldSticky ? "sticky" : "static"}
+ position={
+ shouldSticky ? 'sticky' : 'static'
+ }
left={shouldSticky ? 0 : undefined}
- zIndex={shouldSticky ? 4 : 1}
- boxShadow={shouldSticky ? "2px 0 5px -2px rgba(0,0,0,0.2)" : "none"}
- color="white"
- textAlign="center"
- fontSize="xs"
- textTransform="none"
- verticalAlign="middle"
- whiteSpace="nowrap"
- bg="red.600"
- pt={1} pb={1}
+ zIndex={shouldSticky ? 4 : 1}
+ boxShadow={
+ shouldSticky
+ ? '2px 0 5px -2px rgba(0,0,0,0.2)'
+ : 'none'
+ }
+ color='white'
+ textAlign='center'
+ fontSize='xs'
+ textTransform='none'
+ verticalAlign='middle'
+ whiteSpace='nowrap'
+ bg='red.600'
+ pt={1}
+ pb={1}
px={isMobile ? 2 : 4}
>
{sub.label}
@@ -786,29 +887,42 @@ const ProductDetail = ({ product }: Props) => {
<Tbody>
{sortedVariants.map((v, vIdx) => (
- <Tr key={v.id} bg={vIdx % 2 === 0 ? 'white' : 'gray.50'}>
+ <Tr
+ key={v.id}
+ bg={vIdx % 2 === 0 ? 'white' : 'gray.50'}
+ >
{flatSpecs.map((spec, sIdx) => {
const rawValue = spec.values[v.id] || '-';
const isFirstCol = sIdx === 0;
return (
- <Td
- key={sIdx}
- position={isFirstCol ? "sticky" : "static"}
+ <Td
+ key={sIdx}
+ position={
+ isFirstCol ? 'sticky' : 'static'
+ }
left={isFirstCol ? 0 : undefined}
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"}
- verticalAlign="middle"
- px={isMobile ? 1 : 2}
- py={3}
- minW={isMobile ? "100px" : "120px"}
- maxW="200px"
- whiteSpace="normal"
- overflowWrap="break-word"
- fontWeight={isFirstCol ? "bold" : "normal"}
+ 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'}
+ verticalAlign='middle'
+ px={isMobile ? 1 : 2}
+ py={3}
+ minW={isMobile ? '100px' : '120px'}
+ maxW='200px'
+ whiteSpace='normal'
+ overflowWrap='break-word'
+ fontWeight={
+ isFirstCol ? 'bold' : 'normal'
+ }
>
{renderSpecValue(rawValue)}
</Td>
@@ -821,7 +935,9 @@ const ProductDetail = ({ product }: Props) => {
);
})()
) : (
- <Box p={4} color="gray.500" fontSize="sm"><Text>Spesifikasi teknis belum tersedia.</Text></Box>
+ <Box p={4} color='gray.500' fontSize='sm'>
+ <Text>Spesifikasi teknis belum tersedia.</Text>
+ </Box>
)}
</Box>
</TabPanel>
@@ -834,17 +950,48 @@ const ProductDetail = ({ product }: Props) => {
{isDesktop && (
<div className='md:w-3/12'>
{/* 4. INTEGRASI: PASSING HANDLER MODAL KE PRICE ACTION */}
- <PriceAction
- product={product}
- onCompare={() => setCompareOpen(true)}
+ <PriceAction
+ product={product}
+ onCompare={() => setCompareOpen(true)}
/>
-
+
<div className='flex gap-x-5 items-center justify-center py-4'>
- <Button as={Link} href={askAdminUrl} variant='link' target='_blank' colorScheme='gray' leftIcon={<MessageCircleIcon size={18} />} isDisabled={!hasPrice}>Ask Admin</Button>
+ <Button
+ as={Link}
+ href={askAdminUrl}
+ variant='link'
+ target='_blank'
+ colorScheme='gray'
+ leftIcon={<MessageCircleIcon size={18} />}
+ isDisabled={!hasPrice}
+ >
+ Ask Admin
+ </Button>
<span>|</span>
- <div className={hasPrice ? '' : 'opacity-40 pointer-events-none'}><AddToWishlist productId={product.id} /></div>
+ <div className={hasPrice ? '' : 'opacity-40 pointer-events-none'}>
+ <AddToWishlist productId={product.id} />
+ </div>
<span>|</span>
- {canShare && (<RWebShare data={{ text: 'Check out this product', title: `${product.name} - Indoteknik.com`, url: (process.env.NEXT_PUBLIC_SELF_HOST || '') + (router?.asPath || '/'), }}><Button variant='link' colorScheme='gray' leftIcon={<Share2Icon size={18} />} isDisabled={!hasPrice}>Share</Button></RWebShare>)}
+ {canShare && (
+ <RWebShare
+ data={{
+ text: 'Check out this product',
+ title: `${product.name} - Indoteknik.com`,
+ url:
+ (process.env.NEXT_PUBLIC_SELF_HOST || '') +
+ (router?.asPath || '/'),
+ }}
+ >
+ <Button
+ variant='link'
+ colorScheme='gray'
+ leftIcon={<Share2Icon size={18} />}
+ isDisabled={!hasPrice}
+ >
+ Share
+ </Button>
+ </RWebShare>
+ )}
</div>
<div className='h-6' />
@@ -867,4 +1014,4 @@ const ProductDetail = ({ product }: Props) => {
);
};
-export default ProductDetail; \ No newline at end of file
+export default ProductDetail;