import { __experimentalConfirmDialog as ConfirmDialog, __experimentalSpacer as Spacer, Button, Flex, FlexBlock, FlexItem, Modal, Notice, } from '@wordpress/components'; import { usePrevious } from '@wordpress/compose'; import { __, sprintf } from '@wordpress/i18n'; import { createInterpolateElement, forwardRef, useCallback, useState, useEffect, } from '@wordpress/element'; import { dispatch } from '@wordpress/data'; import { getCurrentOrder, getNoneSelectedShipmentItems, getSelectablesCount, getSubItems, hasSubItems, normalizeShipments, } from 'utils'; import { SelectableItems } from './selectable-items'; import { useLabelPurchaseContext } from 'context/label-purchase'; import { StaticHeader } from './header'; import { MoveTo } from './move-to'; import { SHOW_SPLIT_SHIPMENT_NOTICE } from './constants'; import { cloneDeep } from 'lodash'; import { labelPurchaseStore } from 'data/label-purchase'; import { recordEvent } from 'utils/tracks'; import { ShipmentItem, ShipmentSubItem } from 'types'; interface SplitShipmentModalProps { setStartSplitShipment: ( startSplitShipment: boolean ) => void; } export const SplitShipmentModal = forwardRef< HTMLBaseElement, SplitShipmentModalProps >( ( { setStartSplitShipment }, ref ) => { const [ showCreationNotice, setCreationNotice ] = useState( ! [ 'false', false ].includes( localStorage.getItem( SHOW_SPLIT_SHIPMENT_NOTICE ) ?? false ) ); const [ updateError, setUpdateError ] = useState< unknown | boolean >( false ); const [ isLoading, setIsLoading ] = useState( false ); const [ confirmClose, setConfirmClose ] = useState( false ); const { shipment: { shipments, setShipments, selections, setSelection, resetSelections, currentShipmentId, revertLabelShipmentIdsToUpdate, labelShipmentIdsToUpdate, hasVariations, hasMultipleShipments, isReturnShipment, }, labels: { hasPurchasedLabel }, customs: { updateCustomsItems }, } = useLabelPurchaseContext(); const selectPreviousTab = () => { if ( ref && 'current' in ref && ref.current ) { const previousTab = ref.current.querySelector< HTMLButtonElement >( `.shipment-tab-${ currentShipmentId }` ); previousTab?.click(); } }; const closeOrCancelShipmentEdit = () => { selectPreviousTab(); setStartSplitShipment( false ); }; const previousShipmentsState = usePrevious( shipments ); const selectedItemsAndSubitems = () => { const firstSelection = Object.values( selections )[ 0 ] ?? []; return firstSelection.flatMap( ( selection ) => selection.subItems ); }; /** * Store the initial state of shipments to enable resetting the state when * the modal is closed. */ // eslint-disable-next-line react-hooks/exhaustive-deps const getInitialState = useCallback( () => cloneDeep( shipments ), [] ); const createShipment = () => { const oldShipments = getNoneSelectedShipmentItems( shipments, selections ); const newShipment = Object.values( selections ).flat(); const newShipments = { ...oldShipments, [ Object.keys( oldShipments ).length ]: newShipment, }; const { normalizedShipments } = normalizeShipments( newShipments ); setShipments( normalizedShipments ); resetSelections( Object.keys( normalizedShipments ) ); }; const addSelectionForShipment = ( index: string | number ) => ( selection: ShipmentItem[] | ShipmentSubItem[] ) => { setSelection( { ...selections, [ index ]: selection } ); }; const shouldDisableCreateShipmentButton = () => { const orderItems = Object.values( shipments ).flat(); const selectablesCount = orderItems.reduce( ( acc, item ) => { return acc + item.quantity; }, 0 ); const selectedCount = Object.values( selections ).flat().length; return selectedCount === 0 || selectedCount === selectablesCount; }; const selectAll = ( index: number | string ) => ( add: boolean ) => { if ( add ) { setSelection( { ...selections, [ index ]: shipments[ index ] .map( ( item ) => hasSubItems( item ) ? getSubItems( item ) : item ) .flat() as ShipmentItem[], } ); } else { setSelection( { [ currentShipmentId ]: [], } ); } }; const save = async () => { setIsLoading( true ); setUpdateError( false ); const simplifiedShipments = Object.entries( shipments ).reduce( ( acc, [ key, shipment ] ) => ( { ...acc, [ key ]: shipment.map( ( { id, subItems } ) => ( { id, subItems: subItems.map( ( { id: subItemId } ) => subItemId ), } ) ), } ), {} ); const result = await dispatch( labelPurchaseStore ).updateShipments( { shipments: simplifiedShipments, orderId: `${ getCurrentOrder()?.id }`, shipmentIdsToUpdate: labelShipmentIdsToUpdate, } ); setIsLoading( false ); if ( 'error' in result ) { // @ts-ignore setUpdateError( result?.error?.message ); } else { closeOrCancelShipmentEdit(); } updateCustomsItems(); }; const shouldClose = () => { if ( previousShipmentsState ) { setConfirmClose( true ); } else { recordEvent( 'split_shipment_modal_closed' ); closeOrCancelShipmentEdit(); } }; const dismissNotice = () => { setCreationNotice( false ); localStorage.setItem( SHOW_SPLIT_SHIPMENT_NOTICE, 'false' ); recordEvent( 'split_shipment_modal_notice_dismissed' ); }; // Reset selections when modal opens // we need to do this because items can have been selected for "extra labels" // and we don't want to carry over those extra labels to the split shipment items table. useEffect( () => { resetSelections( Object.keys( shipments ) ); }, [] ); // eslint-disable-line react-hooks/exhaustive-deps return ( { showCreationNotice && ( { createInterpolateElement( __( 'To create a split shipment, please select the respective products and click on create new shipment.', 'woocommerce-shipping' ), { strong: , } ) } ) } { showCreationNotice && updateError !== false && ( ) } { updateError !== false && ( { sprintf( // translators: %s: error message __( 'There was an error while updating the shipments. %s', 'woocommerce-shipping' ), String( updateError ) ) } ) } { hasMultipleShipments && ( ) } { Object.entries( shipments ).map( ( [ key, shipment ] ) => { const index = parseInt( key, 10 ); const hasShipmentPurchasedLabel = hasPurchasedLabel( true, true, key ); return ( 1 } select={ addSelectionForShipment( index ) } selections={ selections[ index ] || [] } orderItems={ shipment } selectAll={ selectAll( key ) } shipmentIndex={ index } isDisabled={ hasShipmentPurchasedLabel || isReturnShipment( key ) } /> ); } ) } { setShipments( getInitialState() ); revertLabelShipmentIdsToUpdate(); recordEvent( 'split_shipment_modal_close_confirm_clicked' ); closeOrCancelShipmentEdit(); } } onCancel={ () => { recordEvent( 'split_shipment_modal_close_cancel_clicked' ); setConfirmClose( false ); } } confirmButtonText={ __( 'Close and revert the changes', 'woocommerce-shipping' ) } cancelButtonText={ __( 'Continue editing the shipments', 'woocommerce-shipping' ) } >

{ __( 'There are unsaved changes', 'woocommerce-shipping' ) }

{ __( 'Are you sure you want to close the split shipment modal?', 'woocommerce-shipping' ) }
); } );