From 491d609e0e52998c024d634e759eca3275ce7943 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Tue, 4 Nov 2025 17:18:05 +0700 Subject: cr renca & scale manufacture product detail --- .../product-detail/components/Information.tsx | 7 +- .../product-detail/components/PriceAction.tsx | 168 +++++++++++++++++---- .../product-detail/components/ProductDetail.tsx | 3 - .../product-detail/styles/price-action.module.css | 2 +- 4 files changed, 141 insertions(+), 39 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index a7a58cbc..fd73d8b6 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -66,14 +66,13 @@ const Information = ({ product }: Props) => { }, [selectedVariant]); useEffect(() => { - if (isLoading){ + if (isLoading) { setSla(null); } if (slaVariant) { setSla(slaVariant); } }, [slaVariant, isLoading]); - const handleOnChange = (vals: any) => { setDisableFilter(true); @@ -188,7 +187,7 @@ const Information = ({ product }: Props) => {
{selectedVariant?.code}
-
Manufacture
+
Manufacture
{!!product.manufacture.name ? ( { width={100} src={product.manufacture.logo} alt={product.manufacture.name} - className='h-8 object-fit' + className='object-fit object-contain items-center' /> ) : (

diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 6cc2f0bf..a84fa134 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -14,6 +14,7 @@ import odooApi from '~/libs/odooApi'; import { Button, Skeleton } from '@chakra-ui/react'; import DesktopView from '@/core/components/views/DesktopView'; import MobileView from '@/core/components/views/MobileView'; +import { TicketIcon } from '@heroicons/react/24/solid'; type Props = { product: IProductDetail; @@ -70,6 +71,20 @@ const PriceAction = ({ product }: Props) => { const pricedigit = String(Math.floor(price)).length; const fontSize = pricedigit >= 9 ? '20px' : undefined; + // voucher hanya diterapkan kalau TIDAK ada discount_percentage (bukan flash/price rule) + let voucherCut = 0; + + // apply voucher only when NOT a flash/price rule + if (activePrice && !(activePrice.discount_percentage > 0)) { + voucherCut = getVoucherCut(product, activePrice); + } + + const basePriceForDisplay = + Number(activePrice?.price_discount ?? 0) || Number(activePrice?.price ?? 0); + + const finalAfterVoucher = Math.max(basePriceForDisplay - voucherCut, 0); + const hasVoucherApplied = voucherCut > 0 && !activePrice?.discount_percentage; + // let voucherPastiHemat = 0; // if ( @@ -82,6 +97,51 @@ const PriceAction = ({ product }: Props) => { // voucherPastiHemat = JSON.parse(validJsonString); // } + // --- (1) helper: hitung potongan voucher berdasar product-level voucher + harga variant aktif + function getVoucherCut( + product: IProductDetail, + activePrice?: { price?: number; price_discount?: number } + ) { + try { + const raw = Array.isArray((product as any)?.new_voucher_pasti_hemat) + ? (product as any).new_voucher_pasti_hemat[0] + : (product as any)?.new_voucher_pasti_hemat; + + if (!raw) return 0; + + const discount_type = String( + raw.discount_type ?? raw.discountType ?? '' + ).toLowerCase(); + const discount_amount = Number( + raw.discount_amount ?? raw.discountAmount ?? 0 + ); + const max_discount = Number(raw.max_discount ?? raw.maxDiscount ?? 0); + const min_purchase = Number(raw.min_purchase ?? raw.minPurchase ?? 0); + + // base price ambil price_discount dulu, kalau kosong pakai price + const base = + Number(activePrice?.price_discount ?? 0) || + Number(activePrice?.price ?? 0); + if (!base) return 0; + if (min_purchase > 0 && base < min_purchase) return 0; + + let cut = 0; + if (discount_type.startsWith('percent')) { + // support nilai 0..1 atau 0..100 + const pct = + discount_amount <= 1 ? discount_amount * 100 : discount_amount; + cut = Math.floor(base * (pct / 100)); + } else { + cut = Math.floor(discount_amount || 0); + } + + if (max_discount > 0) cut = Math.min(cut, max_discount); + return Math.max(0, cut); + } catch { + return 0; + } + } + return (

{ <>
- {activePrice.discount_percentage > 0 && ( -
- {Math.floor(activePrice.discount_percentage)}% -
- )} -
- Rp{' '} - {formatCurrency( - activePrice.discount_percentage > 0 - ? activePrice.price_discount || 0 - : activePrice.price || 0 - )} -
- {activePrice.discount_percentage > 0 && ( -
- Rp {formatCurrency(activePrice.price || 0)} + {/* Jika ada discount_percentage (flash/price rule) → pakai UI lama */} + {activePrice.discount_percentage > 0 ? ( + <> +
+ {Math.floor(activePrice.discount_percentage)}% +
+
+ Rp {formatCurrency(activePrice.price_discount || 0)} +
+
+ Rp {formatCurrency(activePrice.price || 0)} +
+ + ) : hasVoucherApplied ? ( + // Tidak ada discount bawaan, tapi ada voucher → tampilkan harga setelah voucher + <> +
+ + Rp {formatCurrency(finalAfterVoucher)} +
+
+ Rp {formatCurrency(basePriceForDisplay)} +
+ + ) : ( + // Normal tanpa disc & tanpa voucher +
+ Rp {formatCurrency(basePriceForDisplay)}
)}
+
Termasuk PPN: Rp{' '} {formatCurrency( Math.round( (activePrice.discount_percentage > 0 - ? activePrice.price_discount - : activePrice.price) * PPN + ? Number(activePrice.price_discount || 0) + : hasVoucherApplied + ? finalAfterVoucher + : basePriceForDisplay) * PPN ) )}
@@ -131,33 +212,58 @@ const PriceAction = ({ product }: Props) => {
+ {/* Jika ada discount_percentage (flash/price rule) → pakai UI lama */} {activePrice.discount_percentage > 0 ? ( <>
{Math.floor(activePrice.discount_percentage)}%
- - {/* harga setelah diskon (main-price) di kiri */} -
+
Rp {formatCurrency(activePrice.price_discount || 0)}
- - {/* harga coret di kanan */}
Rp {formatCurrency(activePrice.price || 0)}
+ ) : hasVoucherApplied ? ( + // Tidak ada discount bawaan, tapi ada voucher → tampilkan harga setelah voucher + <> +
+ Rp {formatCurrency(finalAfterVoucher)} +
+
+ Rp {formatCurrency(basePriceForDisplay)} +
+ ) : ( - // kalau tidak ada diskon, tampilkan harga normal saja -
- Rp {formatCurrency(activePrice.price || 0)} + // Normal tanpa disc & tanpa voucher +
+ Rp {formatCurrency(basePriceForDisplay)}
)}
-
+
+
Termasuk PPN: Rp{' '} - {formatCurrency(Math.round(activePrice.price_discount * PPN))} + {formatCurrency( + Math.round( + (activePrice.discount_percentage > 0 + ? Number(activePrice.price_discount || 0) + : hasVoucherApplied + ? finalAfterVoucher + : basePriceForDisplay) * PPN + ) + )}
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 4c75c61b..17cd03ca 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -204,9 +204,6 @@ const ProductDetail = ({ product }: Props) => { setVoucherDiscount(cut); }, [product, selectedVariant]); - console.log(discount); - console.log(selectedVariant); - return ( <>
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 88acdfc5..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,7 +2,7 @@ @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; -- cgit v1.2.3