diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2023-03-28 16:32:45 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2023-03-28 16:32:45 +0700 |
| commit | f15a7f8a4387d215abd1051ee520adca8944fa05 (patch) | |
| tree | a51a50ba27a53ff03da38c388e36a17149ad60fa | |
| parent | 8ca9069251873e87338f1c7f581b418bebb11e9a (diff) | |
checkout, quotation
| -rw-r--r-- | src/core/components/elements/Navbar/NavbarDesktop.jsx | 10 | ||||
| -rw-r--r-- | src/core/components/elements/Popup/BottomPopup.jsx | 6 | ||||
| -rw-r--r-- | src/lib/cart/components/Cart.jsx | 2 | ||||
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 53 | ||||
| -rw-r--r-- | src/lib/invoice/components/Invoice.jsx | 10 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductDesktop.jsx | 18 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 12 | ||||
| -rw-r--r-- | src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx | 6 | ||||
| -rw-r--r-- | src/lib/quotation/components/Quotation.jsx | 37 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transaction.jsx | 14 | ||||
| -rw-r--r-- | src/pages/api/shop/midtrans-payment.js | 20 | ||||
| -rw-r--r-- | src/pages/shop/quotation/finish.jsx | 47 |
12 files changed, 122 insertions, 113 deletions
diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index db281e9a..7e66a234 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -67,7 +67,7 @@ const NavbarDesktop = () => { <a href='https://wa.me/628' className='flex items-center gap-x-1 !text-gray_r-12/80'> <Image src='/images/socials/Whatsapp-2.png' alt='Whatsapp' width={48} height={48} /> <div> - <div className='font-semibold'>Order Via WA</div> + <div className='font-semibold'>Whatsapp</div> 0812 8080 622 (Chat) </div> </a> @@ -92,19 +92,13 @@ const NavbarDesktop = () => { </button> <div className='w-6/12 flex gap-x-1 px-1 bg-gray_r-1'> <Link - href='/' - className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 bg-gray_r-2 hover:bg-gray_r-4 border border-gray_r-6 idt-transition' - > - Promo Produk - </Link> - <Link href='/shop/brands' className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 bg-gray_r-2 hover:bg-gray_r-4 border border-gray_r-6 idt-transition' > Semua Brand </Link> <Link - href='/' + href='/shop/search?orderBy=stock' className='p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 bg-gray_r-2 hover:bg-gray_r-4 border border-gray_r-6 idt-transition' > Ready Stock diff --git a/src/core/components/elements/Popup/BottomPopup.jsx b/src/core/components/elements/Popup/BottomPopup.jsx index 5828d222..1fc77932 100644 --- a/src/core/components/elements/Popup/BottomPopup.jsx +++ b/src/core/components/elements/Popup/BottomPopup.jsx @@ -3,6 +3,7 @@ import { AnimatePresence, motion } from 'framer-motion' import { useEffect } from 'react' import MobileView from '../../views/MobileView' import DesktopView from '../../views/DesktopView' +import ReactDOM from 'react-dom' const transition = { ease: 'linear', duration: 0.2 } @@ -15,7 +16,7 @@ const BottomPopup = ({ children, active = false, title, close }) => { } }, [active]) - return ( + return ReactDOM.createPortal( <> <AnimatePresence> {active && ( @@ -66,7 +67,8 @@ const BottomPopup = ({ children, active = false, title, close }) => { </> )} </AnimatePresence> - </> + </>, + document.querySelector('body') ) } diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 7ebee14b..8d995103 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -115,7 +115,7 @@ const Cart = () => { const selectedProduct = () => { if (!products) return [] - return products?.filter((product) => product.selected == true) + return products?.filter((product) => product?.selected == true) } const deleteProduct = (productId) => { diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 048bb24e..ffb33eb7 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -78,7 +78,7 @@ const Checkout = () => { } const dataProducts = await CartApi({ variantIds }) - const productsWithQuantity = dataProducts.map(async (product) => { + const productsWithQuantity = dataProducts?.map(async (product) => { const productPrice = await variantPriceApi({ id: product.id }) return { ...product, @@ -87,12 +87,16 @@ const Checkout = () => { discountPercentage: productPrice.discount, priceDiscount: productPrice.priceExcludeAfterDiscount }, - quantity: query.quantity ? query.quantity : getItemCart({ productId: product.id }).quantity + quantity: query.quantity + ? query.quantity + : getItemCart({ productId: product.id }).quantity } }) - Promise.all(productsWithQuantity).then((resolvedProducts) => { - setProducts(resolvedProducts) - }) + if (productsWithQuantity) { + Promise.all(productsWithQuantity).then((resolvedProducts) => { + setProducts(resolvedProducts) + }) + } } loadProducts() }, [router]) @@ -150,6 +154,8 @@ const Checkout = () => { window.location.href = payment.data.redirectUrl } + const taxTotal = (totalAmount - totalDiscountAmount) * 0.11 + return ( <> <MobileView> @@ -202,14 +208,14 @@ const Checkout = () => { </div> <div className='flex gap-x-2 justify-between'> <div className='text-gray_r-11'>PPN 11% (Incl.)</div> - <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + <div>{currencyFormat(taxTotal)}</div> </div> </div> <hr className='my-4 border-gray_r-6' /> <div className='flex gap-x-2 justify-between mb-4'> <div>Grand Total</div> <div className='font-semibold text-gray_r-12'> - {currencyFormat(totalAmount - totalDiscountAmount)} + {currencyFormat(totalAmount - totalDiscountAmount + taxTotal)} </div> </div> <p className='text-caption-2 text-gray_r-10 mb-2'>*) Belum termasuk biaya pengiriman</p> @@ -233,31 +239,6 @@ const Checkout = () => { <Divider /> <div className='p-4'> - <div className='font-medium mb-4'>Metode Pembayaran</div> - <div className='flex flex-col gap-y-3'> - <div - className={`p-2 idt-transition border rounded text-gray_r-12/80 ${ - paymentMethod == 'manual' ? 'border-yellow_r-8 bg-yellow_r-2' : 'border-gray_r-6' - }`} - onClick={() => setPaymentMethod('manual')} - > - Bank BCA (PT. Indoteknik Dotcom) - <div className='mt-1'>8870-4000-81</div> - </div> - <div - className={`p-2 idt-transition border rounded ${ - paymentMethod == 'midtrans' ? 'border-yellow_r-8 bg-yellow_r-2' : 'border-gray_r-6' - }`} - onClick={() => setPaymentMethod('midtrans')} - > - <Image src='/images/payments/midtrans.jpg' alt='Midtrans Payment' /> - </div> - </div> - </div> - - <Divider /> - - <div className='p-4'> <div className='font-medium'>Purchase Order</div> <div className='mt-4 flex gap-x-3'> @@ -286,7 +267,7 @@ const Checkout = () => { onClick={checkout} disabled={isLoading || !products || products?.length == 0} > - {isLoading ? 'Loading...' : 'Bayar'} + {isLoading ? 'Loading...' : 'Lanjut Pembayaran'} </button> </div> </MobileView> @@ -402,8 +383,8 @@ const Checkout = () => { <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11% (Incl.)</div> - <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + <div className='text-gray_r-11'>PPN 11%</div> + <div>{currencyFormat(taxTotal)}</div> </div> </div> @@ -412,7 +393,7 @@ const Checkout = () => { <div className='flex gap-x-2 justify-between mb-4'> <div>Grand Total</div> <div className='font-semibold text-gray_r-12'> - {currencyFormat(totalAmount - totalDiscountAmount)} + {currencyFormat(totalAmount - totalDiscountAmount + taxTotal)} </div> </div> <p className='text-caption-2 text-gray_r-11 mb-2'> diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx index 211d1ae1..6012e4ea 100644 --- a/src/lib/invoice/components/Invoice.jsx +++ b/src/lib/invoice/components/Invoice.jsx @@ -152,9 +152,7 @@ const Invoice = ({ id }) => { <div>: {invoice?.data?.invoiceDate}</div> <div>Purchase Order</div> - <div> - : {invoice?.data?.purchaseOrderName || '-'} - </div> + <div>: {invoice?.data?.purchaseOrderName || '-'}</div> <div>Ketentuan Pembayaran</div> <div>: {invoice?.data?.paymentTerm}</div> @@ -252,13 +250,13 @@ const Invoice = ({ id }) => { {currencyFormat(-totalDiscountAmount)} </div> - <div className='text-right'>PPN 11% (Incl.)</div> - <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</div> - <div className='text-right'>Grand Total</div> <div className='text-right font-medium text-gray_r-12'> {currencyFormat(invoice.data?.amountTotal)} </div> + + <div className='text-right'>PPN 11% (Incl.)</div> + <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</div> </div> </div> </div> diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index 2f73d124..79e7bb45 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -11,8 +11,10 @@ import { updateItemCart } from '@/core/utils/cart' import useVariantPrice from '@/lib/variant/hooks/useVariantPrice' import useProductPrice from '../../hooks/useProductPrice' import PriceSkeleton from '@/core/components/elements/Skeleton/PriceSkeleton' +import { useRouter } from 'next/router' const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { + const router = useRouter() const { productPrice } = useProductPrice({ id: product.id }) const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) @@ -41,6 +43,12 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { toast.success('Berhasil menambahkan ke keranjang') } + const handleBuy = (variantId) => { + const quantity = variantQuantityRefs.current[variantId].value + if (!validQuantity(quantity)) return + router.push(`/shop/checkout?productId=${variantId}&quantity=${quantity}`) + } + const variantSectionRef = useRef(null) const goToVariantSection = () => { if (variantSectionRef.current) { @@ -193,7 +201,11 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { > Keranjang </button> - <button type='button' className='flex-1 py-2 btn-solid-red'> + <button + type='button' + onClick={() => handleBuy(variant.id)} + className='flex-1 py-2 btn-solid-red' + > Beli </button> </td> @@ -264,7 +276,9 @@ const VariantPrice = ({ id }) => { {variantPrice?.data?.priceExcludeAfterDiscount > 0 ? ( currencyFormat(variantPrice?.data?.priceExcludeAfterDiscount) ) : ( - <a href='https://wa.me/' className='text-red_r-11'>Call for price</a> + <a href='https://wa.me/' className='text-red_r-11'> + Call for price + </a> )} </> ) diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index b5d7c974..b00d68a4 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -41,11 +41,6 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { } ) - const [open, setOpen] = useState(1) - - const handleOpen = (value) => { - setOpen(open === value ? 0 : value) - } const orderOptions = [ { value: 'price-asc', label: 'Harga Terendah' }, { value: 'price-desc', label: 'Harga Tertinggi' }, @@ -69,13 +64,10 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { } }, [query, products, productSearch]) - if (productSearch.isLoading) { - return <ProductSearchSkeleton /> - } - return ( <> <MobileView> + {productSearch.isLoading && <ProductSearchSkeleton />} <div className='p-4'> <h1 className='mb-2 font-semibold text-h-sm'>Produk</h1> @@ -178,6 +170,7 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { <select name='urutan' className='form-input mt-2' + value={router.query?.orderBy || ''} onChange={(e) => handleOrderBy(e)} > <option value=''>Urutkan</option> @@ -191,6 +184,7 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { </div> </div> </div> + {productSearch.isLoading && <ProductSearchSkeleton />} <div className='grid grid-cols-5 gap-x-3 gap-y-6'> {products && products.map((product) => <ProductCard product={product} key={product.id} />)} diff --git a/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx b/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx index fa1e175d..3447df79 100644 --- a/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx +++ b/src/lib/product/components/Skeleton/ProductSearchSkeleton.jsx @@ -1,7 +1,11 @@ import ProductCardSkeleton from '@/core/components/elements/Skeleton/ProductCardSkeleton' const ProductSearchSkeleton = () => ( - <div className='p-4 grid grid-cols-2 gap-4'> + <div className='p-4 grid grid-cols-2 md:grid-cols-5 gap-4'> + <ProductCardSkeleton /> + <ProductCardSkeleton /> + <ProductCardSkeleton /> + <ProductCardSkeleton /> <ProductCardSkeleton /> <ProductCardSkeleton /> <ProductCardSkeleton /> diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx index 3054616c..a95a149a 100644 --- a/src/lib/quotation/components/Quotation.jsx +++ b/src/lib/quotation/components/Quotation.jsx @@ -15,6 +15,7 @@ import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' import MobileView from '@/core/components/views/MobileView' import DesktopView from '@/core/components/views/DesktopView' import Image from '@/core/components/elements/Image/Image' +import variantPriceApi from '@/lib/variant/api/variantPriceApi' const Quotation = () => { const router = useRouter() @@ -31,11 +32,23 @@ const Quotation = () => { .map((o) => o.productId) .join(',') const dataProducts = await CartApi({ variantIds }) - const dataProductsQuantity = _.map(dataProducts, (o) => ({ - ...o, - quantity: getItemCart({ productId: o.id }).quantity - })) - setProducts(dataProductsQuantity) + const productsWithQuantity = dataProducts?.map(async (product) => { + const productPrice = await variantPriceApi({ id: product.id }) + return { + ...product, + price: { + price: productPrice.priceExclude, + discountPercentage: productPrice.discount, + priceDiscount: productPrice.priceExcludeAfterDiscount + }, + quantity: getItemCart({ productId: product.id }).quantity + } + }) + if (productsWithQuantity) { + Promise.all(productsWithQuantity).then((resolvedProducts) => { + setProducts(resolvedProducts) + }) + } } loadProducts() }, []) @@ -78,6 +91,8 @@ const Quotation = () => { toast.error('Gagal melakukan transaksi, terjadi kesalahan internal') } + const taxTotal = (totalAmount - totalDiscountAmount) * 0.11 + return ( <> <MobileView> @@ -121,15 +136,15 @@ const Quotation = () => { <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11% (Incl.)</div> - <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + <div className='text-gray_r-11'>PPN 11%</div> + <div>{currencyFormat(taxTotal)}</div> </div> </div> <hr className='my-4 border-gray_r-6' /> <div className='flex gap-x-2 justify-between mb-4'> <div>Grand Total</div> <div className='font-semibold text-gray_r-12'> - {currencyFormat(totalAmount - totalDiscountAmount)} + {currencyFormat(totalAmount - totalDiscountAmount + taxTotal)} </div> </div> <p className='text-caption-2 text-gray_r-10 mb-2'>*) Belum termasuk biaya pengiriman</p> @@ -244,8 +259,8 @@ const Quotation = () => { <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11% (Incl.)</div> - <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + <div className='text-gray_r-11'>PPN 11%</div> + <div>{currencyFormat(taxTotal)}</div> </div> </div> @@ -254,7 +269,7 @@ const Quotation = () => { <div className='flex gap-x-2 justify-between mb-4'> <div>Grand Total</div> <div className='font-semibold text-gray_r-12'> - {currencyFormat(totalAmount - totalDiscountAmount)} + {currencyFormat(totalAmount - totalDiscountAmount + taxTotal)} </div> </div> <p className='text-caption-2 text-gray_r-11 mb-2'> diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index b921eb0d..669966da 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -103,14 +103,14 @@ const Transaction = ({ id }) => { <p className='text-gray_r-12/70'>Total Diskon</p> <p>{currencyFormat(-totalDiscountAmount)}</p> </div> - <div className='flex justify-between mt-1'> - <p className='text-gray_r-12/70'>PPN 11% (Incl.)</p> - <p>{currencyFormat(totalAmount * 0.11)}</p> - </div> <div className='flex justify-between mt-1 font-medium'> <p>Total Belanja</p> <p>{currencyFormat(transaction.data?.amountTotal)}</p> </div> + <div className='flex justify-between mt-1'> + <p className='text-gray_r-12/70'>PPN 11% (Incl.)</p> + <p>{currencyFormat(totalAmount * 0.11)}</p> + </div> </div> ), [transaction.data, totalAmount, totalDiscountAmount] @@ -414,13 +414,13 @@ const Transaction = ({ id }) => { {currencyFormat(-totalDiscountAmount)} </div> - <div className='text-right'>PPN 11% (Incl.)</div> - <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</div> - <div className='text-right'>Grand Total</div> <div className='text-right font-medium text-gray_r-12'> {currencyFormat(transaction.data?.amountTotal)} </div> + + <div className='text-right'>PPN 11% (Incl.)</div> + <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</div> </div> </div> diff --git a/src/pages/api/shop/midtrans-payment.js b/src/pages/api/shop/midtrans-payment.js index be676d38..1772e9e0 100644 --- a/src/pages/api/shop/midtrans-payment.js +++ b/src/pages/api/shop/midtrans-payment.js @@ -31,6 +31,20 @@ export default async function handler(req, res) { serverKey: process.env.MIDTRANS_SERVER_KEY }) + let itemDetails = transaction.products.map((product) => ({ + id: product.code, + price: Math.round(product.price.priceDiscount), + quantity: product.quantity, + name: product.name?.substring(0, 50) + })) + + itemDetails.push({ + id: 'TAX', + price: transaction.amountTax, + quantity: 1, + name: 'PPN 11%' + }) + const parameter = { transaction_details: { order_id: transaction.name?.replaceAll('/', '-'), @@ -39,12 +53,6 @@ export default async function handler(req, res) { credit_card: { secure: true }, - item_details: transaction.products.map((product) => ({ - id: product.code, - price: Math.round(product.price.priceDiscount), - quantity: product.quantity, - name: product.name?.substring(0, 50) - })), customer_details: { first_name: transaction.address.customer.name, email: transaction.address.customer.email || '', diff --git a/src/pages/shop/quotation/finish.jsx b/src/pages/shop/quotation/finish.jsx index 15881ea0..32638b62 100644 --- a/src/pages/shop/quotation/finish.jsx +++ b/src/pages/shop/quotation/finish.jsx @@ -12,31 +12,30 @@ export default function FinishQuotation() { return ( <IsAuth> <BasicLayout> - <div className='m-4 px-4 py-6 shadow-md border border-gray_r-3'> - <div className='flex'> - <span className='p-3 mx-auto bg-yellow_r-3 border border-yellow_r-6 rounded'> - <EnvelopeIcon className='w-8 text-yellow_r-11' /> - </span> - </div> - <p className='h2 text-center mt-6'>Terima Kasih {auth?.name}</p> - <p className='text-center mt-3 leading-6 text-gray_r-11'> - Penawaran harga kamu di Indoteknik.com berhasil dikirimkan, tim kami akan segera - menghubungi anda. - </p> - {id && ( - <Link - href={`/my/transaction/${id}`} - className='btn-yellow !text-gray_r-12 mt-6 w-full' - > - Lihat Penawaran + <div className='mx-auto container'> + <div className='m-4 md:m-0 md:mt-10 px-4 py-6 shadow-md border border-gray_r-3'> + <div className='flex'> + <span className='p-3 mx-auto bg-yellow_r-3 border border-yellow_r-6 rounded'> + <EnvelopeIcon className='w-8 text-yellow_r-11' /> + </span> + </div> + <p className='h2 text-center mt-6'>Terima Kasih {auth?.name}</p> + <p className='text-center mt-3 leading-6 text-gray_r-11'> + Penawaran harga kamu di Indoteknik.com berhasil dikirimkan, tim kami akan segera + menghubungi anda. + </p> + {id && ( + <Link + href={`/my/transaction/${id}`} + className='btn-yellow !text-gray_r-12 mt-6 w-full md:w-1/3 md:mx-auto' + > + Lihat Penawaran + </Link> + )} + <Link href='/' className='btn-light !text-gray_r-12 mt-2 w-full md:w-1/3 md:mx-auto'> + Ke Halaman Utama </Link> - )} - <Link - href='/' - className='btn-light !text-gray_r-12 mt-2 w-full' - > - Ke Halaman Utama - </Link> + </div> </div> </BasicLayout> </IsAuth> |
