import { Skeleton, Spinner } from '@chakra-ui/react'; import { BanknotesIcon, ChevronLeftIcon, ClockIcon, ExclamationCircleIcon, } from '@heroicons/react/24/outline'; import axios from 'axios'; import { AnimatePresence, motion } from 'framer-motion'; import { useRouter } from 'next/router'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { toast } from 'react-hot-toast'; import { useQuery } from 'react-query'; import snakecaseKeys from 'snakecase-keys'; import Alert from '@/core/components/elements/Alert/Alert'; import Divider from '@/core/components/elements/Divider/Divider'; import Image from '@/core/components/elements/Image/Image'; import Link from '@/core/components/elements/Link/Link'; import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; import DesktopView from '@/core/components/views/DesktopView'; import MobileView from '@/core/components/views/MobileView'; import useAuth from '@/core/hooks/useAuth'; import { getItemAddress } from '@/core/utils/address'; import { deleteItemCart } from '@/core/utils/cart'; import currencyFormat from '@/core/utils/currencyFormat'; import getFileBase64 from '@/core/utils/getFileBase64'; import { gtagPurchase } from '@/core/utils/googleTag'; import whatsappUrl from '@/core/utils/whatsappUrl'; import addressesApi from '@/lib/address/api/addressesApi'; import { MapPinIcon } from 'lucide-react'; import CartItem from '~/modules/cart/components/Item.tsx'; import ExpedisiList from '../api/ExpedisiList'; import { getVoucher } from '../api/getVoucher'; import { useAddress } from '../stores/useAdress'; import SectionExpedition from './SectionExpedition'; const SELF_PICKUP_ID = 32; const { checkoutApi } = require('../api/checkoutApi'); const { getProductsCheckout } = require('../api/checkoutApi'); function convertToInternational(number) { if (typeof number !== 'string') { throw new Error("Input harus berupa string"); } if (number.startsWith('08')) { return '+62' + number.slice(2); } return number; } const Checkout = () => { const router = useRouter(); const query = router.query.source ?? null; const qVoucher = router.query.voucher ?? null; const auth = useAuth(); const [activeVoucher, SetActiveVoucher] = useState(null); const [activeVoucherShipping, setActiveVoucherShipping] = useState(null); const { data: cartCheckout } = useQuery( ['cartCheckout', activeVoucher, activeVoucherShipping], () => getProductsCheckout({ source: query, voucher: activeVoucher, voucher_shipping: activeVoucherShipping, }) ); const { selectedAddress, setSelectedAddress, addresses, setAddresses, setAddressMaps, setCoordinate, setPostalCode, } = useAddress(); useEffect(() => { if (!auth) return; const getAddresses = async () => { const dataAddresses = await addressesApi(); setAddresses(dataAddresses); }; getAddresses(); }, [auth]); useEffect(() => { if (!addresses) return; const matchAddress = (key) => { if (key === 'invoicing') { key = 'invoice'; } const addressToMatch = getItemAddress(key); const foundAddress = addresses.filter( (address) => address.id == addressToMatch ); if (foundAddress.length > 0) { return foundAddress[0]; } return addresses[0]; }; let ship = matchAddress('shipping'); setSelectedAddress({ shipping: matchAddress('shipping'), invoicing: matchAddress('invoicing'), }); setPostalCode(ship?.zip); if (ship?.addressMap) { setAddressMaps(ship?.addressMap); setCoordinate({ destination_latitude: ship?.latitude, destination_longitude: ship?.longtitude, }); } }, [addresses]); const [products, setProducts] = useState(null); 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); const [bottomPopup, SetBottomPopup] = useState(null); const [bottomPopupTnC, SetBottomPopupTnC] = useState(null); const [itemTnC, setItemTnC] = useState(null); const [listVouchers, SetListVoucher] = useState(null); const [listVoucherShippings, SetListVoucherShipping] = useState(null); const [discountVoucher, SetDiscountVoucher] = useState(0); const [codeVoucher, SetCodeVoucher] = useState(null); const [findCodeVoucher, SetFindVoucher] = useState(null); const [selisihHargaCode, SetSelisihHargaCode] = useState(null); const [buttonTerapkan, SetButtonTerapkan] = useState(false); const [checkoutValidation, setCheckoutValidation] = useState(false); const [loadingVoucher, setLoadingVoucher] = useState(true); const [loadingRajaOngkir, setLoadingRajaOngkir] = useState(false); const [grandTotal, setGrandTotal] = useState(0); const [hasFlashSale, setHasFlashSale] = useState(false); const expedisiValidation = useRef(null); const voucher = async () => { if (!listVouchers) { try { setLoadingVoucher(true); let dataVoucher = await getVoucher(auth?.id, { source: query, type: 'all,brand', }); SetListVoucher(dataVoucher); let dataVoucherShipping = await getVoucher(auth?.id, { source: query, type: 'shipping', }); SetListVoucherShipping(dataVoucherShipping); } finally { setLoadingVoucher(false); } } }; const VoucherCode = async (code) => { // let dataVoucher = await findVoucher(code, auth.id, query); let dataVoucher = await getVoucher(auth?.id, { source: query, code: code, }); if (dataVoucher.length <= 0) { SetFindVoucher(1); return; } dataVoucher.forEach((addNewLine) => { if (addNewLine.applyType !== 'shipping') { // Mencari voucher dalam listVouchers let checkList = listVouchers?.findIndex( (voucher) => voucher.code === addNewLine.code ); if (checkList >= 0) { if (listVouchers[checkList].canApply) { ToggleSwitch(addNewLine.code); // Perbaikan: Gunakan code voucher yang benar SetCodeVoucher(null); } else { SetSelisihHargaCode(listVouchers[checkList].differenceToApply); SetFindVoucher(2); } return; // Hentikan eksekusi lebih lanjut pada iterasi ini } // Memeriksa apakah subtotal memenuhi syarat minimal pembelian if (cartCheckout?.subtotal < addNewLine.minPurchaseAmount) { SetSelisihHargaCode( currencyFormat( addNewLine.minPurchaseAmount - cartCheckout?.subtotal ) ); SetFindVoucher(2); return; } else { SetFindVoucher(3); SetButtonTerapkan(true); } // Tambahkan voucher ke list dan set voucher aktif SetListVoucher((prevList) => [addNewLine, ...prevList]); if (addNewLine.canApply) { SetActiveVoucher(addNewLine.code); } } else { // Mencari voucher dalam listVoucherShippings let checkList = listVoucherShippings?.findIndex( (voucher) => voucher.code === addNewLine.code ); if (checkList >= 0) { if (listVoucherShippings[checkList].canApply) { ToggleSwitch(addNewLine.code); // Perbaikan: Gunakan code voucher yang benar SetCodeVoucher(null); } else { SetSelisihHargaCode( listVoucherShippings[checkList].differenceToApply ); SetFindVoucher(2); } return; // Hentikan eksekusi lebih lanjut pada iterasi ini } // Memeriksa apakah subtotal memenuhi syarat minimal pembelian if (cartCheckout?.subtotal < addNewLine.minPurchaseAmount) { SetSelisihHargaCode( currencyFormat( addNewLine.minPurchaseAmount - cartCheckout?.subtotal ) ); SetFindVoucher(2); return; } else { SetFindVoucher(3); SetButtonTerapkan(true); } // Tambahkan voucher ke list pengiriman dan set voucher aktif pengiriman SetListVoucherShipping((prevList) => [addNewLine, ...prevList]); if (addNewLine.canApply) { setActiveVoucherShipping(addNewLine.code); } } }); // let addNewLine = dataVoucher[0]; }; useEffect(() => { SetFindVoucher(null); }, [bottomPopup]); useEffect(() => { // voucher(); const loadExpedisi = async () => { let dataExpedisi = await ExpedisiList(); dataExpedisi = dataExpedisi.map((expedisi) => ({ value: expedisi.id, label: expedisi.name, carrierId: expedisi.deliveryCarrierId, })); setExpedisi(dataExpedisi); }; loadExpedisi(); const handlePopState = () => { router.push('/shop/cart'); }; window.onpopstate = handlePopState; return () => { window.onpopstate = null; }; }, []); useEffect(() => { if (qVoucher === 'PASTIHEMAT' && listVouchers) { let code = qVoucher; VoucherCode(code); } }, [listVouchers]); useEffect(() => { setProducts(cartCheckout?.products); setCheckWeight(cartCheckout?.hasProductWithoutWeight); setTotalWeight(cartCheckout?.totalWeight.g); const hasFlashSale = cartCheckout?.products.some( (product) => product.hasFlashsale ); setHasFlashSale(hasFlashSale); }, [cartCheckout]); useEffect(() => { setCheckoutValidation(false); const loadServiceRajaOngkir = async () => { setLoadingRajaOngkir(true); 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) ); setLoadingRajaOngkir(false); 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(','); if (serviceType[0] === 0) return; setselectedCarrier(serviceType[0]); setselectedCarrierId(serviceType[1]); setListServiceExpedisi([]); } }, [selectedExpedisi]); const poNumber = useRef(null); const poFile = useRef(null); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const GT = cartCheckout?.grandTotal + Math.round(parseInt(finalShippingAmt * 1.1) / 1000) * 1000; const finalGT = GT < 0 ? 0 : GT; setGrandTotal(finalGT); }, [ biayaKirim, cartCheckout?.grandTotal, activeVoucher, activeVoucherShipping, ]); 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; } if (selectedExpedisi === 0) { setCheckoutValidation(true); if (expedisiValidation.current) { const position = expedisiValidation.current.getBoundingClientRect(); window.scrollTo({ top: position.top - 300 + window.pageYOffset, behavior: 'smooth', }); } return; } if (selectedCarrier != 1 && biayaKirim == 0) { toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.'); return; } setIsLoading(true); const productOrder = products.map((product) => ({ product_id: product.id, quantity: product.quantity, available_quantity: product?.availableQuantity, })); let data = { // partner_shipping_id: auth.partnerId, // partner_invoice_id: auth.partnerId, partner_shipping_id: selectedAddress?.shipping?.id || auth.partnerId, partner_invoice_id: selectedAddress?.invoicing?.id || auth.partnerId, user_id: auth.id, order_line: JSON.stringify(productOrder), delivery_amount: biayaKirim, carrier_id: selectedCarrierId, estimated_arrival_days: splitDuration(etd), delivery_service_type: selectedExpedisiService, flash_sale: hasFlashSale, // dibuat negasi untuk ngetest kebalikan nilai false voucher: activeVoucher, voucher_shipping: activeVoucherShipping, type: 'sale_order', }; if (query) { data.source = 'buy'; } 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; } else { gtagPurchase(products, biayaKirim, isCheckouted.name); gtag('event', 'conversion', { send_to: 'AW-954540379/nDymCL3BhaQYENvClMcD', value: cartCheckout?.grandTotal + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000, currency: 'IDR', transaction_id: isCheckouted.id, }); gtag('set', 'user_data', { email: auth.email, phone_number: convertToInternational(auth.mobile) ?? convertToInternational(auth.phone), }); gtag('config', 'AW-954540379', { ' allow_enhanced_conversions':true } ) ; for (const product of products) deleteItemCart({ productId: product.id }); if (grandTotal > 0) { 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; } else { window.location.href = `${ process.env.NEXT_PUBLIC_SELF_HOST }/shop/checkout/success?order_id=${isCheckouted.name.replace( /\//g, '-' )}`; } } /* const midtrans = async () => { for (const product of products) deleteItemCart({ productId: product.id }); if (grandTotal > 0) { 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; } else { window.location.href = `${ process.env.NEXT_PUBLIC_SELF_HOST }/shop/checkout/success?order_id=${isCheckouted.name.replace( /\//g, '-' )}`; } };*/ }; const handlingActivateCode = async () => { VoucherCode(codeVoucher); }; const handleUseVoucher = async (code, isCheck) => { if (isCheck) { if (code === activeVoucher) { SetActiveVoucher(null); SetDiscountVoucher(0); } else { SetActiveVoucher(code); SetFindVoucher(null); document.getElementById('uniqCode') ? (document.getElementById('uniqCode').value = '') : ''; SetButtonTerapkan(false); } } else { SetActiveVoucher(code); SetFindVoucher(null); document.getElementById('uniqCode') ? (document.getElementById('uniqCode').value = '') : ''; SetButtonTerapkan(false); } }; const onChangeCodeVoucher = async (e) => { SetCodeVoucher(e.target.value); SetButtonTerapkan(false); }; const [isChecked, setIsChecked] = useState(false); const ToggleSwitch = (code) => { setIsChecked(!isChecked); handleUseVoucher(code, !isChecked); }; const handlingTnC = async (item) => { setItemTnC(item); SetBottomPopupTnC(true); }; // const taxTotal = (totalAmount - totalDiscountAmount - discountVoucher) * 0.11 const hasNoPrice = useMemo(() => { if (!products) return false; for (const item of products) { if (item.price.priceDiscount == 0) return true; } return false; }, [products]); const voucherShippingAmt = cartCheckout?.discountVoucherShipping || 0; const discShippingAmt = Math.min(biayaKirim, voucherShippingAmt); const finalShippingAmt = biayaKirim - discShippingAmt; const totalDiscountVoucher = cartCheckout?.discountVoucher + (cartCheckout?.discountVoucherShipping || 0); return ( <> { SetBottomPopupTnC(false); SetBottomPopup(false); }} title={
{' '}
} >

