2023-08-29 06:00:54 +00:00
/ * *
* External dependencies
* /
2023-09-06 06:21:09 +00:00
import { createMachine , sendParent } from 'xstate' ;
2023-09-12 06:32:50 +00:00
import { getQuery } from '@woocommerce/navigation' ;
2023-08-29 06:00:54 +00:00
/ * *
* Internal dependencies
* /
import {
designWithAiStateMachineContext ,
designWithAiStateMachineEvents ,
2023-09-20 04:26:15 +00:00
FontPairing ,
2023-09-21 01:49:00 +00:00
Header ,
Footer ,
2023-09-20 07:27:08 +00:00
ColorPaletteResponse ,
2023-08-29 06:00:54 +00:00
} from './types' ;
import {
BusinessInfoDescription ,
LookAndFeel ,
ToneOfVoice ,
ApiCallLoader ,
} from './pages' ;
import { actions } from './actions' ;
2023-09-06 06:21:09 +00:00
import { services } from './services' ;
2023-09-21 01:49:00 +00:00
import {
defaultColorPalette ,
fontPairings ,
defaultHeader ,
defaultFooter ,
} from './prompts' ;
2023-09-12 06:32:50 +00:00
export const hasStepInUrl = (
_ctx : unknown ,
_evt : unknown ,
{ cond } : { cond : unknown }
) = > {
const { path = '' } = getQuery ( ) as { path : string } ;
const pathFragments = path . split ( '/' ) ;
return (
pathFragments [ 3 ] === // [0] '', [1] 'customize-store', [2] cys step slug [3] design-with-ai step slug
( cond as { step : string | undefined } ) . step
) ;
} ;
2023-08-29 06:00:54 +00:00
export const designWithAiStateMachineDefinition = createMachine (
{
id : 'designWithAi' ,
predictableActionArguments : true ,
preserveActionOrder : true ,
schema : {
context : { } as designWithAiStateMachineContext ,
events : { } as designWithAiStateMachineEvents ,
} ,
2023-09-12 06:32:50 +00:00
invoke : {
src : 'browserPopstateHandler' ,
} ,
on : {
EXTERNAL_URL_UPDATE : {
target : 'navigate' ,
} ,
AI_WIZARD_CLOSED_BEFORE_COMPLETION : {
2023-09-15 04:48:12 +00:00
actions : [
sendParent ( ( _context , event ) = > event ) ,
'recordTracksStepClosed' ,
] ,
2023-09-12 06:32:50 +00:00
} ,
} ,
2023-08-29 06:00:54 +00:00
context : {
businessInfoDescription : {
descriptionText : '' ,
} ,
lookAndFeel : {
choice : '' ,
} ,
toneOfVoice : {
choice : '' ,
} ,
2023-09-19 08:41:52 +00:00
aiSuggestions : {
2023-09-20 07:27:08 +00:00
defaultColorPalette : { } as ColorPaletteResponse ,
2023-09-20 04:26:15 +00:00
fontPairing : '' as FontPairing [ 'pair_name' ] ,
2023-09-21 01:49:00 +00:00
header : '' as Header [ 'slug' ] ,
footer : '' as Footer [ 'slug' ] ,
2023-09-19 08:41:52 +00:00
} ,
2023-08-29 06:00:54 +00:00
} ,
2023-09-12 06:32:50 +00:00
initial : 'navigate' ,
2023-08-29 06:00:54 +00:00
states : {
2023-09-12 06:32:50 +00:00
navigate : {
always : [
{
target : 'businessInfoDescription' ,
cond : {
type : 'hasStepInUrl' ,
step : 'business-info-description' ,
} ,
} ,
{
target : 'lookAndFeel' ,
cond : {
type : 'hasStepInUrl' ,
step : 'look-and-feel' ,
} ,
} ,
{
target : 'toneOfVoice' ,
cond : {
type : 'hasStepInUrl' ,
step : 'tone-of-voice' ,
} ,
} ,
{
target : 'apiCallLoader' ,
cond : {
type : 'hasStepInUrl' ,
step : 'api-call-loader' ,
} ,
} ,
{
target : 'businessInfoDescription' ,
} ,
] ,
} ,
2023-08-29 06:00:54 +00:00
businessInfoDescription : {
id : 'businessInfoDescription' ,
initial : 'preBusinessInfoDescription' ,
states : {
preBusinessInfoDescription : {
// if we need to prefetch options, other settings previously populated from core profiler, do it here
always : {
target : 'businessInfoDescription' ,
} ,
} ,
businessInfoDescription : {
meta : {
component : BusinessInfoDescription ,
} ,
2023-09-15 04:48:12 +00:00
entry : [
{
type : 'recordTracksStepViewed' ,
step : 'business_info_description' ,
} ,
] ,
2023-08-29 06:00:54 +00:00
on : {
BUSINESS_INFO_DESCRIPTION_COMPLETE : {
2023-09-22 12:43:42 +00:00
actions : [
'assignBusinessInfoDescription' ,
'spawnSaveDescriptionToOption' ,
] ,
2023-08-29 06:00:54 +00:00
target : 'postBusinessInfoDescription' ,
} ,
} ,
} ,
postBusinessInfoDescription : {
2023-09-06 06:21:09 +00:00
invoke : {
src : 'getLookAndTone' ,
onError : {
2023-09-15 04:48:12 +00:00
actions : [
{
type : 'recordTracksStepCompleted' ,
step : 'business_info_description' ,
} ,
'logAIAPIRequestError' ,
] ,
2023-09-06 06:21:09 +00:00
target : '#lookAndFeel' ,
} ,
onDone : {
2023-09-15 04:48:12 +00:00
actions : [
{
type : 'recordTracksStepCompleted' ,
step : 'business_info_description' ,
} ,
'assignLookAndTone' ,
] ,
2023-09-06 06:21:09 +00:00
target : '#lookAndFeel' ,
} ,
2023-08-29 06:00:54 +00:00
} ,
} ,
} ,
} ,
lookAndFeel : {
id : 'lookAndFeel' ,
initial : 'preLookAndFeel' ,
states : {
preLookAndFeel : {
always : {
target : 'lookAndFeel' ,
} ,
} ,
lookAndFeel : {
meta : {
component : LookAndFeel ,
} ,
2023-09-12 06:32:50 +00:00
entry : [
{
type : 'updateQueryStep' ,
step : 'look-and-feel' ,
} ,
2023-09-15 04:48:12 +00:00
{
type : 'recordTracksStepViewed' ,
step : 'look_and_feel' ,
} ,
2023-09-12 06:32:50 +00:00
] ,
2023-08-29 06:00:54 +00:00
on : {
LOOK_AND_FEEL_COMPLETE : {
2023-09-15 04:48:12 +00:00
actions : [
{
type : 'recordTracksStepCompleted' ,
step : 'look_and_feel' ,
} ,
'assignLookAndFeel' ,
] ,
2023-08-29 06:00:54 +00:00
target : 'postLookAndFeel' ,
} ,
} ,
} ,
postLookAndFeel : {
always : {
target : '#toneOfVoice' ,
} ,
} ,
} ,
} ,
toneOfVoice : {
id : 'toneOfVoice' ,
initial : 'preToneOfVoice' ,
states : {
preToneOfVoice : {
always : {
target : 'toneOfVoice' ,
} ,
} ,
toneOfVoice : {
meta : {
component : ToneOfVoice ,
} ,
2023-09-12 06:32:50 +00:00
entry : [
{
type : 'updateQueryStep' ,
step : 'tone-of-voice' ,
} ,
2023-09-15 04:48:12 +00:00
{
type : 'recordTracksStepViewed' ,
step : 'tone_of_voice' ,
} ,
2023-09-12 06:32:50 +00:00
] ,
2023-08-29 06:00:54 +00:00
on : {
TONE_OF_VOICE_COMPLETE : {
2023-09-15 04:48:12 +00:00
actions : [
'assignToneOfVoice' ,
{
type : 'recordTracksStepCompleted' ,
step : 'tone_of_voice' ,
} ,
] ,
2023-08-29 06:00:54 +00:00
target : 'postToneOfVoice' ,
} ,
} ,
} ,
postToneOfVoice : {
always : {
target : '#apiCallLoader' ,
} ,
} ,
} ,
} ,
apiCallLoader : {
id : 'apiCallLoader' ,
initial : 'preApiCallLoader' ,
states : {
preApiCallLoader : {
always : {
target : 'apiCallLoader' ,
} ,
} ,
apiCallLoader : {
meta : {
component : ApiCallLoader ,
} ,
2023-09-12 06:32:50 +00:00
entry : [
{
type : 'updateQueryStep' ,
step : 'api-call-loader' ,
} ,
] ,
2023-09-19 08:41:52 +00:00
type : 'parallel' ,
states : {
chooseColorPairing : {
2023-09-21 05:30:57 +00:00
initial : 'pending' ,
states : {
pending : {
invoke : {
src : 'queryAiEndpoint' ,
data : ( context ) = > {
return {
. . . defaultColorPalette ,
prompt : defaultColorPalette.prompt (
context
. businessInfoDescription
. descriptionText ,
context . lookAndFeel
. choice ,
context . toneOfVoice
. choice
) ,
} ;
} ,
onDone : {
actions : [
'assignDefaultColorPalette' ,
] ,
target : 'success' ,
} ,
} ,
2023-09-19 08:41:52 +00:00
} ,
2023-09-21 05:30:57 +00:00
success : { type : 'final' } ,
2023-09-19 08:41:52 +00:00
} ,
} ,
2023-09-20 04:26:15 +00:00
chooseFontPairing : {
2023-09-21 05:30:57 +00:00
initial : 'pending' ,
states : {
pending : {
invoke : {
src : 'queryAiEndpoint' ,
data : ( context ) = > {
return {
. . . fontPairings ,
prompt : fontPairings.prompt (
context
. businessInfoDescription
. descriptionText ,
context . lookAndFeel
. choice ,
context . toneOfVoice
. choice
) ,
} ;
} ,
onDone : {
actions : [
'assignFontPairing' ,
] ,
target : 'success' ,
} ,
} ,
2023-09-20 04:26:15 +00:00
} ,
2023-09-21 05:30:57 +00:00
success : { type : 'final' } ,
2023-09-20 04:26:15 +00:00
} ,
} ,
2023-09-21 01:49:00 +00:00
chooseHeader : {
2023-09-21 05:30:57 +00:00
initial : 'pending' ,
states : {
pending : {
invoke : {
src : 'queryAiEndpoint' ,
data : ( context ) = > {
return {
. . . defaultHeader ,
prompt : defaultHeader.prompt (
context
. businessInfoDescription
. descriptionText ,
context . lookAndFeel
. choice ,
context . toneOfVoice
. choice
) ,
} ;
} ,
onDone : {
actions : [ 'assignHeader' ] ,
target : 'success' ,
} ,
} ,
2023-09-21 01:49:00 +00:00
} ,
2023-09-21 05:30:57 +00:00
success : { type : 'final' } ,
2023-09-21 01:49:00 +00:00
} ,
} ,
chooseFooter : {
2023-09-21 05:30:57 +00:00
initial : 'pending' ,
states : {
pending : {
invoke : {
src : 'queryAiEndpoint' ,
data : ( context ) = > {
return {
. . . defaultFooter ,
prompt : defaultFooter.prompt (
context
. businessInfoDescription
. descriptionText ,
context . lookAndFeel
. choice ,
context . toneOfVoice
. choice
) ,
} ;
} ,
onDone : {
actions : [ 'assignFooter' ] ,
target : 'success' ,
} ,
} ,
2023-09-21 01:49:00 +00:00
} ,
2023-09-21 05:30:57 +00:00
success : { type : 'final' } ,
} ,
} ,
updateStorePatterns : {
initial : 'pending' ,
states : {
pending : {
invoke : {
src : 'updateStorePatterns' ,
onDone : {
target : 'success' ,
} ,
onError : {
// TODO: handle error
target : 'success' ,
} ,
} ,
2023-09-21 01:49:00 +00:00
} ,
2023-09-21 05:30:57 +00:00
success : { type : 'final' } ,
2023-09-21 01:49:00 +00:00
} ,
} ,
2023-09-19 08:41:52 +00:00
} ,
2023-09-21 05:30:57 +00:00
onDone : 'postApiCallLoader' ,
} ,
postApiCallLoader : {
2023-09-22 12:43:42 +00:00
type : 'parallel' ,
states : {
assembleSite : {
initial : 'pending' ,
states : {
pending : {
invoke : {
src : 'assembleSite' ,
onDone : {
target : 'done' ,
} ,
onError : {
target : 'failed' ,
} ,
} ,
} ,
done : {
type : 'final' ,
} ,
failed : {
type : 'final' , // If there's an error we should not block the user from proceeding. They'll just not see the AI suggestions, but that's better than being stuck
} ,
} ,
} ,
saveAiResponse : {
initial : 'pending' ,
states : {
pending : {
invoke : {
src : 'saveAiResponseToOption' ,
onDone : {
target : 'done' ,
} ,
onError : {
target : 'failed' ,
} ,
} ,
} ,
done : {
type : 'final' ,
} ,
failed : {
type : 'final' ,
} ,
} ,
2023-09-21 05:30:57 +00:00
} ,
} ,
2023-09-22 12:43:42 +00:00
onDone : {
actions : [
sendParent ( ( ) = > ( {
type : 'THEME_SUGGESTED' ,
} ) ) ,
] ,
} ,
2023-08-29 06:00:54 +00:00
} ,
} ,
} ,
} ,
} ,
{
actions ,
2023-09-06 06:21:09 +00:00
services ,
2023-09-12 06:32:50 +00:00
guards : {
hasStepInUrl ,
} ,
2023-08-29 06:00:54 +00:00
}
) ;