/** * WordPress dependencies */ import { Dashicon, SelectControl } from '@safe-wordpress/components'; import { useDispatch, useSelect } from '@safe-wordpress/data'; import { createInterpolateElement } from '@safe-wordpress/element'; import { _x, sprintf } from '@safe-wordpress/i18n'; /** * External dependencies */ import { findIndex } from 'lodash'; import { v4 as uuid } from 'uuid'; import { usePluginSetting } from '@nab/data'; import { createSegment } from '@nab/utils'; import type { Dict, Segment as RealSegment, SegmentationRule, SegmentId, } from '@nab/types'; type Segment = Omit< RealSegment, 'segmentationRules' >; /** * Internal dependencies */ import './style.scss'; import { useExperimentAttribute } from '../hooks'; import { Segment as SegmentView } from '../segment'; import { SegmentList } from '../segment-list'; import { ReusableSegmentDialog } from '../reusable-segment-dialog'; import { store as NAB_EDITOR } from '../../store'; export const SegmentationSection = (): JSX.Element => { const [ segment, updateActiveSegment ] = useActiveSegment(); const [ status ] = useExperimentAttribute( 'status' ); const [ segmentEvaluation, setSegmentEvaluation ] = useSegmentEvaluation(); const globalSegmentEvaluation = usePluginSetting( 'segmentEvaluation' ); const isExperimentPaused = ( status || '' ).includes( 'paused' ); const removeActiveSegment = useActiveSegmentRemover(); const canSegmentBeRemoved = ! isExperimentPaused; const canSegmentBeDuplicated = ! isExperimentPaused; const rules = useSegmentRules( segment?.id ); const { addSegments, addSegmentationRulesIntoSegment } = useDispatch( NAB_EDITOR ); const duplicateSegment = () => { if ( ! segment ) { return; } const newSegment = { ...createSegment(), attributes: { ...segment.attributes, name: segment.attributes.name ? sprintf( /* translators: %s: Name of an item being duplicated. */ _x( 'Copy of %s', 'text', 'nelio-ab-testing' ), segment.attributes.name ) : '', }, }; const newRules: ReadonlyArray< SegmentationRule > = rules.map( ( r ) => ( { ...r, id: uuid(), } ) ); void addSegments( newSegment ); void addSegmentationRulesIntoSegment( newSegment.id, newRules ); }; return (

{ createInterpolateElement( sprintf( /* translators: %s: Dashicon. */ _x( '%s Segmentation', 'text', 'nelio-ab-testing' ), '' ), { icon: ( ), } ) } { 'custom' === globalSegmentEvaluation && (
) }

{ segment ? ( updateActiveSegment( { ...segment.attributes, name, } ) } duplicateSegment={ canSegmentBeDuplicated ? duplicateSegment : undefined } removeSegment={ canSegmentBeRemoved ? removeActiveSegment : undefined } /> ) : (

{ _x( 'Add a new segment to narrow your tested audience and target only a subset of your visitors.', 'user', 'nelio-ab-testing' ) }

) }
); }; // ===== // HOOKS // ===== const useActiveSegment = () => { const segment = useSelect( ( select ) => select( NAB_EDITOR ).getActiveSegment(), [] ); const { updateSegment: doUpdate } = useDispatch( NAB_EDITOR ); const updateSegment = ( attributes: Dict ) => { if ( segment?.id ) { void doUpdate( segment.id, attributes ); } }; return [ segment, updateSegment ] as const; }; const useActiveSegmentRemover = () => { const [ segment ] = useActiveSegment(); const segments = useSelect( ( select ) => select( NAB_EDITOR ).getSegments(), [] ); const { removeSegments, setActiveSegment } = useDispatch( NAB_EDITOR ); if ( ! segment ) { return () => null; } return () => { const nextSegment = getAdjacentSegment( segments, segment.id ); if ( nextSegment ) { void setActiveSegment( nextSegment.id ); } void removeSegments( segment.id ); }; }; const useSegmentEvaluation = () => { const segmentEvaluation = useSelect( ( select ) => select( NAB_EDITOR ).getSegmentEvaluation(), [] ); const { setSegmentEvaluation } = useDispatch( NAB_EDITOR ); return [ segmentEvaluation, setSegmentEvaluation ] as const; }; const useSegmentRules = ( id: SegmentId | undefined ) => useSelect( ( select ) => id ? select( NAB_EDITOR ).getSegmentationRules( id ) : [], [ id ] ); // ======= // HELPERS // ======= function getAdjacentSegment( segments: ReadonlyArray< Segment >, segmentId: SegmentId ): Segment | false { const index = findIndex( segments, { id: segmentId } ); return segments[ index + 1 ] || segments[ index - 1 ] || false; }