{itemTnC?.name}

{' '} {' '} Berakhir dalam :{' '} {itemTnC?.remainingTime} lagi
Kode Voucher : {itemTnC?.code}

Syarat dan Ketentuan


SetBottomPopup(false)} title='Gunakan Promo' >
onChangeCodeVoucher(e)} />
{findCodeVoucher === 3 && activeVoucher === codeVoucher && (
Voucher berhasil ditambahkan{' '}
)} {findCodeVoucher === 1 && (
Kode voucher salah / sudah tidak berlaku lagi. Coba voucher lainnya, ya.
)} {findCodeVoucher === 2 && (
Tambah {selisihHargaCode}{' '} untuk pakai promo ini
)}
{listVoucherShippings && listVoucherShippings?.length > 0 && (

Promo Extra Potongan Ongkir

{listVoucherShippings?.map((item) => (
{item.canApply && ( )} {!item.canApply && ( )}
{item.canApply === false && (
)}
{item.name}

{item.name}

{item.description}{' '}

Kode Voucher :{' '} {item.code}

{activeVoucher === item.code && ( Voucher digunakan{' '} )}

Berakhir dalam{' '} {item.remainingTime} {' '} lagi,{' '}
handlingTnC(item)} > Baca S&K

))}
)}
{!loadingVoucher && listVouchers?.length > 0 && (

Promo Khusus Untuk {auth?.name}

)} {loadingVoucher && ( <>
)} {!loadingVoucher && listVouchers?.map((item) => (
{item.canApply && ( )} {!item.canApply && ( )}
{item.canApply === false && (
)}
{item.name}

{item.name}

{item.description}{' '}

Kode Voucher :{' '} {item.code}

{activeVoucher === item.code && ( Voucher digunakan{' '} )}

Berakhir dalam{' '} {item.remainingTime} {' '} lagi,{' '}
handlingTnC(item)} > Baca S&K

))}
Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami disini
{selectedCarrierId == SELF_PICKUP_ID && (
)} {selectedCarrierId == SELF_PICKUP_ID && ( )} {selectedCarrierId != SELF_PICKUP_ID && ( )}
{!!products && snakecaseKeys(products).map((item, index) => ( ))}
Ringkasan Pesanan

