/**
* 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;
};