/** * ScanForm utility functions for domestic/international grouping and label exclusion notices. * * The server is authoritative for domestic/international classification — * it populates `label.is_domestic` in the origins response. These helpers * just group by that flag; they do not re-apply USPS territory rules on * the client. */ import { getProtocol } from '@wordpress/url'; import { sprintf, _n } from '@wordpress/i18n'; import type { ScanFormLabel } from 'types'; const SAFE_URL_PROTOCOLS = [ 'http:', 'https:' ]; /** * Returns true when a URL uses http or https — safe to pass to window.open(). * Rejects javascript:, data:, and any other protocol that could execute code. */ export const isSafePdfUrl = ( url: string ): boolean => SAFE_URL_PROTOCOLS.includes( getProtocol( url ) ?? '' ); /** * Per-reason notice messages for labels excluded from SCAN Form eligibility. * Add a new entry here when a new exclusion reason is introduced server-side. * Unknown reasons fall back to the generic message in getExclusionNotice(). */ const EXCLUSION_NOTICES: Record< string, ( count: number ) => string > = { missing_destination: ( count ) => sprintf( /* translators: %d is number of labels excluded due to missing destination country */ _n( '%d label was excluded because its destination country could not be determined and cannot be added to a SCAN Form.', '%d labels were excluded because their destination country could not be determined and cannot be added to a SCAN Form.', count, 'woocommerce-shipping' ), count ), envelope_type: ( count ) => sprintf( /* translators: %d is number of envelope labels excluded from SCAN Form */ _n( '%d label is an envelope shipment and cannot be included in a SCAN Form.', '%d labels are envelope shipments and cannot be included in a SCAN Form.', count, 'woocommerce-shipping' ), count ), }; /** * Get a localized notice string for a given exclusion reason and label count. * Falls back to a generic message for unknown reasons. */ export const getExclusionNotice = ( reason: string, count: number ): string => EXCLUSION_NOTICES[ reason ]?.( count ) ?? sprintf( /* translators: %d is number of labels that could not be added */ _n( '%d label could not be added to a SCAN Form.', '%d labels could not be added to a SCAN Form.', count, 'woocommerce-shipping' ), count ); /** * Check whether a label has been classified as a domestic USPS shipment. */ export const isDomesticShipment = ( label: ScanFormLabel ): boolean => !! label.is_domestic; /** * Split an array of label IDs into domestic and international batches * using the server-provided classification. * * @param labelIds Array of label IDs to split. * @param labels Array of label objects carrying is_domestic. * @return Object with domestic and international label ID arrays. */ export const splitLabelsByDestination = ( labelIds: number[], labels: ScanFormLabel[] ): { domestic: number[]; international: number[] } => { const labelMap = new Map( labels.map( ( l ) => [ l.label_id, l ] ) ); const domestic: number[] = []; const international: number[] = []; for ( const labelId of labelIds ) { const label = labelMap.get( labelId ); if ( ! label ) { continue; } if ( label.is_domestic ) { domestic.push( labelId ); } else { international.push( labelId ); } } return { domestic, international }; };