{!cartCheckout ? (
) : (
Total Belanja
{currencyFormat(cartCheckout?.totalPurchase)}
Diskon Produk
- {currencyFormat(cartCheckout?.totalDiscount)}
{activeVoucher && (
Diskon Voucher
- {currencyFormat(cartCheckout?.discountVoucher)}
)}
Subtotal
{currencyFormat(cartCheckout?.subtotal)}
PPN 11%
{currencyFormat(cartCheckout?.tax)}
Biaya Kirim

{etdFix}

{currencyFormat( Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 )}
{activeVoucherShipping && voucherShippingAmt && (
Diskon Kirim
- {currencyFormat(discShippingAmt)}
)}
)}
{!cartCheckout ? (
{' '}
) : (
Grand Total
{currencyFormat(grandTotal)}
)}
{/*

*) 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 && ( )} {selectedCarrierId != SELF_PICKUP_ID && ( )} {products && }
Detail Pesanan
{!!products && snakecaseKeys(products).map((item, index) => ( ))}
Ringkasan Pesanan
{cartCheckout?.totalWeight.kg} Kg

{!cartCheckout ? (
) : (
Total Belanja
{currencyFormat(cartCheckout?.totalPurchase)}
Diskon Produk
- {currencyFormat(cartCheckout?.totalDiscount)}
{activeVoucher && (
Diskon Voucher
- {currencyFormat(cartCheckout?.discountVoucher)}
)}
Subtotal
{currencyFormat(cartCheckout?.subtotal)}
PPN 11%
{currencyFormat(cartCheckout?.tax)}
Biaya Kirim

{etdFix}

{currencyFormat( Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 )}
{activeVoucherShipping && voucherShippingAmt && (
Diskon Kirim
- {currencyFormat(discShippingAmt)}
)}
)}
{!cartCheckout ? (
{' '}
) : (
Grand Total
{currencyFormat(grandTotal)}
)}

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}

{address.addressMap ? ( ) : ( )}
)}
); const SectionValidation = ({ address }) => address?.stateId == 0 && (
Mohon untuk memperbarui alamat Anda dengan mengklik tombol di bawah ini.{' '}
Update Alamat
); const SectionExpedisi = ({ address, listExpedisi, setSelectedExpedisi, checkWeigth, checkoutValidation, expedisiValidation, loadingRajaOngkir, }) => address?.rajaongkirCityId > 0 && (
Pilih Ekspedisi:
{loadingRajaOngkir && ( )}
{checkoutValidation && ( *silahkan 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 && ( <>
Tipe Layanan Ekspedisi:
); 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 ''; } function splitDuration(duration) { if (duration) { let estimationDate = null; if (duration.includes('-')) { estimationDate = duration.split('-'); estimationDate = parseInt(estimationDate[1]); } else { estimationDate = parseInt(duration); } return estimationDate; } 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 : 0817-1718-1922

); export default Checkout;