diff options
Diffstat (limited to 'src/lib/checkout/components')
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 107 | ||||
| -rw-r--r-- | src/lib/checkout/components/FinishCheckout.jsx | 19 | ||||
| -rw-r--r-- | src/lib/checkout/components/SectionExpedition.jsx | 152 | ||||
| -rw-r--r-- | src/lib/checkout/components/SectionQuotationExpedition.jsx | 19 |
4 files changed, 171 insertions, 126 deletions
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index d8ede118..6cda069c 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -55,7 +55,9 @@ function convertToInternational(number) { } const Checkout = () => { - const PPN = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0; + const PPN = process.env.NEXT_PUBLIC_PPN + ? parseFloat(process.env.NEXT_PUBLIC_PPN) + : 0; const router = useRouter(); const query = router.query.source ?? null; const qVoucher = router.query.voucher ?? null; @@ -172,7 +174,7 @@ const Checkout = () => { selectedService, listExpedisi, setExpedisi, - productSla + productSla, } = useCheckout(); const expedisiValidation = useRef(null); @@ -184,7 +186,7 @@ const Checkout = () => { let dataVoucher = await getVoucher(auth?.id, { source: query, type: 'all,brand', - partner_id : auth?.partnerId, + partner_id: auth?.partnerId, }); SetListVoucher(dataVoucher); @@ -210,7 +212,6 @@ const Checkout = () => { return; } - dataVoucher.forEach((addNewLine) => { if (addNewLine.applyType !== 'shipping') { // Mencari voucher dalam listVouchers @@ -374,17 +375,21 @@ const Checkout = () => { } return; } - if (selectedCourierId !== SELF_PICKUP_ID) { // Menggunakan selectedCourierId karena lebih spesifik dan numerik - if (!selectedService) { // Jika kurir bukan Self Pickup, maka harus ada layanan yang dipilih - toast.error('Harap pilih tipe layanan pengiriman'); - return; - } - // Validasi biaya kirim hanya untuk kurir selain Self Pickup (dan ID kurir 1 jika itu kasus khusus) - // Jika selectedCourierId adalah 1 (misalnya kurir internal yang bisa gratis), lewati validasi biayaKirim 0 - if (selectedCourierId !== 1 && biayaKirim === 0) { - toast.error('Maaf, layanan tidak tersedia untuk ekspedisi ini. Mohon pilih ekspedisi lain atau layanan lain.'); - return; - } + if (selectedCourierId !== SELF_PICKUP_ID) { + // Menggunakan selectedCourierId karena lebih spesifik dan numerik + if (!selectedService) { + // Jika kurir bukan Self Pickup, maka harus ada layanan yang dipilih + toast.error('Harap pilih tipe layanan pengiriman'); + return; + } + // Validasi biaya kirim hanya untuk kurir selain Self Pickup (dan ID kurir 1 jika itu kasus khusus) + // Jika selectedCourierId adalah 1 (misalnya kurir internal yang bisa gratis), lewati validasi biayaKirim 0 + if (selectedCourierId !== 1 && biayaKirim === 0) { + toast.error( + 'Maaf, layanan tidak tersedia untuk ekspedisi ini. Mohon pilih ekspedisi lain atau layanan lain.' + ); + return; + } } setIsLoading(true); const productOrder = products.map((product) => ({ @@ -415,7 +420,8 @@ const Checkout = () => { order_line: JSON.stringify(productOrder), delivery_amount: biayaKirim, carrier_id: selectedCourierId, - estimated_arrival_days_start : parseInt(eta_courier_start) + parseInt(productSla), + estimated_arrival_days_start: + parseInt(eta_courier_start) + parseInt(productSla), estimated_arrival_days: parseInt(eta_courier) + parseInt(productSla), delivery_service_type: selectedService?.service_type, flash_sale: hasFlashSale, // dibuat negasi untuk ngetest kebalikan nilai false @@ -430,8 +436,8 @@ const Checkout = () => { if (typeof file == 'undefined') { toast.error( 'Nomor PO ' + - poNumber.current.value + - ' telah dimasukkan, Harap upload file PO yang dimaksud' + poNumber.current.value + + ' telah dimasukkan, Harap upload file PO yang dimaksud' ); setIsLoading(false); return; @@ -1019,10 +1025,10 @@ const Checkout = () => { </div> )} - {selectedCarrierId == SELF_PICKUP_ID && ( + {selectedCourierId == SELF_PICKUP_ID && ( <PickupAddress label='Alamat Pickup' /> )} - {selectedCarrierId != SELF_PICKUP_ID && ( + {selectedCourierId != SELF_PICKUP_ID && ( <Skeleton isLoaded={!!selectedAddress.invoicing && !!selectedAddress.shipping} minHeight={320} @@ -1137,7 +1143,9 @@ const Checkout = () => { <div>{currencyFormat(cartCheckout?.subtotal)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div> + <div className='text-gray_r-11'> + PPN {((PPN - 1) * 100).toFixed(0)}% + </div> <div>{currencyFormat(cartCheckout?.tax)}</div> </div> <div className='flex gap-x-2 justify-between'> @@ -1269,10 +1277,7 @@ const Checkout = () => { className='flex-1 btn-yellow' onClick={checkout} disabled={ - !products || - products?.length == 0 || - priceCheck || - hasNoPrice + !products || products?.length == 0 || priceCheck || hasNoPrice || isLoading } > {isLoading ? 'Loading...' : 'Lanjut Pembayaran'} @@ -1317,16 +1322,11 @@ const Checkout = () => { <div className='flex'> {' '} <div className='w-3/4 border border-gray_r-6 rounded bg-white'> - {selectedCarrierId == SELF_PICKUP_ID && ( + {selectedCourierId == SELF_PICKUP_ID && ( <PickupAddress label='Alamat Pickup' /> )} - {selectedCarrierId != SELF_PICKUP_ID && ( - <Skeleton - isLoaded={ - !!selectedAddress.invoicing && !!selectedAddress.shipping - } - minHeight={290} - > + {selectedCourierId != SELF_PICKUP_ID && ( + <Skeleton isLoaded minHeight={290}> <SectionAddress address={selectedAddress.shipping} label='Alamat Pengiriman' @@ -1444,7 +1444,9 @@ const Checkout = () => { <div>{currencyFormat(cartCheckout?.subtotal)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div> + <div className='text-gray_r-11'> + PPN {((PPN - 1) * 100).toFixed(0)}% + </div> <div>{currencyFormat(cartCheckout?.tax)}</div> </div> <div className='flex gap-x-2 justify-between'> @@ -1577,7 +1579,8 @@ const Checkout = () => { !products || products?.length == 0 || priceCheck || - hasNoPrice + hasNoPrice || + isLoading } > {isLoading ? 'Loading...' : 'Lanjut Pembayaran'} @@ -1633,8 +1636,14 @@ const SectionAddress = ({ address, label, url }) => ( {address.addressMap ? ( <label>Sudah Pinpoint</label> ) : ( - <Link href={'/my/address/' + address.id + '/edit'} target='_blank' className='cursor-pointer'> - <label className='text-red-500 cursor-pointer '>Belum Pinpoint</label> + <Link + href={'/my/address/' + address.id + '/edit'} + target='_blank' + className='cursor-pointer' + > + <label className='text-red-500 cursor-pointer '> + Belum Pinpoint + </label> </Link> )} </div> @@ -1644,7 +1653,7 @@ const SectionAddress = ({ address, label, url }) => ( ); const SectionValidation = ({ address }) => - address?.stateId == 0 && ( + address?.stateId === 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.{' '} @@ -1661,14 +1670,14 @@ const SectionValidation = ({ address }) => ); const SectionExpedisi = ({ - address, - listExpedisi, - setSelectedExpedisi, - checkWeigth, - checkoutValidation, - expedisiValidation, - loadingRajaOngkir, - }) => + address, + listExpedisi, + setSelectedExpedisi, + checkWeigth, + checkoutValidation, + expedisiValidation, + loadingRajaOngkir, +}) => address?.rajaongkirCityId > 0 && ( <div className='p-4' ref={expedisiValidation}> <div className='flex justify-between items-center'> @@ -1720,9 +1729,9 @@ const SectionExpedisi = ({ )} </div> <style jsx>{` - .shake { - animation: shake 0.4s ease-in-out; - } + .shake { + animation: shake 0.4s ease-in-out; + } `}</style> </div> {checkWeigth == true && ( diff --git a/src/lib/checkout/components/FinishCheckout.jsx b/src/lib/checkout/components/FinishCheckout.jsx index d533325e..51837a59 100644 --- a/src/lib/checkout/components/FinishCheckout.jsx +++ b/src/lib/checkout/components/FinishCheckout.jsx @@ -55,12 +55,21 @@ const FinishCheckout = ({ query }) => { <div className='text-title-sm md:text-title-lg text-center font-semibold'> Terima Kasih atas Pembelian di Indoteknik.com </div> - <p className='text-title-sm md:text-title-lg font-semibold my-2'>No. Transaksi: <span className='text-red-500'>{query?.order_id?.replaceAll('-', '/')}</span></p> + <p className='text-title-sm md:text-title-lg font-semibold my-2'> + No. Transaksi:{' '} + <span className='text-red-500'> + {query?.order_id?.replaceAll('-', '/')} + </span> + </p> <div className='flex flex-col justify-center items-center text-body-2 md:text-body-1 text-center mt-3 px-24 md:px-36 py-4 border-2 gap-y-2 rounded'> - <p className="text-title-sm md:text-title-xl text-gray-500 mt-1">Estimasi Barang Siap pada Tanggal</p> - <p className="text-title-sm md:text-title-xl text-red-500 font-semibold my-2">{data?.expectedReadyToShip}</p> + <p className='text-title-sm md:text-title-xl text-gray-500 mt-1'> + Estimasi Barang Siap pada Tanggal + </p> + <p className='text-title-sm md:text-title-xl text-red-500 font-semibold my-2'> + {data?.expectedReadyToShip} + </p> <Link - href={`/my/quotations/${data?.id}`} + href={`/my/transactions/${data?.id}`} className='btn-solid-red rounded-md text-base' > Cek Detail Transaksi @@ -74,7 +83,7 @@ const FinishCheckout = ({ query }) => { </a>{' '} atau{' '} <span onClick={sendEmail} className='text-red-500 cursor-pointer'> - kirim rincian pesanan ulang + Kirim ulang rincian pesanan ke Email anda. </span> . </div> diff --git a/src/lib/checkout/components/SectionExpedition.jsx b/src/lib/checkout/components/SectionExpedition.jsx index 2e92ffbc..66182589 100644 --- a/src/lib/checkout/components/SectionExpedition.jsx +++ b/src/lib/checkout/components/SectionExpedition.jsx @@ -18,7 +18,7 @@ import { getProductsSla } from '../api/checkoutApi'; function mappingItems(products) { return products?.map((item) => ({ // name: item.parent.name || item?.name || 'Unknown Product', - name: item?.name, + name: item?.name, description: `${item.code} - ${item.name}`, value: item.price.priceDiscount, weight: item.weight * 1000, @@ -27,61 +27,71 @@ function mappingItems(products) { } function reverseMappingCourier(couriersOdoo, couriers, includeInstant = false) { - // Buat peta courier berdasarkan nama courier dari couriers + // Bangun peta dari hasil Biteship (pakai dua key: code & name) const courierMap = couriers.reduce((acc, item) => { - const { courier_name, courier_code, courier_service_code } = item; - const key = courier_code.toLowerCase(); - - if ( - !includeInstant && (['hours'].includes(item.shipment_duration_unit.toLowerCase()) || item.service_type == 'same_day') - - ) { - return acc; - } + const codeKey = (item.courier_code || '').toLowerCase(); + const nameKey = (item.courier_name || '').toLowerCase(); + + const isInstant = + (item.shipment_duration_unit || '').toLowerCase() === 'hours' || + (item.service_type || '').toLowerCase() === 'same_day'; + if (!includeInstant && isInstant) return acc; + + const ensureEntry = (key) => { + if (!key) return; + if (!acc[key]) { + acc[key] = { + courier_name: item.courier_name, + courier_code: item.courier_code, + service_type: {}, + }; + } + }; - if (!acc[key]) { - acc[key] = { - courier_name: item.courier_name, - courier_code: courier_code, - service_type: {}, - }; - } + ensureEntry(codeKey); + ensureEntry(nameKey); - acc[key].service_type[courier_service_code] = { + const svc = { service_name: item.courier_service_name, duration: item.duration, shipment_range: item.shipment_duration_range, shipment_unit: item.shipment_duration_unit, price: item.price, - service_type: courier_service_code, + service_type: item.courier_service_code, description: item.description, }; + if (codeKey && acc[codeKey]) { + acc[codeKey].service_type[item.courier_service_code] = svc; + } + if (nameKey && acc[nameKey]) { + acc[nameKey].service_type[item.courier_service_code] = svc; + } return acc; }, {}); - // Iterasi berdasarkan couriersOdoo - return couriersOdoo.map((courierOdoo) => { - const courierNameKey = courierOdoo.label.toLowerCase(); - const carrierId = courierOdoo.carrierId; + // Petakan Odoo ke map Biteship dan FILTER hanya yg punya layanan + return couriersOdoo + .map((courierOdoo) => { + const key = (courierOdoo.label || '').toLowerCase(); + const matched = courierMap[key] || null; - const mappedCourier = courierMap[courierNameKey] || false; + if (!matched) return { ...courierOdoo, courier: false }; - if (!mappedCourier) { return { ...courierOdoo, - courier: false, + courier: { + ...matched, + courier_id_odoo: courierOdoo.carrierId, // penting: simpan id Odoo di sini + }, }; - } - - return { - ...courierOdoo, - courier: { - ...mappedCourier, - courier_id_odoo: carrierId, - }, - }; - }); + }) + .filter( + (x) => + x.courier && + x.courier.service_type && + Object.keys(x.courier.service_type).length > 0 + ); } function mappingCourier(couriersOdoo, couriers, notIncludeInstant = false) { @@ -214,7 +224,7 @@ export default function SectionExpedition({ products }) { let data = { products: JSON.stringify(productsMapped), - } + }; const res = await odooApi('POST', `/api/v1/product/variants/sla`, data); setSlaProducts(res); } catch (error) { @@ -328,13 +338,13 @@ export default function SectionExpedition({ products }) { }; useEffect(() => { - if (serviceOptions.length > 0) { - setSavedServiceOptions(serviceOptions); - } -}, [serviceOptions]); + if (serviceOptions.length > 0) { + setSavedServiceOptions(serviceOptions); + } + }, [serviceOptions]); return ( - <form > + <form> <div className='px-4 py-2'> <div className='flex justify-between items-center'> <div className='font-medium'>Pilih Ekspedisi: </div> @@ -371,27 +381,40 @@ export default function SectionExpedition({ products }) { <p className='font-semibold'>SELF PICKUP</p> </div> </div> - {couriers?.map((courier) => ( - <div - key={courier?.courier?.courier_code} - onClick={() => onCourierChange(courier)} - className='flex justify-between p-2 items-center hover:bg-gray-100 cursor-pointer' - > - <div> - <p className='font-semibold'> - {courier?.label} - </p> + {couriers + ?.map((c) => c) // sudah ter-filter di reverseMappingCourier, tapi aman buat double-check + .filter( + (c) => + c.courier && + Object.keys(c.courier.service_type).length > 0 + ) + .map((courier) => ( + <div + key={ + courier?.courier?.courier_code || + courier?.label + } + onClick={() => onCourierChange(courier)} + className='flex justify-between p-2 items-center hover:bg-gray-100 cursor-pointer' + > + <div> + <p className='font-semibold'> + {courier?.label} + </p> + </div> + <span className='font-semibold'> + <Image + src={courier?.logo} + alt={ + courier?.courier?.courier_name || + courier?.label + } + width={50} + height={50} + /> + </span> </div> - <span className='font-semibold'> - <Image - src={courier?.logo} - alt={courier?.courier?.courier_name} - width={50} - height={50} - /> - </span> - </div> - ))} + ))} </> ) : ( <> @@ -432,8 +455,7 @@ export default function SectionExpedition({ products }) { )} </div> - {(serviceOptions.length > 0 || - selectedService )&& + {(serviceOptions.length > 0 || selectedService) && selectedCourier && selectedCourier !== 32 && selectedCourier !== 0 && ( diff --git a/src/lib/checkout/components/SectionQuotationExpedition.jsx b/src/lib/checkout/components/SectionQuotationExpedition.jsx index b8ea04ef..817cd21b 100644 --- a/src/lib/checkout/components/SectionQuotationExpedition.jsx +++ b/src/lib/checkout/components/SectionQuotationExpedition.jsx @@ -239,7 +239,7 @@ export default function SectionExpeditionQuotation({ products }) { <div className='px-4 py-2'> <div className='flex justify-between items-center'> <div className='font-medium'>Pilih Ekspedisi: </div> - <div className='w-[350px]'> + <div className='relative w-[350px]'> <div className='w-full p-2 border rounded-lg bg-white cursor-pointer' onClick={() => setOnFocuseSelectedCourier(!onFocusSelectedCourier)} @@ -253,7 +253,10 @@ export default function SectionExpeditionQuotation({ products }) { )} </div> {onFocusSelectedCourier && ( - <div className='absolute bg-white border rounded-lg mt-1 shadow-lg z-10 max-h-[200px] overflow-y-auto w-[350px]'> + <div + className='absolute left-0 top-full mt-1 bg-white border rounded-lg shadow-lg z-50 + max-h-[200px] overflow-y-auto w-full sm:w-[350px]' + > {!isLoading ? ( <> <div @@ -297,8 +300,8 @@ export default function SectionExpeditionQuotation({ products }) { {checkWeigth && ( <p className='mt-4 text-gray-600'> - Mohon maaf, pengiriman hanya tersedia untuk self pickup karena ada barang - yang belum memiliki berat. Silakan hubungi admin via{' '} + Mohon maaf, pengiriman hanya tersedia untuk self pickup karena ada + barang yang belum memiliki berat. Silakan hubungi admin via{' '} <a className='text-blue-600 underline' href='https://api.whatsapp.com/send?phone=6281717181922' @@ -316,7 +319,7 @@ export default function SectionExpeditionQuotation({ products }) { selectedCourier !== 0 && ( <div className='mt-4 flex justify-between'> <div className='font-medium mb-2'>Tipe Layanan Ekspedisi:</div> - <div className='relative w-[350px]'> + <div className='relative w-full sm:w-[350px]'> <div className='p-2 border rounded-lg bg-white cursor-pointer' onClick={() => setIsOpen(!isOpen)} @@ -331,11 +334,13 @@ export default function SectionExpeditionQuotation({ products }) { </span> </div> ) : ( - <span className='text-gray-500'>Pilih layanan pengiriman</span> + <span className='text-gray-500'> + Pilih layanan pengiriman + </span> )} </div> {isOpen && ( - <div className='absolute bg-white border rounded-lg mt-1 shadow-lg z-10 w-full'> + <div className='absolute left-0 top-full mt-1 bg-white border rounded-lg shadow-lg z-50 w-full'> {serviceOptions.map((service) => ( <div key={service.service_type} |
