/** * Reusable: dropdown to select a courier from quotes + list of selected couriers with remove. * Use with useQuotesFetcher; parent controls when to fetch and passes fetcher state. */ import { __, sprintf } from '@wordpress/i18n'; import { memo, useState, useEffect, useCallback } from '@wordpress/element'; import { Flex, Notice, Button, Dropdown, MenuGroup, MenuItem, } from '@wordpress/components'; import { chevronDown } from '@wordpress/icons'; import { Spinner } from '@woocommerce/components'; import type { Quote, Service } from '../../types/quote'; import { getCourierLogo } from '../utils/getAsset'; const REMOVED_NOTICE_DURATION_MS = 4000; function pillStyle(background: string, color: string) { return { display: 'inline-flex' as const, alignItems: 'center' as const, gap: 6, padding: '2px 8px', borderRadius: 12, background, color, fontSize: 12, lineHeight: 1.4, }; } export interface CourierSelectWithListProps { /** Call when user focuses the select to trigger quote fetch. */ onOpenDropdown: () => void | boolean; quotesList: Quote[]; quotesLoading: boolean; quotesError: string | null; /** Selected services with courier name and slug for logo display. */ selectedCouriers: Service[]; /** Called when user selects a courier from quotes. */ onAddCourier: (service: Service) => void; /** Called when user removes a selected courier. */ onRemoveCourier: (service: Service) => void; disabled?: boolean; canFetch: boolean; missingMessage: string | null; clearAllButton?: { label: string; onClick: () => void; disabled?: boolean; isBusy?: boolean; }; } function CourierSelectWithListInner({ onOpenDropdown, quotesList, quotesLoading, quotesError, selectedCouriers, onAddCourier, onRemoveCourier, disabled = false, canFetch, missingMessage, clearAllButton, }: CourierSelectWithListProps) { const [lastRemovedCourier, setLastRemovedCourier] = useState( null ); useEffect(() => { if (!lastRemovedCourier) return; const id = window.setTimeout( () => setLastRemovedCourier(null), REMOVED_NOTICE_DURATION_MS ); return () => clearTimeout(id); }, [lastRemovedCourier]); const handleRemoveCourier = useCallback( (service: Service) => { onRemoveCourier(service); const displayName = service.courier || service.name; setLastRemovedCourier(displayName); }, [onRemoveCourier] ); return ( {missingMessage && !canFetch && !disabled && ( {missingMessage} )} {lastRemovedCourier && ( setLastRemovedCourier(null)} > {`"${lastRemovedCourier}" ${__('removed from list.', 'parcel2go-shipping')}`} )} ( )} renderContent={({ onClose }) => { if (quotesLoading) { return ( {__('Loading quotes…', 'parcel2go-shipping')} ); } if (quotesError) { return (
{quotesError}
); } if (quotesList.length === 0) { return (

{__('No quotes found.', 'parcel2go-shipping')}

); } return ( {quotesList.map((quote, index) => { const service = quote.service; if (!service) return null; const displayName = service.courier && service.name ? `${service.courier} – ${service.name}` : service.name || service.courier || service.slug || '—'; const logo = getCourierLogo( service.courierSlug, service.courier ); const alreadyAdded = selectedCouriers.some( (selected) => selected.slug === service.slug ); const uniqueKey = `${String(quote.id ?? '')}-${service.courierSlug ?? ''}-${service.slug ?? ''}-${index}`; return ( { if (!alreadyAdded && service) { onAddCourier(service); onClose(); } }} disabled={alreadyAdded || disabled} > {logo && ( )} {displayName} {alreadyAdded && ( )} ); })} ); }} /> {selectedCouriers.length > 0 && ( <> {sprintf( /* translators: %d: number of selected services */ __('Selected Services (%d).', 'parcel2go-shipping'), selectedCouriers.length )} { clearAllButton && ( )} {selectedCouriers.map((service, index) => { const logo = getCourierLogo( service.courierSlug, service.courier ); const displayName = service.courier || service.name; const pillKey = `${service.courier}-${service.courierSlug}-${index}`; return ( {logo && ( {service.courier} )} {displayName} ); })} )}
); } export default memo(CourierSelectWithListInner);