summaryrefslogtreecommitdiff
path: root/src-migrate/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src-migrate/modules')
-rw-r--r--src-migrate/modules/product-detail/components/Image.tsx3
-rw-r--r--src-migrate/modules/product-detail/components/PriceAction.tsx73
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx96
-rw-r--r--src-migrate/modules/product-detail/styles/image.module.css14
-rw-r--r--src-migrate/modules/product-detail/styles/price-action.module.css10
-rw-r--r--src-migrate/modules/promo/components/Voucher.tsx2
-rw-r--r--src-migrate/modules/promo/styles/voucher.module.css33
7 files changed, 166 insertions, 65 deletions
diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx
index 96ae2027..d99b683c 100644
--- a/src-migrate/modules/product-detail/components/Image.tsx
+++ b/src-migrate/modules/product-detail/components/Image.tsx
@@ -50,8 +50,7 @@ const Image = ({ product }: Props) => {
<ImageUI
src={image}
alt={product.name}
- width={256}
- height={256}
+ fill
className={style['image']}
loading='eager'
priority
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx
index ffc9ba40..6cc2f0bf 100644
--- a/src-migrate/modules/product-detail/components/PriceAction.tsx
+++ b/src-migrate/modules/product-detail/components/PriceAction.tsx
@@ -66,17 +66,21 @@ const PriceAction = ({ product }: Props) => {
setQuantityInput('1');
}, [selectedVariant]);
- let voucherPastiHemat = 0;
+ const price = activePrice?.price_discount || activePrice?.price || 0;
+ const pricedigit = String(Math.floor(price)).length;
+ const fontSize = pricedigit >= 9 ? '20px' : undefined;
- if (
- product?.voucher_pasti_hemat
- ? product?.voucher_pasti_hemat.length
- : voucherPastiHemat > 0
- ) {
- const stringVoucher = product?.voucher_pasti_hemat[0];
- const validJsonString = stringVoucher.replace(/'/g, '"');
- voucherPastiHemat = JSON.parse(validJsonString);
- }
+ // let voucherPastiHemat = 0;
+
+ // if (
+ // product?.voucher_pasti_hemat
+ // ? product?.voucher_pasti_hemat.length
+ // : voucherPastiHemat > 0
+ // ) {
+ // const stringVoucher = product?.voucher_pasti_hemat[0];
+ // const validJsonString = stringVoucher.replace(/'/g, '"');
+ // voucherPastiHemat = JSON.parse(validJsonString);
+ // }
return (
<div
@@ -91,25 +95,40 @@ const PriceAction = ({ product }: Props) => {
<DesktopView>
<div className='flex items-end gap-x-2'>
{activePrice.discount_percentage > 0 && (
- <>
- <div className={style['disc-badge']}>
- {Math.floor(activePrice.discount_percentage)}%
- </div>
- <div className={style['disc-price']}>
- Rp {formatCurrency(activePrice.price || 0)}
- </div>
- </>
+ <div className={style['disc-badge']}>
+ {Math.floor(activePrice.discount_percentage)}%
+ </div>
)}
- <div className={style['main-price']}>
- Rp {formatCurrency(activePrice.price_discount || 0)}
+ <div
+ className={style['main-price']}
+ style={fontSize ? { fontSize } : undefined}
+ >
+ Rp{' '}
+ {formatCurrency(
+ activePrice.discount_percentage > 0
+ ? activePrice.price_discount || 0
+ : activePrice.price || 0
+ )}
</div>
+ {activePrice.discount_percentage > 0 && (
+ <div className={style['disc-price']}>
+ Rp {formatCurrency(activePrice.price || 0)}
+ </div>
+ )}
</div>
<div className='h-1' />
<div className={style['secondary-text']}>
Termasuk PPN: Rp{' '}
- {formatCurrency(Math.round(activePrice.price_discount * PPN))}
+ {formatCurrency(
+ Math.round(
+ (activePrice.discount_percentage > 0
+ ? activePrice.price_discount
+ : activePrice.price) * PPN
+ )
+ )}
</div>
</DesktopView>
+
<MobileView>
<div className='flex items-end gap-x-2'>
{activePrice.discount_percentage > 0 ? (
@@ -159,7 +178,7 @@ const PriceAction = ({ product }: Props) => {
<DesktopView>
<div className='h-4' />
- <div className='flex gap-x-5 items-center'>
+ <div className='flex gap-x-4 items-center'>
{/* Qty */}
<div className='relative flex items-center'>
<button
@@ -191,20 +210,20 @@ const PriceAction = ({ product }: Props) => {
</div>
{/* Stok */}
- <div>
+ <div className='min-w-[89px]'>
<Skeleton
isLoaded={sla}
h='21px'
className={sla?.qty < 10 ? 'text-red-600 font-medium' : ''}
>
- Stock : {sla?.qty}{' '}
+ Stock : {sla?.qty}
</Skeleton>
</div>
{/* Pickup badge */}
- <div>
+ <div className='shrink-0'>
{qtyPickUp > 0 && (
- <div className='flex items-center gap-2'>
+ <div className='flex items-center'>
<Link href='/panduan-pick-up-service' className='group'>
<Image
src='/images/PICKUP-NOW.png'
@@ -218,7 +237,7 @@ const PriceAction = ({ product }: Props) => {
)}
</div>
</div>
- <span className='text-[12px] text-red-500 italic'>
+ <span className='block text-[12px] text-red-500 italic mt-1'>
* {qtyPickUp} barang bisa di pickup
</span>
</DesktopView>
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index f32bb38e..51b080ef 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -22,16 +22,17 @@ import PriceAction from './PriceAction';
import SimilarBottom from './SimilarBottom';
import SimilarSide from './SimilarSide';
import dynamic from 'next/dynamic';
-
+import { TicketIcon } from '@heroicons/react/24/solid';
import { gtagProductDetail } from '@/core/utils/googleTag';
+import currencyFormat from '@/core/utils/currencyFormat';
type Props = {
product: IProductDetail;
};
const RWebShare = dynamic(
- () => import('react-web-share').then(m => m.RWebShare),
+ () => import('react-web-share').then((m) => m.RWebShare),
{ ssr: false }
);
@@ -42,7 +43,9 @@ const ProductDetail = ({ product }: Props) => {
const router = useRouter();
const [auth, setAuth] = useState<any>(null);
useEffect(() => {
- try { setAuth(getAuth() ?? null); } catch { }
+ try {
+ setAuth(getAuth() ?? null);
+ } catch {}
}, []);
const canShare =
@@ -87,7 +90,6 @@ const ProductDetail = ({ product }: Props) => {
setSelectedVariant(selectedVariant);
}, []);
-
const allImages = (() => {
const arr: string[] = [];
if (product?.image) arr.push(product.image);
@@ -95,7 +97,6 @@ const ProductDetail = ({ product }: Props) => {
Array.isArray(product?.image_carousel) &&
product.image_carousel.length
) {
-
const set = new Set(arr);
for (const img of product.image_carousel) {
if (!set.has(img)) {
@@ -108,15 +109,14 @@ const ProductDetail = ({ product }: Props) => {
})();
const [mainImage, setMainImage] = useState(allImages[0] || '');
+ const [discount, setDiscount] = useState(0);
useEffect(() => {
-
if (!allImages.includes(mainImage)) {
setMainImage(allImages[0] || '');
}
}, [allImages]);
-
const sliderRef = useRef<HTMLDivElement | null>(null);
const [currentIdx, setCurrentIdx] = useState(0);
@@ -138,6 +138,47 @@ const ProductDetail = ({ product }: Props) => {
setMainImage(allImages[i] || '');
};
+ const voucherNew = Array.isArray(product?.new_voucher_pasti_hemat)
+ ? product.new_voucher_pasti_hemat[0]
+ : undefined;
+
+ const voucher = voucherNew ?? null;
+
+ const type = voucher?.discount_type ?? ''; // 'percentage' | 'percent' | 'fixed'
+ const amount = Number(
+ voucher?.discountAmount ?? voucher?.discount_amount ?? 0
+ );
+ const max = Number(voucher?.max_discount ?? 0);
+ const min = Number(voucher?.min_purchase ?? 0);
+
+ const basePrice =
+ Number(product?.lowest_price?.price_discount ?? 0) ||
+ Number(product?.lowest_price?.price ?? 0);
+
+ function calcVoucherDiscount() {
+ if (!voucher || !basePrice) return 0;
+ if (min > 0 && basePrice < min) return 0;
+
+ const percent = type.toLowerCase().startsWith('percent')
+ ? amount <= 1
+ ? amount * 100
+ : amount
+ : 0;
+
+ let cut = 0;
+ if (type.toLowerCase().startsWith('percent')) {
+ cut = basePrice * (percent / 100);
+ } else {
+ cut = amount || 0;
+ }
+
+ if (max > 0) cut = Math.min(cut, max);
+ return Math.max(0, cut);
+ }
+
+ useEffect(() => {
+ setDiscount(calcVoucherDiscount());
+ }, [product?.lowest_price]);
return (
<>
@@ -165,7 +206,6 @@ const ProductDetail = ({ product }: Props) => {
>
{allImages.length > 0 ? (
allImages.map((img, i) => (
-
<div
key={i}
className='w-full flex-shrink-0 snap-center flex justify-center items-center'
@@ -200,8 +240,9 @@ const ProductDetail = ({ product }: Props) => {
<button
key={i}
aria-label={`Ke slide ${i + 1}`}
- className={`w-2 h-2 rounded-full ${currentIdx === i ? 'bg-gray-800' : 'bg-gray-300'
- }`}
+ className={`w-2 h-2 rounded-full ${
+ currentIdx === i ? 'bg-gray-800' : 'bg-gray-300'
+ }`}
onClick={() => scrollToIndex(i)}
/>
))}
@@ -220,10 +261,11 @@ const ProductDetail = ({ product }: Props) => {
{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'
- }`}
+ 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
@@ -249,6 +291,17 @@ const ProductDetail = ({ product }: Props) => {
{/* ===== Kolom kanan: info ===== */}
<div className='md:w-8/12 px-4 md:pl-6'>
<div className='h-6 md:h-0' />
+ {isMobile && (
+ <div className='text text-sm font-medium'>
+ <TicketIcon className='inline text-yellow-300 w-5 h-5' />{' '}
+ Pakai{' '}
+ <span className='text-green-600 font-extrabold'>
+ {' '}
+ Voucher belanja hemat {currencyFormat(discount)}
+ </span>{' '}
+ saat checkout
+ </div>
+ )}
<h1 className={style['title']}>{product.name}</h1>
<div className='h-3 md:h-0' />
<Information product={product} />
@@ -281,7 +334,8 @@ const ProductDetail = ({ product }: Props) => {
className={style['description']}
dangerouslySetInnerHTML={{
__html:
- !product.description || product.description == '<p><br></p>'
+ !product.description ||
+ product.description == '<p><br></p>'
? 'Belum ada deskripsi'
: product.description,
}}
@@ -317,10 +371,16 @@ const ProductDetail = ({ product }: Props) => {
data={{
text: 'Check out this product',
title: `${product.name} - Indoteknik.com`,
- url: (process.env.NEXT_PUBLIC_SELF_HOST || '') + (router?.asPath || '/'),
+ url:
+ (process.env.NEXT_PUBLIC_SELF_HOST || '') +
+ (router?.asPath || '/'),
}}
>
- <Button variant='link' colorScheme='gray' leftIcon={<Share2Icon size={18} />}>
+ <Button
+ variant='link'
+ colorScheme='gray'
+ leftIcon={<Share2Icon size={18} />}
+ >
Share
</Button>
</RWebShare>
@@ -350,8 +410,6 @@ const ProductDetail = ({ product }: Props) => {
</div>
</>
);
-
-
};
export default ProductDetail;
diff --git a/src-migrate/modules/product-detail/styles/image.module.css b/src-migrate/modules/product-detail/styles/image.module.css
index e472fe8d..f33a659b 100644
--- a/src-migrate/modules/product-detail/styles/image.module.css
+++ b/src-migrate/modules/product-detail/styles/image.module.css
@@ -1,9 +1,21 @@
-.wrapper {
+/* .wrapper {
@apply h-[250px] md:h-[340px] flex items-center justify-center border border-gray-200 rounded-lg p-4 relative;
}
.image {
@apply object-contain object-center h-full w-full;
+} */
+
+.wrapper {
+ @apply relative border border-gray-200 rounded-lg overflow-hidden;
+
+ @apply w-full aspect-[1/1];
+
+ @apply mx-auto;
+}
+
+.image {
+ @apply w-full h-full object-cover object-center block;
}
.absolute-info {
diff --git a/src-migrate/modules/product-detail/styles/price-action.module.css b/src-migrate/modules/product-detail/styles/price-action.module.css
index cea50bff..b94c33f7 100644
--- a/src-migrate/modules/product-detail/styles/price-action.module.css
+++ b/src-migrate/modules/product-detail/styles/price-action.module.css
@@ -2,16 +2,16 @@
@apply font-medium text-gray-500;
}
.main-price {
- @apply font-medium text-danger-500 text-title-md;
+ @apply font-medium text-danger-500 text-title-sm;
}
.action-wrapper {
@apply flex gap-x-2.5;
}
.quantity-input {
- @apply w-24 h-10 text-center border border-gray-300 rounded focus:outline-none;
- /* Padding di kiri dan kanan untuk memberi ruang bagi tombol */
- padding-left: 2rem;
- padding-right: 2rem;
+ @apply w-24 h-10 text-center border border-gray-300 rounded focus:outline-none;
+ /* Padding di kiri dan kanan untuk memberi ruang bagi tombol */
+ padding-left: 2rem;
+ padding-right: 2rem;
}
.contact-us {
diff --git a/src-migrate/modules/promo/components/Voucher.tsx b/src-migrate/modules/promo/components/Voucher.tsx
index 0c225c74..c2c65766 100644
--- a/src-migrate/modules/promo/components/Voucher.tsx
+++ b/src-migrate/modules/promo/components/Voucher.tsx
@@ -123,7 +123,7 @@ const VoucherComponent = () => {
<>
<h1 className={style['title']}>Pakai Voucher Belanja</h1>
- <div className='h-6' />
+ {/* <div className='h-6' /> */}
{voucherQuery?.isLoading && (
<div className='grid grid-cols-3 gap-x-4 animate-pulse'>
diff --git a/src-migrate/modules/promo/styles/voucher.module.css b/src-migrate/modules/promo/styles/voucher.module.css
index 22d07f91..ab652a5f 100644
--- a/src-migrate/modules/promo/styles/voucher.module.css
+++ b/src-migrate/modules/promo/styles/voucher.module.css
@@ -2,42 +2,55 @@
@apply text-h-sm md:text-h-lg font-semibold;
}
+/* beri ruang dari footer */
.voucher-section {
- @apply w-full;
+ @apply w-full pb-6 md:pb-8;
+}
+
+/* biar card bisa full-width tanpa ada gutter ekstra */
+.voucher-section :global(.swiper-slide) {
+ @apply flex items-start px-0 md:px-0;
}
.voucher-card {
- @apply w-full md:w-11/12 h-3/4 rounded-xl border items-center border-gray-200 shadow-md p-4 flex gap-x-4 ;
+ /* FULL width (hapus md:w-11/12), tetap horizontal & kompak */
+ @apply w-full rounded-xl border border-gray-200 shadow-md
+ p-4 flex items-start gap-x-4 bg-white;
}
.voucher-image {
- @apply bg-gray-100 rounded-lg w-4/12 h-fit object-contain object-center;
+ /* gambar kecil & stabil */
+ @apply bg-gray-100 rounded-lg w-3/12 md:w-2/12 h-auto object-contain object-center;
}
.voucher-content {
- @apply flex-1 flex flex-col;
+ /* penting: min-w-0 supaya teks bisa wrap/ellipsis dan card memanjang mulus */
+ @apply flex-1 min-w-0 flex flex-col justify-between;
}
.voucher-title {
- @apply font-medium text-body-1 leading-6 mb-1;
+ @apply font-medium text-body-1 leading-6 line-clamp-2 mb-1;
}
-
.voucher-desc {
- @apply text-gray-800 line-clamp-2 text-caption-1;
+ @apply text-gray-800 text-caption-1 line-clamp-1;
}
.voucher-bottom {
- @apply flex justify-between mt-2;
+ /* biar rapih di semua ukuran layar */
+ @apply flex flex-col sm:flex-row justify-between items-start sm:items-center
+ gap-2 sm:gap-3 mt-3;
}
.voucher-code-desc {
@apply text-gray-500 text-caption-1;
}
-
.voucher-code {
@apply text-red-700 font-medium;
}
+/* tombol tetap kecil, tapi lebar penuh di mobile; tidak keluar dari card */
.voucher-copy {
- @apply bg-gray-200 hover:bg-danger-500 text-danger-500 hover:text-white transition-colors rounded-lg flex items-center justify-center px-6;
+ @apply bg-gray-200 hover:bg-danger-500 text-danger-500 hover:text-white transition-colors
+ rounded-lg flex items-center justify-center px-5 py-1.5 text-sm whitespace-nowrap
+ w-full md:w-auto;
}