/** * WordPress dependencies */ import { Button, Dashicon, PanelBody } from '@safe-wordpress/components'; import { useSelect, useDispatch } from '@safe-wordpress/data'; import { useCallback, useState } from '@safe-wordpress/element'; import { _x } from '@safe-wordpress/i18n'; /** * External dependencies */ import { noop } from 'lodash'; import { store as NAB_ACTIONS } from '@nab/conversion-actions'; import { usePluginSetting } from '@nab/data'; import { getConversionActionScopeError, isEmpty } from '@nab/utils'; import type { ConversionAction as CA, ConversionActionId, ConversionActionScope, ConversionActionTypeName, GoalId, Maybe, } from '@nab/types'; /** * Internal dependencies */ import './style.scss'; import { store as NAB_EDITOR } from '../../../store'; export type ConversionActionProps = { readonly readOnly?: boolean; readonly action: CA; readonly goalId: GoalId; }; export const ConversionAction = ( { readOnly, action, goalId, }: ConversionActionProps ): JSX.Element | null => { const experimentId = useExperimentId(); const goal = useActiveGoal(); const goalIndex = useActiveGoalIndex(); const actionType = useConversionActionType( action.type ); const scope = useActionScope( action ); const View = actionType?.view; const Edit = actionType?.edit; const Icon = actionType?.icon; const attributes = action?.attributes || {}; const validate = actionType?.validate ?? ( () => ( {} ) ); const scopeError = getConversionActionScopeError( scope ); const errors = { ...validate( attributes ), ...( scopeError ? { _scope: scopeError } : {} ), }; const hasErrors = ! isEmpty( errors ); const [ opened, onToggle ] = useState( hasErrors ); const { setAttributes, setScope } = useUpdater( goalId, action.id ); const remove = useRemove( goalId, action.id ); if ( ! goal ) { return null; } if ( readOnly ) { return (
{ !! Icon ? ( ) : ( ) }
{ !! View ? ( ) : ( { _x( 'Invalid conversion action.', 'text', 'nelio-ab-testing' ) } ) }
); } const showActualIcon = !! Icon && ( ! hasErrors || opened ); return ( { showActualIcon ? ( ) : ( ) }
{ !! View ? ( ) : ( { _x( 'Invalid conversion action.', 'text', 'nelio-ab-testing' ) } ) }
) as unknown as string } >
{ !! Edit ? ( ) : ( { _x( 'This conversion action can’t be properly loaded. Please consider removing it.', 'text', 'nelio-ab-testing' ) } ) }
); }; // ===== // HOOKS // ===== const useExperimentId = () => useSelect( ( select ) => select( NAB_EDITOR ).getExperimentId(), [] ); const useActiveGoal = () => useSelect( ( select ) => select( NAB_EDITOR ).getActiveGoal(), [] ); const useActiveGoalIndex = () => useSelect( ( select ) => select( NAB_EDITOR ).getActiveGoalIndex(), [] ); const useConversionActionType = ( type: ConversionActionTypeName ) => useSelect( ( select ) => select( NAB_ACTIONS ).getConversionActionType( type ), [ type ] ); const useActionScope = ( action: CA ): ConversionActionScope => { const goalTracking = usePluginSetting( 'goalTracking' ); if ( 'custom' !== goalTracking ) { return { type: goalTracking }; } if ( action?.scope ) { return action.scope; } return { type: 'test-scope' }; }; const useUpdater = ( goalId: GoalId, actionId: ConversionActionId ) => { const { updateConversionAction } = useDispatch( NAB_EDITOR ); const update = useCallback( ( attributes: Maybe< Partial< CA[ 'attributes' ] > >, scope: Maybe< ConversionActionScope > ) => updateConversionAction( goalId, actionId, attributes, scope ), [ goalId, actionId, updateConversionAction ] ); const setAttributes = useCallback( ( attributes: Partial< CA[ 'attributes' ] > ) => update( attributes, undefined ), [ update ] ); const setScope = useCallback( ( scope: ConversionActionScope ) => update( undefined, scope ), [ update ] ); return { setAttributes: goalId && actionId ? setAttributes : noop, setScope: goalId && actionId ? setScope : noop, }; }; const useRemove = ( goalId: GoalId, actionId: ConversionActionId ) => { const { removeConversionActionsFromGoal } = useDispatch( NAB_EDITOR ); return goalId && actionId ? () => removeConversionActionsFromGoal( goalId, actionId ) : noop; };