/** * WordPress dependencies */ import { store as CORE } from '@safe-wordpress/core-data'; import { useSelect } from '@safe-wordpress/data'; import { createInterpolateElement } from '@safe-wordpress/element'; import { decodeEntities } from '@safe-wordpress/html-entities'; import { sprintf, _x } from '@safe-wordpress/i18n'; import type { Taxonomy } from '@safe-wordpress/core-data'; /** * External dependencies */ import { find, map, trim, reverse } from 'lodash'; import { store as NAB_DATA } from '@nab/data'; import { hasHead, isSingleton, listify } from '@nab/utils'; import type { CAViewProps, DownloadId, OrderStatusName, Maybe, SelectedDownloadIds, SelectedDownloadTaxonomies, SelectedDownloadTerms, SomeDownloads, TaxonomyName, TermId, } from '@nab/types'; /** * Internal dependencies */ import type { Attributes } from './types'; export const View = ( { attributes, ...props }: CAViewProps< Attributes > ): JSX.Element => { const { value: selection } = attributes; const { orderStatusForConversion = 'complete' } = props.goal; switch ( selection.type ) { case 'all-downloads': return ; case 'some-downloads': return ( ); } }; // ============ // HELPER VIEWS // ============ type HelperViewProps< T = unknown > = { readonly selection: T; readonly status: OrderStatusName; }; const AllDownloadsView = ( { status, }: Pick< HelperViewProps, 'status' > ): JSX.Element => { const statusName = useStatusName( status ); if ( status === 'complete' ) { return ( <>{ _x( 'An order is completed.', 'text', 'nelio-ab-testing' ) } ); } return beautify( sprintf( /* translators: %s: Woocommerce order status. */ _x( 'The status of an order is set to %s.', 'text', 'nelio-ab-testing' ), em( statusName ) ) ); }; const SomeDownloadsView = ( { selection, status, }: HelperViewProps< SomeDownloads[ 'value' ] > ): JSX.Element => { switch ( selection.type ) { case 'download-ids': return ; case 'download-taxonomies': return ( ); } }; const DownloadList = ( { selection, status, }: HelperViewProps< SelectedDownloadIds > ): JSX.Element => { const { downloadIds, mode, excluded = false } = selection; const statusName = em( useStatusName( status ) ); const downloadNames = map( useDownloadNames( downloadIds ), strong ); switch ( downloadNames.length ) { case 0: return beautify( sprintf( ORDER_STATUS, statusName ) ); case 1: return beautify( // eslint-disable-next-line @wordpress/valid-sprintf sprintf( excluded ? DOWNLOAD_LABELS.singleExcluded : DOWNLOAD_LABELS.singleIncluded, statusName, downloadNames[ 0 ] ) ); default: return beautify( // eslint-disable-next-line @wordpress/valid-sprintf sprintf( excluded ? DOWNLOAD_LABELS.multipleExcluded : DOWNLOAD_LABELS.multipleIncluded, statusName, listify( mode, downloadNames ) ) ); } }; const DownloadTaxonomies = ( { selection, status, }: HelperViewProps< SelectedDownloadTaxonomies > ): JSX.Element => { const statusName = em( useStatusName( status ) ); const value = selection.value.filter( ( t ) => !! t.termIds.length ); const cat = find( value, { taxonomy: 'download_category' } ); // eslint-disable-next-line @wordpress/no-unused-vars-before-return const catNames = map( useTermNames( cat?.taxonomy, cat?.termIds ), strong ); const tag = find( value, { taxonomy: 'download_tag' } ); // eslint-disable-next-line @wordpress/no-unused-vars-before-return const tagNames = map( useTermNames( tag?.taxonomy, tag?.termIds ), strong ); if ( ! hasHead( value ) ) { return beautify( sprintf( ORDER_STATUS, statusName ) ); } if ( isSingleton( value ) && ( cat || tag ) ) { const terms = catNames || tagNames || []; const mode = cat?.mode || tag?.mode || 'or'; const excluded = !! cat?.excluded || !! tag?.excluded || false; const labels = cat ? CATEGORY_LABELS : TAG_LABELS; if ( isSingleton( terms ) ) { return beautify( // eslint-disable-next-line @wordpress/valid-sprintf sprintf( excluded ? labels.singleExcluded : labels.singleIncluded, statusName, terms[ 0 ] ) ); } return beautify( // eslint-disable-next-line @wordpress/valid-sprintf sprintf( excluded ? labels.multipleExcluded : labels.multipleIncluded, statusName, listify( mode, terms ) ) ); } const isTwoTaxonomies = 2 === value.length; if ( isTwoTaxonomies && cat && tag ) { return beautify( sprintf( /* translators: %1$s: Woocommerce order status. %2$s: List of category names. %3$s: List of tag names. */ _x( 'The status of an order is set to %1$s and said order has at least one download in category %2$s and is tagged as %3$s.', 'text', 'nelio-ab-testing' ), statusName, listify( cat.mode, catNames ), listify( tag.mode, tagNames ) ) ); } const [ last, ...rest ] = reverse( value ); const init = reverse( rest ); return ( <> { beautify( sprintf( /* translators: %s: Woocommerce order status. */ _x( 'The status of an order is set to %s and said order has at least one download that', 'text', 'nelio-ab-testing' ), statusName ) ) } { init.map( ( t ) => ( <> { init.length === 1 ? '' : '; ' } ) ) } { isSingleton( value ) ? '' : AND_PLUS } { '.' } ); }; const DownloadTaxonomyTerms = ( { selection, }: HelperViewProps< SelectedDownloadTerms > ): JSX.Element => { const { taxonomy, mode, termIds, excluded } = selection; const taxonomyName = em( useTaxonomyName( taxonomy ) ); const termNames = map( useTermNames( taxonomy, termIds ), strong ); return beautify( // eslint-disable-next-line @wordpress/valid-sprintf sprintf( excluded ? TERM_LABELS.excluded : TERM_LABELS.included, taxonomyName, listify( mode, termNames ) ) ); }; // ======= // HELPERS // ======= const beautify = ( text: string ): JSX.Element => createInterpolateElement( text, { em: , strong: } ); const em = ( t: string ): string => `${ t }`; const strong = ( t: string ): string => `${ t }`; // ===== // HOOKS // ===== const useStatusName = ( status: OrderStatusName ): string => useSelect( ( select ) => find( select( NAB_DATA ).getECommerceSetting( 'edd', 'orderStatuses' ), { value: status, } )?.label ?? status, [ status ] ); const useDownloadNames = ( downloadIds: ReadonlyArray< DownloadId > ) => useSelect( ( select ) => downloadIds.map( ( downloadId ) => trim( select( NAB_DATA ).getEntityRecord( 'download', downloadId )?.title ) || sprintf( /* translators: %d: Product id. */ _x( 'Download #%d', 'text', 'nelio-ab-testing' ), downloadId ) ), [ downloadIds ] ); const useTaxonomyName = ( taxonomyName: TaxonomyName ) => useSelect( ( select ) => { const taxonomy: Maybe< Taxonomy > = select( CORE ).getEntityRecord( 'root', 'taxonomy', taxonomyName ); return taxonomy?.labels.singular_name ?? taxonomyName; }, [ taxonomyName ] ); const useTermNames = ( taxonomy: Maybe< TaxonomyName >, termIds: Maybe< ReadonlyArray< TermId > > = [] ) => useSelect( ( select ): ReadonlyArray< string > => { select( CORE ); if ( ! taxonomy || ! termIds ) { return []; } return termIds.map( ( id ) => { const term = select( CORE ).getEntityRecord( 'taxonomy', taxonomy, id ); const name = hasName( term ) ? term.name : `#${ id }`; return decodeEntities( name ); } ); }, [ taxonomy, termIds ] ); const hasName = ( o: unknown ): o is { name: string } => !! o && typeof o === 'object' && 'name' in o && !! o.name && 'string' === typeof o.name; // ========= // CONSTANTS // ========= type Labels = { readonly singleExcluded: string; readonly singleIncluded: string; readonly multipleExcluded: string; readonly multipleIncluded: string; }; /* translators: %s: Woocommerce order status. */ const ORDER_STATUS = _x( 'The status of an order is set to %s.', 'text', 'nelio-ab-testing' ); const DOWNLOAD_LABELS: Labels = { /* translators: %1$s: Woocomerce status. %2$s: Download name. */ singleExcluded: _x( 'The status of an order not containing download %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), /* translators: %1$s: Woocomerce status. %2$s: Download name. */ singleIncluded: _x( 'The status of an order containing the download %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), /* translators: %1$s: Woocomerce status. %2$s: List of download names. */ multipleExcluded: _x( 'The status of an order not containing any of the downloads %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), /* translators: %1$s: Woocomerce status. %2$s: List of download names. */ multipleIncluded: _x( 'The status of an order containing downloads %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), }; const CATEGORY_LABELS: Labels = { /* translators: %1$s: Woocomerce status. %2$s: Woocommerce category name. */ singleExcluded: _x( 'The status of an order not containing any download in category %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), /* translators: %1$s: Woocomerce status. %2$s: Woocommerce category name. */ singleIncluded: _x( 'The status of an order containing at least one download in category %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), /* translators: %1$s: Woocomerce status. %2$s: List of woocommerce category names. */ multipleExcluded: _x( 'The status of an order not containing any downloads in categories %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), /* translators: %1$s: Woocomerce status. %2$s: List of woocommerce category names. */ multipleIncluded: _x( 'The status of an order containing at least one download in categories %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), }; const TAG_LABELS_AUX = { /* translators: %1$s: Woocommerce order status. %2$s: Woocomerce tag name. */ singleExcluded: _x( 'The status of an order not containing any download tagged as %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), /* translators: %1$s: Woocommerce order status. %2$s: Woocomerce tag name. */ singleIncluded: _x( 'The status of an order containing at least one download tagged as %2$s is set to %1$s.', 'text', 'nelio-ab-testing' ), }; const TAG_LABELS: Labels = { ...TAG_LABELS_AUX, multipleExcluded: TAG_LABELS_AUX.singleExcluded, multipleIncluded: TAG_LABELS_AUX.singleIncluded, }; const TERM_LABELS = { empty: '', /* translators: %1$s: Woocomerce taxonomy name. %2$s: List of woocommerce term names. */ excluded: _x( 'doesn’t have %2$s in taxonomy %1$s', 'text (implicit subject: download)', 'nelio-ab-testing' ), /* translators: %1$s: Woocomerce taxonomy name. %2$s: List of woocommerce term names. */ included: _x( 'has %2$s in taxonomy %1$s', 'text (implicit subject: download)', 'nelio-ab-testing' ), }; // ========= // CONSTANTS // ========= // eslint-disable-next-line @wordpress/i18n-no-flanking-whitespace const AND_PLUS = _x( ', and ', 'text (2+ item list)', 'nelio-ab-testing' );