import { Skeleton, Spinner } from '@chakra-ui/react'; import axios from 'axios'; import { AnimatePresence, motion } from 'framer-motion'; import React, { useEffect, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useQuery } from 'react-query'; import { useAddress } from '../stores/useAdress'; import currencyFormat from '@/core/utils/currencyFormat'; import { useCheckout } from '../stores/stateCheckout'; import { formatShipmentRange } from '../utils/functionCheckouit'; import Image from 'next/image'; import toast from 'react-hot-toast'; import odooApi from '@/core/api/odooApi'; import { getProductsSla } from '../api/checkoutApi'; function mappingItems(products) { return products?.map((item) => ({ // name: item.parent.name || item?.name || 'Unknown Product', 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) { // Bangun peta dari hasil Biteship (pakai dua key: code & name) const courierMap = couriers.reduce((acc, item) => { 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: {}, }; } }; ensureEntry(codeKey); ensureEntry(nameKey); 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: 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; }, {}); // 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; if (!matched) return { ...courierOdoo, courier: false }; return { ...courierOdoo, courier: { ...matched, courier_id_odoo: courierOdoo.carrierId, // penting: simpan id Odoo di sini }, }; }) .filter( (x) => x.courier && x.courier.service_type && Object.keys(x.courier.service_type).length > 0 ); } function mappingCourier(couriersOdoo, couriers, notIncludeInstant = false) { const validCourierMap = couriersOdoo.reduce((acc, courier) => { acc[courier.label.toLowerCase()] = courier.carrierId; return acc; }, {}); return couriers?.reduce((result, item) => { const { courier_name, courier_code, courier_service_code } = item; if (!validCourierMap[courier_name.toLowerCase()]) { return result; // Jika tidak ada, lewati item ini } if ( notIncludeInstant && ['hours'].includes(item.shipment_duration_unit.toLowerCase()) ) { return result; } const carrierId = validCourierMap[courier_name]; // Jika courier_code belum ada di result, buat objek baru untuknya if (!result[courier_code]) { result[courier_code] = { courier_name: item.courier_name, courier_code: courier_code, courier_id_odoo: carrierId, 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: item.service_type, description: item.description, }, }, }; } else { result[courier_code].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, shipment_duration: item.duration, price: item.price, service_type: item.service_type, description: item.description, }; } return result; }, {}); } // interface CourierService { // courier_name: string; // courier_code: string; // service_type: { // [key: string]: { // service_name: string; // duration: number; // shipment_duration: number; // price: number; // service_type: string; // description: string; // }; // }; // } // interface ServiceOption { // service_name: string; // duration: number; // shipment_duration: number; // price: number; // service_type: string; // description: string; // } export default function SectionExpedition({ 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, } = useCheckout(); 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 expedition rates:', error); } }; useEffect(() => { fetchSlaProducts(); }, []); useEffect(() => { if (slaProducts) { let productSla = slaProducts?.slaTotal; if (slaProducts.slaUnit === 'jam') { productSla = 1; } setProductSla(productSla); } }, [slaProducts]); const fetchExpedition = async () => { let body = { ...destination, couriers: 'gojek,grab,deliveree,lalamove,jne,ninja,lion,rara,sicepat,jnt,idexpress,rpx,wahana,jdl,pos,anteraja,sap,paxel,borzo', items: items, }; try { const response = await axios.get(`/api/biteship-service`, { params: { body: JSON.stringify(body) }, }); return response; } catch (error) { console.error('Failed to fetch expedition rates:', error); } }; 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 = (code) => { setIsOpen(false); setOnFocuseSelectedCourier(false); const courier = code; 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 Anda Belum Melakukan Pengaturan PinPoint Alamat Pegiriman.' ); } else { toast.error( 'Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.' ); } setServiceOptions([]); } } else { setSelectedCourier(courier === 32 ? 'SELF PICKUP' : null); setSelectedCourierId(courier); setServiceOptions([]); } }; const handleOnFocuse = (value) => { setOnFocuseSelectedCourier(!value); setIsOpen(false); }; 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:
{/* Custom Select Input Field */}
handleOnFocuse(onFocusSelectedCourier)} > {selectedCourier ? (
{selectedCourier}
) : ( Pilih Expedisi )}
{/* Dropdown Options */} {onFocusSelectedCourier && (
{!isLoading ? ( <>
onCourierChange(32)} className='flex justify-between p-2 items-center hover:bg-gray-100 cursor-pointer' >

SELF PICKUP

{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) => (
onCourierChange(courier)} className='flex justify-between p-2 items-center hover:bg-gray-100 cursor-pointer' >

{courier?.label}

{
))} ) : ( <> )}
)}
{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

)}
{(serviceOptions.length > 0 || selectedService) && selectedCourier && selectedCourier !== 32 && selectedCourier !== 0 && (
Tipe Layanan Ekspedisi:
{/* Custom Select Input Field */}
setIsOpen(!isOpen)} > {selectedService ? (
{selectedService.service_name} {currencyFormat( Math.round( parseInt(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( parseInt(service?.price * 1.1) / 1000 ) * 1000 )}
))}
)}
)}
); }