/**
* 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 && (
setSuggestionGenerationState( { status: 'idle' } )
}
>
{ _x( 'Close', 'command', 'nelio-ab-testing' ) }
) }
{ ! 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'
) }
>
) }
) }
);
}