summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src-migrate/modules/product-detail/components/AddToQuotation.tsx93
-rw-r--r--src-migrate/modules/product-detail/components/PriceAction.tsx38
-rw-r--r--src-migrate/modules/product-detail/components/ProductComparisonModal.tsx46
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx95
4 files changed, 196 insertions, 76 deletions
diff --git a/src-migrate/modules/product-detail/components/AddToQuotation.tsx b/src-migrate/modules/product-detail/components/AddToQuotation.tsx
index 3e811330..0ea88830 100644
--- a/src-migrate/modules/product-detail/components/AddToQuotation.tsx
+++ b/src-migrate/modules/product-detail/components/AddToQuotation.tsx
@@ -1,7 +1,8 @@
import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
import style from '../styles/price-action.module.css';
-import { Button, Link, useToast } from '@chakra-ui/react';
-import { ArrowDownTrayIcon } from '@heroicons/react/24/outline';
+import { Button, Link, useToast, Icon } from '@chakra-ui/react';
+// Pastikan icon ini ada (atau ganti dengan text sementara jika error)
+import { ScaleIcon } from '@heroicons/react/24/outline';
import product from 'next-seo/lib/jsonld/product';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
@@ -17,12 +18,15 @@ import { createSlug } from '~/libs/slug';
import formatCurrency from '~/libs/formatCurrency';
import { useProductDetail } from '../stores/useProductDetail';
import useDevice from '@/core/hooks/useDevice';
+import DesktopView from '@/core/components/views/DesktopView';
+import MobileView from '@/core/components/views/MobileView';
type Props = {
variantId: number | null;
quantity?: number;
source?: 'buy' | 'add_to_cart';
products: IProductDetail;
+ onCompare?: () => void; // <--- 1. Tambah Props
};
type Status = 'idle' | 'loading' | 'success';
@@ -32,6 +36,7 @@ const AddToQuotation = ({
quantity = 1,
source = 'add_to_cart',
products,
+ onCompare // <--- 2. Tangkap Props
}: Props) => {
const auth = getAuth();
const router = useRouter();
@@ -106,37 +111,59 @@ const AddToQuotation = ({
}, 3000);
}, [status]);
- const btnConfig = {
- add_to_cart: {
- colorScheme: 'red',
- variant: 'outline',
- text: 'Keranjang',
- },
- buy: {
- colorScheme: 'red',
- variant: 'solid',
- text: 'Beli',
- },
- };
-
return (
- <div className='w-full'>
- <Button
- onClick={handleButton}
- color={'red'}
- colorScheme='white'
- className='w-full border-2 p-2 gap-1 hover:bg-slate-100 flex items-center'
- isDisabled={!hasPrice}
- >
- <ImageNext
- src={isDesktop ? '/images/doc_red.svg' : '/images/doc.svg'}
- alt='penawaran instan'
- className=''
- width={25}
- height={25}
- />
- {isDesktop ? 'Penawaran Harga Instan' : ''}
- </Button>
+ <div className='w-full flex flex-col gap-3'> {/* Ganti div biasa jadi Flex Column gap 3 */}
+
+ {/* 3. TAMPILAN DESKTOP: GRID 2 KOLOM (Bandingkan & Penawaran) */}
+ <DesktopView>
+ <div className="grid grid-cols-2 gap-3 w-full">
+ {/* Tombol Kiri: Bandingkan */}
+ <Button
+ onClick={onCompare} // <--- Jalankan Modal Compare
+ variant="outline"
+ colorScheme="gray"
+ className="w-full border border-gray-300 p-2 gap-2 flex items-center justify-center text-gray-600 hover:text-red-600 hover:border-red-600 transition-all font-normal text-sm"
+ >
+ <Icon as={ScaleIcon} w={5} h={5} />
+ Bandingkan
+ </Button>
+
+ {/* Tombol Kanan: Penawaran (Link WA) */}
+ <Button
+ as={Link}
+ href={askAdminUrl}
+ target='_blank'
+ variant="outline"
+ colorScheme="gray"
+ className="w-full border border-gray-300 p-2 gap-2 flex items-center justify-center text-gray-600 hover:text-red-600 hover:border-red-600 transition-all font-normal text-sm"
+ _hover={{ textDecoration: 'none' }}
+ onClick={handleButton} // Opsional: Jalankan handleButton jika ingin fungsi tombol ini tetap jalan
+ >
+ <ImageNext src="/images/doc_red.svg" width={20} height={20} alt="penawaran" />
+ Penawaran
+ </Button>
+ </div>
+ </DesktopView>
+
+ {/* TAMPILAN MOBILE (Tetap seperti semula - hanya icon Penawaran) */}
+ <MobileView>
+ <Button
+ onClick={handleButton}
+ color={'red'}
+ colorScheme='white'
+ className='w-full border-2 p-2 gap-1 hover:bg-slate-100 flex items-center'
+ isDisabled={!hasPrice}
+ >
+ <ImageNext
+ src='/images/doc.svg'
+ alt='penawaran instan'
+ className=''
+ width={25}
+ height={25}
+ />
+ </Button>
+ </MobileView>
+
<BottomPopup
className='!container'
title='Berhasil Ditambahkan'
@@ -243,4 +270,4 @@ const AddToQuotation = ({
);
};
-export default AddToQuotation;
+export default AddToQuotation; \ No newline at end of file
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx
index d73ab5f6..ee8009ef 100644
--- a/src-migrate/modules/product-detail/components/PriceAction.tsx
+++ b/src-migrate/modules/product-detail/components/PriceAction.tsx
@@ -1,5 +1,4 @@
import style from '../styles/price-action.module.css';
-
import Image from 'next/image';
import Link from 'next/link';
import { useEffect, useState } from 'react';
@@ -15,14 +14,17 @@ import { Button, Skeleton } from '@chakra-ui/react';
import DesktopView from '@/core/components/views/DesktopView';
import MobileView from '@/core/components/views/MobileView';
+// 1. Tambahkan onCompare (Optional) di sini
type Props = {
product: IProductDetail;
+ onCompare?: () => void;
};
const PPN: number = process.env.NEXT_PUBLIC_PPN
? parseFloat(process.env.NEXT_PUBLIC_PPN)
: 0;
-const PriceAction = ({ product }: Props) => {
+
+const PriceAction = ({ product, onCompare }: Props) => {
const {
activePrice,
setActive,
@@ -146,19 +148,6 @@ const PriceAction = ({ product }: Props) => {
</>
)}
- {/* {!!activePrice && activePrice.price === 0 && (
- <span>
- Hubungi kami untuk dapatkan harga terbaik,{' '}
- <Link
- href={askAdminUrl}
- target='_blank'
- className={style['contact-us']}
- >
- klik disini
- </Link>
- </span>
- )} */}
-
<DesktopView>
<div className='h-4' />
<div className='flex gap-x-5 items-center'>
@@ -227,9 +216,6 @@ const PriceAction = ({ product }: Props) => {
)}
</div>
</div>
- {/* <span className='text-[12px] text-red-500 italic'>
- * {qtyPickUp} barang bisa di pickup
- </span> */}
</DesktopView>
{/* ===== MOBILE: grid kiri-kanan, kanan hanya qty ===== */}
@@ -263,12 +249,6 @@ const PriceAction = ({ product }: Props) => {
</Link>
)}
</div>
-
- {/* {qtyPickUp > 0 && (
- <div className='text-[12px] mt-1 text-red-500 italic'>
- * {qtyPickUp} barang bisa di pickup
- </div>
- )} */}
</div>
{/* Kanan: hanya qty, rata kanan */}
@@ -295,9 +275,9 @@ const PriceAction = ({ product }: Props) => {
value={quantityInput}
onChange={(e) => setQuantityInput(e.target.value)}
className='h-11 md:h-12 w-16 md:w-20 text-center text-lg md:text-xl outline-none border-x
- [appearance:textfield]
- [&::-webkit-outer-spin-button]:appearance-none
- [&::-webkit-inner-spin-button]:appearance-none'
+ [appearance:textfield]
+ [&::-webkit-outer-spin-button]:appearance-none
+ [&::-webkit-inner-spin-button]:appearance-none'
disabled={!hasPrice}
/>
@@ -335,11 +315,13 @@ const PriceAction = ({ product }: Props) => {
)}
</div>
<div className='mt-4'>
+ {/* 2. TERUSKAN onCompare KE SINI */}
<AddToQuotation
source='buy'
products={product}
variantId={activeVariantId}
quantity={Number(quantityInput)}
+ onCompare={onCompare}
/>
</div>
</DesktopView>
@@ -376,4 +358,4 @@ const PriceAction = ({ product }: Props) => {
);
};
-export default PriceAction;
+export default PriceAction; \ No newline at end of file
diff --git a/src-migrate/modules/product-detail/components/ProductComparisonModal.tsx b/src-migrate/modules/product-detail/components/ProductComparisonModal.tsx
new file mode 100644
index 00000000..aa4388a6
--- /dev/null
+++ b/src-migrate/modules/product-detail/components/ProductComparisonModal.tsx
@@ -0,0 +1,46 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalBody,
+ ModalCloseButton,
+ Button,
+ ModalFooter,
+ Center,
+ Text,
+ Box
+} from '@chakra-ui/react';
+
+type Props = {
+ isOpen: boolean;
+ onClose: () => void;
+};
+
+const ProductComparisonModal = ({ isOpen, onClose }: Props) => {
+ return (
+ <Modal isOpen={isOpen} onClose={onClose} size="xl" isCentered>
+ <ModalOverlay />
+ <ModalContent>
+ <ModalHeader borderBottom="1px solid #eee">Perbandingan Produk</ModalHeader>
+ <ModalCloseButton />
+
+ <ModalBody py={10}>
+ <Center flexDirection="column">
+ <Box fontSize="4xl" mb={4}>Test</Box>
+ <Text fontWeight="bold" fontSize="lg">Modal Berhasil Terbuka!</Text>
+ <Text fontSize="sm" color="gray.500" mt={2}>
+ Fitur komparasi produk akan muncul di sini nanti.
+ </Text>
+ </Center>
+ </ModalBody>
+
+ <ModalFooter borderTop="1px solid #eee">
+ <Button colorScheme="red" onClick={onClose}>Tutup</Button>
+ </ModalFooter>
+ </ModalContent>
+ </Modal>
+ );
+};
+
+export default ProductComparisonModal; \ No newline at end of file
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index fab5ecf3..f06f958a 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -49,6 +49,9 @@ import SimilarBottom from './SimilarBottom';
import SimilarSide from './SimilarSide';
import dynamic from 'next/dynamic';
+// 1. IMPORT MODAL (Baru)
+import ProductComparisonModal from './ProductComparisonModal';
+
import { gtagProductDetail } from '@/core/utils/googleTag';
type Props = {
@@ -74,6 +77,9 @@ const ProductDetail = ({ product }: Props) => {
const [loadingSpecs, setLoadingSpecs] = useState(false);
+ // 2. STATE MODAL COMPARE (Baru)
+ const [isCompareOpen, setCompareOpen] = useState(false);
+
useEffect(() => {
try {
setAuth(getAuth() ?? null);
@@ -322,6 +328,13 @@ const ProductDetail = ({ product }: Props) => {
return (
<>
+ {/* 3. MODAL POPUP DIRENDER DISINI */}
+ {/* Render di luar layout utama agar tidak tertutup elemen lain */}
+ <ProductComparisonModal
+ isOpen={isCompareOpen}
+ onClose={() => setCompareOpen(false)}
+ />
+
<div className='relative'>
{isDesktop && !hasPrice && (
<div className='absolute inset-0 z-[20] flex items-center justify-center pointer-events-none select-none'>
@@ -406,9 +419,9 @@ const ProductDetail = ({ product }: Props) => {
size={18}
className='text-red-600 shrink-0 mx-2'
/>
- <div className='text-red-600 font-normal text-h-sm p-2'>
+ <h1 className='text-red-600 font-normal text-h-sm'>
Maaf untuk saat ini Produk yang anda cari tidak tersedia
- </div>
+ </h1>
</div>
)}
<div className='h-6 md:h-0' />
@@ -426,9 +439,9 @@ const ProductDetail = ({ product }: Props) => {
size={18}
className='text-red-600 shrink-0 mx-2'
/>
- <div className='text-red-600 font-normal text-h-sm p-2'>
+ <h1 className='text-red-600 font-normal text-h-sm'>
Maaf untuk saat ini Produk yang anda cari tidak tersedia
- </div>
+ </h1>
</div>
)}
<h1 className={style['title']}>{product.name}</h1>
@@ -448,7 +461,10 @@ const ProductDetail = ({ product }: Props) => {
<div className='h-4 md:h-10' />
{!!activeVariantId && !isApproval && (
- <ProductPromoSection product={product} productId={activeVariantId} />
+ <ProductPromoSection
+ product={product}
+ productId={activeVariantId}
+ />
)}
<div className='h-0 md:h-6' />
@@ -457,25 +473,49 @@ const ProductDetail = ({ product }: Props) => {
<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}>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}>Spesifikasi</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}>Detail Lainnya</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}
+ >
+ 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}
+ >
+ Spesifikasi
+ </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}
+ >
+ Detail Lainnya
+ </Tab>
</TabList>
<TabPanels>
{/* DESKRIPSI */}
<TabPanel px={0} py={6}>
<div className='overflow-x-auto text-sm text-gray-700'>
- <div className={style['description']} dangerouslySetInnerHTML={{ __html: !product.description || product.description === '<p><br></p>' ? '<p>Lorem ipsum dolor sit amet.</p>' : product.description, }} />
+ <div
+ className={style['description']}
+ dangerouslySetInnerHTML={{
+ __html:
+ !product.description || product.description === '<p><br></p>'
+ ? '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>'
+ : product.description,
+ }}
+ />
</div>
</TabPanel>
- {/* SPESIFIKASI (LOGIKA GROUPING + RATA TENGAH) */}
+ {/* SPESIFIKASI (MATRIKS & SPLIT PIPA) */}
<TabPanel px={0} py={2}>
<Box border="1px solid" borderColor="gray.200" borderRadius="sm" overflowX="auto">
{loadingSpecs ? (
<Center py={6}><Spinner color='red.500' /></Center>
) : specsMatrix.length > 0 ? (
+ // Cek Single vs Multi Variant
(() => {
const isSingleVariant = product.variants.length === 1;
const globalAlign = isSingleVariant ? "left" : "center";
@@ -484,11 +524,27 @@ const ProductDetail = ({ product }: Props) => {
<Table variant="simple" size="md">
<Thead bg="red.600">
<Tr>
- <Th width={isSingleVariant ? "30%" : "20%"} borderColor="whiteAlpha.300" color="white" fontSize="sm" textTransform="none" verticalAlign="middle">
+ <Th
+ width={isSingleVariant ? "30%" : "20%"}
+ borderColor="whiteAlpha.300"
+ color="white"
+ fontSize="sm"
+ textTransform="none"
+ verticalAlign="middle"
+ >
Spesifikasi
</Th>
+
{product.variants.map(v => (
- <Th key={v.id} borderColor="whiteAlpha.300" color="white" textAlign={globalAlign} fontSize="sm" textTransform="none" verticalAlign="middle">
+ <Th
+ key={v.id}
+ borderColor="whiteAlpha.300"
+ color="white"
+ textAlign={globalAlign}
+ fontSize="sm"
+ textTransform="none"
+ verticalAlign="middle"
+ >
{isSingleVariant ? 'Detail' : (v.attributes && v.attributes.length > 0 ? v.attributes.join(' - ') : v.code)}
</Th>
))}
@@ -563,13 +619,22 @@ const ProductDetail = ({ product }: Props) => {
</div>
</div>
- {/* ... (Bagian Sidebar & Bottom SAMA) ... */}
{isDesktop && (
<div className='md:w-3/12'>
- <PriceAction product={product} />
+ {/* 4. INTEGRASI: PASSING HANDLER MODAL KE PRICE ACTION */}
+ <PriceAction
+ product={product}
+ onCompare={() => setCompareOpen(true)}
+ />
+
<div className='flex gap-x-5 items-center justify-center py-4'>
- {/* ... Buttons ... */}
+ <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>
+ <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>)}
</div>
+
<div className='h-6' />
<div className={style['heading']}>Produk Serupa</div>
<div className='h-4' />