summaryrefslogtreecommitdiff
path: root/src-migrate/modules/product-detail/components/Information.tsx
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2026-01-31 16:22:14 +0000
committerIT Fixcomart <it@fixcomart.co.id>2026-01-31 16:22:14 +0000
commit8c6f1b3bf6eac52041337b33e746888933e1e34a (patch)
tree0787627b48ef1b1db8e6d76066eb3400afb71123 /src-migrate/modules/product-detail/components/Information.tsx
parentec7ab4c654fc5b29b277d42ad84986f4c1220134 (diff)
parent99aa3500fc5bbb3bb24d73461639e6fc88042a85 (diff)
Merged in magento-v2.1 (pull request #472)
Magento v2.1
Diffstat (limited to 'src-migrate/modules/product-detail/components/Information.tsx')
-rw-r--r--src-migrate/modules/product-detail/components/Information.tsx229
1 files changed, 182 insertions, 47 deletions
diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx
index 813b6bf5..ce848267 100644
--- a/src-migrate/modules/product-detail/components/Information.tsx
+++ b/src-migrate/modules/product-detail/components/Information.tsx
@@ -9,17 +9,24 @@ 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)
);
@@ -29,22 +36,53 @@ 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);
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;
@@ -72,14 +110,19 @@ const Information = ({ product }: Props) => {
/* ======================
* Handlers
* ====================== */
- const handleOnChange = (value: string) => {
+ const handleOnChange = (vals: any) => {
setDisableFilter(true);
-
- const variant = variantOptions.find((item) => String(item.id) === value);
-
- if (!variant) return;
-
- setSelectedVariant(variant);
+ 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] : '')
+ );
+ setVariantOptions(product?.variants);
+ }
};
const handleOnKeyUp = (e: any) => {
@@ -87,6 +130,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,10 +171,17 @@ const Information = ({ product }: Props) => {
</InputGroup>
<AutoCompleteList>
- {variantOptions.map((option) => (
+ {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={String(option.id)}
+ value={option.code +
+ (option.attributes?.[0] ? ` - ${option.attributes[0]}` : '')
+ }
_selected={
option.id === selectedVariant?.id
? { bg: 'gray.300' }
@@ -135,15 +193,8 @@ const Information = ({ product }: Props) => {
{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={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)}%
@@ -162,16 +213,49 @@ const Information = ({ product }: Props) => {
))}
</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
@@ -181,8 +265,8 @@ const Information = ({ product }: Props) => {
product.manufacture.id.toString()
)}
>
- {product.manufacture.logo ? (
- <Image
+ {product?.manufacture.logo ? (
+ <ImageNext
height={50}
width={100}
src={product.manufacture.logo}
@@ -201,32 +285,83 @@ 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>
);
};
-export default Information;
+export default Information; \ No newline at end of file