2023-09-20 06:36:20 +00:00
/ * *
* External dependencies
* /
2023-10-04 12:24:51 +00:00
import { useState } from '@wordpress/element' ;
2024-06-05 14:54:29 +00:00
import { useSelect } from '@wordpress/data' ;
2023-09-20 06:36:20 +00:00
import { __ } from '@wordpress/i18n' ;
import { chevronLeft } from '@wordpress/icons' ;
2024-01-25 11:04:44 +00:00
import interpolateComponents from '@automattic/interpolate-components' ;
2024-06-05 14:54:29 +00:00
import { getNewPath } from '@woocommerce/navigation' ;
import { Sender } from 'xstate' ;
2023-09-30 00:17:36 +00:00
import {
2024-01-25 11:04:44 +00:00
Notice ,
2023-09-30 00:17:36 +00:00
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore No types for this exist yet.
__unstableMotion as motion ,
} from '@wordpress/components' ;
2023-09-20 06:36:20 +00:00
2023-08-18 05:30:25 +00:00
/ * *
* Internal dependencies
* /
2024-01-09 14:49:59 +00:00
import { CustomizeStoreComponent , FlowType } from '../types' ;
2023-09-30 00:17:36 +00:00
import { SiteHub } from '../assembler-hub/site-hub' ;
import { ThemeCard } from './theme-card' ;
2023-10-09 05:19:08 +00:00
import {
DesignChangeWarningModal ,
StartNewDesignWarningModal ,
StartOverWarningModal ,
2024-07-24 16:09:12 +00:00
ThemeSwitchWarningModal ,
2023-10-09 05:19:08 +00:00
} from './warning-modals' ;
2023-10-03 01:56:13 +00:00
import { useNetworkStatus } from '~/utils/react-hooks/use-network-status' ;
2023-10-04 12:24:51 +00:00
import './intro.scss' ;
import {
NetworkOfflineBanner ,
ThemeHasModsBanner ,
JetpackOfflineBanner ,
DefaultBanner ,
2023-10-09 05:19:08 +00:00
ExistingAiThemeBanner ,
ExistingThemeBanner ,
2024-01-11 14:32:16 +00:00
NoAIBanner ,
2024-01-18 13:59:10 +00:00
ExistingNoAiThemeBanner ,
2024-07-24 10:07:42 +00:00
ClassicThemeBanner ,
2024-07-24 16:09:12 +00:00
NonDefaultBlockThemeBanner ,
2023-10-04 12:24:51 +00:00
} from './intro-banners' ;
2024-06-05 14:54:29 +00:00
import welcomeTourImg from '../assets/images/design-your-own.svg' ;
import professionalThemeImg from '../assets/images/professional-theme.svg' ;
import { navigateOrParent } from '~/customize-store/utils' ;
import { RecommendThemesAPIResponse } from '~/customize-store/types' ;
import { customizeStoreStateMachineEvents } from '~/customize-store' ;
import { trackEvent } from '~/customize-store/tracking' ;
2024-07-24 16:09:12 +00:00
import { isNoAIFlow as isNoAiFlowGuard } from '../guards' ;
2023-09-20 06:36:20 +00:00
2023-08-18 05:30:25 +00:00
export type events =
| { type : 'DESIGN_WITH_AI' }
2023-09-30 00:17:36 +00:00
| { type : 'JETPACK_OFFLINE_HOWTO' }
2023-08-18 05:30:25 +00:00
| { type : 'CLICKED_ON_BREADCRUMB' }
| { type : 'SELECTED_BROWSE_ALL_THEMES' }
2023-09-29 07:44:22 +00:00
| { type : 'SELECTED_ACTIVE_THEME' ; payload : { theme : string } }
2024-01-11 14:32:16 +00:00
| { type : 'SELECTED_NEW_THEME' ; payload : { theme : string } }
| { type : 'DESIGN_WITHOUT_AI' } ;
2023-08-18 05:30:25 +00:00
export * as actions from './actions' ;
export * as services from './services' ;
2023-10-04 12:24:51 +00:00
type BannerStatus = keyof typeof BANNER_COMPONENTS ;
const BANNER_COMPONENTS = {
'network-offline' : NetworkOfflineBanner ,
'task-incomplete-active-theme-has-mods' : ThemeHasModsBanner ,
'jetpack-offline' : JetpackOfflineBanner ,
2023-10-09 05:19:08 +00:00
'existing-ai-theme' : ExistingAiThemeBanner ,
'existing-theme' : ExistingThemeBanner ,
2024-01-11 14:32:16 +00:00
[ FlowType . noAI ] : NoAIBanner ,
2024-01-18 13:59:10 +00:00
'existing-no-ai-theme' : ExistingNoAiThemeBanner ,
2024-07-24 10:07:42 +00:00
'classic-theme' : ClassicThemeBanner ,
2024-07-24 16:09:12 +00:00
'non-default-block-theme' : NonDefaultBlockThemeBanner ,
2023-10-04 12:24:51 +00:00
default : DefaultBanner ,
} ;
const MODAL_COMPONENTS = {
'no-modal' : null ,
'task-incomplete-override-design-changes' : DesignChangeWarningModal ,
2023-10-09 05:19:08 +00:00
'task-complete-with-ai-theme' : StartOverWarningModal ,
'task-complete-without-ai-theme' : StartNewDesignWarningModal ,
2023-10-04 12:24:51 +00:00
} ;
type ModalStatus = keyof typeof MODAL_COMPONENTS ;
2024-06-05 14:54:29 +00:00
const ThemeCards = ( {
sendEvent ,
themeData ,
} : {
sendEvent : Sender < customizeStoreStateMachineEvents > ;
themeData : RecommendThemesAPIResponse ;
} ) = > {
return (
< >
< p className = "select-theme-text" >
{ __ (
'Or select a professionally designed theme to customize and make your own.' ,
'woocommerce'
) }
< / p >
< div className = "woocommerce-customize-store-theme-cards" >
{ themeData . themes ? . map ( ( theme ) = > (
< ThemeCard
key = { theme . slug }
slug = { theme . slug }
description = { theme . description }
thumbnail_url = { theme . thumbnail_url }
name = { theme . name }
color_palettes = { theme . color_palettes }
total_palettes = { theme . total_palettes }
link_url = { theme ? . link_url }
is_active = { theme . is_active }
2024-06-10 07:30:46 +00:00
is_free = { theme . is_free }
2024-06-05 14:54:29 +00:00
price = { theme . price }
onClick = { ( ) = > {
if ( theme . is_active ) {
sendEvent ( {
type : 'SELECTED_ACTIVE_THEME' ,
payload : { theme : theme.slug } ,
} ) ;
} else {
sendEvent ( {
type : 'SELECTED_NEW_THEME' ,
payload : { theme : theme.slug } ,
} ) ;
}
} }
/ >
) ) }
< / div >
< div className = "woocommerce-customize-store-browse-themes" >
< button
onClick = { ( ) = >
sendEvent ( {
type : 'SELECTED_BROWSE_ALL_THEMES' ,
} )
}
>
{ __ ( 'Browse all themes' , 'woocommerce' ) }
< / button >
< / div >
< / >
) ;
} ;
const CustomizedThemeBanners = ( {
isBlockTheme ,
2024-07-24 16:09:12 +00:00
isDefaultTheme ,
isNoAiFlow ,
2024-06-05 14:54:29 +00:00
sendEvent ,
} : {
isBlockTheme : boolean | undefined ;
2024-07-24 16:09:12 +00:00
isDefaultTheme : boolean | undefined ;
isNoAiFlow : boolean ;
2024-06-05 14:54:29 +00:00
sendEvent : Sender < customizeStoreStateMachineEvents > ;
} ) = > {
2024-07-24 16:09:12 +00:00
const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
2024-06-05 14:54:29 +00:00
return (
< >
< p className = "select-theme-text" >
{ __ ( 'Design or choose a new theme' , 'woocommerce' ) }
< / p >
< div className = "woocommerce-customize-store-cards" >
< div className = "intro-card" >
< img
src = { welcomeTourImg }
alt = { __ ( 'Design your own theme' , 'woocommerce' ) }
/ >
< div >
< h2 className = "intro-card__title" >
{ __ ( 'Design your own theme' , 'woocommerce' ) }
< / h2 >
< button
className = "intro-card__link"
onClick = { ( ) = > {
trackEvent (
'customize_your_store_intro_design_theme' ,
{
theme_type : isBlockTheme
? 'block'
: 'classic' ,
}
) ;
2024-07-24 16:09:12 +00:00
if ( isDefaultTheme && isNoAiFlow ) {
2024-06-05 14:54:29 +00:00
navigateOrParent (
window ,
getNewPath (
{ customizing : true } ,
'/customize-store/assembler-hub' ,
{ }
)
) ;
} else {
2024-07-24 16:09:12 +00:00
setIsModalOpen ( true ) ;
2024-06-05 14:54:29 +00:00
}
} }
>
{ __ ( 'Use the store designer' , 'woocommerce' ) }
< / button >
< / div >
< / div >
< div className = "intro-card" >
< img
src = { professionalThemeImg }
alt = { __ (
'Choose a professionally designed theme' ,
'woocommerce'
) }
/ >
< div >
< h2 className = "intro-card__title" >
{ __ (
'Choose a professionally designed theme' ,
'woocommerce'
) }
< / h2 >
< button
className = "intro-card__link"
onClick = { ( ) = > {
trackEvent (
'customize_your_store_intro_browse_themes'
) ;
sendEvent ( {
type : 'SELECTED_BROWSE_ALL_THEMES' ,
} ) ;
} }
>
{ __ ( 'Browse themes' , 'woocommerce' ) }
< / button >
< / div >
< / div >
< / div >
2024-07-24 16:09:12 +00:00
{ isModalOpen && (
< ThemeSwitchWarningModal
setIsModalOpen = { setIsModalOpen }
isNoAiFlow = { isNoAiFlow }
redirectToCYSFlow = { ( ) = >
sendEvent ( {
type : isNoAiFlow
? 'DESIGN_WITHOUT_AI'
: 'DESIGN_WITH_AI' ,
} )
}
/ >
) }
2024-06-05 14:54:29 +00:00
< / >
) ;
} ;
2023-08-18 05:30:25 +00:00
export const Intro : CustomizeStoreComponent = ( { sendEvent , context } ) = > {
const {
2023-10-09 05:19:08 +00:00
intro : {
2024-03-13 13:55:04 +00:00
activeTheme ,
2023-10-09 05:19:08 +00:00
themeData ,
customizeStoreTaskCompleted ,
currentThemeIsAiGenerated ,
} ,
2024-02-05 11:36:33 +00:00
activeThemeHasMods ,
2023-08-18 05:30:25 +00:00
} = context ;
2023-09-20 06:36:20 +00:00
2023-09-30 00:17:36 +00:00
const isJetpackOffline = false ;
2023-10-03 01:56:13 +00:00
const isNetworkOffline = useNetworkStatus ( ) ;
2023-09-30 00:17:36 +00:00
2024-01-25 11:04:44 +00:00
const [ showError , setShowError ] = useState (
context . flowType === FlowType . noAI && context . intro . hasErrors
) ;
2024-08-02 15:50:13 +00:00
const errorMessage =
context . intro . errorStatus === 403
? __ (
"Sorry, you don't have permission to update the theme." ,
'woocommerce'
)
: __ (
'Oops! We encountered a problem while setting up the foundations. {{anchor}}Please try again{{/anchor}} or start with a theme.' ,
'woocommerce'
) ;
2023-09-29 23:03:50 +00:00
const [ openDesignChangeWarningModal , setOpenDesignChangeWarningModal ] =
useState ( false ) ;
2023-10-04 12:24:51 +00:00
let modalStatus : ModalStatus = 'no-modal' ;
let bannerStatus : BannerStatus = 'default' ;
2024-01-18 13:59:10 +00:00
2024-03-13 13:55:04 +00:00
const isDefaultTheme = activeTheme === 'twentytwentyfour' ;
2024-06-05 14:54:29 +00:00
interface Theme {
is_block_theme? : boolean ;
}
const currentTheme = useSelect ( ( select ) = > {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return select ( 'core' ) . getCurrentTheme ( ) as Theme ;
} , [ ] ) ;
const isBlockTheme = currentTheme ? . is_block_theme ;
2024-03-13 13:55:04 +00:00
2023-10-04 12:24:51 +00:00
switch ( true ) {
case isNetworkOffline :
bannerStatus = 'network-offline' ;
break ;
case isJetpackOffline as boolean :
bannerStatus = 'jetpack-offline' ;
break ;
2024-07-24 16:09:12 +00:00
case context . flowType === FlowType . noAI && ! isBlockTheme :
2024-07-24 10:07:42 +00:00
bannerStatus = 'classic-theme' ;
break ;
2024-01-18 13:59:10 +00:00
case context . flowType === FlowType . noAI &&
2024-06-05 14:54:29 +00:00
isBlockTheme &&
2024-03-13 13:55:04 +00:00
! isDefaultTheme :
2024-07-24 16:09:12 +00:00
bannerStatus = 'non-default-block-theme' ;
break ;
case context . flowType === FlowType . noAI &&
! customizeStoreTaskCompleted :
2024-03-13 13:55:04 +00:00
bannerStatus = FlowType . noAI ;
break ;
2024-01-18 13:59:10 +00:00
case context . flowType === FlowType . noAI && customizeStoreTaskCompleted :
bannerStatus = 'existing-no-ai-theme' ;
break ;
2023-10-09 05:19:08 +00:00
case ! customizeStoreTaskCompleted && activeThemeHasMods :
2023-10-04 12:24:51 +00:00
bannerStatus = 'task-incomplete-active-theme-has-mods' ;
break ;
2023-10-09 05:19:08 +00:00
case customizeStoreTaskCompleted && currentThemeIsAiGenerated :
2023-10-04 12:24:51 +00:00
bannerStatus = 'existing-ai-theme' ;
break ;
2023-10-09 05:19:08 +00:00
case customizeStoreTaskCompleted && ! currentThemeIsAiGenerated :
bannerStatus = 'existing-theme' ;
break ;
2023-10-04 12:24:51 +00:00
}
switch ( true ) {
case openDesignChangeWarningModal === false :
modalStatus = 'no-modal' ;
break ;
case bannerStatus === 'task-incomplete-active-theme-has-mods' :
modalStatus = 'task-incomplete-override-design-changes' ;
break ;
2023-10-09 05:19:08 +00:00
case bannerStatus === 'existing-ai-theme' :
modalStatus = 'task-complete-with-ai-theme' ;
break ;
case bannerStatus === 'existing-theme' :
modalStatus = 'task-complete-without-ai-theme' ;
break ;
2023-10-04 12:24:51 +00:00
}
const ModalComponent = MODAL_COMPONENTS [ modalStatus ] ;
const BannerComponent = BANNER_COMPONENTS [ bannerStatus ] ;
2024-01-16 08:07:47 +00:00
const sidebarMessage =
context . flowType === FlowType . AIOnline
? __ (
2024-02-14 08:07:55 +00:00
'Create a store that reflects your brand and business. Select one of our professionally designed themes to customize, or create your own using AI.' ,
2024-01-16 08:07:47 +00:00
'woocommerce'
)
: __ (
2024-06-05 14:54:29 +00:00
'Design a store that reflects your brand and business. Customize your active theme, select a professionally designed theme, or create a new look using our store designer.' ,
2024-01-16 08:07:47 +00:00
'woocommerce'
) ;
2023-08-18 05:30:25 +00:00
return (
< >
2023-10-04 12:24:51 +00:00
{ ModalComponent && (
< ModalComponent
sendEvent = { sendEvent }
setOpenDesignChangeWarningModal = {
setOpenDesignChangeWarningModal
}
/ >
2023-09-29 23:03:50 +00:00
) }
2023-09-20 06:36:20 +00:00
< div className = "woocommerce-customize-store-header" >
2023-09-30 00:17:36 +00:00
< SiteHub
as = { motion . div }
variants = { {
view : { x : 0 } ,
} }
isTransparent = { false }
className = "woocommerce-customize-store__content"
/ >
2023-09-20 06:36:20 +00:00
< / div >
< div className = "woocommerce-customize-store-container" >
< div className = "woocommerce-customize-store-sidebar" >
< div className = "woocommerce-customize-store-sidebar__title" >
< button
onClick = { ( ) = > {
sendEvent ( 'CLICKED_ON_BREADCRUMB' ) ;
} }
>
{ chevronLeft }
< / button >
{ __ ( 'Customize your store' , 'woocommerce' ) }
< / div >
2024-01-16 08:07:47 +00:00
< p > { sidebarMessage } < / p >
2023-09-20 06:36:20 +00:00
< / div >
< div className = "woocommerce-customize-store-main" >
2024-01-25 11:04:44 +00:00
{ showError && (
< Notice
onRemove = { ( ) = > setShowError ( false ) }
className = "woocommerce-cys-design-with-ai__error-notice"
status = "error"
>
{ interpolateComponents ( {
2024-08-02 15:50:13 +00:00
mixedString : errorMessage ,
2024-01-25 11:04:44 +00:00
components : {
anchor : (
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions, jsx-a11y/anchor-is-valid
< a
className = "woocommerce-customize-store-error-link"
onClick = { ( ) = >
sendEvent ( 'DESIGN_WITHOUT_AI' )
}
/ >
) ,
} ,
} ) }
< / Notice >
) }
2023-10-04 12:24:51 +00:00
< BannerComponent
setOpenDesignChangeWarningModal = {
setOpenDesignChangeWarningModal
}
2024-03-15 08:17:20 +00:00
redirectToCYSFlow = { ( ) = >
sendEvent ( 'DESIGN_WITHOUT_AI' )
}
2023-10-04 12:24:51 +00:00
sendEvent = { sendEvent }
/ >
2023-09-20 06:36:20 +00:00
2024-07-24 16:09:12 +00:00
{ isDefaultTheme && ! customizeStoreTaskCompleted ? (
< ThemeCards
2024-06-05 14:54:29 +00:00
sendEvent = { sendEvent }
2024-07-24 16:09:12 +00:00
themeData = { themeData }
2024-06-05 14:54:29 +00:00
/ >
) : (
2024-07-24 16:09:12 +00:00
< CustomizedThemeBanners
isBlockTheme = { isBlockTheme }
isDefaultTheme = { isDefaultTheme }
2024-06-05 14:54:29 +00:00
sendEvent = { sendEvent }
2024-07-24 16:09:12 +00:00
isNoAiFlow = { isNoAiFlowGuard ( context . flowType ) }
2024-06-05 14:54:29 +00:00
/ >
) }
2023-09-20 06:36:20 +00:00
< / div >
< / div >
2023-08-18 05:30:25 +00:00
< / >
) ;
} ;