diff options
Diffstat (limited to 'src/lib/checkout/components')
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 1000 | ||||
| -rw-r--r-- | src/lib/checkout/components/CheckoutOld.jsx | 2 | ||||
| -rw-r--r-- | src/lib/checkout/components/CheckoutSection.jsx | 257 |
3 files changed, 752 insertions, 507 deletions
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 9a799010..4aafdece 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -1,191 +1,201 @@ -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 { Skeleton, Spinner } from '@chakra-ui/react'; import { BanknotesIcon, ChevronLeftIcon, ClockIcon, - ExclamationCircleIcon -} from '@heroicons/react/24/outline' -import React, { useEffect, useRef, useState } from 'react' -import _ from 'lodash' -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 { 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 BottomPopup from '@/core/components/elements/Popup/BottomPopup' -import { useQuery } from 'react-query' -import { gtagPurchase } from '@/core/utils/googleTag' -import { findVoucher, getVoucher } from '../api/getVoucher' -import CardProdcuctsList from '@/core/components/elements/Product/cartProductsList' -import { Spinner } from '@chakra-ui/react' -import { AnimatePresence, motion } from 'framer-motion' - -const SELF_PICKUP_ID = 32 - -const { checkoutApi } = require('../api/checkoutApi') -const { getProductsCheckout } = require('../api/checkoutApi') + 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 CartItem from '~/modules/cart/components/Item.tsx'; +import ExpedisiList from '../api/ExpedisiList'; +import { findVoucher, getVoucher } from '../api/getVoucher'; + +const SELF_PICKUP_ID = 32; + +const { checkoutApi } = require('../api/checkoutApi'); +const { getProductsCheckout } = require('../api/checkoutApi'); const Checkout = () => { - const router = useRouter() - const query = router.query.source ?? null - const auth = useAuth() + const router = useRouter(); + const query = router.query.source ?? null; + const auth = useAuth(); - const [activeVoucher, SetActiveVoucher] = useState(null) + const [activeVoucher, SetActiveVoucher] = useState(null); const { data: cartCheckout } = useQuery('cartCheckout-' + activeVoucher, () => getProductsCheckout(activeVoucher, query) - ) + ); const [selectedAddress, setSelectedAddress] = useState({ shipping: null, - invoicing: null - }) - const [addresses, setAddresses] = useState(null) + invoicing: null, + }); + const [addresses, setAddresses] = useState(null); useEffect(() => { - if (!auth) return + if (!auth) return; const getAddresses = async () => { - const dataAddresses = await addressesApi() - setAddresses(dataAddresses) - } + const dataAddresses = await addressesApi(); + setAddresses(dataAddresses); + }; - getAddresses() - }, [auth]) + getAddresses(); + }, [auth]); useEffect(() => { - if (!addresses) return + if (!addresses) return; const matchAddress = (key) => { - const addressToMatch = getItemAddress(key) - const foundAddress = addresses.filter((address) => address.id == addressToMatch) + const addressToMatch = getItemAddress(key); + const foundAddress = addresses.filter( + (address) => address.id == addressToMatch + ); if (foundAddress.length > 0) { - return foundAddress[0] + return foundAddress[0]; } - return addresses[0] - } + return addresses[0]; + }; setSelectedAddress({ shipping: matchAddress('shipping'), - invoicing: matchAddress('invoicing') - }) - }, [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 [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 expedisiValidation = useRef(null) + invoicing: matchAddress('invoicing'), + }); + }, [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 [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 { - let dataVoucher = await getVoucher(auth?.id, query) - SetListVoucher(dataVoucher) + let dataVoucher = await getVoucher(auth?.id, query); + SetListVoucher(dataVoucher); } finally { - setLoadingVoucher(false) + setLoadingVoucher(false); } } - } + }; const VoucherCode = async (code) => { - let dataVoucher = await findVoucher(code, auth.id, query) + let dataVoucher = await findVoucher(code, auth.id, query); if (dataVoucher.length <= 0) { - SetFindVoucher(1) - return + SetFindVoucher(1); + return; } - let addNewLine = dataVoucher[0] - let checkList = listVouchers.findIndex((voucher) => voucher.code == addNewLine.code) + let addNewLine = dataVoucher[0]; + let checkList = listVouchers.findIndex( + (voucher) => voucher.code == addNewLine.code + ); if (checkList >= 0) { if (listVouchers[checkList].canApply) { - ToggleSwitch(code) - SetCodeVoucher(null) + ToggleSwitch(code); + SetCodeVoucher(null); } else { - SetSelisihHargaCode(listVouchers[checkList].differenceToApply) - SetFindVoucher(2) + SetSelisihHargaCode(listVouchers[checkList].differenceToApply); + SetFindVoucher(2); } - return + return; } if (cartCheckout?.subtotal < addNewLine.minPurchaseAmount) { - SetSelisihHargaCode(currencyFormat(addNewLine.minPurchaseAmount - cartCheckout?.subtotal)) - SetFindVoucher(2) - return + SetSelisihHargaCode( + currencyFormat(addNewLine.minPurchaseAmount - cartCheckout?.subtotal) + ); + SetFindVoucher(2); + return; } else { - SetFindVoucher(3) - SetButtonTerapkan(true) + SetFindVoucher(3); + SetButtonTerapkan(true); } - SetListVoucher((prevList) => [addNewLine, ...prevList]) - SetActiveVoucher(addNewLine.code) - } + SetListVoucher((prevList) => [addNewLine, ...prevList]); + SetActiveVoucher(addNewLine.code); + }; useEffect(() => { - SetFindVoucher(null) - }, [bottomPopup]) + SetFindVoucher(null); + }, [bottomPopup]); useEffect(() => { const loadExpedisi = async () => { - let dataExpedisi = await ExpedisiList() + let dataExpedisi = await ExpedisiList(); dataExpedisi = dataExpedisi.map((expedisi) => ({ value: expedisi.id, label: expedisi.name, - carrierId: expedisi.deliveryCarrierId - })) - setExpedisi(dataExpedisi) - } - loadExpedisi() + carrierId: expedisi.deliveryCarrierId, + })); + setExpedisi(dataExpedisi); + }; + loadExpedisi(); const handlePopState = () => { - router.push('/shop/cart') - } + router.push('/shop/cart'); + }; - window.onpopstate = handlePopState + window.onpopstate = handlePopState; return () => { - window.onpopstate = null - } + window.onpopstate = null; + }; // voucher() - }, []) + }, []); const hitungDiscountVoucher = (code) => { - let dataVoucherIndex = listVouchers.findIndex((voucher) => voucher.code == code) - let dataActiveVoucher = listVouchers[dataVoucherIndex] + let dataVoucherIndex = listVouchers.findIndex( + (voucher) => voucher.code == code + ); + let dataActiveVoucher = listVouchers[dataVoucherIndex]; - let countDiscount = dataActiveVoucher.discountVoucher + let countDiscount = dataActiveVoucher.discountVoucher; /*if (dataActiveVoucher.discountType === 'percentage') { countDiscount = cartCheckout?.subtotal * (dataActiveVoucher.discountAmount / 100) @@ -199,215 +209,257 @@ const Checkout = () => { countDiscount = dataActiveVoucher.discountAmount }*/ - return countDiscount - } + return countDiscount; + }; useEffect(() => { - if (!listVouchers) return - if (!activeVoucher) return + if (!listVouchers) return; + if (!activeVoucher) return; - const countDiscount = hitungDiscountVoucher(activeVoucher) + const countDiscount = hitungDiscountVoucher(activeVoucher); - SetDiscountVoucher(countDiscount) - }, [activeVoucher, listVouchers]) + SetDiscountVoucher(countDiscount); + }, [activeVoucher, listVouchers]); useEffect(() => { - setProducts(cartCheckout?.products) - setCheckWeight(cartCheckout?.hasProductWithoutWeight) - setTotalWeight(cartCheckout?.totalWeight.g) - }, [cartCheckout]) + setProducts(cartCheckout?.products); + setCheckWeight(cartCheckout?.hasProductWithoutWeight); + setTotalWeight(cartCheckout?.totalWeight.g); + setHasFlashSale(cartCheckout?.products[0]?.hasFlashsale ? cartCheckout.products[0].hasFlashsale : false); + }, [cartCheckout]); + useEffect(() => { - setCheckoutValidation(false) + setCheckoutValidation(false); const loadServiceRajaOngkir = async () => { - setLoadingRajaOngkir(true) + 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) + 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) + 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') + 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.') + toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.'); } - } + }; if (selectedCarrier != 0 && selectedCarrier != 1 && totalWeight > 0) { - loadServiceRajaOngkir() + loadServiceRajaOngkir(); } else { - setListServiceExpedisi() - setBiayaKirim(0) - setselectedExpedisiService() - setEtd() + setListServiceExpedisi(); + setBiayaKirim(0); + setselectedExpedisiService(); + setEtd(); } - }, [selectedCarrier, selectedAddress, totalWeight]) + }, [selectedCarrier, selectedAddress, totalWeight]); useEffect(() => { if (selectedServiceType) { - let serviceType = selectedServiceType.split(',') - setBiayaKirim(serviceType[0]) - setselectedExpedisiService(serviceType[1]) - setEtd(serviceType[2]) + let serviceType = selectedServiceType.split(','); + setBiayaKirim(serviceType[0]); + setselectedExpedisiService(serviceType[1]); + setEtd(serviceType[2]); } - }, [selectedServiceType]) + }, [selectedServiceType]); useEffect(() => { - if (etd) setEtdFix(calculateEstimatedArrival(etd)) - }, [etd]) + if (etd) setEtdFix(calculateEstimatedArrival(etd)); + }, [etd]); useEffect(() => { if (selectedExpedisi) { - let serviceType = selectedExpedisi.split(',') - if (serviceType[0] === 0) return + let serviceType = selectedExpedisi.split(','); + if (serviceType[0] === 0) return; - setselectedCarrier(serviceType[0]) - setselectedCarrierId(serviceType[1]) - setListServiceExpedisi([]) + setselectedCarrier(serviceType[0]); + setselectedCarrierId(serviceType[1]); + setListServiceExpedisi([]); } - }, [selectedExpedisi]) + }, [selectedExpedisi]); + + const poNumber = useRef(null); + const poFile = useRef(null); - const poNumber = useRef(null) - const poFile = useRef(null) + const [isLoading, setIsLoading] = useState(false); - const [isLoading, setIsLoading] = useState(false) + useEffect(() => { + const GT = + cartCheckout?.grandTotal + + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000; + const finalGT = GT < 0 ? 0 : GT; + setGrandTotal(finalGT); + }, [biayaKirim, cartCheckout?.grandTotal, activeVoucher]); const checkout = async () => { - const file = poFile.current.files[0] + const file = poFile.current.files[0]; if (typeof file !== 'undefined' && file.size > 5000000) { - toast.error('Maksimal ukuran file adalah 5MB', { position: 'bottom-center' }) - return + toast.error('Maksimal ukuran file adalah 5MB', { + position: 'bottom-center', + }); + return; } if (selectedExpedisi === 0) { - setCheckoutValidation(true) + setCheckoutValidation(true); if (expedisiValidation.current) { - const position = expedisiValidation.current.getBoundingClientRect() + const position = expedisiValidation.current.getBoundingClientRect(); window.scrollTo({ top: position.top - 300 + window.pageYOffset, - behavior: 'smooth' - }) + behavior: 'smooth', + }); } - return + return; } if (selectedCarrier != 1 && biayaKirim == 0) { - toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.') - return + toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.'); + return; } - setIsLoading(true) + setIsLoading(true); const productOrder = products.map((product) => ({ product_id: product.id, - quantity: product.quantity - })) + quantity: product.quantity, + })); let data = { - partner_shipping_id: auth.partnerId, - partner_invoice_id: auth.partnerId, + // 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, - type: 'sale_order' - } + type: 'sale_order', + }; if (query) { - data.source = 'buy' + data.source = 'buy'; } - if (poNumber.current.value) data.po_number = poNumber.current.value - if (typeof file !== 'undefined') data.po_file = await getFileBase64(file) + 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 }) + const isCheckouted = await checkoutApi({ data }); if (!isCheckouted?.id) { - toast.error('Gagal melakukan transaksi, terjadi kesalahan internal') - return + toast.error('Gagal melakukan transaksi, terjadi kesalahan internal'); + return; } - - gtagPurchase(products, biayaKirim, isCheckouted.name) + + gtagPurchase(products, biayaKirim, isCheckouted.name); const midtrans = async () => { - 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 - } + 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, + '-' + )}`; + } + }; gtag('event', 'conversion', { send_to: 'AW-954540379/nDymCL3BhaQYENvClMcD', - value: cartCheckout?.grandTotal + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000, + value: + cartCheckout?.grandTotal + + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000, currency: 'IDR', transaction_id: isCheckouted.id, - event_callback: midtrans - }) - } + event_callback: midtrans, + }); + }; const handlingActivateCode = async () => { - VoucherCode(codeVoucher) - } + VoucherCode(codeVoucher); + }; const handleUseVoucher = async (code, isCheck) => { if (isCheck) { if (code === activeVoucher) { - SetActiveVoucher(null) - SetDiscountVoucher(0) + SetActiveVoucher(null); + SetDiscountVoucher(0); } else { - SetActiveVoucher(code) - SetFindVoucher(null) - document.getElementById('uniqCode').value = '' - SetButtonTerapkan(false) + SetActiveVoucher(code); + SetFindVoucher(null); + document.getElementById('uniqCode').value = ''; + SetButtonTerapkan(false); } } else { - SetActiveVoucher(code) - SetFindVoucher(null) - document.getElementById('uniqCode').value = '' - SetButtonTerapkan(false) + SetActiveVoucher(code); + SetFindVoucher(null); + document.getElementById('uniqCode').value = ''; + SetButtonTerapkan(false); } - } + }; const onChangeCodeVoucher = async (e) => { - SetCodeVoucher(e.target.value) - SetButtonTerapkan(false) - } + SetCodeVoucher(e.target.value); + SetButtonTerapkan(false); + }; - const [isChecked, setIsChecked] = useState(false) + const [isChecked, setIsChecked] = useState(false); const ToggleSwitch = (code) => { - setIsChecked(!isChecked) - handleUseVoucher(code, !isChecked) - } + setIsChecked(!isChecked); + handleUseVoucher(code, !isChecked); + }; const handlingTnC = async (item) => { - setItemTnC(item) - SetBottomPopupTnC(true) - } + 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]); + return ( <> <BottomPopup className='w-full md:!w-[40%] !min-h-[90vh]' active={bottomPopupTnC} close={() => { - SetBottomPopupTnC(false) - SetBottomPopup(false) + SetBottomPopupTnC(false); + SetBottomPopup(false); }} title={ <div> - <button className='flex gap-x-2 items-center' onClick={() => SetBottomPopupTnC(false)}> - <ChevronLeftIcon class='h- w-5 text-black' /> <span className='text-lg'>Voucher</span> + <button + className='flex gap-x-2 items-center' + onClick={() => SetBottomPopupTnC(false)} + > + <ChevronLeftIcon class='h- w-5 text-black' />{' '} + <span className='text-lg'>Voucher</span> </button>{' '} </div> } @@ -420,13 +472,17 @@ const Checkout = () => { <span className='text-sm'> {' '} Berakhir dalam :{' '} - <span className='text-sm text-red-500'>{itemTnC?.remainingTime} lagi</span> + <span className='text-sm text-red-500'> + {itemTnC?.remainingTime} lagi + </span> </span> </div> <div className='flex items-center gap-x-1'> <BanknotesIcon class='h-6 w-6 text-green-500' /> <span className='text-sm'> Kode Voucher : </span> - <span className='text-red-500 font-semibold'>{itemTnC?.code}</span> + <span className='text-red-500 font-semibold'> + {itemTnC?.code} + </span> </div> </div> <div> @@ -441,6 +497,7 @@ const Checkout = () => { </div> </div> </BottomPopup> + <BottomPopup className='w-full md:!w-[40%] !min-h-[350px]' active={bottomPopup} @@ -448,8 +505,8 @@ const Checkout = () => { title='Gunakan Promo' > <div className='row'> - <div className='flex justify-between items-center'> - <div className='flex md:w-[70%]'> + <div className='flex justify-between items-center gap-x-4'> + <div className='flex flex-1 md:w-[70%]'> <input type='text' id='uniqCode' @@ -481,15 +538,16 @@ const Checkout = () => { {findCodeVoucher === 1 && ( <div className='mt-2'> <span className='text-caption-1 mt-2 text-red-600'> - Kode voucher salah / sudah tidak berlaku lagi. Coba voucher lainnya, ya. + Kode voucher salah / sudah tidak berlaku lagi. Coba voucher + lainnya, ya. </span> </div> )} {findCodeVoucher === 2 && ( <div className='mt-2'> <span className='text-caption-1 mt-2 text-red-600'> - Tambah <span className='text-red-600'>{selisihHargaCode}</span> untuk pakai promo - ini + Tambah <span className='text-red-600'>{selisihHargaCode}</span>{' '} + untuk pakai promo ini </span> </div> )} @@ -500,15 +558,21 @@ const Checkout = () => { <div className='flex items-center justify-center mt-4 mb-4'> <div className='text-center'> <h1 className='font-bold mb-4'>Tidak ada voucher tersedia</h1> - <p className='text-gray-500'>Maaf, saat ini tidak ada voucher yang tersedia.</p> + <p className='text-gray-500'> + Maaf, saat ini tidak ada voucher yang tersedia. + </p> </div> </div> ) : ( - <h3 className='font-semibold mb-4'>Promo Khusus Untuk {auth?.name}</h3> + <h3 className='font-semibold mb-4'> + Promo Khusus Untuk {auth?.name} + </h3> )} {loadingVoucher && ( <> - <div className={`border border-solid w-full hover:cursor-pointer p-2`}> + <div + className={`border border-solid w-full hover:cursor-pointer p-2`} + > <div class='flex items-center space-x-3'> <div class='flex items-center justify-center h-28 w-48 mb-4 bg-gray-300 rounded dark:bg-gray-700'> <svg @@ -529,7 +593,9 @@ const Checkout = () => { </div> </div> </div> - <div className={`border border-solid w-full hover:cursor-pointer p-2`}> + <div + className={`border border-solid w-full hover:cursor-pointer p-2`} + > <div class='flex items-center space-x-3'> <div class='flex items-center justify-center h-28 w-48 mb-4 bg-gray-300 rounded dark:bg-gray-700'> <svg @@ -579,7 +645,9 @@ const Checkout = () => { > <p> Voucher tidak bisa di gunakan,{' '} - <span className='text-red font-bold'>Baca Selengkapnya !</span> + <span className='text-red font-bold'> + Baca Selengkapnya ! + </span> </p> </div> )} @@ -589,14 +657,20 @@ const Checkout = () => { <div className='absolute w-full h-full bg-gray_r-3/40 top-0 left-0 z-50' /> )} <div className='hidden md:w-[250px] md:block'> - <Image src={item.image} alt={item.name} className={`object-cover`} /> + <Image + src={item.image} + alt={item.name} + className={`object-cover`} + /> </div> <div className='w-full'> <div className='flex justify-between gap-x-2 mb-1 items-center'> <div className=''> <h3 className='font-semibold'>{item.name}</h3> <div className='mt-1'> - <span className='text-sm line-clamp-3'>{item.description} </span> + <span className='text-sm line-clamp-3'> + {item.description}{' '} + </span> </div> </div> <div className='flex justify-end'> @@ -605,7 +679,9 @@ const Checkout = () => { type='checkbox' value='' class='sr-only peer' - checked={activeVoucher === item.code ? true : false} + checked={ + activeVoucher === item.code ? true : false + } onChange={() => ToggleSwitch(item.code)} /> <div class="w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700 peer-focus:ring-4 peer-focus:ring-green-300 dark:peer-focus:ring-green-800 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600"></div> @@ -616,11 +692,15 @@ const Checkout = () => { <div className='flex justify-between items-center'> <p className='text-justify text-sm md:text-xs'> Kode Voucher :{' '} - <span className='text-red-500 font-semibold'>{item.code}</span> + <span className='text-red-500 font-semibold'> + {item.code} + </span> </p> <p className='text-sm md:text-xs'> {activeVoucher === item.code && ( - <span className=' text-green-600'>Voucher digunakan </span> + <span className=' text-green-600'> + Voucher digunakan{' '} + </span> )} </p> </div> @@ -642,7 +722,10 @@ const Checkout = () => { <div className='flex justify-between items-center'> <div className='text-left ml-3 text-sm '> Berakhir dalam{' '} - <span className='text-red-600'>{item.remainingTime}</span> lagi,{' '} + <span className='text-red-600'> + {item.remainingTime} + </span>{' '} + lagi,{' '} </div> <div className='text-sm ml-2 text-red-600' @@ -670,6 +753,7 @@ const Checkout = () => { </div> </div> </BottomPopup> + <MobileView> <div className='p-4'> <Alert type='info' className='text-caption-2 flex gap-x-3'> @@ -677,8 +761,8 @@ const Checkout = () => { <ExclamationCircleIcon className='w-7 text-blue-700' /> </div> <span className='leading-5'> - Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami - disini + Jika mengalami kesulitan dalam melakukan pembelian di website + Indoteknik. Hubungi kami disini </span> </Alert> </div> @@ -701,16 +785,22 @@ const Checkout = () => { </svg> <span class='sr-only'>Info</span> <div className='text-justify'> - Fitur Self Pickup, hanya berlaku untuk customer di area jakarta. Apa bila memilih - fitur ini, anda akan dihubungi setelah barang siap diambil. + Fitur Self Pickup, hanya berlaku untuk customer di area jakarta. + Apa bila memilih fitur ini, anda akan dihubungi setelah barang + siap diambil. </div> </div> </div> )} - {selectedCarrierId == SELF_PICKUP_ID && <PickupAddress label='Alamat Pickup' />} + {selectedCarrierId == SELF_PICKUP_ID && ( + <PickupAddress label='Alamat Pickup' /> + )} {selectedCarrierId != SELF_PICKUP_ID && ( - <> + <Skeleton + isLoaded={!!selectedAddress.invoicing && !!selectedAddress.shipping} + minHeight={320} + > <SectionAddress address={selectedAddress.shipping} label='Alamat Pengiriman' @@ -722,7 +812,7 @@ const Checkout = () => { label='Alamat Penagihan' url='/my/address?select=invoice' /> - </> + </Skeleton> )} <Divider /> <SectionValidation address={selectedAddress.invoicing} /> @@ -742,7 +832,10 @@ const Checkout = () => { /> <div className='p-4 flex flex-col gap-y-4'> - {products && <VariantGroupCard openOnClick={false} variants={products} />} + {!!products && + snakecaseKeys(products).map((item, index) => ( + <CartItem key={index} item={item} editable={false} /> + ))} </div> <Divider /> @@ -750,7 +843,6 @@ const Checkout = () => { <div className='p-4'> <div className='flex justify-between items-center'> <div className='font-medium'>Ringkasan Pesanan</div> - <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> </div> <hr className='my-4 border-gray_r-6' /> {!cartCheckout ? ( @@ -804,7 +896,9 @@ const Checkout = () => { {activeVoucher && ( <div className='flex gap-x-2 justify-between'> <div className='text-gray_r-11'>Diskon Voucher</div> - <div className='text-danger-500'>- {currencyFormat(discountVoucher)}</div> + <div className='text-danger-500'> + - {currencyFormat(discountVoucher)} + </div> </div> )} <div className='flex gap-x-2 justify-between'> @@ -819,7 +913,11 @@ const Checkout = () => { <div className='text-gray_r-11'> Biaya Kirim <p className='text-xs mt-3'>{etdFix}</p> </div> - <div>{currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000)}</div> + <div> + {currencyFormat( + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} + </div> </div> </div> )} @@ -839,9 +937,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( - cartCheckout?.grandTotal + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 - )} + {currencyFormat(grandTotal)} </div> </div> )} @@ -852,8 +948,8 @@ const Checkout = () => { <button type='button' onClick={() => { - SetBottomPopup(true) - voucher() + SetBottomPopup(true); + voucher(); }} className='text-gray-900 p-4 flex items-center justify-between rounded-lg bg-white border font-medium border-gray-300 hover:bg-gray-100 py-2.5 h-[50px] w-[100%]' > @@ -886,7 +982,8 @@ const Checkout = () => { </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{' '} + Dengan melakukan pembelian melalui website Indoteknik, saya + menyetujui{' '} <Link href='/syarat-ketentuan' className='inline font-normal'> Syarat & Ketentuan </Link>{' '} @@ -911,10 +1008,16 @@ const Checkout = () => { </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' ref={poNumber} /> + <input + type='text' + className='form-input mt-2 h-12' + ref={poNumber} + /> </div> </div> - <p className='text-caption-2 text-gray_r-11 mt-2'>Ukuran dokumen PO Maksimal 5MB</p> + <p className='text-caption-2 text-gray_r-11 mt-2'> + Ukuran dokumen PO Maksimal 5MB + </p> </div> <Divider /> @@ -923,7 +1026,13 @@ const Checkout = () => { <button className='flex-1 btn-yellow' onClick={checkout} - disabled={isLoading || !products || products?.length == 0 || priceCheck} + disabled={ + isLoading || + !products || + products?.length == 0 || + priceCheck || + hasNoPrice + } > {isLoading ? 'Loading...' : 'Lanjut Pembayaran'} </button> @@ -957,8 +1066,9 @@ const Checkout = () => { </svg> <span class='sr-only'>Info</span> <div> - Fitur Self Pickup, hanya berlaku untuk customer di area jakarta. Apa bila memilih - fitur ini, anda akan dihubungi setelah barang siap diambil. + Fitur Self Pickup, hanya berlaku untuk customer di area jakarta. + Apa bila memilih fitur ini, anda akan dihubungi setelah barang + siap diambil. </div> </div> )} @@ -966,9 +1076,16 @@ const Checkout = () => { <div className='flex'> {' '} <div className='w-3/4 border border-gray_r-6 rounded bg-white'> - {selectedCarrierId == SELF_PICKUP_ID && <PickupAddress label='Alamat Pickup' />} + {selectedCarrierId == SELF_PICKUP_ID && ( + <PickupAddress label='Alamat Pickup' /> + )} {selectedCarrierId != SELF_PICKUP_ID && ( - <> + <Skeleton + isLoaded={ + !!selectedAddress.invoicing && !!selectedAddress.shipping + } + minHeight={290} + > <SectionAddress address={selectedAddress.shipping} label='Alamat Pengiriman' @@ -980,7 +1097,7 @@ const Checkout = () => { label='Alamat Penagihan' url='/my/address?select=invoice' /> - </> + </Skeleton> )} <Divider /> <SectionValidation address={selectedAddress.invoicing} /> @@ -1000,170 +1117,13 @@ const Checkout = () => { /> <div className='p-4'> - <div className='font-medium'>Detail Pesanan</div> - <CardProdcuctsList isLoading={isLoading} products={products} /> - - {/* <table className='table-checkout'> - <thead> - <tr> - <th>Nama Produk</th> - <th>Jumlah</th> - <th>Harga</th> - <th>Subtotal</th> - </tr> - </thead> - <tbody> - {!products ? ( - <tr> - <td colSpan={4}> - <div className='container my-4'> - <div class='h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4'></div> - <div class='h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5'></div> - <div class='h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5'></div> - <div class='h-2 bg-gray-200 rounded-full dark:bg-gray-700'></div> - </div> - </td> - </tr> - ) : ( - products?.map((product) => ( - <> - <tr - key={product.id} - className={`${product.program ? '!border-t-0 !border-b-0' : ''}`} - > - <td className='flex'> - <div className='w-[20%] flex-shrink-0'> - <Image - src={product?.parent?.image} - alt={product?.name} - className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md' - /> - </div> - <div className='px-2 text-left'> - <div className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'> - {product?.parent?.name} - </div> - <div className='text-gray_r-11 mt-2'> - {product?.code}{' '} - {product?.attributes.length > 0 - ? `| ${product?.attributes.join(', ')}` - : ''} - </div> - <div className='text-gray_r-11 mt-2'> - Berat item : {product?.weight} Kg - </div> - </div> - </td> - <td> - <input - className='form-input w-16 py-2 text-center bg-gray_r-1' - type='number' - value={product?.quantity} - disabled - /> - </td> - <td> - {product?.hasFlashsale ? ( - <> - <div className='flex gap-x-1 items-center justify-center mt-3'> - <div className='text-gray_r-11 line-through text-caption-1'> - {currencyFormat(product?.price?.price)} - </div> - <div className='badge-solid-red'> - {product?.price?.discountPercentage}% - </div> - </div> - <div className='font-normal mt-1'> - {currencyFormat(product?.price?.priceDiscount)} - </div> - </> - ) : ( - <div className='font-normal mt-1'> - {product.price.priceDiscount > 0 - ? currencyFormat(product?.price?.priceDiscount) - : 'Call for Inquiry'} - </div> - )} - </td> - <td> - <div className='text-danger-500 font-medium'> - {product.price.priceDiscount > 0 ? ( - currencyFormat(product?.price?.priceDiscount * product?.quantity) - ) : ( - <a - href={whatsappUrl('product', { - name: product.name, - url: createSlug( - '/shop/product/', - product.name, - product.id, - true - ) - })} - className='underline' - > - Call for Inquiry{' '} - </a> - )} - </div> - </td> - </tr> - {product.program && - product.program.items && - product.program.items.map((item) => ( - <> - <tr key={product?.program?.id} className='!border-t-0'> - <td className='flex'> - <div className='w-[20%] flex-shrink-0'> - <Image - src={item.parent.image} - alt={item.name} - className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md' - /> - </div> - <div className='px-2 text-left'> - <div className=''> - <span className='border border-solid border-red-600 rounded-md p-1 text-red-600'> - {product.program.type.label} - </span> - </div> - <div className='mt-2 line-clamp-2 leading-6'>{item.name}</div> - </div> - </td> - <td> - <input - className='form-input w-16 py-2 text-center bg-gray_r-1' - type='number' - value={1} - disabled - /> - </td> - <td> - {item?.price?.discountPercentage > 0 && ( - <div className='flex gap-x-1 items-center justify-center mt-3'> - <div className='text-gray_r-11 line-through text-caption-1'> - {currencyFormat(product?.price?.price)} - </div> - </div> - )} - <div className='font-normal mt-1'> - {item?.price.priceDiscount > 0 ? 'Gratis' : ''} - </div> - </td> - <td> - <div className='text-danger-500 font-medium'> - {item.price.priceDiscount > 0 ? 'Gratis' : ''} - </div> - </td> - <td></td> - </tr> - </> - ))} - </> - )) - )} - </tbody> - </table> */} + <div className='font-medium mb-6'>Detail Pesanan</div> + <div className='flex flex-col gap-y-8 border-t border-gray-300 pt-8'> + {!!products && + snakecaseKeys(products).map((item, index) => ( + <CartItem key={index} item={item} editable={false} /> + ))} + </div> </div> </div> <div className='w-1/4 pl-4'> @@ -1171,7 +1131,7 @@ const Checkout = () => { <div className='flex justify-between items-center'> <div className='font-medium'>Ringkasan Pesanan</div> <div className='text-gray_r-11 text-caption-1'> - {products?.length} Barang - {cartCheckout?.totalWeight.kg} Kg + {cartCheckout?.totalWeight.kg} Kg </div> </div> @@ -1227,7 +1187,9 @@ const Checkout = () => { {activeVoucher && ( <div className='flex gap-x-2 justify-between'> <div className='text-gray_r-11'>Diskon Voucher</div> - <div className='text-danger-500'>- {currencyFormat(discountVoucher)}</div> + <div className='text-danger-500'> + - {currencyFormat(discountVoucher)} + </div> </div> )} <div className='flex gap-x-2 justify-between'> @@ -1244,7 +1206,9 @@ const Checkout = () => { <p className='text-xs mt-3'>{etdFix}</p> </div> <div> - {currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000)} + {currencyFormat( + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} </div> </div> </div> @@ -1265,10 +1229,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( - cartCheckout?.grandTotal + - Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 - )} + {currencyFormat(grandTotal)} </div> </div> )} @@ -1279,8 +1240,8 @@ const Checkout = () => { <button type='button' onClick={() => { - SetBottomPopup(true) - voucher() + SetBottomPopup(true); + voucher(); }} className='text-gray-900 p-3 flex items-center justify-between rounded-lg bg-white border font-medium border-gray-300 hover:bg-gray-100 py-2.5 h-[50px] w-[100%]' > @@ -1312,7 +1273,8 @@ const Checkout = () => { </div> <p className='text-caption-2 text-gray_r-11 leading-5'> - Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + Dengan melakukan pembelian melalui website Indoteknik, saya + menyetujui{' '} <Link href='/syarat-ketentuan' className='inline font-normal'> Syarat & Ketentuan </Link>{' '} @@ -1322,7 +1284,8 @@ const Checkout = () => { <hr className='my-4 border-gray_r-6' /> <div className='font-medium mt-4'> - Purchase Order <span className='font-normal text-gray_r-11'>(Opsional)</span> + Purchase Order{' '} + <span className='font-normal text-gray_r-11'>(Opsional)</span> </div> <div className='mt-4 flex gap-x-3'> @@ -1337,17 +1300,29 @@ const Checkout = () => { </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' ref={poNumber} /> + <input + type='text' + className='form-input mt-2 h-12' + ref={poNumber} + /> </div> </div> - <p className='text-caption-2 text-gray_r-11 mt-2'>Ukuran dokumen PO Maksimal 5MB</p> + <p className='text-caption-2 text-gray_r-11 mt-2'> + Ukuran dokumen PO Maksimal 5MB + </p> <hr className='my-4 border-gray_r-6' /> <button className='w-full btn-yellow mt-4' onClick={checkout} - disabled={isLoading || !products || products?.length == 0 || priceCheck} + disabled={ + isLoading || + !products || + products?.length == 0 || + priceCheck || + hasNoPrice + } > {isLoading ? 'Loading...' : 'Lanjut Pembayaran'} </button> @@ -1367,8 +1342,8 @@ const Checkout = () => { </div> </DesktopView> </> - ) -} + ); +}; const SectionAddress = ({ address, label, url }) => ( <div className='p-4'> @@ -1382,7 +1357,9 @@ const SectionAddress = ({ address, label, url }) => ( {address && ( <div className='mt-4 text-caption-1'> <div className='badge-red mb-2'> - {address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address'} + {address.type.charAt(0).toUpperCase() + + address.type.slice(1) + + ' Address'} </div> <p className='font-medium'>{address.name}</p> <p className='mt-2 text-gray_r-11'>{address.mobile}</p> @@ -1392,7 +1369,7 @@ const SectionAddress = ({ address, label, url }) => ( </div> )} </div> -) +); const SectionValidation = ({ address }) => address?.rajaongkirCityId == 0 && ( @@ -1409,7 +1386,7 @@ const SectionValidation = ({ address }) => </Link> </div> </BottomPopup> - ) + ); const SectionExpedisi = ({ address, @@ -1418,7 +1395,7 @@ const SectionExpedisi = ({ checkWeigth, checkoutValidation, expedisiValidation, - loadingRajaOngkir + loadingRajaOngkir, }) => address?.rajaongkirCityId > 0 && ( <div className='p-4' ref={expedisiValidation}> @@ -1427,7 +1404,9 @@ const SectionExpedisi = ({ <div className='w-[250px]'> <div className='flex items-center gap-x-4'> <select - className={`form-input ${checkoutValidation ? 'border-red-500 shake' : ''}`} + className={`form-input ${ + checkoutValidation ? 'border-red-500 shake' : '' + }`} onChange={(e) => setSelectedExpedisi(e.target.value)} required > @@ -1453,7 +1432,7 @@ const SectionExpedisi = ({ animate={{ opacity: 1, width: '28px' }} exit={{ opacity: 0, width: 0 }} transition={{ - duration: 0.25 + duration: 0.25, }} className='overflow-hidden' > @@ -1463,7 +1442,9 @@ const SectionExpedisi = ({ </AnimatePresence> </div> {checkoutValidation && ( - <span className='text-sm text-red-500'>*silahkan pilih expedisi</span> + <span className='text-sm text-red-500'> + *silahkan pilih expedisi + </span> )} </div> <style jsx>{` @@ -1474,18 +1455,19 @@ const SectionExpedisi = ({ </div> {checkWeigth == true && ( <p className='mt-4 text-gray_r-11 leading-6'> - Mohon maaf, pengiriman hanya tersedia untuk self pickup karena terdapat barang yang belum - diatur beratnya. Mohon atur berat barang dengan menghubungi admin melalui{' '} + Mohon maaf, pengiriman hanya tersedia untuk self pickup karena + terdapat barang yang belum diatur beratnya. Mohon atur berat barang + dengan menghubungi admin melalui{' '} <a className='text-danger-500 inline' - href='https://api.whatsapp.com/send?phone=628128080622' + href='https://api.whatsapp.com/send?phone=6281717181922' > tautan ini </a> </p> )} </div> - ) + ); const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => listserviceExpedisi?.length > 0 && ( @@ -1494,7 +1476,10 @@ const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => <div className='flex justify-between items-center'> <div className='font-medium'>Tipe Layanan Ekspedisi: </div> <div> - <select className='form-input' onChange={(e) => setSelectedServiceType(e.target.value)}> + <select + className='form-input' + onChange={(e) => setSelectedServiceType(e.target.value)} + > {listserviceExpedisi.map((service) => ( <option value={ @@ -1511,7 +1496,9 @@ const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => {' '} {service.description} - {service.service.toUpperCase()} {extractDuration(service.cost[0].etd) && - ` (Estimasi Tiba ${extractDuration(service.cost[0].etd)} Hari)`} + ` (Estimasi Tiba ${extractDuration( + service.cost[0].etd + )} Hari)`} </option> ))} </select> @@ -1520,73 +1507,73 @@ const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => </div> <Divider /> </> - ) + ); function addDays(date, days) { - const result = new Date(date) - result.setDate(result.getDate() + days) - return result + 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}` + 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 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)}` + let etdText = `*Estimasi tiba ${formatDate(from)}`; if (estimationDate[1] > estimationDate[0]) { - etdText += ` - ${formatDate(to)}` + etdText += ` - ${formatDate(to)}`; } - return etdText + return etdText; } - return '' + return ''; } function splitDuration(duration) { if (duration) { - let estimationDate = null + let estimationDate = null; if (duration.includes('-')) { - estimationDate = duration.split('-') - estimationDate = parseInt(estimationDate[1]) + estimationDate = duration.split('-'); + estimationDate = parseInt(estimationDate[1]); } else { - estimationDate = parseInt(duration) + estimationDate = parseInt(duration); } - return estimationDate + return estimationDate; } - return '' + return ''; } const extractDuration = (text) => { - const matches = text.match(/\d+(?:-\d+)?/g) + 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]) + const parts = matches[0].split('-'); + const min = parseInt(parts[0]); + const max = parseInt(parts[1]); if (min === max) { - return min.toString() + return min.toString(); } - return matches[0] + return matches[0]; } - return '' -} + return ''; +}; const PickupAddress = ({ label }) => ( <div className='p-4'> @@ -1596,13 +1583,14 @@ const PickupAddress = ({ label }) => ( <div className='mt-4 text-caption-1'> <p className='font-medium'>Indoteknik</p> <p className='mt-2 mb-2 text-gray_r-11 leading-6'> - Jl. Bandengan Utara Raya No.85, RT.3/RW.16, Penjaringan, Kec. Penjaringan, Kota Jkt Utara, - Daerah Khusus Ibukota Jakarta, Indonesia Kodepos : 14440 + Jl. Bandengan Utara Raya No.85, RT.3/RW.16, Penjaringan, Kec. + Penjaringan, Kota Jkt Utara, Daerah Khusus Ibukota Jakarta, Indonesia + Kodepos : 14440 </p> <p className='mt-1 text-gray_r-11'>Telp : 021-2933 8828/29</p> <p className='mt-1 text-gray_r-11'>Mobile : 0813 9000 7430</p> </div> </div> -) +); -export default Checkout +export default Checkout; diff --git a/src/lib/checkout/components/CheckoutOld.jsx b/src/lib/checkout/components/CheckoutOld.jsx index d57fbd66..e2c45ce6 100644 --- a/src/lib/checkout/components/CheckoutOld.jsx +++ b/src/lib/checkout/components/CheckoutOld.jsx @@ -696,7 +696,7 @@ const SectionExpedisi = ({ address, listExpedisi, setSelectedExpedisi, checkWeig diatur beratnya. Mohon atur berat barang dengan menghubungi admin melalui{' '} <a className='text-danger-500 inline' - href='https://api.whatsapp.com/send?phone=628128080622' + href='https://api.whatsapp.com/send?phone=6281717181922' > tautan ini </a> diff --git a/src/lib/checkout/components/CheckoutSection.jsx b/src/lib/checkout/components/CheckoutSection.jsx new file mode 100644 index 00000000..affe6138 --- /dev/null +++ b/src/lib/checkout/components/CheckoutSection.jsx @@ -0,0 +1,257 @@ +import Link from 'next/link'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import { AnimatePresence, motion } from 'framer-motion'; +import { Divider, Spinner } from '@chakra-ui/react'; + +export const SectionAddress = ({ address, label, url }) => { + return ( + <div className='p-4'> + <div className='flex justify-between items-center'> + <div className='font-medium'>{label}</div> + <Link className='text-caption-1' href={url}> + Pilih Alamat Lain + </Link> + </div> + + {address && ( + <div className='mt-4 text-caption-1'> + <div className='badge-red mb-2'> + {address.type.charAt(0).toUpperCase() + + address.type.slice(1) + + ' Address'} + </div> + <p className='font-medium'>{address.name}</p> + <p className='mt-2 text-gray_r-11'>{address.mobile}</p> + <p className='mt-1 text-gray_r-11'> + {address.street}, {address?.city?.name} + </p> + </div> + )} + </div> + ); +}; + +export const SectionValidation = ({ address }) => + address?.rajaongkirCityId == 0 && ( + <BottomPopup active={true} title='Update Alamat'> + <div className='leading-7 text-gray_r-12/80'> + Mohon untuk memperbarui alamat Anda dengan mengklik tombol di bawah ini.{' '} + </div> + <div className='flex justify-center mt-6 gap-x-4'> + <Link + className='btn-solid-red w-full md:w-fit text-white' + href={`/my/address/${address?.id}/edit`} + > + Update Alamat + </Link> + </div> + </BottomPopup> + ); + +export const SectionExpedisi = ({ + address, + listExpedisi, + setSelectedExpedisi, + checkWeigth, + checkoutValidation, + expedisiValidation, + loadingRajaOngkir, +}) => + address?.rajaongkirCityId > 0 && ( + <div className='p-4' ref={expedisiValidation}> + <div className='flex justify-between items-center'> + <div className='font-medium'>Pilih Ekspedisi: </div> + <div className='w-[250px]'> + <div className='flex items-center gap-x-4'> + <select + className={`form-input ${ + checkoutValidation ? 'border-red-500 shake' : '' + }`} + onChange={(e) => setSelectedExpedisi(e.target.value)} + required + > + <option value='0,0'>Pilih Pengiriman</option> + <option value='1,32'>SELF PICKUP</option> + {checkWeigth != true && + listExpedisi.map((expedisi) => ( + <option + disabled={checkWeigth} + value={expedisi.label + ',' + expedisi.carrierId} + key={expedisi.value} + > + {' '} + {expedisi.label.toUpperCase()}{' '} + </option> + ))} + </select> + + <AnimatePresence> + {loadingRajaOngkir && ( + <motion.div + initial={{ opacity: 0, width: 0 }} + animate={{ opacity: 1, width: '28px' }} + exit={{ opacity: 0, width: 0 }} + transition={{ + duration: 0.25, + }} + className='overflow-hidden' + > + <Spinner thickness='3px' speed='0.5s' color='red.500' /> + </motion.div> + )} + </AnimatePresence> + </div> + {checkoutValidation && ( + <span className='text-sm text-red-500'> + *silahkan pilih expedisi + </span> + )} + </div> + <style jsx>{` + .shake { + animation: shake 0.4s ease-in-out; + } + `}</style> + </div> + {checkWeigth == true && ( + <p className='mt-4 text-gray_r-11 leading-6'> + Mohon maaf, pengiriman hanya tersedia untuk self pickup karena + terdapat barang yang belum diatur beratnya. Mohon atur berat barang + dengan menghubungi admin melalui{' '} + <a + className='text-danger-500 inline' + href='https://api.whatsapp.com/send?phone=6281717181922' + > + tautan ini + </a> + </p> + )} + </div> + ); + +export const SectionListService = ({ + listserviceExpedisi, + setSelectedServiceType, +}) => + listserviceExpedisi?.length > 0 && ( + <> + <div className='p-4'> + <div className='flex justify-between items-center'> + <div className='font-medium'>Tipe Layanan Ekspedisi: </div> + <div> + <select + className='form-input' + onChange={(e) => setSelectedServiceType(e.target.value)} + > + {listserviceExpedisi.map((service) => ( + <option + value={ + service.cost[0].value + + ',' + + service.description + + '-' + + service.service + + ',' + + extractDuration(service.cost[0].etd) + } + key={service.service} + > + {' '} + {service.description} - {service.service.toUpperCase()} + {extractDuration(service.cost[0].etd) && + ` (Estimasi Tiba ${extractDuration( + service.cost[0].etd + )} Hari)`} + </option> + ))} + </select> + </div> + </div> + </div> + <Divider /> + </> + ); + +export const PickupAddress = ({ label }) => ( + <div className='p-4'> + <div className='flex justify-between items-center'> + <div className='font-medium'>{label}</div> + </div> + <div className='mt-4 text-caption-1'> + <p className='font-medium'>Indoteknik</p> + <p className='mt-2 mb-2 text-gray_r-11 leading-6'> + Jl. Bandengan Utara Raya No.85, RT.3/RW.16, Penjaringan, Kec. + Penjaringan, Kota Jkt Utara, Daerah Khusus Ibukota Jakarta, Indonesia + Kodepos : 14440 + </p> + <p className='mt-1 text-gray_r-11'>Telp : 021-2933 8828/29</p> + <p className='mt-1 text-gray_r-11'>Mobile : 0813 9000 7430</p> + </div> + </div> +); + +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 ''; +}; + +export 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 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}`; +} + +export 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 ''; +}
\ No newline at end of file |
