From 43f08a6b39cf6555b9c87d94ade2af5a5f7747bf Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 11 Aug 2025 09:29:11 +0700 Subject: fix biteship quotation --- .../components/SectionQuotationExpedition.jsx | 369 +++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 src/lib/checkout/components/SectionQuotationExpedition.jsx (limited to 'src/lib/checkout/components/SectionQuotationExpedition.jsx') diff --git a/src/lib/checkout/components/SectionQuotationExpedition.jsx b/src/lib/checkout/components/SectionQuotationExpedition.jsx new file mode 100644 index 00000000..b8ea04ef --- /dev/null +++ b/src/lib/checkout/components/SectionQuotationExpedition.jsx @@ -0,0 +1,369 @@ +'use client'; + +import { Skeleton } from '@chakra-ui/react'; +import axios from 'axios'; +import Image from 'next/image'; +import React, { useEffect, useState } from 'react'; +import { useQuery } from 'react-query'; +import toast from 'react-hot-toast'; +import { useAddress } from '../stores/useAdress'; +import { useQuotation } from '../stores/stateQuotation'; + +import currencyFormat from '@/core/utils/currencyFormat'; +import { formatShipmentRange } from '../utils/functionCheckouit'; +import odooApi from '@/core/api/odooApi'; + +function mappingItems(products) { + return products?.map((item) => ({ + name: item?.name, + description: `${item.code} - ${item.name}`, + value: item.price.priceDiscount, + weight: item.weight * 1000, + quantity: item.quantity, + })); +} + +function reverseMappingCourier(couriersOdoo, couriers, includeInstant = false) { + 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; + } + + if (!acc[key]) { + acc[key] = { + courier_name: item.courier_name, + courier_code: courier_code, + service_type: {}, + }; + } + + acc[key].service_type[courier_service_code] = { + 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, + description: item.description, + }; + + return acc; + }, {}); + + return couriersOdoo.map((courierOdoo) => { + const courierNameKey = courierOdoo.label.toLowerCase(); + const carrierId = courierOdoo.carrierId; + + const mappedCourier = courierMap[courierNameKey] || false; + + if (!mappedCourier) { + return { + ...courierOdoo, + courier: false, + }; + } + + return { + ...courierOdoo, + courier: { + ...mappedCourier, + courier_id_odoo: carrierId, + }, + }; + }); +} + +export default function SectionExpeditionQuotation({ products }) { + const { addressMaps, coordinate, postalCode } = useAddress(); + const [serviceOptions, setServiceOptions] = useState([]); + const [isOpen, setIsOpen] = useState(false); + const [onFocusSelectedCourier, setOnFocuseSelectedCourier] = useState(false); + const [couriers, setCouriers] = useState(null); + const [slaProducts, setSlaProducts] = useState(null); + const [savedServiceOptions, setSavedServiceOptions] = useState([]); + + const { + checkWeigth, + checkoutValidation, + setBiayaKirim, + setUnit, + setEtd, + selectedCourier, + setSelectedCourier, + selectedService, + setSelectedService, + listExpedisi, + productSla, + setProductSla, + setSelectedCourierId, + } = useQuotation(); + + let destination = {}; + let items = mappingItems(products); + + if (addressMaps) { + destination = { + origin_latitude: -6.3031123, + origin_longitude: 106.7794934999, + ...coordinate, + }; + } else if (postalCode) { + destination = { + origin_postal_code: 14440, + destination_postal_code: postalCode, + }; + } + + const fetchSlaProducts = async () => { + try { + let productsMapped = products.map((item) => ({ + id: item.id, + quantity: item.quantity, + })); + + let data = { + products: JSON.stringify(productsMapped), + }; + const res = await odooApi('POST', `/api/v1/product/variants/sla`, data); + setSlaProducts(res); + } catch (error) { + console.error('Failed to fetch SLA:', error); + } + }; + + useEffect(() => { + fetchSlaProducts(); + }, []); + + useEffect(() => { + if (slaProducts) { + let productSla = slaProducts?.slaTotal; + if (slaProducts.slaUnit === 'jam') { + productSla = 1; + } + setProductSla(productSla); + } + }, [slaProducts]); + + const fetchExpedition = async () => { + const body = { + ...destination, + couriers: + 'gojek,grab,deliveree,lalamove,jne,tiki,ninja,lion,rara,sicepat,jnt,pos,idexpress,rpx,wahana,jdl,pos,anteraja,sap,paxel,borzo', + items, + }; + const response = await axios.get(`/api/biteship-service`, { + params: { body: JSON.stringify(body) }, + }); + return response; + }; + + const { data, isLoading } = useQuery( + ['expedition', JSON.stringify(destination), JSON.stringify(items)], + fetchExpedition, + { + enabled: + Boolean(Object.keys(destination).length) && + items?.length > 0 && + !checkWeigth && + onFocusSelectedCourier, + staleTime: Infinity, + cacheTime: Infinity, + } + ); + + useEffect(() => { + const instant = slaProducts?.includeInstant || false; + if (data) { + const couriers = reverseMappingCourier( + listExpedisi, + data?.data?.pricing, + instant + ); + setCouriers(couriers); + } + }, [data, slaProducts]); + + const onCourierChange = (courier) => { + setIsOpen(false); + setOnFocuseSelectedCourier(false); + setSelectedService(null); + setBiayaKirim(0); + if (courier !== 0 && courier !== 32) { + if (courier.courier) { + setSelectedCourier(courier.courier.courier_code); + setSelectedCourierId(courier.carrierId); + setServiceOptions(Object.values(courier.courier.service_type)); + } else { + if ( + (courier.label === 'GRAB' || courier.label === 'GOJEK') && + !addressMaps + ) { + toast.error( + `Maaf, layanan kurir ${courier.label} tidak tersedia karena belum mengatur PinPoint.` + ); + } else { + toast.error('Maaf, layanan tidak tersedia. Mohon pilih ekspedisi lain.'); + } + setServiceOptions([]); + } + } else { + setSelectedCourier(courier === 32 ? 'SELF PICKUP' : null); + setSelectedCourierId(courier); + setServiceOptions([]); + } + }; + + const handleSelect = (service) => { + setSelectedService(service); + setBiayaKirim(service?.price); + setEtd(service?.shipment_range); + setUnit(service?.shipment_unit); + setIsOpen(false); + }; + + useEffect(() => { + if (serviceOptions.length > 0) { + setSavedServiceOptions(serviceOptions); + } + }, [serviceOptions]); + + return ( +
+
+
Pilih Ekspedisi:
+
+
setOnFocuseSelectedCourier(!onFocusSelectedCourier)} + > + {selectedCourier ? ( +
+ {selectedCourier} +
+ ) : ( + Pilih Expedisi + )} +
+ {onFocusSelectedCourier && ( +
+ {!isLoading ? ( + <> +
onCourierChange(32)} + className='p-2 hover:bg-gray-100 cursor-pointer' + > +

SELF PICKUP

+
+ {couriers?.map((courier) => ( +
onCourierChange(courier)} + className='flex justify-between p-2 items-center hover:bg-gray-100 cursor-pointer' + > +

{courier?.label}

+ {courier?.courier?.courier_name} +
+ ))} + + ) : ( + <> + + + + )} +
+ )} + {checkoutValidation && ( + + *Silahkan pilih ekspedisi + + )} +
+
+ + {checkWeigth && ( +

+ Mohon maaf, pengiriman hanya tersedia untuk self pickup karena ada barang + yang belum memiliki berat. Silakan hubungi admin via{' '} + + tautan ini + +

+ )} + + {(serviceOptions.length > 0 || selectedService) && + selectedCourier && + selectedCourier !== 32 && + selectedCourier !== 0 && ( +
+
Tipe Layanan Ekspedisi:
+
+
setIsOpen(!isOpen)} + > + {selectedService ? ( +
+ {selectedService.service_name} + + {currencyFormat( + Math.round((selectedService?.price * 1.1) / 1000) * 1000 + )} + +
+ ) : ( + Pilih layanan pengiriman + )} +
+ {isOpen && ( +
+ {serviceOptions.map((service) => ( +
handleSelect(service)} + className='flex justify-between p-2 items-center hover:bg-gray-100 cursor-pointer' + > +
+

{service.service_name}

+

+ {formatShipmentRange( + service.shipment_range, + service.shipment_unit, + productSla + )} +

+
+ + {currencyFormat( + Math.round((service?.price * 1.1) / 1000) * 1000 + )} + +
+ ))} +
+ )} +
+
+ )} +
+ ); +} -- cgit v1.2.3