/** * WordPress dependencies */ import { Button, Dashicon } 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 { ReusableEntitiesDialog } from '@nab/components'; import { store as NAB_DATA } from '@nab/data'; import { store as NAB_EDITOR } from '@nab/editor'; import { createSegmentationRule as createSegmentationRuleWithDefaults } from '@nab/segmentation-rules'; import { EMPTY_ARRAY, isDefined } from '@nab/utils'; import type { Dict, ReusableSegment, SegmentationRuleId, SegmentationRuleTypeName, } from '@nab/types'; /** * Internal dependencies */ import './style.scss'; import { SegmentationRule } from '../../../editor/components/segment/segmentation-rule'; import { SegmentationRuleTypeCategoryList } from '../../../editor/components/segment/segmentation-rule-type-category-list'; export const ParticipationSection = (): JSX.Element => (
<ParticipationConditions /> <ReusableParticipationDialog /> </div> ); // ============ // HELPER VIEWS // ============ const Title = () => ( <h2> { createInterpolateElement( sprintf( /* translators: %s: Dashicon. */ _x( '%s Participation', 'text', 'nelio-ab-testing' ), '<icon></icon>' ), { icon: ( <Dashicon className="nab-participation-section__title-icon" icon="groups" /> ), } ) } </h2> ); const ParticipationConditions = () => { const { conditions, createCondition, updateCondition, removeCondition } = useParticipationConditions(); const { openReusableDialog } = useDispatch( NAB_EDITOR ); const isImportEnabled = useIsImportEnabled(); return ( <div className="nab-edit-experiment-participation-section__content"> <p> { ! conditions.length ? _x( 'All visitors will contribute to this heatmap. If you want to limit participation to a subset of visitors, use the actions below:', 'text', 'nelio-ab-testing' ) : _x( 'Only visitors who meet all the following criteria will contribute to this heatmap:', 'text', 'nelio-ab-testing' ) } </p> <div className="nab-edit-experiment-participation-section__conditions"> { conditions.map( ( condition ) => ( <SegmentationRule key={ condition.id } rule={ condition } setAttributes={ ( attrs ) => updateCondition( condition.id, attrs ) } remove={ () => removeCondition( condition.id ) } /> ) ) } </div> <div className="nab-edit-experiment-participation-section__actions"> <SegmentationRuleTypeCategoryList segmentationRules={ conditions } createSegmentationRule={ createCondition } /> { isImportEnabled && ! conditions.length && ( <Button variant="secondary" onClick={ () => openReusableDialog( 'manage-segments' ) } > { _x( 'Import Segment', 'command', 'nelio-ab-testing' ) } </Button> ) } </div> </div> ); }; function ReusableParticipationDialog(): JSX.Element | null { const dialog = useReusableDialog(); const { closeReusableDialog: onClose } = useDispatch( NAB_EDITOR ); const { setHeatmapData } = useDispatch( NAB_EDITOR ); const { isSaving, reusableSegments } = useSelect( ( select ) => ( { isSaving: select( NAB_DATA ).isSavingReusableEntities(), reusableSegments: select( NAB_DATA ).getReusableSegments(), } ), [] ); if ( 'manage-segments' !== dialog ) { return null; } return ( <ReusableEntitiesDialog< ReusableSegment > mode="manage" isSingular labels={ { title: _x( 'Reusable Segments', 'text', 'nelio-ab-testing' ), replace: _x( 'Import Segment', 'text', 'nelio-ab-testing' ), selectSingle: _x( 'Select a segment to import:', 'user', 'nelio-ab-testing' ), } } items={ reusableSegments } saving={ isSaving } onReplace={ ( selection ) => { if ( ! selection[ 0 ] ) { return; } void setHeatmapData( { participationConditions: selection[ 0 ].segmentationRules .map( ( sr ) => { const newRule = createSegmentationRuleWithDefaults( sr.type ); if ( ! newRule ) { return undefined; } return { ...sr, id: newRule.id }; } ) .filter( isDefined ), } ); void onClose(); } } onClose={ onClose } /> ); } // ===== // HOOKS // ===== const useParticipationConditions = () => { const conditions = useSelect( ( select ) => select( NAB_EDITOR ).getHeatmapAttribute( 'participationConditions' ) ?? EMPTY_ARRAY, [] ); const { setHeatmapData } = useDispatch( NAB_EDITOR ); return { conditions, createCondition: ( type: SegmentationRuleTypeName ) => { const rule = createSegmentationRuleWithDefaults( type ); if ( rule ) { void setHeatmapData( { participationConditions: [ ...conditions, rule ], } ); } }, updateCondition: ( id: SegmentationRuleId, attrs: Dict ) => void setHeatmapData( { participationConditions: conditions.map( ( c ) => c.id === id ? { ...c, attributes: { ...c.attributes, ...attrs } } : c ), } ), removeCondition: ( id: SegmentationRuleId ) => void setHeatmapData( { participationConditions: conditions.filter( ( c ) => c.id !== id ), } ), }; }; const useReusableDialog = () => useSelect( ( select ) => select( NAB_EDITOR ).getReusableDialog(), [] ); const useIsImportEnabled = (): boolean => useSelect( ( select ) => !! select( NAB_DATA ).getReusableGoals().length, [] );