/** * WordPress dependencies */ import { Dashicon, ToggleGroupControl, ToggleGroupControlOption, } from '@safe-wordpress/components'; import { useSelect } from '@safe-wordpress/data'; import { createInterpolateElement, useState } from '@safe-wordpress/element'; import { _x, sprintf } from '@safe-wordpress/i18n'; /** * External dependencies */ import { store as NAB_DATA } from '@nab/data'; import { numberFormat } from '@nab/i18n'; import { EMPTY_OBJECT } from '@nab/utils'; /** * Internal dependencies */ import './style.scss'; import { ArpcChart } from './arpc-chart'; import { ArpcImprovementChart } from './arpc-improvement-chart'; import { ArpcTimelineChart } from './arpc-timeline-chart'; import { ConversionRatesChart } from './conversion-rates-chart'; import { ConversionRatesTimelineChart } from './conversion-rates-timeline-chart'; import { ViewsAndConversionsTimelineChart } from './views-and-conversions-timeline-chart'; import { ConversionImprovementChart } from './conversion-improvement-chart'; import { RevenueChart } from './revenue-chart'; import { RevenueImprovementChart } from './revenue-improvement-chart'; import { RevenueTimelineChart } from './revenue-timeline-chart'; import { getMoneyLabel, getNumberLabel } from '../utils'; import { useAreUniqueResultsVisible } from '../hooks'; type ChartType = | 'conversion-rate' | 'views-and-conversions' | 'revenue' | 'arpc'; export const EvolutionSection = (): JSX.Element => { const [ activeTimeline, setActiveTimeline ] = useState< ChartType >( 'conversion-rate' ); return (
<ChartSelector value={ activeTimeline } onChange={ setActiveTimeline } /> </div> <Summary /> <TimelineChart type={ activeTimeline } /> <ExtraCharts type={ activeTimeline } /> </div> ); }; // ============ // HELPER VIEWS // ============ const Title = () => ( <h2> { createInterpolateElement( sprintf( /* translators: %s: Dashicon. */ _x( '%s Evolution', 'text', 'nelio-ab-testing' ), '<icon />' ), { icon: ( <Dashicon className="nab-evolution-section__title-icon" icon="chart-bar" /> ), } ) } </h2> ); const Summary = (): JSX.Element => { const { areResultsUnique, conversionRate, conversions, ecommerce, uniquePageViews, totalPageViews, revenue, } = useAttributes(); return ( <ul className="nab-evolution-section__page-views-and-conversions"> <li> <span className="nab-evolution-section__label"> { areResultsUnique ? _x( 'Total Page Views:', 'text', 'nelio-ab-testing' ) : _x( 'Page Views:', 'text', 'nelio-ab-testing' ) } </span> <span className="nab-evolution-section__value"> { getNumberLabel( totalPageViews ) } </span> </li> { areResultsUnique && ( <li> <span className="nab-evolution-section__label"> { _x( 'Unique Page Views:', 'text', 'nelio-ab-testing' ) } </span> <span className="nab-evolution-section__value"> { getNumberLabel( uniquePageViews ) } </span> </li> ) } { !! ecommerce && ( <li> <span className="nab-evolution-section__label"> { _x( 'Revenue:', 'text', 'nelio-ab-testing' ) } </span> <span className="nab-evolution-section__value"> { getMoneyLabel( revenue, ecommerce ) } </span> </li> ) } <li> <span className="nab-evolution-section__label"> { areResultsUnique ? _x( 'Unique Conversions:', 'text', 'nelio-ab-testing' ) : _x( 'Conversions:', 'text', 'nelio-ab-testing' ) } </span> <span className="nab-evolution-section__value"> { getNumberLabel( conversions ) } </span> <span className="nab-evolution-section__conversion-rate"> { getConversionRateLabel( conversionRate ) } </span> </li> </ul> ); }; const ChartSelector = ( { value, onChange, }: { value: ChartType; onChange: ( v: ChartType ) => void; } ) => { const { ecommerce } = useAttributes(); return ( <ToggleGroupControl className="nab-evolution-section__timeline-selector" label="" value={ value } onChange={ ( newValue ) => onChange( newValue as ChartType ) } > <ToggleGroupControlOption value="conversion-rate" label={ _x( 'Conversion Rates', 'text', 'nelio-ab-testing' ) } /> <ToggleGroupControlOption value="views-and-conversions" label={ _x( 'Views and Conversions', 'text', 'nelio-ab-testing' ) } /> { !! ecommerce && ( <> <ToggleGroupControlOption value="revenue" label={ _x( 'Revenue', 'text', 'nelio-ab-testing' ) } /> <ToggleGroupControlOption value="arpc" label={ _x( 'ARPC', 'text', 'nelio-ab-testing' ) } showTooltip={ true } /> </> ) } </ToggleGroupControl> ); }; type ChartFunction = ( props: { type: ChartType } ) => JSX.Element | null; const TimelineChart: ChartFunction = ( { type } ) => { const { activeGoal, alternatives, areResultsUnique, ecommerce } = useAttributes(); switch ( type ) { case 'conversion-rate': return ( <div className="nab-evolution-section__timeline-graphic"> <ConversionRatesTimelineChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> </div> ); case 'views-and-conversions': return ( <div className="nab-evolution-section__timeline-graphic"> <ViewsAndConversionsTimelineChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> </div> ); case 'revenue': if ( ! ecommerce ) { return null; } return ( <div className="nab-evolution-section__timeline-graphic"> <RevenueTimelineChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> </div> ); case 'arpc': if ( ! ecommerce ) { return null; } return ( <div className="nab-evolution-section__timeline-graphic"> <ArpcTimelineChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> </div> ); } }; const ExtraCharts: ChartFunction = ( { type } ) => ( <div className="nab-evolution-section__comparison-and-improvement-graphics"> <div className="nab-evolution-section__comparison-graphic"> <ComparisonChart type={ type } /> </div> <div className="nab-evolution-section__improvement-graphic"> <ImprovementChart type={ type } /> </div> </div> ); const ComparisonChart: ChartFunction = ( { type } ) => { const { activeGoal, alternatives, areResultsUnique, ecommerce, minConfidence, winners, } = useAttributes(); switch ( type ) { case 'conversion-rate': case 'views-and-conversions': return ( <ConversionRatesChart alternatives={ alternatives } goal={ activeGoal } winners={ winners } minConfidence={ minConfidence } unique={ areResultsUnique } /> ); case 'revenue': if ( ! ecommerce ) { return null; } return ( <RevenueChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> ); case 'arpc': if ( ! ecommerce ) { return null; } return ( <ArpcChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> ); } }; const ImprovementChart: ChartFunction = ( { type } ) => { const { activeGoal, alternatives, areResultsUnique, ecommerce } = useAttributes(); switch ( type ) { case 'conversion-rate': case 'views-and-conversions': return ( <ConversionImprovementChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> ); case 'revenue': if ( ! ecommerce ) { return null; } return ( <RevenueImprovementChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> ); case 'arpc': if ( ! ecommerce ) { return null; } return ( <ArpcImprovementChart alternatives={ alternatives } goal={ activeGoal } unique={ areResultsUnique } /> ); } }; // ===== // HOOKS // ===== const useAttributes = () => { const [ areResultsUnique ] = useAreUniqueResultsVisible(); return useSelect( ( select ) => { const { getAlternativeResultsInExperiment, getConversions, getExperimentAttribute, getPageAttribute, getPageViews, getTotalPageViews, getUniquePageViews, getPluginSetting, getTotalRevenue, getWinnersInExperiment, getECommercePlugin, } = select( NAB_DATA ); const activeExperiment = getPageAttribute( 'editor/activeExperiment' ) ?? 0; const goals = getExperimentAttribute( activeExperiment, 'goals' ) ?? []; const activeGoalId = getPageAttribute( 'editor/activeGoal' ) ?? goals[ 0 ]?.id ?? ''; const pageViews = getPageViews( activeExperiment ); const conversions = getConversions( activeExperiment, activeGoalId ); const revenue = getTotalRevenue( activeExperiment, activeGoalId ); return { totalPageViews: getTotalPageViews( activeExperiment ), uniquePageViews: getUniquePageViews( activeExperiment ), conversions, conversionRate: ( conversions / pageViews ) * 100 || 0, activeGoal: activeGoalId, areResultsUnique, revenue, ecommerce: getECommercePlugin( activeExperiment, activeGoalId ), alternatives: getAlternativeResultsInExperiment( activeExperiment ), winners: getWinnersInExperiment( activeExperiment ) ?? EMPTY_OBJECT, minConfidence: getPluginSetting( 'minConfidence' ), }; }, [ areResultsUnique ] ); }; // ======= // HELPERS // ======= function getConversionRateLabel( conversionRate?: number ): JSX.Element { if ( ! conversionRate ) { return <></>; } return <>({ numberFormat( conversionRate ) }%)</>; }