diff options
| author | Miqdad <ahmadmiqdad27@gmail.com> | 2025-10-07 12:14:49 +0700 |
|---|---|---|
| committer | Miqdad <ahmadmiqdad27@gmail.com> | 2025-10-07 12:14:49 +0700 |
| commit | 7db9105b7d2ccabb16103be5f0b83c0f4ba3569a (patch) | |
| tree | 17dcdd9b046943d9001033f9618d3a825a6230ef | |
| parent | a9c90f0c7cb3b6e702c66dd26851c9ec364017c6 (diff) | |
<Miqdad> match prod detail from google ads w/ prod detail website
| -rw-r--r-- | src/lib/product/components/Product/ProductDesktopVariant.jsx | 2 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductMobileVariant.jsx | 162 |
2 files changed, 106 insertions, 58 deletions
diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index e770d66b..6d6e15f7 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -482,7 +482,7 @@ const ProductDesktopVariant = ({ )} </h3> )} - <div className='flex justify-between items-center py-5 px-3'> + <div className='flex gap-x-5 items-center py-5'> <div className='relative flex items-center'> <button type='button' diff --git a/src/lib/product/components/Product/ProductMobileVariant.jsx b/src/lib/product/components/Product/ProductMobileVariant.jsx index d1e7c7e9..c3c8a584 100644 --- a/src/lib/product/components/Product/ProductMobileVariant.jsx +++ b/src/lib/product/components/Product/ProductMobileVariant.jsx @@ -27,6 +27,8 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { let auth = getAuth(); const [quantity, setQuantity] = useState('1'); const [selectedVariant, setSelectedVariant] = useState(product.id); + const [quantityInput, setQuantityInput] = useState(String(1)); + const [qtyPickUp, setQtyPickUp] = useState(0); const [informationTab, setInformationTab] = useState( informationTabOptions[0].value ); @@ -63,30 +65,33 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { } }, [selectedVariant, product]); - const validAction = () => { - let isValid = true; + const validAction = (q) => { if (!selectedVariant) { toast.error('Pilih varian terlebih dahulu'); - isValid = false; + return false; } - if (!quantity || quantity < 1 || isNaN(parseInt(quantity))) { + if (!Number.isInteger(q) || q < 1) { toast.error('Jumlah barang minimal 1'); - isValid = false; + return false; } - return isValid; + return true; }; + const getQty = () => Math.max(1, toInt(quantityInput)); + const handleClickCart = async () => { + const q = getQty(); + if (!auth) { router.push(`/login?next=/shop/product/${slug}?srsltid=${srsltid}`); return; } + if (!validAction(q)) return; - if (!validAction()) return; - gtagAddToCart(activeVariant, quantity); + gtagAddToCart(activeVariant, q); updateItemCart({ productId: product.id, - quantity, + quantity: q, programLineId: null, selected: true, source: null, @@ -95,37 +100,29 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { }; const handleClickBuy = async () => { - let isLoggedIn = typeof auth === 'object'; + const q = getQty(); + let isLoggedIn = typeof auth === 'object'; if (!isLoggedIn) { const currentUrl = encodeURIComponent(router.asPath); await router.push(`/login?next=${currentUrl}`); - - // Tunggu login berhasil, misalnya dengan memantau perubahan status auth. - const authCheckInterval = setInterval(() => { - const newAuth = getAuth(); - if (typeof newAuth === 'object') { - isLoggedIn = true; - auth = newAuth; // Update nilai auth setelah login - clearInterval(authCheckInterval); - } - }, 500); // Periksa status login setiap 500ms - await new Promise((resolve) => { - const checkLogin = setInterval(() => { - if (isLoggedIn) { - clearInterval(checkLogin); + const t = setInterval(() => { + const newAuth = getAuth(); + if (typeof newAuth === 'object') { + auth = newAuth; + clearInterval(t); resolve(null); } }, 500); }); } - if (!validAction()) return; + if (!validAction(q)) return; updateItemCart({ productId: product.id, - quantity, + quantity: q, programLineId: null, selected: true, source: 'buy', @@ -133,8 +130,21 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { router.push(`/shop/checkout?source=buy`); }; + const toInt = (v) => { + const n = parseInt(String(v ?? '').trim(), 10); + return Number.isFinite(n) ? n : 0; + }; + + const validQuantity = (q) => { + if (!Number.isInteger(q) || q < 1) { + toast.error('Jumlah barang minimal 1'); + return false; + } + return true; + }; + const handleButton = (variant) => { - const quantity = quantityInput; + const quantity = Math.max(1, toInt(quantityInput)); // clamp min 1 if (!validQuantity(quantity)) return; updateItemCart({ @@ -168,9 +178,10 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { return ( <MobileView> - <div - className={`px-4 block md:sticky md:top-[150px] md:py-6 fixed bottom-0 left-0 right-0 bg-white p-2 z-10 pb-6 pt-6 rounded-lg shadow-[rgba(0,0,4,0.1)_0px_-4px_4px_0px] `} - > + {/* PRICE & ACTIONS: tetap punyamu, hanya hapus input number lama */} + {/* ===== BAR BAWAH (fixed) ===== */} + <div className='px-4 fixed bottom-0 left-0 right-0 bg-white z-10 pb-6 pt-4 rounded-t-2xl shadow-[rgba(0,0,4,0.1)_0px_-4px_4px_0px]'> + {/* HARGA & PPN (logikamu tetap) */} {activeVariant.isFlashSale && activeVariant?.price?.discountPercentage > 0 ? ( <> @@ -225,58 +236,94 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { )} </div> )} - <div className=''> - <div className='mt-4 mb-2'>Jumlah</div> - <div className='flex gap-x-3'> - <div className='w-2/12'> + + {/* ⬇️ TAMBAHKAN BLOK INI DI DALAM BAR: STOK & STEPPER */} + <div className='grid grid-cols-12 items-center gap-3 mt-3'> + <div className='col-span-7'> + <div + className={`text-[14px] ${ + product?.sla?.qty < 10 ? 'text-red-600 font-medium' : '' + }`} + > + Stock : {activeVariant?.stock ?? 0} + </div> + {qtyPickUp > 0 && ( + <div className='text-[16px] mt-0.5 text-red-500 italic'> + * {qtyPickUp} barang bisa di pickup + </div> + )} + </div> + <div className='col-span-5 flex justify-end'> + <div className='inline-flex items-stretch border rounded-xl overflow-hidden'> + <button + type='button' + className='h-10 w-10 grid place-items-center text-gray-700 hover:bg-gray-100 active:scale-95' + onClick={() => + setQuantityInput( + String(Math.max(1, Number(quantityInput || 1) - 1)) + ) + } + aria-label='Kurangi' + > + <span className='text-2xl leading-none'>–</span> + </button> <input - name='quantity' type='number' - className='form-input' - value={quantity} - onChange={(e) => setQuantity(e.target.value)} + min={1} + value={quantityInput} + onChange={(e) => setQuantityInput(e.target.value)} + className='h-10 w-16 text-center text-lg outline-none border-x + [appearance:textfield] + [&::-webkit-outer-spin-button]:appearance-none + [&::-webkit-inner-spin-button]:appearance-none' /> + <button + type='button' + className='h-10 w-10 grid place-items-center text-gray-700 hover:bg-gray-100 active:scale-95' + onClick={() => + setQuantityInput(String(Number(quantityInput || 1) + 1)) + } + aria-label='Tambah' + > + <span className='text-2xl leading-none'>+</span> + </button> </div> + </div> + </div> + + {/* TOMBOL AKSI */} + <div className='mt-3'> + <div className='flex gap-x-3'> <Button - onClick={() => handleClickCart(product.id)} + onClick={() => + handleClickCart(product.id, Number(quantityInput || 1)) + } className='flex-1' colorScheme='red' - variant={'outline'} + variant='outline' > Keranjang </Button> <Button - onClick={() => handleClickBuy(product.id)} + onClick={() => + handleClickBuy(product.id, Number(quantityInput || 1)) + } className='flex-1' colorScheme='red' > Beli </Button> - {/* <button - type='button' - className='btn-yellow flex-1' - onClick={handleClickCart} - > - Keranjang - </button> - <button - type='button' - className='btn-solid-red flex-1' - onClick={handleClickBuy} - > - Beli - </button> */} </div> + <Button onClick={() => handleButton(product.id)} - color={'red'} + color='red' colorScheme='white' className='w-full border-2 p-2 gap-1 mt-2 hover:bg-slate-100 flex items-center' > <ImageNext src='/images/writing.png' alt='penawaran instan' - className='' width={25} height={25} /> @@ -284,6 +331,7 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { </Button> </div> </div> + <Image src={product.image + '?variant=True'} alt={product.name} |
