/** * WordPress dependencies */ import { useDispatch, useSelect } from '@safe-wordpress/data'; import { useCallback, useEffect } from '@safe-wordpress/element'; import { _x } from '@safe-wordpress/i18n'; /** * External dependencies */ import { keyBy, values } from 'lodash'; import { store as NAB_DATA } from '@nab/data'; import { isInTheFuture } from '@nab/date'; import { store as NAB_EDITOR } from '@nab/editor'; import { store as NAB_SEGMENTS } from '@nab/segmentation-rules'; import { getLocalUrlError, isEmpty } from '@nab/utils'; import type { Experiment, Maybe, SegmentationRule } from '@nab/types'; export const StatusManager = (): null => { const attributes = useAttributes(); const updateStatus = useStatusUpdater(); const paused = attributes.status.includes( 'paused' ); const getConditionError = useGetConditionError(); useEffect( () => { const homeUrl = attributes.homeUrl; const mode = attributes.mode; const name = attributes.name; const participationConditions = attributes.participationConditions; const postId = attributes.postId; const postType = attributes.postType; const startDate = attributes.startDate; const status = attributes.status; const url = attributes.url; if ( 'running' === status ) { return; } if ( isEmpty( name ) ) { updateStatus( paused ? 'paused_draft' : 'draft', _x( 'Please name your Heatmap test', 'user', 'nelio-ab-testing' ) ); return; } if ( 'post' === mode && ! postId ) { switch ( postType ) { case 'product': updateStatus( paused ? 'paused_draft' : 'draft', _x( 'Please select the product to track', 'user', 'nelio-ab-testing' ) ); return; case 'post': updateStatus( paused ? 'paused_draft' : 'draft', _x( 'Please select the post to track', 'user', 'nelio-ab-testing' ) ); return; case 'page': updateStatus( paused ? 'paused_draft' : 'draft', _x( 'Please select the page to track', 'user', 'nelio-ab-testing' ) ); return; default: updateStatus( paused ? 'paused_draft' : 'draft', _x( 'Please select a page to track', 'user', 'nelio-ab-testing' ) ); return; } } if ( 'url' === mode && getLocalUrlError( url, homeUrl ) ) { updateStatus( paused ? 'paused_draft' : 'draft', getLocalUrlError( url, homeUrl ) || '' ); return; } const participationError = participationConditions.reduce( ( error, condition ) => error ? error : getConditionError( condition ), undefined as Maybe< string > ); if ( participationError ) { updateStatus( paused ? 'paused_draft' : 'draft', participationError ); return; } if ( 'scheduled' === status && startDate && ! isInTheFuture( startDate ) ) { updateStatus( paused ? 'paused' : 'ready' ); return; } if ( 'scheduled' !== status ) { updateStatus( paused ? 'paused' : 'ready' ); } }, [ attributes.homeUrl, attributes.mode, attributes.name, attributes.participationConditions, attributes.postId, attributes.postType, attributes.startDate, attributes.status, attributes.url, paused, updateStatus, getConditionError, ] ); return null; }; // ===== // HOOKS // ===== const useAttributes = () => useSelect( ( select ) => { const { getPluginSetting } = select( NAB_DATA ); const { getExperimentAttribute, getHeatmapAttribute } = select( NAB_EDITOR ); return { homeUrl: getPluginSetting( 'homeUrl' ), mode: getHeatmapAttribute( 'trackingMode' ) ?? 'post', name: getExperimentAttribute( 'name' ), participationConditions: getHeatmapAttribute( 'participationConditions' ) ?? [], postId: getHeatmapAttribute( 'trackedPostId' ) ?? 0, postType: getHeatmapAttribute( 'trackedPostType' ) ?? 'page', startDate: getExperimentAttribute( 'startDate' ), status: getExperimentAttribute( 'status' ), url: getHeatmapAttribute( 'trackedUrl' ) ?? '', }; }, [] ); const useStatusUpdater = () => { const { setDraftStatusRationale, setExperimentData } = useDispatch( NAB_EDITOR ); return useCallback( ( status: Experiment[ 'status' ], rationale?: string ) => { void setExperimentData( { status } ); void setDraftStatusRationale( rationale ?? '' ); }, [ setExperimentData, setDraftStatusRationale ] ); }; const useGetConditionError = () => { const segmentationRuleTypes = useSelect( ( select ) => keyBy( select( NAB_SEGMENTS ).getSegmentationRuleTypes() || [], 'name' ), [] ); return useCallback( ( condition: SegmentationRule ): string => { const errors = segmentationRuleTypes[ condition.type ]?.validate?.( condition.attributes ) ?? {}; return values( errors )[ 0 ] ?? ''; }, [ segmentationRuleTypes ] ); };