/**
* 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' );