/** * WordPress dependencies */ import { Button, ExternalLink, Tooltip } from '@safe-wordpress/components'; import { useDispatch, useSelect } from '@safe-wordpress/data'; import { useState } from '@safe-wordpress/element'; import { _x, sprintf } from '@safe-wordpress/i18n'; import { addQueryArgs } from '@safe-wordpress/url'; /** * External dependencies */ import clsx from 'clsx'; import { isFunction, noop } from 'lodash'; import { ConfirmationDialog, FancyIcon, Preview } from '@nab/components'; import { store as NAB_DATA, usePluginSetting } from '@nab/data'; import { store as NAB_EXPERIMENTS } from '@nab/experiments'; import { getLetter, isExperimentTooOldToHaveHeatmaps } from '@nab/utils'; import type { Alternative, AlternativeTrackingData, ECommercePlugin, Experiment, ExperimentId, GoalId, } from '@nab/types'; /** * Internal dependencies */ import { getAlternativeName, getMoneyLabel, getNumberLabel, getPercentageLabel, getImprovementLabel, } from '../utils'; export type AlternativeContainerProps = { readonly index: number; readonly alternative: Alternative; readonly areResultsUnique: boolean; readonly isPublicView: boolean; readonly ecommerce?: ECommercePlugin; readonly experimentId: ExperimentId; readonly experimentStatus: Experiment[ 'status' ]; readonly goal: GoalId; readonly result?: AlternativeTrackingData; readonly controlRevenue?: number; readonly controlArpc?: number; readonly isWinner?: boolean; readonly pageViews?: number; }; export const AlternativeContainer = ( props: AlternativeContainerProps ): JSX.Element => { const [ isApplyDialogOpen, openApplyDialog ] = useState( false ); const minSampleSize = usePluginSetting( 'minSampleSize' ); const { homeUrl, alternativeBeingApplied, endDate, supportsAlternativeApplication, supportsAlternativePreviewDialog, } = useAttributes( props ) || {}; const { applyAlternative, viewAlternative } = useAlternativeActions( props ); const { index, experimentId, alternative: { isLastApplied, attributes: { name }, links: { heatmap, preview }, }, areResultsUnique: isUnique, isPublicView, result: { visits: allVisits, conversions: allConversions = {}, conversionRates: allConvRates = {}, values: allValues = {}, improvementFactors: allImprovFactors = {}, uniqueVisits: uniqVisits, uniqueConversions: uniqConversions = {}, uniqueConversionRates: uniqConvRates = {}, uniqueValues: uniqValues = {}, uniqueImprovementFactors: uniqImprovFactors = {}, } = {}, ecommerce, experimentStatus, goal, controlRevenue, controlArpc, isWinner, pageViews = 0, } = props; const visits = isUnique ? uniqVisits : allVisits; const conversions = isUnique ? uniqConversions : allConversions; const conversionRates = isUnique ? uniqConvRates : allConvRates; const revenues = isUnique ? uniqValues : allValues; const improvementFactors = isUnique ? uniqImprovFactors : allImprovFactors; const revenue = revenues[ goal ] || 0; const revenuePerView = revenue / ( visits || 1 ); const revenueImprovement = ! controlRevenue ? 0 : Math.round( ( ( revenue - controlRevenue ) / controlRevenue ) * 1000 ) / 10; const conv = conversions[ goal ] ?? 0; const arpc = conversions[ goal ] ? revenue / conv : 0; const arpcImprovement = ! controlArpc ? 0 : Math.round( ( ( arpc - controlArpc ) / controlArpc ) * 1000 ) / 10; const screenshot = preview; const alternativeName = getAlternativeName( index, name ); const showHeatmapButton = !! heatmap && ! isExperimentTooOldToHaveHeatmaps( endDate ); const showApplyButton = experimentStatus === 'finished' && !! supportsAlternativeApplication && !! applyAlternative; const showPreviewInDialog = !! supportsAlternativePreviewDialog; const isClearWinner = isWinner && minSampleSize <= pageViews; const heatmapResultPageUrl = isPublicView ? addQueryArgs( homeUrl, { 'nab-result': true, experiment: experimentId, heatmap: index, preview: '', } ) : addQueryArgs( 'admin.php', { page: 'nelio-ab-testing-experiment-view', experiment: experimentId, heatmap: index, } ); return (
{ isClearWinner && ( ) }

{ getLetter( index ) } { !! preview ? ( { alternativeName } ) : ( alternativeName ) }

{ !! preview ? ( ) : ( ) }
{ isUnique ? _x( 'Unique Page Views', 'text', 'nelio-ab-testing' ) : _x( 'Page Views', 'text', 'nelio-ab-testing' ) } { getNumberLabel( visits ) }
{ isUnique ? _x( 'Unique Conversions', 'text', 'nelio-ab-testing' ) : _x( 'Conversions', 'text', 'nelio-ab-testing' ) } { getNumberLabel( conversions[ goal ] ) }
{ _x( 'Conversion Rate', 'text', 'nelio-ab-testing' ) } { getPercentageLabel( conversionRates[ goal ] || 0 ) } { !! index && ( 0, }, { 'nab-alternative-metric__value--worse': ( improvementFactors[ goal ] || 0 ) < 0, }, ] ) } > { getImprovementLabel( improvementFactors[ goal ] || 0 ) } ) }
{ !! ecommerce && ( <>
{ _x( 'Revenue', 'text', 'nelio-ab-testing' ) } { getMoneyLabel( revenue, ecommerce ) } { !! index && ( 0, }, { 'nab-alternative-metric__value--worse': revenueImprovement < 0, }, ] ) } > { getImprovementLabel( revenueImprovement ) } ) }
{ _x( 'ARPC', 'text', 'nelio-ab-testing' ) } { getMoneyLabel( arpc, ecommerce ) } { !! index && ( 0, }, { 'nab-alternative-metric__value--worse': arpcImprovement < 0, }, ] ) } > { getImprovementLabel( arpcImprovement ) } ) }
) }
{ ( showHeatmapButton || showApplyButton || showPreviewInDialog ) && ( ) }
); }; // ===== // HOOKS // ===== const useAttributes = ( { experimentId, alternative: { id: alternativeId }, }: AlternativeContainerProps ) => useSelect( ( select ): typeof NO_ATTRIBUTES => { const { getExperiment, getPluginSetting } = select( NAB_DATA ); const experiment = getExperiment( experimentId ); if ( ! experiment ) { return NO_ATTRIBUTES; } const { getExperimentAttribute, getPageAttribute } = select( NAB_DATA ); const { getExperimentSupport } = select( NAB_EXPERIMENTS ); const alternativeApplication = getExperimentSupport( experiment.type, 'alternativeApplication' ); return { homeUrl: getPluginSetting( 'homeUrl' ), supportsAlternativeApplication: isFunction( alternativeApplication ) ? alternativeApplication( experiment.alternatives[ 0 ]?.attributes ) : !! alternativeApplication, supportsAlternativePreviewDialog: 'control' !== alternativeId && !! getExperimentSupport( experiment.type, 'alternativePreviewDialog' ), alternativeBeingApplied: !! getPageAttribute( 'editor/alternativeBeingApplied' ), endDate: getExperimentAttribute( experimentId, 'endDate' ) || '', }; }, [ experimentId, alternativeId ] ); const useAlternativeActions = ( props: AlternativeContainerProps ): typeof NO_ACTIONS => { const { applyAlternative, setPageAttribute } = useDispatch( NAB_DATA ); if ( ! props.alternative?.id ) { return NO_ACTIONS; } return { applyAlternative: () => applyAlternative( props.alternative.id ), viewAlternative: () => setPageAttribute( 'editor/alternativeInPreviewDialog', props.alternative.id ), }; }; // ==== // DATA // ==== const NO_ATTRIBUTES = { homeUrl: '', supportsAlternativeApplication: false, supportsAlternativePreviewDialog: false, alternativeBeingApplied: false, endDate: '', }; const NO_ACTIONS = { applyAlternative: noop as () => Promise< unknown >, viewAlternative: noop, };