import React, { useCallback, useEffect, useRef, useState } from 'react' import apiFetch from '@wordpress/api-fetch' import { Button, Snackbar, Spinner, Notice, DropdownMenu, MenuGroup, MenuItem, SelectControl, __experimentalHeading as Heading, } from '@wordpress/components' import { Card, Text } from '@wordpress/ui' import { BookingRequest, MybringBooking, MybringCustomerAndServices, Order, OrderItem, ServiceDetails, } from './model/models' import CustomerSelector from './components/CustomerSelector' import { useI18n } from '@wordpress/react-i18n' import { pages, undo, redo, reusableBlock, Icon, search, } from '@wordpress/icons' import { useEnableWpCompatOverlaySlot } from '@wordpress/ui' import OrderListItem from './components/OrderListItem' import BookedOrderListItem from './components/BookedOrderListItem' import { sprintf } from '@wordpress/i18n' import CustomsInformationHsCodes from './components/CustomsInformationHsCodes' const OrderList = () => { const [orders, setOrders] = useState>([]) const [customers, setCustomers] = useState>( [] ) const [customerNumber, setCustomerNumber] = useState() const [bookingStatus, setBookingStatus] = useState>({}) const [inProgress, setInProgress] = useState(false) const [loading, setLoading] = useState(false) const [labelFailure, setLabelFailure] = useState(false) const [allInProgress, setAllInProgress] = useState(false) const [waybillsFailure, setWaybillsFailure] = useState(false) const [labelsInProgress, setLabelsInProgress] = useState(false) const [waybillsInProgress, setWaybillsInProgress] = useState(false) const [returnLabelsInProgress, setReturnLabelsInProgress] = useState(false) const [displayHsCodeEditor, setDisplayHsCodeEditor] = useState(false) const [itemsMissingHsCode, setItemsMissingHsCode] = useState< Array >([]) const [labelSize, setLabelSize] = useState<'ALL' | 'STANDARD' | 'MAILBOX'>( 'ALL' ) const [menuOpen, setMenuOpen] = useState(false) const [availableServices, setAvailableServices] = useState< Array >([]) const { __, _n } = useI18n() const orderButtonRef = useRef(null) const labelButtonRef = useRef(null) const [focusOrderButton, setFocusOrderButton] = useState(false) const [focusLabelButton, setFocusLabelButton] = useState(false) useEnableWpCompatOverlaySlot() useEffect(() => { if (focusOrderButton && orderButtonRef.current) { orderButtonRef.current.focus() setFocusOrderButton(false) } }, [focusOrderButton]) useEffect(() => { if (focusLabelButton && labelButtonRef.current) { labelButtonRef.current.focus() setFocusLabelButton(false) } }, [focusLabelButton]) const fetchCustomers = useCallback(async () => { const response: MybringCustomerAndServices[] = await apiFetch({ path: '/posten-bring-checkout/customers', }) if (response) { setCustomers(response) setCustomerNumber(current => { if (current || response.length === 0) { return current } const defaultCustomer = response.find(it => it.defaultCustomer) return defaultCustomer?.customerNumber ?? response[0].customerNumber }) } }, []) const fetchOrders = async (params: string) => { setLoading(true) const response: Order[] = await apiFetch>({ path: '/posten-bring-checkout/bookings' + (params ? '?' + params : ''), }).finally(() => setLoading(false)) if (response) { setOrders(response) setItemsMissingHsCode([ ...new Map( response .filter(order => order.nvit) .flatMap(order => order.items.filter(item => !item.hs_code || item.hs_code === '') ) .map(item => [item.product_id, item]) ).values(), ]) setFocusOrderButton(true) } } const fetchAvailableServices = async () => { const response: ServiceDetails[] = await apiFetch>({ path: '/posten-bring-checkout/services', }) if (response) setAvailableServices(response) } const updateOrders = useCallback(() => { const orderIdsString = sessionStorage.getItem('posten_order_ids') if (orderIdsString) { const orderIdsArray = orderIdsString.split(',') const queryParamsArray = orderIdsArray.map(id => `orderIds[]=${id}`) const params = queryParamsArray.join('&') fetchOrders(params) } }, []) useEffect(() => { fetchCustomers() updateOrders() fetchAvailableServices() const handleSessionStorageChange = () => { setOrders([]) updateOrders() } window.addEventListener( 'posten-order-ids-changed', handleSessionStorageChange ) return () => { window.removeEventListener( 'posten-order-ids-changed', handleSessionStorageChange ) } }, [fetchCustomers, updateOrders]) const handleBooking = async () => { if (!orders.length) { return } setInProgress(true) const newBookingStatus = { ...bookingStatus } const requests = orders .filter(order => (order.mybring_bookings?.length || 0) === 0) .map(async order => { const data = { orderId: order.order_id, testIndicator: false, selectedShippingOption: order.order_shipping_option, customerNumber, orderRef: order.order_id, packages: [ { weightInKg: order.total_weight / 1000, }, ], } as BookingRequest if ( !order.order_validation_errors.some(value => value.bookingDisabled) && order.order_shipping_option ) { try { const result = await apiFetch>({ path: '/posten-bring-checkout/bookings?orderId=' + order.order_id, method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) setOrders(prevOrders => prevOrders.map(prevOrder => { if (prevOrder.order_id === order.order_id) { return { ...prevOrder, mybring_bookings: result, } } return prevOrder }) ) newBookingStatus[order.order_id] = 'success' // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error: unknown) { newBookingStatus[order.order_id] = 'error' } const newStatus = { ...newBookingStatus } setBookingStatus(newStatus) } }) await Promise.all(requests) setInProgress(false) setFocusLabelButton(true) } const mapServiceId = (orderShippingOption: string | undefined) => { if (!orderShippingOption) { return ( {__('No shipping option selected', 'posten-bring-checkout')} ) } return ( availableServices && availableServices.find( service => service.service_id === orderShippingOption )?.name ) } const onSave = (orderId: string, editedOrder?: Order) => { setOrders( orders.map(order => order.order_id === orderId ? editedOrder || order : order ) ) } const bookableOrders = orders.filter( order => (order.mybring_bookings?.length || 0) === 0 && bookingStatus[order.order_id] !== 'success' ) const bookedOrders = orders .filter( order => (order.mybring_bookings?.length || 0) > 0 && order.order_shipping_option && order.order_shipping_option !== '' ) .filter(order => { if (labelSize === 'MAILBOX') { return order.order_shipping_option === '3584' } else if (labelSize === 'STANDARD') { return order.order_shipping_option !== '3584' } else { return true } }) const getLabels = async (labelType: string) => { setLabelFailure(false) const newTab = window.open('', '_blank') const orderIds = orders .filter( order => bookingStatus[order.order_id] === 'success' || (order.mybring_bookings?.length || 0) > 0 ) .filter(order => { if (labelSize === 'MAILBOX') { return order.order_shipping_option === '3584' } else if (labelSize === 'STANDARD') { return order.order_shipping_option !== '3584' } else { return true } }) .map(order => order.order_id) .join(',') const response = await apiFetch({ path: `/posten-bring-checkout/labels?orderIds=${orderIds}&labelType=${labelType}`, method: 'GET', headers: { Accept: 'application/pdf', }, parse: false, }) .catch(() => { setLabelFailure(true) }) .finally(() => { setAllInProgress(false) setLabelsInProgress(false) setReturnLabelsInProgress(false) }) setLabelsInProgress(false) setReturnLabelsInProgress(false) if (response?.ok) { const blob = await response.blob() const url = window.URL.createObjectURL( new Blob([blob], { type: 'application/pdf' }) ) if (newTab) { newTab.location.href = url } } } const getWaybills = async () => { setWaybillsFailure(false) const newTab = window.open('', '_blank') const orderIds = orders .filter( order => order.mybring_bookings.find(b => b.waybillUrl) && (bookingStatus[order.order_id] === 'success' || (order.mybring_bookings?.length || 0) > 0) ) .map(order => order.order_id) .join(',') const response = await apiFetch({ path: `/posten-bring-checkout/waybills?orderIds=${orderIds}`, method: 'GET', headers: { Accept: 'application/pdf', }, parse: false, }).catch(() => { setWaybillsFailure(true) }) setWaybillsInProgress(false) if (response?.ok) { const blob = await response.blob() const url = window.URL.createObjectURL( new Blob([blob], { type: 'application/pdf' }) ) if (newTab) { newTab.location.href = url } } } return (
{displayHsCodeEditor ? (
setDisplayHsCodeEditor(false)} onSave={() => { setDisplayHsCodeEditor(false) updateOrders() }} />
) : ( <> {(loading || bookableOrders.length > 0) && ( {sprintf( _n( '%d order awaiting shipment booking', '%d orders awaiting shipment booking', bookableOrders.length, 'posten-bring-checkout' ), bookableOrders.length )}
{itemsMissingHsCode.length > 0 && ( )} setCustomerNumber(selectedCustomerNumber) } />
    {loading && (
    )} {bookableOrders.map(order => ( ))}
)} {sprintf( _n( '%d order ready to be shipped', '%d orders ready to be shipped', bookedOrders.length, 'posten-bring-checkout' ), bookedOrders.length )}
{labelFailure && ( { setLabelFailure(false) }} > {__( 'An error occurred while generating labels. Please try again', 'posten-bring-checkout' )} )} {waybillsFailure && ( { setWaybillsFailure(false) }} > {__( 'An error occurred while generating waybills. Please try again', 'posten-bring-checkout' )} )}
{({ onClose }) => ( ) : ( reusableBlock ) } onClick={() => { setAllInProgress(true) getLabels('ALL').finally(() => onClose()) }} > {__('Print all', 'posten-bring-checkout')} {bookedOrders.find(order => order.mybring_bookings.find(b => b.returnLabelUrl) ) ? ( <> ) : ( redo ) } onClick={() => { setLabelsInProgress(true) getLabels('LABEL').finally(() => onClose()) }} > {__('Print labels', 'posten-bring-checkout')} ) : ( undo ) } onClick={() => { setReturnLabelsInProgress(true) getLabels('RETURN').finally(() => onClose()) }} > {__( 'Print return labels', 'posten-bring-checkout' )} ) : null} {bookedOrders.find(order => order.mybring_bookings.find(b => b.waybillUrl) ) && ( ) : ( pages ) } onClick={() => { setWaybillsInProgress(true) getWaybills().finally(() => onClose()) }} > {__('Print waybills', 'posten-bring-checkout')} )} )}
{bookedOrders.length > 0 ? (
    {bookedOrders.map(order => ( ))}
) : ( <> {loading ? (
) : (
{__('No orders found', 'posten-bring-checkout')}
)} )}
)}
) } export default OrderList