diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-06 17:01:34 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-06 17:01:34 +0700 |
| commit | ada443445767679dd2d2b2c889bfac02609778b8 (patch) | |
| tree | b12b6cc287dcecb4d89ed70df0c7f25a2780d6b4 /src/pages/shop/checkout | |
| parent | fca30ebbb935b38fc1244b43f4d40184ce0745bd (diff) | |
no message
Diffstat (limited to 'src/pages/shop/checkout')
| -rw-r--r-- | src/pages/shop/checkout/finish.js | 47 | ||||
| -rw-r--r-- | src/pages/shop/checkout/index.js | 319 |
2 files changed, 366 insertions, 0 deletions
diff --git a/src/pages/shop/checkout/finish.js b/src/pages/shop/checkout/finish.js new file mode 100644 index 00000000..df284f8a --- /dev/null +++ b/src/pages/shop/checkout/finish.js @@ -0,0 +1,47 @@ +import WithAuth from "@/components/auth/WithAuth"; +import Link from "@/components/elements/Link"; +import AppBar from "@/components/layouts/AppBar"; +import Header from "@/components/layouts/Header"; +import Layout from "@/components/layouts/Layout"; +import apiOdoo from "@/core/utils/apiOdoo"; +import { useAuth } from "@/core/utils/auth"; +import { EnvelopeIcon } from "@heroicons/react/24/outline"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; + +export default function FinishCheckout() { + const router = useRouter(); + const { id } = router.query; + const [ auth ] = useAuth(); + const [ transaction, setTransactions ] = useState(null); + + useEffect(() => { + const loadTransaction = async () => { + if (auth && id) { + const dataTransaction = await apiOdoo('GET', `/api/v1/partner/${auth.partner_id}/sale_order/${id}`); + setTransactions(dataTransaction); + } + }; + loadTransaction(); + }); + + return ( + <WithAuth> + <Layout> + <AppBar title="Pembelian Berhasil" /> + + <div className="m-4 rounded-xl bg-yellow_r-4 text-center border border-yellow_r-7"> + <div className="px-4 py-6 text-yellow_r-12"> + <p className="h2 mb-2">Terima Kasih atas Pembelian Anda</p> + <p className="text-yellow_r-11 mb-4 leading-6">Rincian belanja sudah kami kirimkan ke email anda. Mohon dicek kembali. jika tidak menerima email, anda dapat menghubungi kami disini.</p> + <p className="mb-2 font-medium">{ transaction?.name }</p> + <p className="text-caption-2 text-yellow_r-11">No. Transaksi</p> + </div> + <Link href={transaction?.id ? `/my/transaction/${transaction.id}` : '/'} className="bg-yellow_r-6 text-yellow_r-12 rounded-b-xl py-4 block"> + Lihat detail pembelian Anda disini + </Link> + </div> + </Layout> + </WithAuth> + ); +}
\ No newline at end of file diff --git a/src/pages/shop/checkout/index.js b/src/pages/shop/checkout/index.js new file mode 100644 index 00000000..57fe5d3a --- /dev/null +++ b/src/pages/shop/checkout/index.js @@ -0,0 +1,319 @@ +import { ExclamationCircleIcon } from "@heroicons/react/24/solid"; +import { useEffect, useState } from "react"; +import Alert from "@/components/elements/Alert"; +import AppBar from "@/components/layouts/AppBar"; +import Layout from "@/components/layouts/Layout"; +import LineDivider from "@/components/elements/LineDivider"; +import Link from "@/components/elements/Link"; +import ProgressBar from "@/components/elements/ProgressBar"; +import Spinner from "@/components/elements/Spinner"; +import apiOdoo from "@/core/utils/apiOdoo"; +import { useAuth } from "@/core/utils/auth"; +import { deleteItemCart, getCart } from "@/core/utils/cart"; +import currencyFormat from "@/core/utils/currencyFormat"; +import { getItemAddress } from "@/core/utils/address"; +import { useRouter } from "next/router"; +import WithAuth from "@/components/auth/WithAuth"; +import { toast } from "react-hot-toast"; +import getFileBase64 from "@/core/utils/getFileBase64"; +import VariantCard from "@/components/variants/VariantCard"; + +export default function Checkout() { + const router = useRouter(); + const { product_id, qty } = router.query; + const [ auth ] = useAuth(); + const [ addresses, setAddresses ] = useState(null); + const [ poNumber, setPoNumber ] = useState(''); + const [ poFile, setPoFile ] = useState(''); + const [ selectedAddress, setSelectedAddress ] = useState({ + shipping: null, + invoicing: null + }); + const [ selectedPayment, setSelectedPayment ] = useState(null); + const [ products, setProducts ] = useState(null); + const [ totalAmount, setTotalAmount ] = useState(0); + const [ totalDiscountAmount, setTotalDiscountAmount ] = useState(0); + + const payments = [ + { name: 'BCA', number: '8870-4000-81' }, + { name: 'MANDIRI', number: '155-0067-6869-75' }, + ]; + + useEffect(() => { + const getAddresses = async () => { + if (auth) { + const dataAddresses = await apiOdoo('GET', `/api/v1/user/${auth.id}/address`); + setAddresses(dataAddresses); + } + }; + getAddresses(); + }, [auth]); + + useEffect(() => { + const getProducts = async () => { + let cart = getCart(); + let productIds = []; + if (product_id) { + productIds = [parseInt(product_id)]; + } else { + productIds = Object + .values(cart) + .filter((itemCart) => itemCart.selected == true) + .map((itemCart) => itemCart.product_id); + } + if (productIds.length > 0) { + productIds = productIds.join(','); + let dataProducts = await apiOdoo('GET', `/api/v1/product_variant/${productIds}`); + dataProducts = dataProducts.map((product) => { + if (product_id) { + product.quantity = 1; + if (qty) product.quantity = parseInt(qty); + } else { + product.quantity = cart[product.id].quantity; + } + return product; + }); + setProducts(dataProducts); + } + }; + getProducts(); + }, [router, auth, product_id, qty]); + + useEffect(() => { + if (addresses) { + const matchAddress = (key) => { + const addressToMatch = getItemAddress(key); + let foundAddress = addresses.filter((address) => address.id == addressToMatch); + if (foundAddress.length > 0) { + return foundAddress[0]; + } + return addresses[0]; + } + setSelectedAddress({ + shipping: matchAddress('shipping'), + invoicing: matchAddress('invoicing'), + }); + }; + }, [addresses]); + + useEffect(() => { + if (products) { + let calculateTotalAmount = 0; + let calculateTotalDiscountAmount = 0; + products.forEach(product => { + calculateTotalAmount += product.price.price * product.quantity; + calculateTotalDiscountAmount += (product.price.price - product.price.price_discount) * product.quantity; + }); + setTotalAmount(calculateTotalAmount); + setTotalDiscountAmount(calculateTotalDiscountAmount); + } + }, [products]); + + const submit = async () => { + if (!selectedPayment) { + toast.error('Mohon pilih metode pembayaran', { + position: 'bottom-center' + }); + return; + } + if (poFile && poFile.size > 5000000) { + toast.error('Maksimal ukuran file adalah 5MB', { + position: 'bottom-center' + }); + return; + } + let productOrder = products.map((product) => ({ 'product_id': product.id, 'quantity': product.quantity })); + let data = { + 'partner_shipping_id': selectedAddress.shipping.id, + 'partner_invoice_id': selectedAddress.invoicing.id, + 'order_line': JSON.stringify(productOrder), + 'type': 'sale_order' + }; + if (poNumber) data.po_number = poNumber; + if (poFile) data.po_file = await getFileBase64(poFile); + + const checkoutToOdoo = await apiOdoo('POST', `/api/v1/partner/${auth.partner_id}/sale_order/checkout`, data); + for (const product of products) { + deleteItemCart(product.id); + } + router.push(`/shop/checkout/finish?id=${checkoutToOdoo.id}`); + } + + return ( + <WithAuth> + <Layout> + <AppBar title={"Checkout"} /> + { !products && !addresses && ( + <div className="flex justify-center items-center gap-x-3 mt-14"> + <Spinner className="w-10 text-gray_r-8 fill-gray_r-12" /> + </div> + ) } + + { products && addresses && ( + <> + <ProgressBar + current={2} + labels={['Keranjang', 'Pembayaran', 'Selesai']} + /> + + <LineDivider/> + + <div className="p-4"> + <Alert type="info" className="text-caption-2 flex gap-x-3 items-center"> + <div> + <ExclamationCircleIcon className="w-6 text-blue-700"/> + </div> + <span>Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami disini</span> + </Alert> + </div> + + <LineDivider/> + + <div className="p-4"> + <div className="flex justify-between items-center"> + <h2>Alamat Pengiriman</h2> + <Link className="text-caption-1" href="/my/address?select=shipping">Pilih Alamat Lain</Link> + </div> + + { selectedAddress.shipping && ( + <div className="mt-4 text-caption-1"> + <div className="badge-red mb-2">{ selectedAddress.shipping.type.charAt(0).toUpperCase() + selectedAddress.shipping.type.slice(1) + ' Address' }</div> + <p className="font-medium">{ selectedAddress.shipping.name }</p> + <p className="mt-2 text-gray_r-11">{ selectedAddress.shipping.mobile }</p> + <p className="mt-1 text-gray_r-11">{ selectedAddress.shipping.street }, { selectedAddress.shipping?.city?.name }</p> + </div> + ) } + </div> + + <LineDivider/> + + <div className="p-4 flex flex-col gap-y-4"> + {products.map((product, index) => ( + <VariantCard + data={product} + openOnClick={false} + key={index} + /> + ))} + </div> + + <LineDivider/> + + <div className="p-4"> + <div className="flex justify-between items-center"> + <h2>Ringkasan Pesanan</h2> + <p className="text-gray_r-11 text-caption-1">{products.length} Barang</p> + </div> + <hr className="my-4 border-gray_r-6"/> + <div className="flex flex-col gap-y-4"> + <div className="flex gap-x-2 justify-between"> + <p>Total Belanja</p> + <p className="font-medium">{currencyFormat(totalAmount)}</p> + </div> + <div className="flex gap-x-2 justify-between"> + <p>Total Diskon</p> + <p className="font-medium text-red_r-11">- {currencyFormat(totalDiscountAmount)}</p> + </div> + <div className="flex gap-x-2 justify-between"> + <p>Subtotal</p> + <p className="font-medium">{currencyFormat(totalAmount - totalDiscountAmount)}</p> + </div> + <div className="flex gap-x-2 justify-between"> + <p>PPN 11% (Incl.)</p> + <p className="font-medium">{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</p> + </div> + </div> + <hr className="my-4 border-gray_r-6"/> + <div className="flex gap-x-2 justify-between mb-4"> + <p>Grand Total</p> + <p className="font-medium text-yellow_r-11">{currencyFormat(totalAmount - totalDiscountAmount)}</p> + </div> + <p className="text-caption-2 text-gray_r-10 mb-2">*) Belum termasuk biaya pengiriman</p> + <p className="text-caption-2 text-gray_r-10 leading-5"> + Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui <Link href="/">Syarat & Ketentuan</Link> yang berlaku + </p> + </div> + + <LineDivider/> + + <div className="p-4"> + <div className="flex justify-between items-center"> + <h2>Alamat Penagihan</h2> + <Link className="text-caption-1" href="/my/address?select=invoicing">Pilih Alamat Lain</Link> + </div> + + { selectedAddress.invoicing && ( + <div className="mt-4 text-caption-1"> + <div className="badge-red mb-2">{ selectedAddress.invoicing.type.charAt(0).toUpperCase() + selectedAddress.invoicing.type.slice(1) + ' Address' }</div> + <p className="font-medium">{ selectedAddress.invoicing.name }</p> + <p className="mt-2 text-gray_r-11">{ selectedAddress.invoicing.mobile }</p> + <p className="mt-1 text-gray_r-11">{ selectedAddress.invoicing.street } { selectedAddress.invoicing.street2 }</p> + </div> + ) } + </div> + + <LineDivider/> + + <div className="p-4"> + <h2>Metode Pembayaran <span className="font-normal text-gray_r-11">(Wajib dipilih)</span></h2> + <div className="grid gap-y-3 mt-4"> + { payments.map((payment, index) => ( + <button + type="button" + className={"text-left border border-gray_r-6 rounded-md p-3 " + (selectedPayment == payment.name && 'border-yellow_r-10 bg-yellow_r-3')} + onClick={() => setSelectedPayment(payment.name)} + key={index} + > + <p>{payment.name} - {payment.number}</p> + <p className="mt-1 text-gray_r-11">PT. Indoteknik Dotcom Gemilang</p> + </button> + )) } + </div> + </div> + + <LineDivider/> + + <div className="p-4"> + <h2>Purchase Order</h2> + + <div className="mt-4 flex gap-x-3"> + <div className="w-6/12"> + <label className="form-label font-normal"> + Dokumen PO + </label> + <input + type="file" + className="form-input mt-2 h-12" + accept="image/*,application/pdf" + onChange={(e) => setPoFile(e.target.files[0])} + /> + </div> + <div className="w-6/12"> + <label className="form-label font-normal">Nomor PO</label> + <input + type="text" + className="form-input mt-2 h-12" + value={poNumber} + onChange={(e) => setPoNumber(e.target.value)} + /> + </div> + </div> + <p className="text-caption-2 text-gray_r-11 mt-2">Ukuran dokumen PO Maksimal 5MB</p> + </div> + + <LineDivider/> + + <div className="flex gap-x-3 p-4"> + <button + className="flex-1 btn-yellow" + onClick={submit} + > + Bayar + </button> + </div> + </> + ) } + </Layout> + </WithAuth> + ) +}
\ No newline at end of file |
