/** * WordPress dependencies */ import { Button, Modal } from '@safe-wordpress/components'; import { useDispatch, useSelect } from '@safe-wordpress/data'; import { useEffect, useState } from '@wordpress/element'; import { _x, sprintf } from '@safe-wordpress/i18n'; /** * External dependencies */ import clsx from 'clsx'; import { last } from 'lodash'; import { store as NAB_DATA } from '@nab/data'; import type { ExperimentSuggestion, Maybe, Opportunity } from '@nab/types'; /** * Internal dependencies */ import './style.scss'; import { AiIcon } from '../ai-icon'; import { CreateExperimentFromOpportunityButton } from '../create-experiment-from-opportunity-button'; import { LoadingAnimation } from '../loading-animation'; import { ExperimentSuggestionView } from '../experiment-suggestion-view'; export type ExperimentSuggestionsModalType = { readonly isFollowUp?: boolean; }; export function ExperimentSuggestionsModal( props: ExperimentSuggestionsModalType ): JSX.Element | null { const { opportunityBeingInstantiated, state } = useSelect( ( select ) => ( { opportunityBeingInstantiated: select( NAB_DATA ).getOpportunityBeingInstantiated(), state: select( NAB_DATA ).getSuggestionGenerationState(), } ), [] ); const { setSuggestionGenerationState } = useDispatch( NAB_DATA ); if ( 'idle' === state.status ) { return null; } const isLoading = 'opportunity-scan-ready' !== state.status; const isDismissible = ! isLoading && ! opportunityBeingInstantiated; return ( { ' ' } { _x( 'Test Suggestions', 'text', 'nelio-ab-testing' ) }{ ' ' }
{ _x( 'AI-powered ideas to improve your site', 'text', 'nelio-ab-testing' ) }
) as unknown as string } isDismissible={ isDismissible } shouldCloseOnEsc={ isDismissible } shouldCloseOnClickOutside={ isDismissible } onRequestClose={ () => isDismissible ? setSuggestionGenerationState( { status: 'idle' } ) : void null } > { state.status === 'opportunity-scan-ready' ? ( ) : ( ) }
); } // ============ // HELPER VIEWS // ============ const MESSAGE_DURATION_IN_MS = 2500; const SCANNING_MESSAGES = [ _x( 'Analyzing this opportunity…', 'text', 'nelio-ab-testing' ), _x( 'Exploring possibilities…', 'text', 'nelio-ab-testing' ), _x( 'Crafting test ideas…', 'text', 'nelio-ab-testing' ), _x( 'Almost there…', 'text', 'nelio-ab-testing' ), ] as const; const FOLLOWUP_SCANNING_MESSAGES = [ _x( 'Analyzing this test…', 'text', 'nelio-ab-testing' ), _x( 'Exploring possibilities…', 'text', 'nelio-ab-testing' ), _x( 'Crafting follow-up tests…', 'text', 'nelio-ab-testing' ), _x( 'Almost there…', 'text', 'nelio-ab-testing' ), ]; function ScanningBody( { isFollowUp, }: ExperimentSuggestionsModalType ): JSX.Element { const [ messageIndex, setMessageIndex ] = useState( 0 ); useEffect( () => { setTimeout( () => { setMessageIndex( messageIndex + 1 ); }, MESSAGE_DURATION_IN_MS ); }, [ messageIndex ] ); const messages = isFollowUp ? FOLLOWUP_SCANNING_MESSAGES : SCANNING_MESSAGES; return (
{ messages[ messageIndex ] ?? last( messages ) }
); } function SuggestionsBody( { opportunity, suggestions, instantiatingSuggestion, isFollowUp, }: { readonly opportunity: Opportunity; readonly suggestions: ReadonlyArray< ExperimentSuggestion >; readonly instantiatingSuggestion: Maybe< ExperimentSuggestion >; } & ExperimentSuggestionsModalType ) { const { setSuggestionGenerationState } = useDispatch( NAB_DATA ); const opportunityBeingInstantiated = useSelect( ( select ) => select( NAB_DATA ).getOpportunityBeingInstantiated(), [] ); const { instantiateSuggestionAsExperiment } = useDispatch( NAB_DATA ); return (
{ suggestions.length ? (
{ ' ' } { isFollowUp ? sprintf( /* translators: %d: Number of suggestions. */ _x( '%d ideas found as follow-up', 'text', 'nelio-ab-testing' ), suggestions.length ) : sprintf( /* translators: %d: Number of suggestions. */ _x( '%d ideas found for this opportunity', 'text', 'nelio-ab-testing' ), suggestions.length ) }
) : (
{ isFollowUp ? sprintf( /* translators: %s: Nelio AI. */ _x( '%s couldn’t come up with a suitable suggestion for this page.', 'text', 'nelio-ab-testing' ), 'Nelio AI' ) + ' ' + _x( 'Start with a blank test and choose what you’d like to optimize.', 'user', 'nelio-ab-testing' ) : sprintf( /* translators: %s: Nelio AI. */ _x( '%s couldn’t find a suitable follow-up for this test.', 'text', 'nelio-ab-testing' ), 'Nelio AI' ) + ' ' + _x( 'Explore more opportunities in the Overview screen or create a new test from scratch.', 'user', 'nelio-ab-testing' ) }
) } { suggestions.map( ( suggestion, index ) => ( instantiateSuggestionAsExperiment( suggestion ) } /> ) ) } { isFollowUp && ! suggestions.length && (
) } { ! isFollowUp && (
{ !! suggestions.length && ( <>
{ _x( 'These are AI-generated suggestions', 'text', 'nelio-ab-testing' ) }
{ _x( 'Review each idea and create the test that make the most sense for you.', 'user', 'nelio-ab-testing' ) }
) }
) }
); }