From 637c22f1886cecf7307ced88dc951134d466a3fa Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Mon, 19 Jun 2023 15:46:03 +0700 Subject: checkout --- src/lib/checkout/api/checkoutApi.js | 9 +- src/lib/checkout/components/Checkout.jsx | 90 +-- src/lib/checkout/components/CheckoutOld.jsx | 811 ++++++++++++++++++++++++++++ 3 files changed, 840 insertions(+), 70 deletions(-) create mode 100644 src/lib/checkout/components/CheckoutOld.jsx (limited to 'src/lib/checkout') diff --git a/src/lib/checkout/api/checkoutApi.js b/src/lib/checkout/api/checkoutApi.js index b76c9b7f..94de8721 100644 --- a/src/lib/checkout/api/checkoutApi.js +++ b/src/lib/checkout/api/checkoutApi.js @@ -1,7 +1,7 @@ import odooApi from '@/core/api/odooApi' import { getAuth } from '@/core/utils/auth' -const checkoutApi = async ({ data }) => { +export const checkoutApi = async ({ data }) => { const auth = getAuth() const dataCheckout = await odooApi( 'POST', @@ -11,4 +11,9 @@ const checkoutApi = async ({ data }) => { return dataCheckout } -export default checkoutApi +export const getProductsCheckout = async () => { + const id = getAuth()?.id + const products = await odooApi('GET',`/api/v1/user/${id}/sale_order/checkout`) + + return products +} diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 088b641b..09fdbd86 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -4,15 +4,14 @@ import Link from '@/core/components/elements/Link/Link' import useAuth from '@/core/hooks/useAuth' import { getItemAddress } from '@/core/utils/address' import addressesApi from '@/lib/address/api/addressesApi' -import CartApi from '@/lib/cart/api/CartApi' import { ExclamationCircleIcon } from '@heroicons/react/24/outline' import React, { useEffect, useRef, useState } from 'react' import _ from 'lodash' -import { deleteItemCart, getCart, getItemCart } from '@/core/utils/cart' +import { deleteItemCart, getCartApi } from '@/core/utils/cart' import currencyFormat from '@/core/utils/currencyFormat' import { toast } from 'react-hot-toast' import getFileBase64 from '@/core/utils/getFileBase64' -import checkoutApi from '../api/checkoutApi' +// import checkoutApi from '../api/checkoutApi' import { useRouter } from 'next/router' import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' import axios from 'axios' @@ -24,12 +23,18 @@ import whatsappUrl from '@/core/utils/whatsappUrl' import { createSlug } from '@/core/utils/slug' import { Button, Modal } from 'flowbite-react' import BottomPopup from '@/core/components/elements/Popup/BottomPopup' +import { useQuery } from 'react-query' const SELF_PICKUP_ID = 32 +const { checkoutApi } = require('../api/checkoutApi') +const { getProductsCheckout } = require('../api/checkoutApi') + const Checkout = () => { const router = useRouter() const auth = useAuth() + const { data: cartCheckout } = useQuery('cartCheckout', getProductsCheckout) + const [selectedAddress, setSelectedAddress] = useState({ shipping: null, invoicing: null @@ -66,8 +71,6 @@ const Checkout = () => { }, [addresses]) const [products, setProducts] = useState(null) - const [totalAmount, setTotalAmount] = useState(0) - const [totalDiscountAmount, setTotalDiscountAmount] = useState(0) const [totalWeight, setTotalWeight] = useState(0) const [priceCheck, setPriceCheck] = useState(false) const [listExpedisi, setExpedisi] = useState([]) @@ -96,52 +99,10 @@ const Checkout = () => { }, []) useEffect(() => { - const loadProducts = async () => { - let variantIds = '' - let { query } = router - if (query?.productId) { - variantIds = query.productId - } else { - const cart = getCart() - variantIds = _.filter(cart, (o) => o.selected == true) - .map((o) => o.productId) - .join(',') - } - - const dataProducts = await CartApi({ variantIds }) - const productsWithQuantity = dataProducts?.map((product) => { - if (product.price.priceDiscount == 0) setPriceCheck(true) - return { - ...product, - quantity: query.quantity - ? query.quantity - : getItemCart({ productId: product.id }).quantity - } - }) - setProducts(productsWithQuantity) - } - loadProducts() - }, [router]) - - useEffect(() => { - if (products) { - let calculateTotalAmount = 0 - let calculateTotalDiscountAmount = 0 - let calcuateTotalWeight = 0 - products.forEach((product) => { - calculateTotalAmount += product.price.price * product.quantity - calculateTotalDiscountAmount += - (product.price.price - product.price.priceDiscount) * product.quantity - calcuateTotalWeight += product.weight * product.quantity - if (product.weight == 0) { - setCheckWeight(true) - } - }) - setTotalAmount(calculateTotalAmount) - setTotalDiscountAmount(calculateTotalDiscountAmount) - setTotalWeight(calcuateTotalWeight * 1000) - } - }, [products]) + setProducts(cartCheckout?.products) + setCheckWeight(cartCheckout?.hasProductWithoutWeight) + setTotalWeight(cartCheckout?.totalWeight.g) + }, [cartCheckout]) useEffect(() => { const loadServiceRajaOngkir = async () => { @@ -240,7 +201,6 @@ const Checkout = () => { setIsLoading(false) window.location.href = payment.data.redirectUrl } - const taxTotal = (totalAmount - totalDiscountAmount) * 0.11 return ( <> @@ -304,19 +264,19 @@ const Checkout = () => {
Total Belanja
-
{currencyFormat(totalAmount)}
+
{currencyFormat(cartCheckout?.totalPurchase)}
Total Diskon
-
- {currencyFormat(totalDiscountAmount)}
+
- {currencyFormat(cartCheckout?.totalDiscount)}
Subtotal
-
{currencyFormat(totalAmount - totalDiscountAmount)}
+
{currencyFormat(cartCheckout?.subtotal)}
PPN 11%
-
{currencyFormat(taxTotal)}
+
{currencyFormat(cartCheckout?.tax)}
@@ -329,10 +289,7 @@ const Checkout = () => {
Grand Total
- {currencyFormat( - totalAmount - - totalDiscountAmount + - taxTotal + + {currencyFormat(cartCheckout?.grandTotal + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 )}
@@ -526,19 +483,19 @@ const Checkout = () => {
Total Belanja
-
{currencyFormat(totalAmount)}
+
{currencyFormat(cartCheckout?.totalPurchase)}
Total Diskon
-
- {currencyFormat(totalDiscountAmount)}
+
- {currencyFormat(cartCheckout?.totalDiscount)}
Subtotal
-
{currencyFormat(totalAmount - totalDiscountAmount)}
+
{currencyFormat(cartCheckout?.subtotal)}
PPN 11%
-
{currencyFormat(taxTotal)}
+
{currencyFormat(cartCheckout?.tax)}
@@ -554,10 +511,7 @@ const Checkout = () => {
Grand Total
- {currencyFormat( - totalAmount - - totalDiscountAmount + - taxTotal + + {currencyFormat(cartCheckout?.grandTotal + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 )}
diff --git a/src/lib/checkout/components/CheckoutOld.jsx b/src/lib/checkout/components/CheckoutOld.jsx new file mode 100644 index 00000000..088b641b --- /dev/null +++ b/src/lib/checkout/components/CheckoutOld.jsx @@ -0,0 +1,811 @@ +import Alert from '@/core/components/elements/Alert/Alert' +import Divider from '@/core/components/elements/Divider/Divider' +import Link from '@/core/components/elements/Link/Link' +import useAuth from '@/core/hooks/useAuth' +import { getItemAddress } from '@/core/utils/address' +import addressesApi from '@/lib/address/api/addressesApi' +import CartApi from '@/lib/cart/api/CartApi' +import { ExclamationCircleIcon } from '@heroicons/react/24/outline' +import React, { useEffect, useRef, useState } from 'react' +import _ from 'lodash' +import { deleteItemCart, getCart, getItemCart } from '@/core/utils/cart' +import currencyFormat from '@/core/utils/currencyFormat' +import { toast } from 'react-hot-toast' +import getFileBase64 from '@/core/utils/getFileBase64' +import checkoutApi from '../api/checkoutApi' +import { useRouter } from 'next/router' +import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' +import axios from 'axios' +import Image from '@/core/components/elements/Image/Image' +import MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' +import ExpedisiList from '../api/ExpedisiList' +import whatsappUrl from '@/core/utils/whatsappUrl' +import { createSlug } from '@/core/utils/slug' +import { Button, Modal } from 'flowbite-react' +import BottomPopup from '@/core/components/elements/Popup/BottomPopup' + +const SELF_PICKUP_ID = 32 + +const Checkout = () => { + const router = useRouter() + const auth = useAuth() + const [selectedAddress, setSelectedAddress] = useState({ + shipping: null, + invoicing: null + }) + const [addresses, setAddresses] = useState(null) + + useEffect(() => { + if (!auth) return + + const getAddresses = async () => { + const dataAddresses = await addressesApi() + setAddresses(dataAddresses) + } + + getAddresses() + }, [auth]) + + useEffect(() => { + if (!addresses) return + + const matchAddress = (key) => { + const addressToMatch = getItemAddress(key) + const 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]) + + const [products, setProducts] = useState(null) + const [totalAmount, setTotalAmount] = useState(0) + const [totalDiscountAmount, setTotalDiscountAmount] = useState(0) + const [totalWeight, setTotalWeight] = useState(0) + const [priceCheck, setPriceCheck] = useState(false) + const [listExpedisi, setExpedisi] = useState([]) + const [listserviceExpedisi, setListServiceExpedisi] = useState([]) + const [selectedExpedisi, setSelectedExpedisi] = useState(0) + const [selectedCarrierId, setselectedCarrierId] = useState(0) + const [selectedCarrier, setselectedCarrier] = useState(0) + const [biayaKirim, setBiayaKirim] = useState(0) + const [checkWeigth, setCheckWeight] = useState(false) + const [selectedServiceType, setSelectedServiceType] = useState(null) + const [selectedExpedisiService, setselectedExpedisiService] = useState(null) + const [etd, setEtd] = useState(null) + const [etdFix, setEtdFix] = useState(null) + + useEffect(() => { + const loadExpedisi = async () => { + let dataExpedisi = await ExpedisiList() + dataExpedisi = dataExpedisi.map((expedisi) => ({ + value: expedisi.id, + label: expedisi.name, + carrierId: expedisi.deliveryCarrierId + })) + setExpedisi(dataExpedisi) + } + loadExpedisi() + }, []) + + useEffect(() => { + const loadProducts = async () => { + let variantIds = '' + let { query } = router + if (query?.productId) { + variantIds = query.productId + } else { + const cart = getCart() + variantIds = _.filter(cart, (o) => o.selected == true) + .map((o) => o.productId) + .join(',') + } + + const dataProducts = await CartApi({ variantIds }) + const productsWithQuantity = dataProducts?.map((product) => { + if (product.price.priceDiscount == 0) setPriceCheck(true) + return { + ...product, + quantity: query.quantity + ? query.quantity + : getItemCart({ productId: product.id }).quantity + } + }) + setProducts(productsWithQuantity) + } + loadProducts() + }, [router]) + + useEffect(() => { + if (products) { + let calculateTotalAmount = 0 + let calculateTotalDiscountAmount = 0 + let calcuateTotalWeight = 0 + products.forEach((product) => { + calculateTotalAmount += product.price.price * product.quantity + calculateTotalDiscountAmount += + (product.price.price - product.price.priceDiscount) * product.quantity + calcuateTotalWeight += product.weight * product.quantity + if (product.weight == 0) { + setCheckWeight(true) + } + }) + setTotalAmount(calculateTotalAmount) + setTotalDiscountAmount(calculateTotalDiscountAmount) + setTotalWeight(calcuateTotalWeight * 1000) + } + }, [products]) + + useEffect(() => { + const loadServiceRajaOngkir = async () => { + const body = { + origin: 2127, + destination: selectedAddress.shipping.rajaongkirCityId, + weight: totalWeight, + courier: selectedCarrier, + originType: 'subdistrict', + destinationType: 'subdistrict' + } + setBiayaKirim(0) + const dataService = await axios('/api/rajaongkir-service?body=' + JSON.stringify(body)) + setListServiceExpedisi(dataService.data[0].costs) + if (dataService.data[0].costs[0]) { + setBiayaKirim(dataService.data[0].costs[0]?.cost[0].value) + setselectedExpedisiService( + dataService.data[0].costs[0]?.description + '-' + dataService.data[0].costs[0]?.service + ) + setEtd(dataService.data[0].costs[0]?.cost[0].etd) + toast.success('Harap pilih tipe layanan pengiriman') + } else { + toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.') + } + } + if (selectedCarrier != 0 && selectedCarrier != 1 && totalWeight > 0) { + loadServiceRajaOngkir() + } else { + setListServiceExpedisi() + setBiayaKirim(0) + setselectedExpedisiService() + setEtd() + } + }, [selectedCarrier, selectedAddress, totalWeight]) + + useEffect(() => { + if (selectedServiceType) { + let serviceType = selectedServiceType.split(',') + setBiayaKirim(serviceType[0]) + setselectedExpedisiService(serviceType[1]) + setEtd(serviceType[2]) + } + }, [selectedServiceType]) + + useEffect(() => { + if (etd) setEtdFix(calculateEstimatedArrival(etd)) + }, [etd]) + + useEffect(() => { + if (selectedExpedisi) { + let serviceType = selectedExpedisi.split(',') + setselectedCarrier(serviceType[0]) + setselectedCarrierId(serviceType[1]) + setListServiceExpedisi([]) + } + }, [selectedExpedisi]) + + const poNumber = useRef(null) + const poFile = useRef(null) + + const [isLoading, setIsLoading] = useState(false) + + const checkout = async () => { + const file = poFile.current.files[0] + if (typeof file !== 'undefined' && file.size > 5000000) { + toast.error('Maksimal ukuran file adalah 5MB', { position: 'bottom-center' }) + return + } + setIsLoading(true) + const productOrder = products.map((product) => ({ + product_id: product.id, + quantity: product.quantity + })) + let data = { + partner_shipping_id: auth.partnerId, + partner_invoice_id: auth.partnerId, + order_line: JSON.stringify(productOrder), + delivery_amount: biayaKirim, + carrier_id: selectedCarrierId, + delivery_service_type: selectedExpedisiService, + type: 'sale_order' + } + if (poNumber.current.value) data.po_number = poNumber.current.value + if (typeof file !== 'undefined') data.po_file = await getFileBase64(file) + + const isCheckouted = await checkoutApi({ data }) + if (!isCheckouted?.id) { + toast.error('Gagal melakukan transaksi, terjadi kesalahan internal') + return + } + + for (const product of products) deleteItemCart({ productId: product.id }) + const payment = await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${isCheckouted.id}` + ) + setIsLoading(false) + window.location.href = payment.data.redirectUrl + } + const taxTotal = (totalAmount - totalDiscountAmount) * 0.11 + + return ( + <> + +
+ +
+ +
+ + Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami + disini + +
+
+ + + + {selectedCarrierId == SELF_PICKUP_ID && } + {selectedCarrierId != SELF_PICKUP_ID && ( + <> + + + + + )} + + + + + + +
+ {products && } +
+ + + +
+
+
Ringkasan Pesanan
+
{products?.length} Barang
+
+
+
+
+
Total Belanja
+
{currencyFormat(totalAmount)}
+
+
+
Total Diskon
+
- {currencyFormat(totalDiscountAmount)}
+
+
+
Subtotal
+
{currencyFormat(totalAmount - totalDiscountAmount)}
+
+
+
PPN 11%
+
{currencyFormat(taxTotal)}
+
+
+
+ Biaya Kirim

{etdFix}

+
+
{currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000)}
+
+
+
+
+
Grand Total
+
+ {currencyFormat( + totalAmount - + totalDiscountAmount + + taxTotal + + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} +
+
+ {/*

*) Belum termasuk biaya pengiriman

*/} +

+ Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + + Syarat & Ketentuan + {' '} + yang berlaku +

+
+ + + +
+
Purchase Order
+ +
+
+ + +
+
+ + +
+
+

Ukuran dokumen PO Maksimal 5MB

+
+ + + +
+ +
+ {priceCheck && ( +
+ + *) Terdapat produk yang belum memiliki harga,{' '} + + Hubungi Kami untuk meminta harga. + + +
+ )} +
+ + +
+
+ {selectedCarrierId == SELF_PICKUP_ID && } + {selectedCarrierId != SELF_PICKUP_ID && ( + <> + + + + + )} + + + + + + +
+
Detail Pesanan
+ + + + + + + + + + + {products?.map((product) => ( + + + + + + + ))} + +
Nama ProdukJumlahHargaSubtotal
+
+ {product?.name} +
+
+
+ {product?.parent?.name} +
+
+ {product?.code}{' '} + {product?.attributes.length > 0 + ? `| ${product?.attributes.join(', ')}` + : ''} +
+
+ Berat item : {product?.weight} Kg +
+
+
+ + + {product?.price?.discountPercentage > 0 && ( +
+
+ {currencyFormat(product?.price?.price)} +
+
+ {product?.price?.discountPercentage}% +
+
+ )} +
+ {product.price.priceDiscount > 0 + ? currencyFormat(product?.price?.priceDiscount) + : 'Call For Price'} +
+
+
+ {product.price.priceDiscount > 0 ? ( + currencyFormat(product?.price?.priceDiscount * product?.quantity) + ) : ( + + Call For Price{' '} + + )} +
+
+
+
+ +
+
+
+
Ringkasan Pesanan
+
{products?.length} Barang
+
+ +
+ +
+
+
Total Belanja
+
{currencyFormat(totalAmount)}
+
+
+
Total Diskon
+
- {currencyFormat(totalDiscountAmount)}
+
+
+
Subtotal
+
{currencyFormat(totalAmount - totalDiscountAmount)}
+
+
+
PPN 11%
+
{currencyFormat(taxTotal)}
+
+
+
+ Biaya Kirim +

{etdFix}

+
+
{currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000)}
+
+
+ +
+ +
+
Grand Total
+
+ {currencyFormat( + totalAmount - + totalDiscountAmount + + taxTotal + + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} +
+
+

+ Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + + Syarat & Ketentuan + {' '} + yang berlaku +

+ +
+ +
+ Purchase Order (Opsional) +
+ +
+
+ + +
+
+ + +
+
+

Ukuran dokumen PO Maksimal 5MB

+ +
+ + + {priceCheck && ( +
+ + *) Terdapat produk yang belum memiliki harga,{' '} + + Hubungi Kami untuk meminta harga. + + +
+ )} +
+
+
+
+ + ) +} + +const SectionAddress = ({ address, label, url }) => ( +
+
+
{label}
+ + Pilih Alamat Lain + +
+ + {address && ( +
+
+ {address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address'} +
+

{address.name}

+

{address.mobile}

+

+ {address.street}, {address?.city?.name} +

+
+ )} +
+) + +const SectionValidation = ({ address }) => + address?.rajaongkirCityId == 0 && ( + +
+ Mohon untuk memperbarui alamat Anda dengan mengklik tombol di bawah ini.{' '} +
+
+ + Update Alamat + +
+
+ ) + +const SectionExpedisi = ({ address, listExpedisi, setSelectedExpedisi, checkWeigth }) => + address?.rajaongkirCityId > 0 && ( +
+
+
Pilih Expedisi :
+
+ +
+
+ {checkWeigth == true && ( +

+ Mohon maaf, pengiriman hanya tersedia untuk self pickup karena terdapat barang yang belum + diatur beratnya. Mohon atur berat barang dengan menghubungi admin melalui{' '} + + tautan ini + +

+ )} +
+ ) + +const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => + listserviceExpedisi?.length > 0 && ( + <> +
+
+
Service Type Expedisi :
+
+ +
+
+
+ + + ) + +function addDays(date, days) { + const result = new Date(date) + result.setDate(result.getDate() + days) + return result +} + +function formatDate(date) { + const day = date.getDate() + const month = date.toLocaleString('default', { month: 'short' }) + return `${day} ${month}` +} + +function calculateEstimatedArrival(duration) { + if (duration) { + let estimationDate = duration.split('-') + estimationDate[0] = parseInt(estimationDate[0]) + estimationDate[1] = parseInt(estimationDate[1]) + const from = addDays(new Date(), estimationDate[0] + 3) + const to = addDays(new Date(), estimationDate[1] + 3) + + let etdText = `*Estimasi tiba ${formatDate(from)}` + + if (estimationDate[1] > estimationDate[0]) { + etdText += ` - ${formatDate(to)}` + } + + return etdText + } + + return '' +} + +const extractDuration = (text) => { + const matches = text.match(/\d+(?:-\d+)?/g) + + if (matches && matches.length === 1) { + const parts = matches[0].split('-') + const min = parseInt(parts[0]) + const max = parseInt(parts[1]) + + if (min === max) { + return min.toString() + } + + return matches[0] + } + + return '' +} + +const PickupAddress = ({ label }) => ( +
+
+
{label}
+
+
+

Indoteknik

+

+ Jl. Bandengan Utara Raya No.85, RT.3/RW.16, Penjaringan, Kec. Penjaringan, Kota Jkt Utara, + Daerah Khusus Ibukota Jakarta, Indonesia Kodepos : 14440 +

+

Telp : 021-2933 8828/29

+

Mobile : 0813 9000 7430

+
+
+) + +export default Checkout -- cgit v1.2.3