diff options
| author | Miqdad <ahmadmiqdad27@gmail.com> | 2025-11-04 17:18:05 +0700 |
|---|---|---|
| committer | Miqdad <ahmadmiqdad27@gmail.com> | 2025-11-04 17:18:05 +0700 |
| commit | 491d609e0e52998c024d634e759eca3275ce7943 (patch) | |
| tree | 262a1aa3fce1e8b46c4e57cde3b4d6b608e3b3ad | |
| parent | 7c188f31373cd647db96d209b1ca2359de640037 (diff) | |
<Miqdad> cr renca & scale manufacture product detail
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) => { <div className={style['value']}>{selectedVariant?.code}</div> </div> <div className={style['row']}> - <div className={style['label']}>Manufacture</div> + <div className={`${style['label']} items-center `}>Manufacture</div> <div className={style['value']}> {!!product.manufacture.name ? ( <Link @@ -204,7 +203,7 @@ const Information = ({ product }: Props) => { width={100} src={product.manufacture.logo} alt={product.manufacture.name} - className='h-8 object-fit' + className='object-fit object-contain items-center' /> ) : ( <p className='font-bold text-red-500'> 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 ( <div className={`block md:sticky md:top-[150px] md:py-6 fixed bottom-0 left-0 right-0 bg-white p-2 z-10 ${ @@ -94,36 +154,57 @@ 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['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)} + {/* Jika ada discount_percentage (flash/price rule) → pakai UI lama */} + {activePrice.discount_percentage > 0 ? ( + <> + <div className={style['disc-badge']}> + {Math.floor(activePrice.discount_percentage)}% + </div> + <div + className={style['main-price']} + style={fontSize ? { fontSize } : undefined} + > + Rp {formatCurrency(activePrice.price_discount || 0)} + </div> + <div className={style['disc-price']}> + Rp {formatCurrency(activePrice.price || 0)} + </div> + </> + ) : hasVoucherApplied ? ( + // Tidak ada discount bawaan, tapi ada voucher → tampilkan harga setelah voucher + <> + <div + className={`${style['main-price']} inline-flex items-center gap-2 leading-none bg-red-100 px-2 py-0.5 rounded-sm`} + style={fontSize ? { fontSize } : undefined} + > + <TicketIcon className='w-5 h-5 shrink-0' aria-hidden /> + Rp {formatCurrency(finalAfterVoucher)} + </div> + <div className={style['disc-price']}> + Rp {formatCurrency(basePriceForDisplay)} + </div> + </> + ) : ( + // Normal tanpa disc & tanpa voucher + <div + className={style['main-price']} + style={fontSize ? { fontSize } : undefined} + > + Rp {formatCurrency(basePriceForDisplay)} </div> )} </div> + <div className='h-1' /> <div className={style['secondary-text']}> 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 ) )} </div> @@ -131,33 +212,58 @@ const PriceAction = ({ product }: Props) => { <MobileView> <div className='flex items-end gap-x-2'> + {/* Jika ada discount_percentage (flash/price rule) → pakai UI lama */} {activePrice.discount_percentage > 0 ? ( <> <div className={style['disc-badge']}> {Math.floor(activePrice.discount_percentage)}% </div> - - {/* harga setelah diskon (main-price) di kiri */} - <div className={style['main-price']}> + <div + className={style['main-price']} + style={fontSize ? { fontSize } : undefined} + > Rp {formatCurrency(activePrice.price_discount || 0)} </div> - - {/* harga coret di kanan */} <div className={style['disc-price']}> Rp {formatCurrency(activePrice.price || 0)} </div> </> + ) : hasVoucherApplied ? ( + // Tidak ada discount bawaan, tapi ada voucher → tampilkan harga setelah voucher + <> + <div + className={style['main-price']} + style={fontSize ? { fontSize } : undefined} + > + Rp {formatCurrency(finalAfterVoucher)} + </div> + <div className={style['disc-price']}> + Rp {formatCurrency(basePriceForDisplay)} + </div> + </> ) : ( - // kalau tidak ada diskon, tampilkan harga normal saja - <div className={style['main-price']}> - Rp {formatCurrency(activePrice.price || 0)} + // Normal tanpa disc & tanpa voucher + <div + className={style['main-price']} + style={fontSize ? { fontSize } : undefined} + > + Rp {formatCurrency(basePriceForDisplay)} </div> )} </div> - <div className='text-md text-gray-500 shadow-0'> + <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 + ? Number(activePrice.price_discount || 0) + : hasVoucherApplied + ? finalAfterVoucher + : basePriceForDisplay) * PPN + ) + )} </div> </MobileView> </> 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 ( <> <div className='md:flex md:flex-wrap'> 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; |
