diff --git a/src/OnboardingSPA/chapters/sitegen.js b/src/OnboardingSPA/chapters/sitegen.js new file mode 100644 index 000000000..12aef1f25 --- /dev/null +++ b/src/OnboardingSPA/chapters/sitegen.js @@ -0,0 +1,28 @@ +import { CHAPTER_SITEGEN } from '../../constants'; +import { Chapter } from '../data/models/Chapter'; +import { __ } from '@wordpress/i18n'; +import { stepSiteGenWelcome } from '../steps/SiteGen/Welcome/step'; +import { stepSiteGenSiteDetails } from '../steps/SiteGen/SiteDetails/step'; +import { stepSiteGenSiteLogo } from '../steps/SiteGen/SiteLogo/step'; +import { stepSiteGenSocialMedia } from '../steps/SiteGen/SocialMedia/step'; +import { stepSiteGenExperience } from '../steps/SiteGen/Experience/step'; +import { stepSiteGenBuilding } from '../steps/SiteGen/Building/step'; +import { stepSiteGenPreview } from '../steps/SiteGen/Preview/step'; +import { stepSiteGenEditor } from '../steps/SiteGen/Editor/step'; + +const steps = [ + stepSiteGenWelcome, + stepSiteGenSiteDetails, + stepSiteGenSiteLogo, + stepSiteGenSocialMedia, + stepSiteGenExperience, + stepSiteGenBuilding, + stepSiteGenPreview, + stepSiteGenEditor, +]; + +export const sitegen = new Chapter( { + id: CHAPTER_SITEGEN, + name: __( 'Site Generation', 'wp-module-onboarding' ), + steps, +} ); diff --git a/src/OnboardingSPA/components/AdminBar/index.js b/src/OnboardingSPA/components/AdminBar/index.js new file mode 100644 index 000000000..efa6c8b4c --- /dev/null +++ b/src/OnboardingSPA/components/AdminBar/index.js @@ -0,0 +1,9 @@ +const AdminBar = () => { + return ( +
+ Admin Bar Goes Here +
+ ); +}; + +export default AdminBar; diff --git a/src/OnboardingSPA/components/AdminBar/stylesheet.scss b/src/OnboardingSPA/components/AdminBar/stylesheet.scss new file mode 100644 index 000000000..c87ab6c6c --- /dev/null +++ b/src/OnboardingSPA/components/AdminBar/stylesheet.scss @@ -0,0 +1,12 @@ +.nfd-onboarding-header { + + &__admin-bar { + height: 32px; + background-color: var(--nfd-onboarding-admin-bar-background); + width: 100%; + color: var(--nfd-onboarding-admin-bar-color); + margin: 0; + padding-left: 10px; + padding-top: 7px; + } +} diff --git a/src/OnboardingSPA/components/App/index.js b/src/OnboardingSPA/components/App/index.js index 0bd68d32d..042a7be14 100644 --- a/src/OnboardingSPA/components/App/index.js +++ b/src/OnboardingSPA/components/App/index.js @@ -1,331 +1,24 @@ -import Header from '../Header'; -import Content from '../Content'; -import Drawer from '../Drawer'; -import Sidebar from '../Sidebar'; -import classNames from 'classnames'; -import { useLocation } from 'react-router-dom'; -import { setFlow } from '../../utils/api/flow'; -import { design as designChapter } from '../../chapters/design'; -import { getSettings, setSettings } from '../../utils/api/settings'; -import { isEmpty, updateWPSettings } from '../../utils/api/ecommerce'; -import { store as nfdOnboardingStore } from '../../store'; - -// eslint-disable-next-line import/no-extraneous-dependencies -import { kebabCase } from 'lodash'; -import { useViewportMatch } from '@wordpress/compose'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { SlotFillProvider } from '@wordpress/components'; -import { useEffect, Fragment, useState } from '@wordpress/element'; import { FullscreenMode } from '@wordpress/interface'; -import { API_REQUEST } from '../../../constants'; -import NewfoldInterfaceSkeleton from '../NewfoldInterfaceSkeleton'; -import { HiiveAnalytics } from '@newfold-labs/js-utility-ui-analytics'; -import { - OnboardingEvent, - trackOnboardingEvent, -} from '../../utils/analytics/hiive'; -import { injectInAllSteps } from '../../data/flows/utils'; -import { - ACTION_FEATURE_ADDED, - ACTION_LOGO_ADDED, - ACTION_SITE_TITLE_SET, - ACTION_SOCIAL_ADDED, - ACTION_STARTER_PAGES_SELECTED, - ACTION_TAGLINE_SET, - CATEGORY, -} from '../../utils/analytics/hiive/constants'; -import { socialMediaStoreToState } from '../SocialMediaForm/utils'; +import { SlotFillProvider } from '@wordpress/components'; +import { Fragment } from '@wordpress/element'; + +import FlowStateHandler from '../StateHandlers/Flow'; /** - * Primary app that renders the . + * Primary app that renders the . * * Is a child of the hash router and error boundary. * * @return {WPComponent} App Component */ const App = () => { - const location = useLocation(); - const isLargeViewport = useViewportMatch( 'medium' ); - const pathname = kebabCase( location.pathname ); - - const { - isDrawerOpen, - newfoldBrand, - onboardingFlow, - currentData, - socialData, - firstStep, - allSteps, - } = useSelect( - ( select ) => { - return { - isDrawerOpen: select( nfdOnboardingStore ).isDrawerOpened(), - newfoldBrand: select( nfdOnboardingStore ).getNewfoldBrand(), - onboardingFlow: - select( nfdOnboardingStore ).getOnboardingFlow(), - currentData: - select( nfdOnboardingStore ).getCurrentOnboardingData(), - socialData: - select( nfdOnboardingStore ).getOnboardingSocialData(), - firstStep: select( nfdOnboardingStore ).getFirstStep(), - allSteps: select( nfdOnboardingStore ).getAllSteps(), - }; - }, - [ location.pathname ] - ); - - const [ isRequestPlaced, setIsRequestPlaced ] = useState( false ); - const [ didVisitBasicInfo, setDidVisitBasicInfo ] = useState( false ); - const [ didVisitEcommerce, setDidVisitEcommerce ] = useState( false ); - const { - setActiveStep, - setActiveFlow, - updateAllSteps, - flushQueue, - enqueueRequest, - setOnboardingSocialData, - setCurrentOnboardingData, - } = useDispatch( nfdOnboardingStore ); - - async function syncSocialSettings() { - const initialData = await getSettings(); - const result = await setSettings( socialData ); - setDidVisitBasicInfo( false ); - if ( result?.error !== null ) { - return initialData?.body; - } - return result?.body; - } - - async function syncStoreDetails() { - const { address } = currentData.storeDetails; - let payload = {}; - if ( address !== undefined ) { - delete address.country; - delete address.state; - payload = address; - } - // if ( tax !== undefined ) { - // delete tax.option; - // delete tax.isStoreDetailsFilled; - // payload = { ...payload, ...tax }; - // } - if ( ! isEmpty( payload ) ) { - await updateWPSettings( payload ); - } - delete currentData.storeDetails.address; - delete currentData.storeDetails.tax; - setDidVisitEcommerce( false ); - } - - async function syncStoreToDB() { - // The First Welcome Step doesn't have any Store changes - const isFirstStep = location?.pathname === firstStep?.path; - if ( currentData && ! isFirstStep ) { - if ( ! isRequestPlaced ) { - setIsRequestPlaced( true ); - - if ( didVisitEcommerce ) { - await syncStoreDetails(); - } - - // If Social Data is changed then sync it - if ( didVisitBasicInfo ) { - const socialDataResp = await syncSocialSettings(); - - // If Social Data is changed then Sync that also to the store - if ( socialDataResp ) { - setOnboardingSocialData( socialDataResp ); - } - } - flushQueue(); - enqueueRequest( API_REQUEST.SET_FLOW, () => - setFlow( currentData ) - ); - setIsRequestPlaced( false ); - } - } - - // Check if the Basic Info page was visited - if ( location?.pathname.includes( 'basic-info' ) ) { - setDidVisitBasicInfo( true ); - } - if ( location?.pathname.includes( 'ecommerce' ) ) { - setDidVisitEcommerce( true ); - } - } - - function handleConditionalDesignStepsRoutes() { - if ( - location?.pathname.includes( 'colors' ) || - location?.pathname.includes( 'fonts' ) - ) { - const updates = injectInAllSteps( - allSteps, - designChapter.conditionalSteps - ); - updateAllSteps( updates.allSteps ); - if ( ! currentData.data.customDesign ) { - currentData.data.customDesign = true; - setCurrentOnboardingData( currentData ); - } - } - } - - const handlePreviousStepTracking = () => { - const previousStep = window.nfdOnboarding?.previousStep; - if ( typeof previousStep !== 'object' ) { - window.nfdOnboarding.previousStep = { - path: location.pathname, - url: window.location.href, - }; - HiiveAnalytics.dispatchEvents( CATEGORY ); - return; - } - - const previousStepPath = previousStep.path; - const previousStepURL = previousStep.url; - - if ( previousStepPath.includes( 'basic-info' ) ) { - const siteTitle = currentData.data.blogName; - const siteDescription = currentData.data.blogDescription; - const siteLogo = currentData.data.siteLogo.url; - if ( siteTitle ) { - trackOnboardingEvent( - new OnboardingEvent( - ACTION_SITE_TITLE_SET, - siteTitle, - {}, - previousStepURL - ) - ); - } - - if ( siteDescription ) { - trackOnboardingEvent( - new OnboardingEvent( - ACTION_TAGLINE_SET, - siteDescription, - {}, - previousStepURL - ) - ); - } - - if ( siteLogo ) { - trackOnboardingEvent( - new OnboardingEvent( - ACTION_LOGO_ADDED, - undefined, - {}, - previousStepURL - ) - ); - } - - const platforms = Object.keys( - socialMediaStoreToState( socialData ) - ); - if ( platforms.length ) { - platforms.forEach( ( platform ) => { - trackOnboardingEvent( - new OnboardingEvent( - ACTION_SOCIAL_ADDED, - platform, - {}, - previousStepURL - ) - ); - } ); - } - } - - if ( previousStepPath.includes( 'site-pages' ) ) { - const sitePages = currentData.data.sitePages?.other; - if ( ! sitePages || false === sitePages ) { - trackOnboardingEvent( - new OnboardingEvent( - ACTION_STARTER_PAGES_SELECTED, - [], - { - count: 0, - }, - previousStepURL - ) - ); - } else { - trackOnboardingEvent( - new OnboardingEvent( - ACTION_STARTER_PAGES_SELECTED, - sitePages.map( ( sitePage ) => sitePage.title ), - { - count: sitePages.length, - }, - previousStepURL - ) - ); - } - } - - if ( previousStepPath.includes( 'site-features' ) ) { - const siteFeatures = currentData.data.siteFeatures; - for ( const siteFeature in siteFeatures ) { - if ( false !== siteFeatures[ siteFeature ] ) { - trackOnboardingEvent( - new OnboardingEvent( - ACTION_FEATURE_ADDED, - siteFeature, - {}, - previousStepURL - ) - ); - } - } - } - - window.nfdOnboarding.previousStep = { - path: location.pathname, - url: window.location.href, - }; - - HiiveAnalytics.dispatchEvents( CATEGORY ); - }; - - useEffect( () => { - document.body.classList.add( `nfd-brand-${ newfoldBrand }` ); - }, [ newfoldBrand ] ); - - useEffect( () => { - syncStoreToDB(); - handlePreviousStepTracking(); - handleConditionalDesignStepsRoutes(); - if ( location.pathname.includes( '/step' ) ) { - setActiveFlow( onboardingFlow ); - setActiveStep( location.pathname ); - } - }, [ location.pathname, onboardingFlow ] ); - return ( - } - drawer={ } - content={ } - sidebar={ } - /> + ); }; - export default App; diff --git a/src/OnboardingSPA/components/Button/ButtonDark/index.js b/src/OnboardingSPA/components/Button/ButtonDark/index.js new file mode 100644 index 000000000..e1393b5b6 --- /dev/null +++ b/src/OnboardingSPA/components/Button/ButtonDark/index.js @@ -0,0 +1,15 @@ +import { Button } from '@wordpress/components'; +import classNames from 'classnames'; + +const ButtonDark = ( { children, onClick } ) => { + return ( + + ); +}; + +export default ButtonDark; diff --git a/src/OnboardingSPA/components/Button/ButtonDark/stylesheet.scss b/src/OnboardingSPA/components/Button/ButtonDark/stylesheet.scss new file mode 100644 index 000000000..1f886eac0 --- /dev/null +++ b/src/OnboardingSPA/components/Button/ButtonDark/stylesheet.scss @@ -0,0 +1,11 @@ +.nfd-onboarding-button { + + &--dark { + background-color: var(--nfd-onboarding-navigation-back-background); + width: 74px; + height: 36px; + color: var(--nfd-onboarding-primary); + border-radius: 8px; + padding: 0, 13px, 0 13px; + } +} diff --git a/src/OnboardingSPA/components/Content/index.js b/src/OnboardingSPA/components/Content/index.js index 46a050b79..aaf90e31c 100644 --- a/src/OnboardingSPA/components/Content/index.js +++ b/src/OnboardingSPA/components/Content/index.js @@ -1,9 +1,8 @@ import { Route, Routes } from 'react-router-dom'; import { Fragment, memo, Suspense, useCallback } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; import { store as nfdOnboardingStore } from '../../store'; -import { useSelect } from '@wordpress/data'; -import FlowStateHandler from '../StateHandlers/Flow'; /** * Primary content area within the . @@ -32,9 +31,7 @@ const Content = () => { return (
}> - - { getMappedPages( routes ) } - + { getMappedPages( routes ) }
); diff --git a/src/OnboardingSPA/components/Drawer/DrawerPanel/index.js b/src/OnboardingSPA/components/Drawer/DrawerPanel/index.js index ec939542e..fb3320091 100644 --- a/src/OnboardingSPA/components/Drawer/DrawerPanel/index.js +++ b/src/OnboardingSPA/components/Drawer/DrawerPanel/index.js @@ -34,11 +34,12 @@ import WithDesignBack from './WithDesignBack'; const DrawerPanel = () => { const { isDrawerOpen, drawerView } = useSelect( ( select ) => { - const { isDrawerOpened, getDrawerView } = select( nfdOnboardingStore ); + const { isDrawerOpened, getActiveDrawerView } = + select( nfdOnboardingStore ); return { isDrawerOpen: isDrawerOpened(), - drawerView: getDrawerView(), + drawerView: getActiveDrawerView(), }; }, [] ); diff --git a/src/OnboardingSPA/components/Drawer/DrawerToggle/index.js b/src/OnboardingSPA/components/Drawer/DrawerToggle/index.js index cfc95b188..354b9146b 100644 --- a/src/OnboardingSPA/components/Drawer/DrawerToggle/index.js +++ b/src/OnboardingSPA/components/Drawer/DrawerToggle/index.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line @wordpress/no-unsafe-wp-apis import { Button, __unstableMotion as motion } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useEffect, useRef } from '@wordpress/element'; @@ -7,13 +8,18 @@ import classNames from 'classnames'; import { store as nfdOnboardingStore } from '../../../store'; const DrawerToggle = ( { isOpen } ) => { - const { isDrawerOpen, isDrawerSuppressed } = useSelect( ( select ) => { - return { - isDrawerOpen: select( nfdOnboardingStore ).isDrawerOpened(), - isDrawerSuppressed: - select( nfdOnboardingStore ).isDrawerSuppressed(), - }; - }, [] ); + const { isDrawerOpen, isDrawerSuppressed, activeDrawerView } = useSelect( + ( select ) => { + return { + isDrawerOpen: select( nfdOnboardingStore ).isDrawerOpened(), + isDrawerSuppressed: + select( nfdOnboardingStore ).isDrawerSuppressed(), + activeDrawerView: + select( nfdOnboardingStore ).getActiveDrawerView(), + }; + }, + [] + ); const { setIsDrawerOpened } = useDispatch( nfdOnboardingStore ); @@ -21,40 +27,45 @@ const DrawerToggle = ( { isOpen } ) => { useEffect( () => { if ( ! isDrawerOpen ) { - drawerToggleRef.current.focus(); + drawerToggleRef?.current?.focus(); } }, [ isDrawerOpen ] ); const toggleDrawer = () => { - isDrawerSuppressed || setIsDrawerOpened( ! isDrawerOpen ); + if ( isDrawerSuppressed ) { + return; + } + setIsDrawerOpened( ! isDrawerOpen ); }; return ( - - - + + + ) ); }; diff --git a/src/OnboardingSPA/components/Header/components/HeaderEnd.js b/src/OnboardingSPA/components/Header/components/SiteBuildHeader/HeaderEnd.js similarity index 79% rename from src/OnboardingSPA/components/Header/components/HeaderEnd.js rename to src/OnboardingSPA/components/Header/components/SiteBuildHeader/HeaderEnd.js index 3c0f9fcf3..fca052189 100644 --- a/src/OnboardingSPA/components/Header/components/HeaderEnd.js +++ b/src/OnboardingSPA/components/Header/components/SiteBuildHeader/HeaderEnd.js @@ -2,9 +2,9 @@ import { Fragment } from '@wordpress/element'; import { Slot } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; -import StepNavigation from '../step-navigation'; -import { store as nfdOnboardingStore } from '../../../store'; -import { SIDEBAR_MENU_SLOTFILL_PREFIX } from '../../../../constants'; +import StepNavigation from './step-navigation'; +import { store as nfdOnboardingStore } from '../../../../store'; +import { SIDEBAR_MENU_SLOTFILL_PREFIX } from '../../../../../constants'; const HeaderEnd = () => { const { sidebars, isHeaderNavigationEnabled } = useSelect( ( select ) => { diff --git a/src/OnboardingSPA/components/Header/components/SiteBuildHeader/index.js b/src/OnboardingSPA/components/Header/components/SiteBuildHeader/index.js new file mode 100644 index 000000000..e41262533 --- /dev/null +++ b/src/OnboardingSPA/components/Header/components/SiteBuildHeader/index.js @@ -0,0 +1,20 @@ +import { memo } from '@wordpress/element'; +import { Fill } from '@wordpress/components'; + +import HeaderEnd from './HeaderEnd'; +import { HEADER_END, HEADER_SITEBUILD } from '../../../../../constants'; + +/** + * Interface header rendered into header render prop in . + * + * @return {WPComponent} Header + */ +const SiteBuildHeader = () => { + return ( + + + + ); +}; + +export default memo( SiteBuildHeader ); diff --git a/src/OnboardingSPA/components/Header/step-navigation.js b/src/OnboardingSPA/components/Header/components/SiteBuildHeader/step-navigation.js similarity index 90% rename from src/OnboardingSPA/components/Header/step-navigation.js rename to src/OnboardingSPA/components/Header/components/SiteBuildHeader/step-navigation.js index 9e8371a23..538225de9 100644 --- a/src/OnboardingSPA/components/Header/step-navigation.js +++ b/src/OnboardingSPA/components/Header/components/SiteBuildHeader/step-navigation.js @@ -4,15 +4,15 @@ import { Button, ButtonGroup } from '@wordpress/components'; import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; -import { setFlow } from '../../utils/api/flow'; -import { store as nfdOnboardingStore } from '../../store'; -import { pluginDashboardPage } from '../../../constants'; -import { activateInitialPlugins } from '../../utils/api/plugins'; +import { setFlow } from '../../../../utils/api/flow'; +import { store as nfdOnboardingStore } from '../../../../store'; +import { pluginDashboardPage } from '../../../../../constants'; +import { activateInitialPlugins } from '../../../../utils/api/plugins'; import { OnboardingEvent, sendOnboardingEvent, -} from '../../utils/analytics/hiive'; -import { ACTION_ONBOARDING_COMPLETE } from '../../utils/analytics/hiive/constants'; +} from '../../../../utils/analytics/hiive'; +import { ACTION_ONBOARDING_COMPLETE } from '../../../../utils/analytics/hiive/constants'; /** * Back step Navigation button. diff --git a/src/OnboardingSPA/components/Header/components/SiteGenHeader/index.js b/src/OnboardingSPA/components/Header/components/SiteGenHeader/index.js new file mode 100644 index 000000000..2f0027111 --- /dev/null +++ b/src/OnboardingSPA/components/Header/components/SiteGenHeader/index.js @@ -0,0 +1,59 @@ +import { memo } from '@wordpress/element'; +import AdminBar from '../../../AdminBar'; +import ProgressBar from '../../../ProgressBar'; + +import { Fill } from '@wordpress/components'; +import { + HEADER_SITEGEN, + HEADER_START, + HEADER_TOP, +} from '../../../../../constants'; + +import { useSelect } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../../store'; +import StepNavigation from './step-navigation'; + +// eslint-disable-next-line import/no-extraneous-dependencies +import { findIndex } from 'lodash'; + +/** + * Interface header rendered into header render prop in . + * + * @return {WPComponent} Header + */ +const SiteGenHeader = () => { + const { isHeaderNavigationEnabled, currentStep, allSteps } = useSelect( + ( select ) => { + return { + currentStep: select( nfdOnboardingStore ).getCurrentStep(), + isHeaderNavigationEnabled: + select( nfdOnboardingStore ).isHeaderNavigationEnabled(), + allSteps: select( nfdOnboardingStore ).getAllSteps(), + }; + } + ); + + const currentStepIndex = findIndex( allSteps, { + path: currentStep.path, + } ); + const progress = ( currentStepIndex / allSteps.length ) * 100; + + return ( + <> + + <> + + { isHeaderNavigationEnabled && ( + + ) } + + + + <>{ isHeaderNavigationEnabled && } + + { currentStep?.header && } + + ); +}; + +export default memo( SiteGenHeader ); diff --git a/src/OnboardingSPA/components/Header/components/SiteGenHeader/step-navigation.js b/src/OnboardingSPA/components/Header/components/SiteGenHeader/step-navigation.js new file mode 100644 index 000000000..b66548465 --- /dev/null +++ b/src/OnboardingSPA/components/Header/components/SiteGenHeader/step-navigation.js @@ -0,0 +1,59 @@ +import { useSelect, useDispatch } from '@wordpress/data'; +import { useNavigate } from 'react-router-dom'; +import { Icon, chevronLeft } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; + +import { store as nfdOnboardingStore } from '../../../../store'; +import ButtonDark from '../../../Button/ButtonDark'; + +/** + * Back step Navigation button. + * + * @param {*} param0 + * + * @return {WPComponent} Back Component + */ +const Back = ( { path, showErrorDialog } ) => { + const { setNavErrorContinuePath } = useDispatch( nfdOnboardingStore ); + const navigate = useNavigate(); + const navigateBack = () => { + if ( showErrorDialog !== false ) { + setNavErrorContinuePath( path ); + } else { + navigate( path, { state: { origin: 'header' } } ); + } + }; + return ( + + + { __( 'Back', 'wp-module-onboarding' ) } + + ); +}; + +/** + * Step buttons presented in Header. + * + * @return {WPComponent} StepNavigation Component + */ +const StepNavigation = () => { + const { previousStep, showErrorDialog } = useSelect( ( select ) => { + return { + previousStep: select( nfdOnboardingStore ).getPreviousStep(), + showErrorDialog: select( nfdOnboardingStore ).getShowErrorDialog(), + }; + }, [] ); + const isFirstStep = null === previousStep || false === previousStep; + return ( +
+ { isFirstStep ? null : ( + + ) } +
+ ); +}; + +export default StepNavigation; diff --git a/src/OnboardingSPA/components/Header/index.js b/src/OnboardingSPA/components/Header/index.js index 079332dfc..5950c1f4b 100644 --- a/src/OnboardingSPA/components/Header/index.js +++ b/src/OnboardingSPA/components/Header/index.js @@ -1,23 +1,60 @@ -import { memo } from '@wordpress/element'; +import { Slot } from '@wordpress/components'; +import { Fragment, Suspense } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; -import HeaderEnd from './components/HeaderEnd'; +import { store as nfdOnboardingStore } from '../../store'; +import { + HEADER_CENTER, + HEADER_END, + HEADER_START, + HEADER_TOP, +} from '../../../constants'; -/** - * Interface header rendered into header render prop in . - * - * @return {WPComponent} Header - */ const Header = () => { + const { headers, headerActiveView, isHeaderEnabled } = useSelect( + ( select ) => { + return { + headers: select( nfdOnboardingStore ).getHeaders(), + headerActiveView: + select( nfdOnboardingStore ).getHeaderActiveView(), + isHeaderEnabled: select( nfdOnboardingStore ).isHeaderEnabled(), + }; + } + ); + return ( -
-
- { /* Centered Header Slot */ } -
-
- -
-
+ <> + }> + { headers.map( ( header ) => { + return ( + + + + ); + } ) } + + + { isHeaderEnabled && ( +
+
+ +
+
+ +
+
+ +
+
+ ) } + ); }; -export default memo( Header ); +export default Header; diff --git a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteBuild/index.js b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteBuild/index.js new file mode 100644 index 000000000..6384313b7 --- /dev/null +++ b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteBuild/index.js @@ -0,0 +1,447 @@ +import Header from '../../Header'; +import Content from '../../Content'; +import Drawer from '../../Drawer'; +import Sidebar from '../../Sidebar'; +import classNames from 'classnames'; +import { useLocation } from 'react-router-dom'; +import { setFlow } from '../../../utils/api/flow'; +import { design as designChapter } from '../../../chapters/design'; +import { + getSettings, + setSettings, + initialize as initializeSettings, +} from '../../../utils/api/settings'; +import { isEmpty, updateWPSettings } from '../../../utils/api/ecommerce'; +import { store as nfdOnboardingStore } from '../../../store'; +import { getQueryParam } from '../../../utils'; +import { useEffect, useState } from '@wordpress/element'; + +// eslint-disable-next-line import/no-extraneous-dependencies +import { kebabCase } from 'lodash'; +import { useViewportMatch } from '@wordpress/compose'; +import { useDispatch, useSelect } from '@wordpress/data'; + +import { API_REQUEST } from '../../../../constants'; +import NewfoldInterfaceSkeleton from '../index'; +import { HiiveAnalytics } from '@newfold-labs/js-utility-ui-analytics'; +import { + getChapterFromId, + getChaptersFromTopPriorityAndExperienceLevel, + stateToStore, +} from '../../../chapters/utils'; +import { resolveGetDataForFlow, getInitialChapters } from '../../../data/flows'; +import { + OnboardingEvent, + trackOnboardingEvent, +} from '../../../utils/analytics/hiive'; +import { injectInAllSteps } from '../../../data/flows/utils'; +import { + ACTION_ONBOARDING_CHAPTER_COMPLETE, + ACTION_ONBOARDING_CHAPTER_STARTED, + ACTION_ONBOARDING_STARTED, + ACTION_FEATURE_ADDED, + ACTION_LOGO_ADDED, + ACTION_SITE_TITLE_SET, + ACTION_SOCIAL_ADDED, + ACTION_STARTER_PAGES_SELECTED, + ACTION_TAGLINE_SET, + CATEGORY, +} from '../../../utils/analytics/hiive/constants'; +import { socialMediaStoreToState } from '../../SocialMediaForm/utils'; +import { init as initializePlugins } from '../../../utils/api/plugins'; +import { init as initializeThemes } from '../../../utils/api/themes'; +import { trigger as cronTrigger } from '../../../utils/api/cronTrigger'; +import { stepTheFork } from '../../../steps/TheFork/step'; + +const SiteBuild = () => { + const location = useLocation(); + const isLargeViewport = useViewportMatch( 'medium' ); + const pathname = kebabCase( location.pathname ); + + const { + isDrawerOpen, + newfoldBrand, + onboardingFlow, + currentData, + currentStep, + lastChapter, + socialData, + firstStep, + allSteps, + experienceLevel, + topPriority, + brandConfig, + initialize, + pluginInstallHash, + } = useSelect( + ( select ) => { + return { + isDrawerOpen: select( nfdOnboardingStore ).isDrawerOpened(), + newfoldBrand: select( nfdOnboardingStore ).getNewfoldBrand(), + onboardingFlow: + select( nfdOnboardingStore ).getOnboardingFlow(), + currentData: + select( nfdOnboardingStore ).getCurrentOnboardingData(), + socialData: + select( nfdOnboardingStore ).getOnboardingSocialData(), + firstStep: select( nfdOnboardingStore ).getFirstStep(), + allSteps: select( nfdOnboardingStore ).getAllSteps(), + topPriority: select( nfdOnboardingStore ).getTopPriority(), + experienceLevel: + select( nfdOnboardingStore ).getExperienceLevel(), + currentStep: select( nfdOnboardingStore ).getCurrentStep(), + lastChapter: select( nfdOnboardingStore ).getCurrentChapter(), + brandConfig: + select( nfdOnboardingStore ).getNewfoldBrandConfig(), + initialize: select( nfdOnboardingStore ).getInitialize(), + pluginInstallHash: + select( nfdOnboardingStore ).getPluginInstallHash(), + }; + }, + [ location.pathname ] + ); + + const [ isRequestPlaced, setIsRequestPlaced ] = useState( false ); + const [ didVisitBasicInfo, setDidVisitBasicInfo ] = useState( false ); + const [ didVisitEcommerce, setDidVisitEcommerce ] = useState( false ); + const { + setActiveChapter, + flushQueue, + enqueueRequest, + setOnboardingSocialData, + updateAllSteps, + updateRoutes, + updateTopSteps, + updateDesignRoutes, + setCurrentOnboardingData, + } = useDispatch( nfdOnboardingStore ); + + async function syncSocialSettings() { + const initialData = await getSettings(); + const result = await setSettings( socialData ); + setDidVisitBasicInfo( false ); + if ( result?.error !== null ) { + return initialData?.body; + } + return result?.body; + } + + async function syncStoreDetails() { + const { address } = currentData.storeDetails; + let payload = {}; + if ( address !== undefined ) { + delete address.country; + delete address.state; + payload = address; + } + // if ( tax !== undefined ) { + // delete tax.option; + // delete tax.isStoreDetailsFilled; + // payload = { ...payload, ...tax }; + // } + if ( ! isEmpty( payload ) ) { + await updateWPSettings( payload ); + } + delete currentData.storeDetails.address; + delete currentData.storeDetails.tax; + setDidVisitEcommerce( false ); + } + + async function syncStoreToDB() { + // The First Welcome Step doesn't have any Store changes + const isFirstStep = location?.pathname === firstStep?.path; + if ( currentData && ! isFirstStep ) { + if ( ! isRequestPlaced ) { + setIsRequestPlaced( true ); + + if ( didVisitEcommerce ) { + await syncStoreDetails(); + } + + // If Social Data is changed then sync it + if ( didVisitBasicInfo ) { + const socialDataResp = await syncSocialSettings(); + + // If Social Data is changed then Sync that also to the store + if ( socialDataResp ) { + setOnboardingSocialData( socialDataResp ); + } + } + flushQueue(); + enqueueRequest( API_REQUEST.SET_FLOW, () => + setFlow( currentData ) + ); + setIsRequestPlaced( false ); + } + } + + // Check if the Basic Info page was visited + if ( location?.pathname.includes( 'basic-info' ) ) { + setDidVisitBasicInfo( true ); + } + if ( location?.pathname.includes( 'ecommerce' ) ) { + setDidVisitEcommerce( true ); + } + } + + function handleConditionalDesignStepsRoutes() { + if ( + location?.pathname.includes( 'colors' ) || + location?.pathname.includes( 'fonts' ) + ) { + const updates = injectInAllSteps( + allSteps, + designChapter.conditionalSteps + ); + updateAllSteps( updates.allSteps ); + if ( ! currentData.data.customDesign ) { + currentData.data.customDesign = true; + setCurrentOnboardingData( currentData ); + } + } + } + + const handlePreviousStepTracking = () => { + const previousStep = window.nfdOnboarding?.previousStep; + if ( typeof previousStep !== 'object' ) { + window.nfdOnboarding.previousStep = { + path: location.pathname, + url: window.location.href, + }; + HiiveAnalytics.dispatchEvents( CATEGORY ); + return; + } + + const previousStepPath = previousStep.path; + const previousStepURL = previousStep.url; + + if ( previousStepPath.includes( 'basic-info' ) ) { + const siteTitle = currentData.data.blogName; + const siteDescription = currentData.data.blogDescription; + const siteLogo = currentData.data.siteLogo.url; + if ( siteTitle ) { + trackOnboardingEvent( + new OnboardingEvent( + ACTION_SITE_TITLE_SET, + siteTitle, + {}, + previousStepURL + ) + ); + } + + if ( siteDescription ) { + trackOnboardingEvent( + new OnboardingEvent( + ACTION_TAGLINE_SET, + siteDescription, + {}, + previousStepURL + ) + ); + } + + if ( siteLogo ) { + trackOnboardingEvent( + new OnboardingEvent( + ACTION_LOGO_ADDED, + undefined, + {}, + previousStepURL + ) + ); + } + + const platforms = Object.keys( + socialMediaStoreToState( socialData ) + ); + if ( platforms.length ) { + platforms.forEach( ( platform ) => { + trackOnboardingEvent( + new OnboardingEvent( + ACTION_SOCIAL_ADDED, + platform, + {}, + previousStepURL + ) + ); + } ); + } + } + + if ( previousStepPath.includes( 'site-pages' ) ) { + const sitePages = currentData.data.sitePages?.other; + if ( ! sitePages || false === sitePages ) { + trackOnboardingEvent( + new OnboardingEvent( + ACTION_STARTER_PAGES_SELECTED, + [], + { + count: 0, + }, + previousStepURL + ) + ); + } else { + trackOnboardingEvent( + new OnboardingEvent( + ACTION_STARTER_PAGES_SELECTED, + sitePages.map( ( sitePage ) => sitePage.title ), + { + count: sitePages.length, + }, + previousStepURL + ) + ); + } + } + + if ( previousStepPath.includes( 'site-features' ) ) { + const siteFeatures = currentData.data.siteFeatures; + for ( const siteFeature in siteFeatures ) { + if ( false !== siteFeatures[ siteFeature ] ) { + trackOnboardingEvent( + new OnboardingEvent( + ACTION_FEATURE_ADDED, + siteFeature, + {}, + previousStepURL + ) + ); + } + } + } + + window.nfdOnboarding.previousStep = { + path: location.pathname, + url: window.location.href, + }; + + HiiveAnalytics.dispatchEvents( CATEGORY ); + }; + + const trackChapters = () => { + if ( location.pathname === firstStep.path ) { + trackOnboardingEvent( + new OnboardingEvent( ACTION_ONBOARDING_STARTED ) + ); + } + + const currentChapter = currentStep?.chapter; + + if ( lastChapter !== currentChapter ) { + if ( lastChapter ) { + currentData.data.chapters[ lastChapter ].completed = true; + trackOnboardingEvent( + new OnboardingEvent( + ACTION_ONBOARDING_CHAPTER_COMPLETE, + lastChapter + ) + ); + } + + if ( currentChapter ) { + currentData.data.chapters[ currentChapter ].completed = false; + trackOnboardingEvent( + new OnboardingEvent( + ACTION_ONBOARDING_CHAPTER_STARTED, + currentChapter + ) + ); + } + + setActiveChapter( currentChapter ); + } + + if ( currentChapter ) { + currentData.data.chapters[ currentChapter ].lastStep = + currentStep?.path ?? ''; + } + }; + + useEffect( () => { + trackChapters(); + }, [ currentStep ] ); + + const prioritizeFlow = () => { + const currentFlow = window.nfdOnboarding.currentFlow; + const initialChapters = getInitialChapters( currentFlow ); + const chapterQueryArg = getQueryParam( 'chapter' ); + + let chapters; + if ( chapterQueryArg ) { + chapters = getChapterFromId( chapterQueryArg ); + } else { + chapters = getChaptersFromTopPriorityAndExperienceLevel( + initialChapters, + topPriority, + experienceLevel + ); + } + + const getData = resolveGetDataForFlow( currentFlow ); + const data = getData( chapters, chapterQueryArg ); + + currentData.data.chapters = stateToStore( + initialChapters, + currentData.data.chapters, + currentStep + ); + + setCurrentOnboardingData( currentData ); + updateAllSteps( data.steps ); + updateTopSteps( data.topSteps ); + updateRoutes( data.routes ); + updateDesignRoutes( data.designRoutes ); + }; + + useEffect( () => { + if ( initialize ) { + initializePlugins( pluginInstallHash ); + initializeThemes(); + initializeSettings(); + setInterval( cronTrigger, 45000 ); + } + }, [ initialize ] ); + + useEffect( () => { + if ( false !== brandConfig?.prioritization ) { + return prioritizeFlow(); + } + }, [ experienceLevel, topPriority ] ); + + useEffect( () => { + document.body.classList.add( `nfd-brand-${ newfoldBrand }` ); + }, [ newfoldBrand ] ); + + useEffect( () => { + syncStoreToDB(); + handlePreviousStepTracking(); + handleConditionalDesignStepsRoutes(); + }, [ location.pathname, onboardingFlow ] ); + return ( + } + drawer={ } + content={ } + sidebar={ } + /> + ); +}; + +export default SiteBuild; diff --git a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js new file mode 100644 index 000000000..87e29e351 --- /dev/null +++ b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js @@ -0,0 +1,21 @@ +import NewfoldInterfaceSkeleton from '../index'; +import Header from '../../Header'; +import Content from '../../Content'; +import Sidebar from '../../Sidebar'; +import classNames from 'classnames'; + +const SiteGen = () => { + return ( + } + content={ } + sidebar={ } + /> + ); +}; + +export default SiteGen; diff --git a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss index b805ea042..7d47de994 100644 --- a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss +++ b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss @@ -184,3 +184,10 @@ html.nfd-interface-interface-skeleton__html-container { bottom: 0; } } + +.nfd-onboarding-skeleton { + + &--sitegen { + background-image: var(--sitegen-background); + } +} diff --git a/src/OnboardingSPA/components/ProgressBar/index.js b/src/OnboardingSPA/components/ProgressBar/index.js new file mode 100644 index 000000000..404f746f4 --- /dev/null +++ b/src/OnboardingSPA/components/ProgressBar/index.js @@ -0,0 +1,12 @@ +const ProgressBar = ( { progress = 20 } ) => { + return ( +
+
+
+ ); +}; + +export default ProgressBar; diff --git a/src/OnboardingSPA/components/ProgressBar/stylesheet.scss b/src/OnboardingSPA/components/ProgressBar/stylesheet.scss new file mode 100644 index 000000000..bb8490289 --- /dev/null +++ b/src/OnboardingSPA/components/ProgressBar/stylesheet.scss @@ -0,0 +1,13 @@ +.nfd-onboarding-header { + + &__progress-bar { + width: 100%; + background-color: var(--nfd-onboarding-progress-bar-background); + height: 16px; + + &__progress { + background-color: var(--nfd-onboarding-progress-bar-fill); + height: 16px; + } + } +} diff --git a/src/OnboardingSPA/components/SiteGenPlaceholder/index.js b/src/OnboardingSPA/components/SiteGenPlaceholder/index.js new file mode 100644 index 000000000..ceebd3913 --- /dev/null +++ b/src/OnboardingSPA/components/SiteGenPlaceholder/index.js @@ -0,0 +1,31 @@ +import { useNavigate } from 'react-router-dom'; + +import { useSelect } from '@wordpress/data'; +import { Button } from '@wordpress/components'; +import { store as nfdOnboardingStore } from '../../store'; + +const SiteGenPlaceholder = ( { heading } ) => { + const navigate = useNavigate(); + const { nextStep } = useSelect( ( select ) => { + return { + nextStep: select( nfdOnboardingStore ).getNextStep(), + }; + } ); + return ( +
+

+ { heading } +

+ +
+ ); +}; + +export default SiteGenPlaceholder; diff --git a/src/OnboardingSPA/components/SiteGenPlaceholder/stylesheet.scss b/src/OnboardingSPA/components/SiteGenPlaceholder/stylesheet.scss new file mode 100644 index 000000000..741c92f2a --- /dev/null +++ b/src/OnboardingSPA/components/SiteGenPlaceholder/stylesheet.scss @@ -0,0 +1,25 @@ +.nfd-onboarding-placeholder { + + &--site-gen { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + &__heading { + color: var(--nfd-onboarding-primary); + font-size: 70px; + } + + &__button { + display: block; + background-color: var(--nfd-onboarding-primary); + color: var(--nfd-onboarding-secondary); + text-align: center; + width: 164px; + height: 50px; + font-size: 15px; + margin-top: 24px; + } + } +} diff --git a/src/OnboardingSPA/components/StateHandlers/Flow/index.js b/src/OnboardingSPA/components/StateHandlers/Flow/index.js index 865602156..2adc29f72 100644 --- a/src/OnboardingSPA/components/StateHandlers/Flow/index.js +++ b/src/OnboardingSPA/components/StateHandlers/Flow/index.js @@ -2,69 +2,37 @@ import { useEffect, useState } from '@wordpress/element'; import { useLocation } from 'react-router-dom'; import { useSelect, useDispatch } from '@wordpress/data'; import { getFragment } from '@wordpress/url'; +import { HiiveAnalytics } from '@newfold-labs/js-utility-ui-analytics'; import { store as nfdOnboardingStore } from '../../../store'; import { switchFlow } from '../../../utils/api/flow'; import { MAX_RETRIES_FLOW_SWITCH } from '../../../../constants'; -import { - getChapterFromId, - getChaptersFromTopPriorityAndExperienceLevel, - stateToStore, -} from '../../../chapters/utils'; -import { resolveGetDataForFlow, getInitialChapters } from '../../../data/flows'; -import { - OnboardingEvent, - trackOnboardingEvent, -} from '../../../utils/analytics/hiive'; -import { - ACTION_ONBOARDING_CHAPTER_COMPLETE, - ACTION_ONBOARDING_CHAPTER_STARTED, - ACTION_ONBOARDING_STARTED, -} from '../../../utils/analytics/hiive/constants'; import { ECOMMERCE_FLOW } from '../../../data/flows/constants'; -import { getQueryParam, removeQueryParam } from '../../../utils'; +import { removeQueryParam } from '../../../utils'; import { commerce } from '../../../chapters/commerce'; import EcommerceStepLoader from '../../Loaders/Step/Ecommerce'; -import { HiiveAnalytics } from '@newfold-labs/js-utility-ui-analytics'; +import SiteBuild from '../../NewfoldInterfaceSkeleton/SiteBuild'; +import SiteGen from '../../NewfoldInterfaceSkeleton/SiteGen'; +import { validateFlow } from '../../../data/flows/utils'; -const FlowStateHandler = ( { children } ) => { +const FlowStateHandler = () => { const location = useLocation(); - const [ newFlow, setNewFlow ] = useState( false ); - const { - brandConfig, - experienceLevel, - topPriority, - currentData, - currentStep, - lastChapter, - firstStep, - } = useSelect( ( select ) => { + const { brandConfig, onboardingFlow } = useSelect( ( select ) => { return { - brandName: select( nfdOnboardingStore ).getNewfoldBrandName(), brandConfig: select( nfdOnboardingStore ).getNewfoldBrandConfig(), - experienceLevel: select( nfdOnboardingStore ).getExperienceLevel(), - topPriority: select( nfdOnboardingStore ).getTopPriority(), - currentData: - select( nfdOnboardingStore ).getCurrentOnboardingData(), - lastChapter: select( nfdOnboardingStore ).getCurrentChapter(), - currentStep: select( nfdOnboardingStore ).getCurrentStep(), - firstStep: select( nfdOnboardingStore ).getFirstStep(), + onboardingFlow: select( nfdOnboardingStore ).getOnboardingFlow(), }; }, [] ); const { - updateAllSteps, - updateRoutes, - updateTopSteps, - updateDesignRoutes, - setCurrentOnboardingData, - setActiveChapter, setIsDrawerOpened, setIsDrawerSuppressed, setIsHeaderNavigationEnabled, setSidebarActiveView, + setActiveFlow, + setActiveStep, } = useDispatch( nfdOnboardingStore ); const handleCommerceFlow = async ( flow, retries = 0 ) => { @@ -90,101 +58,16 @@ const FlowStateHandler = ( { children } ) => { }; const switchToNewFlow = async ( flow ) => { - const enabledFlows = brandConfig?.enabled_flows ?? {}; - if ( ! ( flow in enabledFlows ) || enabledFlows[ flow ] !== true ) { + if ( ! validateFlow( brandConfig, flow ) ) { return; } switch ( flow ) { case ECOMMERCE_FLOW: - handleCommerceFlow( flow ); - break; - default: - setNewFlow( false ); + return handleCommerceFlow( flow ); } }; - const prioritizeFlow = () => { - const currentFlow = window.nfdOnboarding.currentFlow; - const initialChapters = getInitialChapters( currentFlow ); - const chapterQueryArg = getQueryParam( 'chapter' ); - - let chapters; - if ( chapterQueryArg ) { - chapters = getChapterFromId( chapterQueryArg ); - } else { - chapters = getChaptersFromTopPriorityAndExperienceLevel( - initialChapters, - topPriority, - experienceLevel - ); - } - - const getData = resolveGetDataForFlow( currentFlow ); - const data = getData( chapters, chapterQueryArg ); - - currentData.data.chapters = stateToStore( - initialChapters, - currentData.data.chapters, - currentStep - ); - - setCurrentOnboardingData( currentData ); - updateAllSteps( data.steps ); - updateTopSteps( data.topSteps ); - updateRoutes( data.routes ); - updateDesignRoutes( data.designRoutes ); - }; - - const trackChapters = () => { - if ( location.pathname === firstStep.path ) { - trackOnboardingEvent( - new OnboardingEvent( ACTION_ONBOARDING_STARTED ) - ); - } - - const currentChapter = currentStep?.chapter; - - if ( lastChapter !== currentChapter ) { - if ( lastChapter ) { - currentData.data.chapters[ lastChapter ].completed = true; - trackOnboardingEvent( - new OnboardingEvent( - ACTION_ONBOARDING_CHAPTER_COMPLETE, - lastChapter - ) - ); - } - - if ( currentChapter ) { - currentData.data.chapters[ currentChapter ].completed = false; - trackOnboardingEvent( - new OnboardingEvent( - ACTION_ONBOARDING_CHAPTER_STARTED, - currentChapter - ) - ); - } - - setActiveChapter( currentChapter ); - } - - if ( currentChapter ) { - currentData.data.chapters[ currentChapter ].lastStep = - currentStep?.path ?? ''; - } - }; - - useEffect( () => { - if ( false !== brandConfig?.prioritization ) { - return prioritizeFlow(); - } - }, [ experienceLevel, topPriority ] ); - - useEffect( () => { - trackChapters(); - }, [ currentStep ] ); - const disableNavigation = () => { setIsDrawerOpened( false ); setIsDrawerSuppressed( true ); @@ -199,6 +82,9 @@ const FlowStateHandler = ( { children } ) => { setNewFlow( flow ); switchToNewFlow( flow ); window.nfdOnboarding.newFlow = undefined; + } else if ( location.pathname.includes( '/step' ) ) { + setActiveFlow( onboardingFlow ); + setActiveStep( location.pathname ); } }, [ location.pathname ] ); @@ -207,8 +93,14 @@ const FlowStateHandler = ( { children } ) => { switch ( newFlow ) { case 'ecommerce': return ; - default: - return children; + } + + switch ( window.nfdOnboarding.currentFlow ) { + case 'wp-setup': + case 'ecommerce': + return ; + case 'sitegen': + return ; } }; diff --git a/src/OnboardingSPA/data/flows/constants.js b/src/OnboardingSPA/data/flows/constants.js index d4bf6e8e4..0f8760156 100644 --- a/src/OnboardingSPA/data/flows/constants.js +++ b/src/OnboardingSPA/data/flows/constants.js @@ -1,2 +1,3 @@ export const DEFAULT_FLOW = 'wp-setup'; export const ECOMMERCE_FLOW = 'ecommerce'; +export const SITEGEN_FLOW = 'sitegen'; diff --git a/src/OnboardingSPA/data/flows/default.js b/src/OnboardingSPA/data/flows/default.js index 4cee09c68..f7b3740c1 100644 --- a/src/OnboardingSPA/data/flows/default.js +++ b/src/OnboardingSPA/data/flows/default.js @@ -12,6 +12,7 @@ import { PseudoStep } from '../models/PseudoStep'; import { indexPage } from '../../pages/IndexPage/page'; import { brush } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; +import { stepTheFork } from '../../steps/TheFork/step'; export const pages = [ indexPage, errorPage ]; @@ -19,6 +20,7 @@ export const initialChapters = [ demographic, design, layoutContent, features ]; export const getSteps = ( chapters = initialChapters ) => { let steps = []; + steps.push( stepTheFork ); steps.push( stepWelcome ); chapters.forEach( ( chapter ) => { steps = steps.concat( [ @@ -32,6 +34,7 @@ export const getSteps = ( chapters = initialChapters ) => { export const getRoutes = ( chapters = initialChapters ) => { let routes = [ ...pages ]; + routes.push( stepTheFork ); routes.push( stepWelcome ); chapters.forEach( ( chapter ) => { routes = routes.concat( [ diff --git a/src/OnboardingSPA/data/flows/ecommerce.js b/src/OnboardingSPA/data/flows/ecommerce.js index 68842cd98..68bca7e04 100644 --- a/src/OnboardingSPA/data/flows/ecommerce.js +++ b/src/OnboardingSPA/data/flows/ecommerce.js @@ -18,6 +18,7 @@ import { layoutContent } from '../../chapters/layoutContent'; import { filter } from 'lodash'; import { store } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; +import { stepTheFork } from '../../steps/TheFork/step'; export const pages = [ indexPage, errorPage ]; @@ -31,6 +32,7 @@ export const initialChapters = [ export const getSteps = ( chapters = initialChapters ) => { let steps = []; + steps.push( stepTheFork ); steps.push( stepWelcome ); chapters.forEach( ( chapter ) => { steps = steps.concat( [ @@ -41,14 +43,13 @@ export const getSteps = ( chapters = initialChapters ) => { steps = steps.concat( [ stepComplete, stepWhatNext ] ); // TODO: Filter to be removed once Chapter Prioritization is enabled. return filter( steps, ( step ) => { - return ( - ! step.path.includes( '/wp-setup/step/top-priority' ) - ); + return ! step.path.includes( '/wp-setup/step/top-priority' ); } ); }; export const getRoutes = ( chapters = initialChapters ) => { let routes = [ ...pages ]; + routes.push( stepTheFork ); routes.push( stepWelcome ); chapters.forEach( ( chapter ) => { routes = routes.concat( [ @@ -60,9 +61,7 @@ export const getRoutes = ( chapters = initialChapters ) => { routes = routes.concat( [ stepComplete, stepWhatNext ] ); // TODO: Filter to be removed once Chapter Prioritization is enabled. return filter( routes, ( route ) => { - return ( - ! route.path.includes( '/wp-setup/step/top-priority' ) - ); + return ! route.path.includes( '/wp-setup/step/top-priority' ); } ); }; @@ -90,7 +89,7 @@ export const getTopSteps = ( steps ) => { return ( step instanceof PseudoStep || ( ! step.path.includes( '/ecommerce/step' ) && - ! step.path.includes( '/wp-setup/step/top-priority' ) ) // TODO: Filter to be removed once Chapter Prioritization is enabled. + ! step.path.includes( '/wp-setup/step/top-priority' ) ) // TODO: Filter to be removed once Chapter Prioritization is enabled. ); } ); }; diff --git a/src/OnboardingSPA/data/flows/index.js b/src/OnboardingSPA/data/flows/index.js index ea7748d4d..717ddcde0 100644 --- a/src/OnboardingSPA/data/flows/index.js +++ b/src/OnboardingSPA/data/flows/index.js @@ -1,4 +1,4 @@ -import { DEFAULT_FLOW, ECOMMERCE_FLOW } from './constants'; +import { DEFAULT_FLOW, ECOMMERCE_FLOW, SITEGEN_FLOW } from './constants'; import { getRoutes as defaultGetRoutes, getSteps as defaultGetSteps, @@ -16,6 +16,15 @@ import { getData as ecommerceGetData, } from './ecommerce'; +import { + getSteps as sitegenGetSteps, + getRoutes as sitegenGetRoutes, + getTopSteps as sitegenGetTopSteps, + getDesignRoutes as sitegenGetDesignRoutes, + initialChapters as sitegenInitialChapters, + getData as sitegenGetData, +} from './sitegen'; + export const getCurrentFlow = () => { return window.nfdOnboarding.currentFlow ?? DEFAULT_FLOW; }; @@ -37,6 +46,14 @@ const routerMap = { getData: ecommerceGetData, getDesignRoutes: defaultGetDesignRoutes, }, + [ SITEGEN_FLOW ]: { + getRoutes: sitegenGetRoutes, + getSteps: sitegenGetSteps, + chapters: sitegenInitialChapters, + getData: sitegenGetData, + getTopSteps: sitegenGetTopSteps, + getDesignRoutes: sitegenGetDesignRoutes, + }, }; export const initialRoutes = routerMap[ getCurrentFlow() ].getRoutes(); diff --git a/src/OnboardingSPA/data/flows/sitegen.js b/src/OnboardingSPA/data/flows/sitegen.js new file mode 100644 index 000000000..c0a078b3e --- /dev/null +++ b/src/OnboardingSPA/data/flows/sitegen.js @@ -0,0 +1,48 @@ +import { sitegen } from '../../chapters/sitegen'; +import { errorPage } from '../../pages/ErrorPage/page'; +import { indexPage } from '../../pages/IndexPage/page'; +import { stepTheFork } from '../../steps/TheFork/step'; + +export const pages = [ indexPage, errorPage ]; + +export const initialChapters = [ sitegen ]; + +export const getSteps = ( chapters = initialChapters ) => { + let steps = []; + steps.push( stepTheFork ); + chapters.forEach( ( chapter ) => { + steps = steps.concat( [ + ...chapter.steps, + // ...chapter.interstitialSteps, + ] ); + } ); + return steps; +}; + +export const getRoutes = ( chapters = initialChapters ) => { + let routes = [ ...pages ]; + routes.push( stepTheFork ); + chapters.forEach( ( chapter ) => { + routes = routes.concat( [ + ...chapter.steps, + ...chapter.conditionalSteps, + // ...chapter.interstitialSteps, + ] ); + } ); + return routes; +}; + +export const getData = () => { + return { + steps: getSteps(), + routes: getRoutes(), + }; +}; + +export const getTopSteps = () => { + return []; +}; + +export const getDesignRoutes = () => { + return []; +}; diff --git a/src/OnboardingSPA/data/flows/utils.js b/src/OnboardingSPA/data/flows/utils.js index a9367343b..4fe3d68d0 100644 --- a/src/OnboardingSPA/data/flows/utils.js +++ b/src/OnboardingSPA/data/flows/utils.js @@ -42,3 +42,11 @@ export const addAfterChapter = ( chapters, chapterOne, chapterTwo ) => { chapters.splice( position + 1, 0, chapterTwo ); return chapters; }; + +export const validateFlow = ( brandConfig, flow ) => { + const enabledFlows = brandConfig?.enabled_flows ?? {}; + if ( ! ( flow in enabledFlows ) || enabledFlows[ flow ] !== true ) { + return false; + } + return true; +}; diff --git a/src/OnboardingSPA/data/headers/index.js b/src/OnboardingSPA/data/headers/index.js new file mode 100644 index 000000000..12c7b2af3 --- /dev/null +++ b/src/OnboardingSPA/data/headers/index.js @@ -0,0 +1,24 @@ +import { lazy } from '@wordpress/element'; + +import { HEADER_SITEBUILD, HEADER_SITEGEN } from '../../../constants'; + +const SiteGenHeader = lazy( () => + import( '../../components/Header/components/SiteGenHeader' ) +); + +const SiteBuildHeader = lazy( () => + import( '../../components/Header/components/SiteBuildHeader' ) +); + +export const headers = [ + { + id: HEADER_SITEGEN, + header: SiteGenHeader, + enabled: true, + }, + { + id: HEADER_SITEBUILD, + header: SiteBuildHeader, + enabled: true, + }, +]; diff --git a/src/OnboardingSPA/data/models/Step.js b/src/OnboardingSPA/data/models/Step.js index 6407a8586..e532f6002 100644 --- a/src/OnboardingSPA/data/models/Step.js +++ b/src/OnboardingSPA/data/models/Step.js @@ -6,6 +6,7 @@ export class Step { icon, drawerView, sidebars, + header, data, } ) { this.path = path; @@ -15,5 +16,6 @@ export class Step { this.drawerView = drawerView; this.sidebars = sidebars; this.data = data; + this.header = header; } } diff --git a/src/OnboardingSPA/data/translations/index.js b/src/OnboardingSPA/data/translations/index.js index 585b6c1d6..2c05800b4 100644 --- a/src/OnboardingSPA/data/translations/index.js +++ b/src/OnboardingSPA/data/translations/index.js @@ -1,5 +1,5 @@ import { _x } from '@wordpress/i18n'; -import { DEFAULT_FLOW, ECOMMERCE_FLOW } from '../flows/constants'; +import { DEFAULT_FLOW, ECOMMERCE_FLOW, SITEGEN_FLOW } from '../flows/constants'; export const translationMap = { [ DEFAULT_FLOW ]: { @@ -18,4 +18,12 @@ export const translationMap = { noun: _x( 'store', 'noun', 'wp-module-onboarding' ), }, }, + [ SITEGEN_FLOW ]: { + site: { + noun: _x( 'site', 'noun', 'wp-module-onboarding' ), + }, + website: { + noun: _x( 'website', 'noun', 'wp-module-onboarding' ), + }, + }, }; diff --git a/src/OnboardingSPA/index.js b/src/OnboardingSPA/index.js index 1348ab20f..72d17d521 100644 --- a/src/OnboardingSPA/index.js +++ b/src/OnboardingSPA/index.js @@ -1,10 +1,6 @@ import './styles/app.scss'; import { store as nfdOnboardingStore } from './store'; /* must import prior to App! */ import { getFlow } from './utils/api/flow'; -import { init as initializePlugins } from './utils/api/plugins'; -import { init as initializeThemes } from './utils/api/themes'; -import { trigger as cronTrigger } from './utils/api/cronTrigger'; -import { initialize as initializeSettings } from './utils/api/settings'; import { DESIGN_STEPS_THEME } from '../constants'; import App from './components/App'; @@ -43,10 +39,6 @@ const initializeFlowData = ( currentData ) => { * @param {Object} runtime - Expects runtime data from window.nfdOnboarding. */ export async function initializeNFDOnboarding( id, runtime ) { - initializePlugins(); - initializeThemes(); - setInterval( cronTrigger, 45000 ); - const DOM_TARGET = document.getElementById( id ); dispatch( nfdOnboardingStore ).setRuntime( runtime ); if ( runtime.previewSettings.settings.preRequisites?.themes ) { @@ -67,7 +59,6 @@ export async function initializeNFDOnboarding( id, runtime ) { if ( null !== DOM_TARGET && 'undefined' !== typeof render ) { render( , DOM_TARGET ); - initializeSettings(); } else { // eslint-disable-next-line no-console console.log( 'Could not find mount element or wp.element.render().' ); diff --git a/src/OnboardingSPA/static/images/sitegen-bg.png b/src/OnboardingSPA/static/images/sitegen-bg.png new file mode 100644 index 000000000..c36c9b9ba Binary files /dev/null and b/src/OnboardingSPA/static/images/sitegen-bg.png differ diff --git a/src/OnboardingSPA/steps/GetStarted/Welcome/index.js b/src/OnboardingSPA/steps/GetStarted/Welcome/index.js index 2bf5620ec..356bc66a7 100644 --- a/src/OnboardingSPA/steps/GetStarted/Welcome/index.js +++ b/src/OnboardingSPA/steps/GetStarted/Welcome/index.js @@ -13,6 +13,7 @@ import TabPanelHover from '../../../components/TabPanelHover'; import { VIEW_NAV_GET_STARTED, SIDEBAR_LEARN_MORE, + HEADER_SITEBUILD, } from '../../../../constants'; import getContents from './contents'; import ButtonWhite from '../../../components/Button/ButtonWhite'; @@ -33,6 +34,8 @@ const StepWelcome = () => { setSidebarActiveView, setIsDrawerSuppressed, setIsHeaderNavigationEnabled, + setHeaderActiveView, + setIsHeaderEnabled, } = useDispatch( nfdOnboardingStore ); useEffect( () => { @@ -40,6 +43,8 @@ const StepWelcome = () => { setIsHeaderNavigationEnabled( true ); setIsDrawerSuppressed( true ); setDrawerActiveView( VIEW_NAV_GET_STARTED ); + setHeaderActiveView( HEADER_SITEBUILD ); + setIsHeaderEnabled( true ); }, [] ); const content = getContents( brandName ); diff --git a/src/OnboardingSPA/steps/SiteGen/Building/index.js b/src/OnboardingSPA/steps/SiteGen/Building/index.js new file mode 100644 index 000000000..80c6b9bbc --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Building/index.js @@ -0,0 +1,35 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const SiteGenBuilding = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default SiteGenBuilding; diff --git a/src/OnboardingSPA/steps/SiteGen/Building/step.js b/src/OnboardingSPA/steps/SiteGen/Building/step.js new file mode 100644 index 000000000..2cd24d672 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Building/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const SiteGenBuilding = lazy( () => import( './index' ) ); + +export const stepSiteGenBuilding = new Step( { + path: '/sitegen/step/building', + title: __( 'Page Layouts', 'wp-module-onboarding' ), + Component: SiteGenBuilding, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/SiteGen/Editor/index.js b/src/OnboardingSPA/steps/SiteGen/Editor/index.js new file mode 100644 index 000000000..05fc7fee6 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Editor/index.js @@ -0,0 +1,35 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const StepSiteGenEditor = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default StepSiteGenEditor; diff --git a/src/OnboardingSPA/steps/SiteGen/Editor/step.js b/src/OnboardingSPA/steps/SiteGen/Editor/step.js new file mode 100644 index 000000000..ab25039d9 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Editor/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const StepSiteGenEditor = lazy( () => import( './index' ) ); + +export const stepSiteGenEditor = new Step( { + path: '/sitegen/step/editor', + title: __( 'Page Layouts', 'wp-module-onboarding' ), + Component: StepSiteGenEditor, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/SiteGen/Experience/index.js b/src/OnboardingSPA/steps/SiteGen/Experience/index.js new file mode 100644 index 000000000..1e2d51463 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Experience/index.js @@ -0,0 +1,35 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const SiteGenExperience = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default SiteGenExperience; diff --git a/src/OnboardingSPA/steps/SiteGen/Experience/step.js b/src/OnboardingSPA/steps/SiteGen/Experience/step.js new file mode 100644 index 000000000..d0a216dd1 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Experience/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const SiteGenExperience = lazy( () => import( './index' ) ); + +export const stepSiteGenExperience = new Step( { + path: '/sitegen/step/experience', + title: __( 'Page Layouts', 'wp-module-onboarding' ), + Component: SiteGenExperience, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/index.js b/src/OnboardingSPA/steps/SiteGen/Preview/index.js new file mode 100644 index 000000000..f16ba778e --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Preview/index.js @@ -0,0 +1,35 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const SiteGenPreview = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default SiteGenPreview; diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/step.js b/src/OnboardingSPA/steps/SiteGen/Preview/step.js new file mode 100644 index 000000000..ca992dc40 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Preview/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const SiteGenPreview = lazy( () => import( './index' ) ); + +export const stepSiteGenPreview = new Step( { + path: '/sitegen/step/preview', + title: __( 'Page Layouts', 'wp-module-onboarding' ), + Component: SiteGenPreview, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/SiteGen/SiteDetails/index.js b/src/OnboardingSPA/steps/SiteGen/SiteDetails/index.js new file mode 100644 index 000000000..06fea7cac --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/SiteDetails/index.js @@ -0,0 +1,35 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const SiteGenSiteDetails = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default SiteGenSiteDetails; diff --git a/src/OnboardingSPA/steps/SiteGen/SiteDetails/step.js b/src/OnboardingSPA/steps/SiteGen/SiteDetails/step.js new file mode 100644 index 000000000..7f6cbd78a --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/SiteDetails/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const SiteGenSiteDetails = lazy( () => import( './index' ) ); + +export const stepSiteGenSiteDetails = new Step( { + path: '/sitegen/step/site-details', + title: __( 'Site Details', 'wp-module-onboarding' ), + Component: SiteGenSiteDetails, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/SiteGen/SiteLogo/index.js b/src/OnboardingSPA/steps/SiteGen/SiteLogo/index.js new file mode 100644 index 000000000..f7dbff317 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/SiteLogo/index.js @@ -0,0 +1,35 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const SiteGenSiteLogo = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default SiteGenSiteLogo; diff --git a/src/OnboardingSPA/steps/SiteGen/SiteLogo/step.js b/src/OnboardingSPA/steps/SiteGen/SiteLogo/step.js new file mode 100644 index 000000000..ca6a0f0c2 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/SiteLogo/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const SiteGenSiteLogo = lazy( () => import( './index' ) ); + +export const stepSiteGenSiteLogo = new Step( { + path: '/sitgen/step/site-logo', + title: __( 'Page Layouts', 'wp-module-onboarding' ), + Component: SiteGenSiteLogo, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/SiteGen/SocialMedia/index.js b/src/OnboardingSPA/steps/SiteGen/SocialMedia/index.js new file mode 100644 index 000000000..71b19295a --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/SocialMedia/index.js @@ -0,0 +1,35 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const SiteGenSiteSocialMedia = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default SiteGenSiteSocialMedia; diff --git a/src/OnboardingSPA/steps/SiteGen/SocialMedia/step.js b/src/OnboardingSPA/steps/SiteGen/SocialMedia/step.js new file mode 100644 index 000000000..cee374291 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/SocialMedia/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const SiteGenSiteSocialMedia = lazy( () => import( './index' ) ); + +export const stepSiteGenSocialMedia = new Step( { + path: '/sitegen/step/social-media', + title: __( 'Page Layouts', 'wp-module-onboarding' ), + Component: SiteGenSiteSocialMedia, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/SiteGen/Welcome/index.js b/src/OnboardingSPA/steps/SiteGen/Welcome/index.js new file mode 100644 index 000000000..fdd0279b8 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Welcome/index.js @@ -0,0 +1,37 @@ +import CommonLayout from '../../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../store'; +import { HEADER_SITEGEN } from '../../../../constants'; + +import SiteGenPlaceholder from '../../../components/SiteGenPlaceholder'; + +const SiteGenWelcome = () => { + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + setIsHeaderNavigationEnabled, + } = useDispatch( nfdOnboardingStore ); + + useEffect( () => { + setIsHeaderEnabled( true ); + setSidebarActiveView( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setIsHeaderNavigationEnabled( true ); + setDrawerActiveView( false ); + } ); + return ( + + + + ); +}; + +export default SiteGenWelcome; diff --git a/src/OnboardingSPA/steps/SiteGen/Welcome/step.js b/src/OnboardingSPA/steps/SiteGen/Welcome/step.js new file mode 100644 index 000000000..705b3f04f --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Welcome/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../../data/models/Step'; + +const SiteGenWelcome = lazy( () => import( './index' ) ); + +export const stepSiteGenWelcome = new Step( { + path: '/sitegen/step/welcome', + title: __( 'Welcome', 'wp-module-onboarding' ), + Component: SiteGenWelcome, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/TheFork/index.js b/src/OnboardingSPA/steps/TheFork/index.js new file mode 100644 index 000000000..0ba4458c5 --- /dev/null +++ b/src/OnboardingSPA/steps/TheFork/index.js @@ -0,0 +1,103 @@ +import CommonLayout from '../../components/Layouts/Common'; + +import { useEffect } from '@wordpress/element'; + +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../store'; +import { HEADER_SITEGEN } from '../../../constants'; + +import { useNavigate } from 'react-router-dom'; + +import { Button } from '@wordpress/components'; +import { SITEGEN_FLOW } from '../../data/flows/constants'; + +import { resolveGetDataForFlow } from '../../data/flows'; +import { validateFlow } from '../../data/flows/utils'; + +const TheFork = () => { + const navigate = useNavigate(); + const { brandConfig, migrationUrl } = useSelect( ( select ) => { + return { + brandConfig: select( nfdOnboardingStore ).getNewfoldBrandConfig(), + migrationUrl: select( nfdOnboardingStore ).getMigrationUrl(), + }; + } ); + + const { + setIsHeaderEnabled, + setSidebarActiveView, + setHeaderActiveView, + setDrawerActiveView, + setIsHeaderNavigationEnabled, + updateAllSteps, + updateTopSteps, + updateRoutes, + updateDesignRoutes, + updateInitialize, + } = useDispatch( nfdOnboardingStore ); + + const switchFlow = ( newFlow ) => { + if ( ! validateFlow( brandConfig, newFlow ) ) { + return false; + } + const currentFlow = window.nfdOnboarding.currentFlow; + const getData = resolveGetDataForFlow( newFlow ); + const data = getData(); + updateAllSteps( data.steps ); + updateTopSteps( data?.topSteps ); + updateRoutes( data.routes ); + updateDesignRoutes( data?.designRoutes ); + if ( SITEGEN_FLOW !== currentFlow ) { + window.nfdOnboarding.oldFlow = currentFlow; + } + window.nfdOnboarding.currentFlow = newFlow; + updateInitialize( true ); + navigate( data.steps[ 1 ].path ); + }; + + useEffect( () => { + setIsHeaderEnabled( false ); + setSidebarActiveView( false ); + setIsHeaderNavigationEnabled( false ); + setHeaderActiveView( HEADER_SITEGEN ); + setDrawerActiveView( false ); + } ); + + const oldFlow = window.nfdOnboarding?.oldFlow + ? window.nfdOnboarding.oldFlow + : window.nfdOnboarding.currentFlow; + return ( + +

+ The Fork +

+
+ + + { migrationUrl && ( + + ) } +
+
+ ); +}; + +export default TheFork; diff --git a/src/OnboardingSPA/steps/TheFork/step.js b/src/OnboardingSPA/steps/TheFork/step.js new file mode 100644 index 000000000..143f79615 --- /dev/null +++ b/src/OnboardingSPA/steps/TheFork/step.js @@ -0,0 +1,18 @@ +import { copy } from '@wordpress/icons'; +import { lazy } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { Step } from '../../data/models/Step'; + +const StepTheFork = lazy( () => import( './index' ) ); + +export const stepTheFork = new Step( { + path: '/wp-setup/step/fork', + title: __( 'Page Layouts', 'wp-module-onboarding' ), + Component: StepTheFork, + icon: copy, + sidebars: { + LearnMore: { + SidebarComponents: [], + }, + }, +} ); diff --git a/src/OnboardingSPA/steps/TheFork/stylesheet.scss b/src/OnboardingSPA/steps/TheFork/stylesheet.scss new file mode 100644 index 000000000..2a0641235 --- /dev/null +++ b/src/OnboardingSPA/steps/TheFork/stylesheet.scss @@ -0,0 +1,35 @@ +.nfd-onboarding-step { + + &--site-gen { + + &__fork { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + &__heading { + color: var(--nfd-onboarding-primary); + font-size: 70px; + } + + &__buttons { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + + &__button { + display: block; + background-color: var(--nfd-onboarding-primary); + color: var(--nfd-onboarding-secondary); + text-align: center; + width: 200px; + height: 50px; + font-size: 15px; + margin: 24px; + } + } + } + } +} diff --git a/src/OnboardingSPA/store/actions.js b/src/OnboardingSPA/store/actions.js index 482925ccc..1d5d057ed 100644 --- a/src/OnboardingSPA/store/actions.js +++ b/src/OnboardingSPA/store/actions.js @@ -147,6 +147,13 @@ export function updateThemeStatus( themeStatus ) { }; } +export function updateInitialize( initialize ) { + return { + type: 'UPDATE_INITIALIZE', + initialize, + }; +} + export function setIsSidebarOpened( isOpen ) { return { type: 'SET_SIDEBAR_OPENED', @@ -168,6 +175,20 @@ export function setIsHeaderNavigationEnabled( isNavigationEnabled ) { }; } +export function setIsHeaderEnabled( isEnabled ) { + return { + type: 'SET_HEADER_ENABLED', + isEnabled, + }; +} + +export function setHeaderActiveView( view ) { + return { + type: 'SET_HEADER_ACTIVE_VIEW', + view, + }; +} + export function updatePreviewSettings( previewSettings ) { return { type: 'SET_PREVIEW_SETTINGS', diff --git a/src/OnboardingSPA/store/reducer.js b/src/OnboardingSPA/store/reducer.js index f987fba16..7e79658c4 100644 --- a/src/OnboardingSPA/store/reducer.js +++ b/src/OnboardingSPA/store/reducer.js @@ -1,6 +1,10 @@ import { combineReducers } from '@wordpress/data'; -import { VIEW_NAV_PRIMARY, THEME_STATUS_INIT } from '../../constants'; +import { + VIEW_NAV_PRIMARY, + THEME_STATUS_INIT, + HEADER_SITEBUILD, +} from '../../constants'; import { initialDesignRoutes, @@ -9,6 +13,7 @@ import { initialTopSteps, } from '../data/flows/index'; import { sidebars } from '../data/sidebars/index'; +import { headers } from '../data/headers'; import apiQueueExecutor from '../utils/api-queuer/api-queue-executor'; import { DEFAULT_FLOW } from '../data/flows/constants'; @@ -190,7 +195,13 @@ export function sidebar( } export function header( - state = { isNavigationEnabled: true, menu: '' }, + state = { + isNavigationEnabled: true, + menu: '', + isEnabled: true, + headers, + view: HEADER_SITEBUILD, + }, action ) { switch ( action.type ) { @@ -199,11 +210,21 @@ export function header( ...state, isNavigationEnabled: action.isNavigationEnabled, }; + case 'SET_HEADER_ENABLED': + return { + ...state, + isEnabled: action.isEnabled, + }; case 'UPDATE_HEADER_MENU_DATA': return { ...state, menu: action.menu, }; + case 'SET_HEADER_ACTIVE_VIEW': + return { + ...state, + view: action.view, + }; } return state; } @@ -231,6 +252,7 @@ export function runtime( state = {}, action ) { export function settings( state = { themeStatus: THEME_STATUS_INIT, + initialize: false, }, action ) { @@ -245,6 +267,11 @@ export function settings( ...state, themeStatus: action.themeStatus, }; + case 'UPDATE_INITIALIZE': + return { + ...state, + initialize: action.initialize, + }; } return state; diff --git a/src/OnboardingSPA/store/selectors.js b/src/OnboardingSPA/store/selectors.js index a024b3cc9..f74172be4 100644 --- a/src/OnboardingSPA/store/selectors.js +++ b/src/OnboardingSPA/store/selectors.js @@ -9,7 +9,7 @@ import { DEFAULT_FLOW } from '../data/flows/constants'; * @param {*} state * @return {string} Drawer View */ -export function getDrawerView( state ) { +export function getActiveDrawerView( state ) { return state.drawer.view; } @@ -37,6 +37,18 @@ export function isHeaderNavigationEnabled( state ) { return state.header.isNavigationEnabled; } +export function isHeaderEnabled( state ) { + return state.header.isEnabled; +} + +export function getHeaders( state ) { + return state.header.headers; +} + +export function getHeaderActiveView( state ) { + return state.header.view; +} + /** * Gets current Newfold brand * @@ -255,6 +267,10 @@ export function getThemeStatus( state ) { return state.settings.themeStatus; } +export function getInitialize( state ) { + return state.settings.initialize; +} + export function getStepPreviewData( state ) { return state.runtime.previewSettings.stepPreviewData; } diff --git a/src/OnboardingSPA/styles/_branding.scss b/src/OnboardingSPA/styles/_branding.scss index fb4630d56..3f3ff7a55 100644 --- a/src/OnboardingSPA/styles/_branding.scss +++ b/src/OnboardingSPA/styles/_branding.scss @@ -294,4 +294,14 @@ body { } } } + + .nfd-onboarding-sitegen-dark { + --nfd-onboarding-admin-bar-background: #1d2327; + --nfd-onboarding-admin-bar-color: #c3c4c7; + --nfd-onboarding-navigation-back-background: rgba(54, 62, 68, 0.35); + --nfd-onboarding-primary: #fff; + --nfd-onboarding-secondary: #000; + --nfd-onboarding-progress-bar-background: #353a40; + --nfd-onboarding-progress-bar-fill: #0060f0; + } } diff --git a/src/OnboardingSPA/styles/_icons.scss b/src/OnboardingSPA/styles/_icons.scss index 31501c2a6..8e66fd4b8 100644 --- a/src/OnboardingSPA/styles/_icons.scss +++ b/src/OnboardingSPA/styles/_icons.scss @@ -36,4 +36,6 @@ body { --site-features-share: url(../static/icons/site-features/share.svg); --site-features-wishlist: url(../static/icons/site-features/wishlist.svg); --site-features-comingsoon: url(../static/icons/site-features/comingsoon.svg); + + --sitegen-background: url(../static/images/sitegen-bg.png); } diff --git a/src/OnboardingSPA/styles/app.scss b/src/OnboardingSPA/styles/app.scss index b92bb1b7f..992bea2af 100644 --- a/src/OnboardingSPA/styles/app.scss +++ b/src/OnboardingSPA/styles/app.scss @@ -43,6 +43,10 @@ @import "../components/Loaders/Chapter/Interstitial/stylesheet"; @import "../components/Grid/stylesheet"; @import "../components/ComingSoon/stylesheet"; +@import "../components/AdminBar/stylesheet"; +@import "../components/ProgressBar/stylesheet"; +@import "../components/Button/ButtonDark/stylesheet"; +@import "../components//SiteGenPlaceholder/stylesheet.scss"; // CSS for Pages @import "../steps/BasicInfo/stylesheet"; @@ -57,6 +61,7 @@ @import "../steps/DesignFonts/stylesheet"; @import "../steps/DesignHeaderMenu/stylesheet"; @import "../steps/SiteFeatures/stylesheet"; +@import "../steps/TheFork/stylesheet"; .nfd-onboarding-container { display: flex; diff --git a/src/OnboardingSPA/utils/api/plugins.js b/src/OnboardingSPA/utils/api/plugins.js index de03704e2..cf18ea854 100644 --- a/src/OnboardingSPA/utils/api/plugins.js +++ b/src/OnboardingSPA/utils/api/plugins.js @@ -5,7 +5,7 @@ import { getQueryParam } from '../index'; import { resolve } from './resolve'; import { NFD_PLUGINS_QUERY_PARAM } from '../../../constants'; -export const init = () => { +export const init = ( pluginInstallHash ) => { // Backend should have done the initialization if this param is present. if ( getQueryParam( NFD_PLUGINS_QUERY_PARAM ) ) { return true; @@ -14,7 +14,9 @@ export const init = () => { url: onboardingRestURL( 'plugins/initialize' ), method: 'POST', headers: { - 'X-NFD-INSTALLER': window.nfdOnboarding.pluginInstallHash, + 'X-NFD-INSTALLER': pluginInstallHash + ? pluginInstallHash + : window.nfdOnboarding.pluginInstallHash, }, } ).catch( ( error ) => { // eslint-disable-next-line no-console diff --git a/src/constants.js b/src/constants.js index fcee9448c..238d3911b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -38,6 +38,12 @@ export const VIEW_NAV_ECOMMERCE_STORE_INFO = 'nav-ecommerce-store-info'; export const SIDEBAR_SLOTFILL_PREFIX = 'Sidebar'; export const SIDEBAR_MENU_SLOTFILL_PREFIX = 'HeaderMenu'; export const SIDEBAR_LEARN_MORE = 'LearnMore'; +export const HEADER_SITEBUILD = 'HeaderSiteBuild'; +export const HEADER_SITEGEN = 'HeaderSiteGen'; +export const HEADER_TOP = 'HeaderTop'; +export const HEADER_START = 'HeaderStart'; +export const HEADER_CENTER = 'HeaderCenter'; +export const HEADER_END = 'HeaderEnd'; export const MAX_RETRIES_API_QUEUER = 2; export const MAX_RETRIES_SETTINGS_INIT = 2; @@ -58,6 +64,7 @@ export const CHAPTER_COMMERCE = 'commerce'; export const CHAPTER_DESIGN = 'design'; export const CHAPTER_LAYOUT_AND_CONTENT = 'layout_and_content'; export const CHAPTER_FEATURES = 'features'; +export const CHAPTER_SITEGEN = 'sitegen'; /** * All views for the component.