diff --git a/includes/RestApi/SiteGenController.php b/includes/RestApi/SiteGenController.php index 8f2be97f3..18328fda5 100644 --- a/includes/RestApi/SiteGenController.php +++ b/includes/RestApi/SiteGenController.php @@ -3,7 +3,9 @@ namespace NewfoldLabs\WP\Module\Onboarding\RestApi; use NewfoldLabs\WP\Module\Onboarding\Permissions; +use NewfoldLabs\WP\Module\AI\SiteGen\SiteGen; use NewfoldLabs\WP\Module\Onboarding\Data\Services\SiteGenService; +use NewfoldLabs\WP\Module\Onboarding\Data\Options; /** * Class SiteGenController @@ -49,6 +51,35 @@ public function register_routes() { 'args' => $this->sitegen_meta_args(), ) ); + \register_rest_route( + $this->namespace, + $this->rest_base . '/get-homepages', + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'get_homepages' ), + 'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ), + 'args' => $this->get_homepages_args(), + ) + ); + \register_rest_route( + $this->namespace, + $this->rest_base . '/get-homepages-regenerate', + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'get_regenerated_homepages' ), + 'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ), + 'args' => $this->get_homepages_regenerate_args(), + ) + ); + \register_rest_route( + $this->namespace, + $this->rest_base . '/favourites', + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'toggle_favourite_homepage' ), + 'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ), + ) + ); } /** @@ -73,6 +104,53 @@ public function sitegen_meta_args() { ); } + /** + * Gets the arguments for the 'get-homepages' endpoint. + * + * @return array The array of arguments. + */ + public function get_homepages_args() { + return array( + 'site_description' => array( + 'required' => true, + 'validate_callback' => function ( $param ) { + return is_string( $param ); + }, + 'sanitize_callback' => 'sanitize_text_field', + ), + 'regenerate' => array( + 'required' => false, + ), + // Add other parameters here as needed. + ); + } + + /** + * Gets the arguments for the 'get-homepages' endpoint. + * + * @return array The array of arguments. + */ + public function get_homepages_regenerate_args() { + return array( + 'site_description' => array( + 'required' => true, + 'validate_callback' => function ( $param ) { + return is_string( $param ); + }, + 'sanitize_callback' => 'sanitize_text_field', + ), + 'regenerate' => array( + 'required' => false, + ), + 'slug' => array( + 'required' => false, + ), + 'colorPalettes' => array( + 'required' => false, + ), + ); + } + /** * Gets all the valid Identifiers * @@ -105,4 +183,126 @@ public function generate_sitegen_meta( \WP_REST_Request $request ) { // TODO Implement the main function and do computations if required. return SiteGenService::instantiate_site_meta( $site_info, $identifier, $skip_cache ); } + + /** + * Gets the preview homepages + * + * @param \WP_REST_Request $request parameter. + * @return array + */ + public function get_homepages( \WP_REST_Request $request ) { + + $site_description = $request->get_param( 'site_description' ); + $regenerate = $request->get_param( 'regenerate' ); + $site_info = array( 'site_description' => $site_description ); + // If the option exists and is not empty, return it. + $existing_homepages = get_option( Options::get_option_name( 'sitegen_homepages' ), array() ); + if ( ! empty( $existing_homepages ) && ! $regenerate ) { + return new \WP_REST_Response( $existing_homepages, 200 ); + } + $target_audience = SiteGenService::instantiate_site_meta( $site_info, 'target_audience' ); + $content_style = SiteGenService::instantiate_site_meta( $site_info, 'content_tones' ); + + if ( ! $target_audience || is_wp_error( $target_audience ) ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Required data is missing.', 'wp-module-onboarding' ), + array( 'status' => 400 ) + ); + } + if ( ! $content_style || is_wp_error( $content_style ) ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Required data is missing.', 'wp-module-onboarding' ), + array( 'status' => 400 ) + ); + } + + $processed_home_pages = SiteGenService::generate_homepages( + $site_description, + $content_style, + $target_audience, + $regenerate + ); + + if ( is_wp_error( $processed_home_pages ) ) { + return $processed_home_pages; + } + + return new \WP_REST_Response( $processed_home_pages, 200 ); + } + + /** + * Gets the regenerated preview homepages + * + * @param \WP_REST_Request $request parameter. + * @return array + */ + public function get_regenerated_homepages( \WP_REST_Request $request ) { + $site_description = $request->get_param( 'site_description' ); + $regenerate_slug = $request->get_param( 'slug' ); + $regenerate_color_palattes = $request->get_param( 'colorPalettes' ); + $is_favourite = $request->get_param( 'isFavourited' ); + $site_info = array( 'site_description' => $site_description ); + $target_audience = SiteGenService::instantiate_site_meta( $site_info, 'target_audience' ); + $content_style = SiteGenService::instantiate_site_meta( $site_info, 'content_tones' ); + + if ( ! $target_audience || is_wp_error( $target_audience ) ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Required data is missing.', 'wp-module-onboarding' ), + array( 'status' => 400 ) + ); + } + + if ( ! $content_style || is_wp_error( $content_style ) ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Required data is missing.', 'wp-module-onboarding' ), + array( 'status' => 400 ) + ); + } + + if ( $is_favourite ) { + $result = SiteGenService::handle_favorite_regeneration( $regenerate_slug, $regenerate_color_palattes ); + } else { + $result = SiteGenService::handle_regular_regeneration( $site_description, $content_style, $target_audience ); + } + + if ( null === $result ) { + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Error at Regenerating home pages.', 'wp-module-onboarding' ), + array( + 'status' => 400, + ) + ); + } + + return new \WP_REST_Response( $result, 200 ); + } + + /** + * Updates favourite status + * + * @param \WP_REST_Request $request parameter. + * @return array + */ + public function toggle_favourite_homepage( \WP_REST_Request $request ) { + $slug = $request->get_param( 'slug' ); + + $response = SiteGenService::toggle_favourite_homepage( $slug ); + + if ( is_wp_error( $response ) ) { + $error_message = $response->get_error_message(); + return new \WP_Error( + 'nfd_onboarding_error', + __( 'Error at updating Favourite status', 'wp-module-onboarding' ), + array( + 'status' => 404, + ) + ); + } + return new \WP_REST_Response( $response, 200 ); + } } diff --git a/src/OnboardingSPA/components/Button/ButtonDark/stylesheet.scss b/src/OnboardingSPA/components/Button/ButtonDark/stylesheet.scss index 1f886eac0..f3d98d096 100644 --- a/src/OnboardingSPA/components/Button/ButtonDark/stylesheet.scss +++ b/src/OnboardingSPA/components/Button/ButtonDark/stylesheet.scss @@ -2,10 +2,14 @@ &--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; + + &:hover { + background-color: #fff; + color: #272d30; + } } } diff --git a/src/OnboardingSPA/components/Button/index.js b/src/OnboardingSPA/components/Button/index.js index 86e7812d7..a5efeaee4 100644 --- a/src/OnboardingSPA/components/Button/index.js +++ b/src/OnboardingSPA/components/Button/index.js @@ -2,22 +2,24 @@ * Common Button Component * Different variants can be added later based on our requirements * - * @returns Button + * @return Button */ -const Button = ({ text, handleClick, disabled, className }) => { +const Button = ( { children, onClick, disabled, className } ) => { const handleBtnClick = () => { - handleClick(); + if ( onClick ) { + onClick(); + } }; return ( ); }; diff --git a/src/OnboardingSPA/components/Footer/components/SiteGenFooter/index.js b/src/OnboardingSPA/components/Footer/components/SiteGenFooter/index.js index aea5f1aa3..1d74c033b 100644 --- a/src/OnboardingSPA/components/Footer/components/SiteGenFooter/index.js +++ b/src/OnboardingSPA/components/Footer/components/SiteGenFooter/index.js @@ -11,20 +11,23 @@ import { FOOTER_END, } from '../../../../../constants'; import NextButtonSiteGen from '../../../Button/NextButtonSiteGen'; +import { stepSiteGenEditor } from '../../.././../steps/SiteGen/Editor/step'; const SiteGenFooter = () => { const isLargeViewport = useViewportMatch( 'small' ); - const { footerNavEnabled } = useSelect( ( select ) => { + const { footerNavEnabled, currentStep } = useSelect( ( select ) => { return { footerNavEnabled: select( nfdOnboardingStore ).getFooterNavEnabled(), + currentStep: select( nfdOnboardingStore ).getCurrentStep(), }; } ); + const isEditorStep = currentStep === stepSiteGenEditor; return ( <> - + { ! isEditorStep && } { ! isLargeViewport && ( diff --git a/src/OnboardingSPA/components/Header/components/SiteGenHeader/index.js b/src/OnboardingSPA/components/Header/components/SiteGenHeader/index.js index db3390a10..df5b3e2ee 100644 --- a/src/OnboardingSPA/components/Header/components/SiteGenHeader/index.js +++ b/src/OnboardingSPA/components/Header/components/SiteGenHeader/index.js @@ -37,7 +37,6 @@ const SiteGenHeader = () => { path: currentStep?.path, } ); const progress = ( currentStepIndex / allSteps.length ) * 100; - return ( <> @@ -48,10 +47,14 @@ const SiteGenHeader = () => { ) } - - <>{ isHeaderNavigationEnabled && } - - { currentStep?.header && } + + { currentStep?.header?.component + ? isHeaderNavigationEnabled && + : isHeaderNavigationEnabled && ( + + + + ) } ); }; diff --git a/src/OnboardingSPA/components/Header/index.js b/src/OnboardingSPA/components/Header/index.js index 03760ff3d..7d7a5b737 100644 --- a/src/OnboardingSPA/components/Header/index.js +++ b/src/OnboardingSPA/components/Header/index.js @@ -1,6 +1,7 @@ import { Slot } from '@wordpress/components'; import { Fragment, Suspense } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; +import classNames from 'classnames'; import { store as nfdOnboardingStore } from '../../store'; import { @@ -9,7 +10,6 @@ import { HEADER_START, HEADER_TOP, } from '../../../constants'; -import classNames from 'classnames'; import { stepSiteGenEditor } from '../../steps/SiteGen/Editor/step'; import { SITEGEN_FLOW } from '../../data/flows/constants'; @@ -17,11 +17,11 @@ const Header = () => { const { headers, headerActiveView, isHeaderEnabled, currentStep } = useSelect( ( select ) => { return { + currentStep: select( nfdOnboardingStore ).getCurrentStep(), headers: select( nfdOnboardingStore ).getHeaders(), headerActiveView: select( nfdOnboardingStore ).getHeaderActiveView(), isHeaderEnabled: select( nfdOnboardingStore ).isHeaderEnabled(), - currentStep: select( nfdOnboardingStore ).getCurrentStep(), }; } ); diff --git a/src/OnboardingSPA/components/Header/stylesheet.scss b/src/OnboardingSPA/components/Header/stylesheet.scss index b2555fc43..1367aa6ca 100644 --- a/src/OnboardingSPA/components/Header/stylesheet.scss +++ b/src/OnboardingSPA/components/Header/stylesheet.scss @@ -1,3 +1,5 @@ +$nfd-onboarding-editor-header-hover: #272d30; + .nfd-onboarding-header { align-items: center; background-color: var(--nfd-onboarding-header-base); @@ -23,6 +25,10 @@ transition-delay: 80ms; @include reduce-motion("transition"); + + @media (max-width: #{ ($break-xlarge) }) { + padding-left: 4px; + } } &__start, @@ -39,16 +45,8 @@ display: flex; align-items: center; height: 100%; - flex-grow: 1; - justify-content: center; - min-width: 5px; - font-weight: 600; font-size: 20px; line-height: $grid-unit-30; - - svg { - margin-right: $grid-unit-10; - } } &__end { @@ -117,4 +115,25 @@ font-size: 13px; padding: 15px 10px !important; } + + &-editor { + padding: 0 13px 0 13px; + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + cursor: pointer; + height: 36px; + background-color: var(--nfd-onboarding-navigation-back-background); + color: var(--nfd-onboarding-primary); + fill: var(--nfd-onboarding-primary); + border-radius: 6px; + + &:hover { + background-color: var(--nfd-onboarding-primary); + color: $nfd-onboarding-editor-header-hover; + fill: $nfd-onboarding-editor-header-hover; + } + } + } diff --git a/src/OnboardingSPA/components/LivePreview/BlockPreview/stylesheet.scss b/src/OnboardingSPA/components/LivePreview/BlockPreview/stylesheet.scss index 1ee2b924e..d89588a41 100644 --- a/src/OnboardingSPA/components/LivePreview/BlockPreview/stylesheet.scss +++ b/src/OnboardingSPA/components/LivePreview/BlockPreview/stylesheet.scss @@ -27,6 +27,17 @@ $main-color-grey-other: #e2e2e2; } } + &-full { + width: 100%; + min-height: 90vh; + overflow: hidden; + position: relative; + align-items: center; + border: 1px solid #e3dfdf; + border-top: 0; + margin-bottom: 30px; + } + &--is-skeleton { z-index: 1; width: 100%; diff --git a/src/OnboardingSPA/components/LivePreview/BlockPreviewSiteGen/auto.js b/src/OnboardingSPA/components/LivePreview/BlockPreviewSiteGen/auto.js new file mode 100644 index 000000000..49f2764da --- /dev/null +++ b/src/OnboardingSPA/components/LivePreview/BlockPreviewSiteGen/auto.js @@ -0,0 +1,130 @@ +import { Disabled } from '@wordpress/components'; +import { useResizeObserver, pure, useRefEffect } from '@wordpress/compose'; +import { useMemo } from '@wordpress/element'; +import { + BlockList, + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __unstableIframe as Iframe, + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __unstableEditorStyles as EditorStyles, +} from '@wordpress/block-editor'; + +// This is used to avoid rendering the block list if the sizes change. +let MemoizedBlockList; + +const MAX_HEIGHT = 6000; + +function ScaledBlockPreview( { + viewportWidth, + settings, + containerWidth, + minHeight, + additionalStyles = [], +} ) { + if ( ! viewportWidth ) { + viewportWidth = containerWidth; + } + + const [ contentResizeListener, { height: contentHeight } ] = + useResizeObserver(); + const { styles, assets } = { + styles: settings.styles, + assets: settings.__unstableResolvedAssets, + }; + + // Avoid scrollbars for pattern previews. + const editorStyles = useMemo( () => { + if ( styles ) { + return [ + ...styles, + { + css: 'body{height:auto;overflow:hidden;border:none;padding:0;}', + __unstableType: 'presets', + }, + ...additionalStyles, + ]; + } + + return styles; + }, [ styles, additionalStyles ] ); + + // Initialize on render instead of module top level, to avoid circular dependency issues. + MemoizedBlockList = MemoizedBlockList || pure( BlockList ); + + const scale = containerWidth / viewportWidth; + const aspectRatio = contentHeight + ? containerWidth / ( contentHeight * scale ) + : 0; + return ( + MAX_HEIGHT ? MAX_HEIGHT * scale : undefined, + minHeight, + } } + > + + + ); +} + +export default function AutoBlockPreview( props ) { + const [ containerResizeListener, { width: containerWidth } ] = + useResizeObserver(); + + return ( + <> +
+ { containerResizeListener } +
+
+ { containerWidth && ( + + ) } +
+ + ); +} diff --git a/src/OnboardingSPA/components/LivePreview/BlockPreviewSiteGen/index.js b/src/OnboardingSPA/components/LivePreview/BlockPreviewSiteGen/index.js new file mode 100644 index 000000000..5e481bb8a --- /dev/null +++ b/src/OnboardingSPA/components/LivePreview/BlockPreviewSiteGen/index.js @@ -0,0 +1,115 @@ +import { useSelect } from '@wordpress/data'; +import { BlockEditorProvider } from '@wordpress/block-editor'; +import { parse } from '@wordpress/blocks'; +import { useEffect, useState, memo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +import AutoHeightBlockPreview from './auto'; +import { useGlobalStylesOutput } from '../../../utils/global-styles/use-global-styles-output'; +import { store as nfdOnboardingStore } from '../../../store'; + +const BlockPreviewSiteGen = ( { + blockGrammer, + viewportWidth = 1300, + styling = 'large', + setIsLoadingParent = false, + previewSettings = false, + skeletonLoadingTime = 2500, + isRegenerating = { isRegenerating }, +} ) => { + const [ blocks, setBlocks ] = useState(); + const [ settings, setSettings ] = useState(); + const [ loading, setIsLoading ] = useState( true ); + + useEffect( () => { + if ( skeletonLoadingTime ) { + const timer = setTimeout( () => { + setIsLoading( false ); + if ( setIsLoadingParent ) { + setIsLoadingParent( false ); + } + }, skeletonLoadingTime ); + return () => clearTimeout( timer ); + } + setIsLoading( false ); + if ( setIsLoadingParent ) { + setIsLoadingParent( false ); + } + }, [ skeletonLoadingTime ] ); + + const { currentData, storedPreviewSettings } = useSelect( ( select ) => { + return { + currentData: + select( nfdOnboardingStore ).getCurrentOnboardingData(), + storedPreviewSettings: + select( nfdOnboardingStore ).getPreviewSettings(), + }; + }, [] ); + + useEffect( () => { + if ( previewSettings ) { + setSettings( + // eslint-disable-next-line react-hooks/rules-of-hooks + useGlobalStylesOutput( previewSettings, storedPreviewSettings ) + ); + } else { + setSettings( storedPreviewSettings ); + } + }, [] ); + + useEffect( () => { + if ( blockGrammer ) { + setBlocks( parse( blockGrammer ) ); + } + }, [ blockGrammer ] ); + + useEffect( () => { + if ( ! previewSettings ) { + setSettings( storedPreviewSettings ); + } + }, [ storedPreviewSettings, currentData ] ); + + const SkeletonLivePreview = memo( () => { + return ( +
+
+

+

+ { isRegenerating + ? __( + 'Regenerating Site', + 'wp-module-onboarding' + ) + : __( + 'Generating Site', + 'wp-module-onboarding' + ) } +

+

+
+
+
+
+
+ ); + } ); + + return ( +
+ { loading && } + { blocks && settings && ( + + + + ) } +
+ ); +}; + +export default memo( BlockPreviewSiteGen ); diff --git a/src/OnboardingSPA/components/LivePreview/SelectableCard/stylesheet.scss b/src/OnboardingSPA/components/LivePreview/SelectableCard/stylesheet.scss index 2ee21e2b1..3a2b5c701 100644 --- a/src/OnboardingSPA/components/LivePreview/SelectableCard/stylesheet.scss +++ b/src/OnboardingSPA/components/LivePreview/SelectableCard/stylesheet.scss @@ -86,10 +86,9 @@ width: 100%; opacity: 0; transition: 0.5s ease; - background-color: - rgb(var(--wp-admin-theme-color-darker-10--rgb)) + background-color: rgb(var(--wp-admin-theme-color-darker-10--rgb)); - &__icon{ + &__icon { fill: var(--nfd-onboarding-white); font-size: 20px; position: absolute; diff --git a/src/OnboardingSPA/components/LivePreview/SiteGenPreviewSelectableCard/index.js b/src/OnboardingSPA/components/LivePreview/SiteGenPreviewSelectableCard/index.js new file mode 100644 index 000000000..e97c10b23 --- /dev/null +++ b/src/OnboardingSPA/components/LivePreview/SiteGenPreviewSelectableCard/index.js @@ -0,0 +1,145 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +import { search, Icon, reusableBlock } from '@wordpress/icons'; +import { useState } from '@wordpress/element'; +import { useNavigate } from 'react-router-dom'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { BlockPreviewSiteGen } from '..'; +import Button from '../../../components/Button'; +import { store as nfdOnboardingStore } from '../../../store'; +import { ReactComponent as FavouriteIconStroked } from '../../../static/icons/sitegen/heart-stroked.svg'; +import { ReactComponent as FavouriteIconFilled } from '../../../static/icons/sitegen/heart-filled.svg'; +import { __ } from '@wordpress/i18n'; + +const SiteGenPreviewSelectableCard = ( { + className = 'live-preview-sitegen--selectable-card', + blockGrammer, + viewportWidth = 1500, + styling = 'large', + previewSettings, + overlay = false, + onClick = false, + onRegenerateClick = false, + skeletonLoadingTime = 2500, + designObject, + handleFavorite, + handlePreview, + isRegenerating, +} ) => { + const { setActiveHomepage } = useDispatch( nfdOnboardingStore ); + const [ loadingParent, setIsLoadingParent ] = useState( true ); + + const navigate = useNavigate(); + const { nextStep } = useSelect( ( select ) => { + return { + nextStep: select( nfdOnboardingStore ).getNextStep(), + }; + } ); + + const onPreviewVersionClick = () => { + setActiveHomepage( designObject ); + navigate( nextStep.path ); + }; + const handleRegenerate = () => { + onRegenerateClick( + designObject?.slug, + designObject?.color, + designObject?.isFavourited + ); + }; + + return ( +
{ + if ( ! loadingParent && typeof onClick === 'function' ) { + onClick(); + } + } } + onKeyDown={ () => { + if ( ! loadingParent && typeof onClick === 'function' ) { + onClick(); + } + } } + > +
+ + { overlay && ! loadingParent && ( +
{ + if ( event.key === 'Enter' ) { + handlePreview(); + } + } } + > + +
+ ) } +
+
handleFavorite( designObject?.slug ) } + onKeyDown={ ( event ) => { + if ( event.key === 'Enter' ) { + handleFavorite( designObject?.slug ); + } + } } + aria-label="Add to Wishlist" + className={ `${ className }__live-preview-container-buttons__button` } + > + + { designObject?.isFavourited ? ( + + ) : ( + + ) } + + { designObject?.title } +
+
handleRegenerate() } + onKeyDown={ ( event ) => { + if ( event.key === 'Enter' ) { + handleRegenerate(); + } + } } + aria-label={ __( + 'Regenerate Content', + 'wp-module-onboarding' + ) } + className={ `${ className }__live-preview-container-buttons__button` } + > + + { __( 'Regenerate', 'wp-module-onboarding' ) } +
+
+
+
+ ); +}; + +export default SiteGenPreviewSelectableCard; diff --git a/src/OnboardingSPA/components/LivePreview/SiteGenPreviewSelectableCard/stylesheet.scss b/src/OnboardingSPA/components/LivePreview/SiteGenPreviewSelectableCard/stylesheet.scss new file mode 100644 index 000000000..cdac5bd0a --- /dev/null +++ b/src/OnboardingSPA/components/LivePreview/SiteGenPreviewSelectableCard/stylesheet.scss @@ -0,0 +1,103 @@ +.live-preview-sitegen--selectable-card { + width: 420px; + padding: 20px; + display: flex; + overflow: hidden; + align-items: center; + flex-direction: column; + justify-content: center; + flex-wrap: wrap; + + @media (min-width: #{ ($break-mobile) }) and (max-width: #{ ($break-wide) }) { + width: 340px; + padding: 15px; + } + + @media (max-width: #{ ($break-mobile) }) { + width: 300px; + padding: 10px; + } + + &__live-preview-container { + position: relative; + width: 100%; + align-items: center; + border-radius: 8px; + + .live-preview { + + &__container { + + &-custom { + width: 100%; + overflow: hidden; + height: 315px; + border-radius: 16px; + } + } + } + + &__overlay { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + top: 0; + bottom: 0; + left: 0; + right: 0; + height: 315px; + z-index: 2; + width: 100%; + opacity: 0; + transition: 0.5s ease; + border-radius: 8px; + + .live-preview-sitegen--selectable-card & { + background-color: var(--nfd-onboarding-card-overlay); + } + + &__button { + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + padding: 6px 56px 6px 56px; + border-radius: 8px; + gap: 6px; + } + + &:hover { + cursor: pointer; + opacity: 1; + } + } + + &-buttons { + padding-top: 20px; + display: flex; + flex-direction: row; + justify-content: space-between; + height: 40px; + opacity: 1; + + &.disabled { + pointer-events: none; + cursor: not-allowed; + opacity: 0.5; + } + + &__button { + display: flex; + justify-content: center; + align-items: center; + color: var(--nfd-onboarding-primary); + gap: 8px; + font-size: 16px; + fill: var(--nfd-onboarding-primary); + cursor: pointer; + text-align: left; + } + } + } +} diff --git a/src/OnboardingSPA/components/LivePreview/index.js b/src/OnboardingSPA/components/LivePreview/index.js index e7cebd4a1..8e18d6370 100644 --- a/src/OnboardingSPA/components/LivePreview/index.js +++ b/src/OnboardingSPA/components/LivePreview/index.js @@ -3,3 +3,5 @@ export { default as LivePreviewSkeleton } from './LivePreviewSkeleton'; export { default as LivePreviewSelectableCard } from './SelectableCard'; export { default as LivePreviewSelectableCardWithInfo } from './SelectableCardWithInfo'; export { default as GlobalStylesProvider } from './GlobalStylesProvider'; +export { default as SiteGenLivePreview } from './SiteGenPreviewSelectableCard'; +export { default as BlockPreviewSiteGen } from './BlockPreviewSiteGen'; diff --git a/src/OnboardingSPA/components/LivePreview/stylesheet.scss b/src/OnboardingSPA/components/LivePreview/stylesheet.scss index f1a74565d..8e43ab671 100644 --- a/src/OnboardingSPA/components/LivePreview/stylesheet.scss +++ b/src/OnboardingSPA/components/LivePreview/stylesheet.scss @@ -1,3 +1,4 @@ @import "./BlockPreview/stylesheet"; @import "./SelectableCard/stylesheet"; @import "./SelectableCardWithInfo/stylesheet"; +@import "./SiteGenPreviewSelectableCard/stylesheet"; diff --git a/src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js b/src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js index 4b3474b36..94cd18a97 100644 --- a/src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js +++ b/src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js @@ -34,11 +34,11 @@ const SiteGenLoader = ( { autoNavigate = false } ) => { useEffect( () => { const percentageValue = - ( currentData.sitegen.siteGenMetaStatus.currentStatus / - currentData.sitegen.siteGenMetaStatus.totalCount ) * + ( currentData?.sitegen?.siteGenMetaStatus?.currentStatus / + currentData?.sitegen?.siteGenMetaStatus?.totalCount ) * 100; setPercentage( percentageValue ); - }, [ currentData.sitegen.siteGenMetaStatus.currentStatus ] ); + }, [ currentData?.sitegen?.siteGenMetaStatus?.currentStatus ] ); useEffect( () => { if ( percentage === 100 ) { diff --git a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js index 8e8b22e2f..412a9ea2a 100644 --- a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js +++ b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js @@ -20,6 +20,7 @@ import { initialize as initializeSettings } from '../../../utils/api/settings'; 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 { MAX_RETRIES_SITE_GEN } from '../../../../constants'; // Wrapping the NewfoldInterfaceSkeleton with the HOC to make theme available const ThemedNewfoldInterfaceSkeleton = themeToggleHOC( @@ -61,6 +62,32 @@ const SiteGen = () => { } } + async function performSiteGenMetaGeneration( + siteInfo, + identifier, + retryCount = 1 + ) { + return new Promise( () => + generateSiteGenMeta( siteInfo, identifier ) + .then( ( data ) => { + if ( data.body !== null ) { + currentData.sitegen.siteGenMetaStatus.currentStatus += 1; + setCurrentOnboardingData( currentData ); + } else if ( retryCount < MAX_RETRIES_SITE_GEN ) { + performSiteGenMetaGeneration( + siteInfo, + identifier, + retryCount + 1 + ); + } + } ) + .catch( ( err ) => { + /* eslint-disable no-console */ + console.log( err ); + } ) + ); + } + async function generateSiteGenData() { // Start the API Requests when the loader is shown. if ( @@ -72,41 +99,25 @@ const SiteGen = () => { return; } - // If the calls are already made then skip doing that again. - if ( - currentData.sitegen.siteGenMetaStatus.currentStatus >= - currentData.sitegen.siteGenMetaStatus.totalCount - ) { - return; - } - let identifiers = await getSiteGenIdentifiers(); identifiers = identifiers.body; const midIndex = Math.floor( identifiers.length / 2 ); if ( location.pathname.includes( 'experience' ) ) { identifiers = identifiers.slice( 0, midIndex ); + currentData.sitegen.siteGenMetaStatus.currentStatus = 0; } else if ( location.pathname.includes( 'building' ) ) { identifiers = identifiers.slice( midIndex ); + currentData.sitegen.siteGenMetaStatus.currentStatus = midIndex; } - + setCurrentOnboardingData( currentData ); const siteInfo = { site_description: currentData.sitegen?.siteDetails?.prompt, }; // Iterate over Identifiers and fire Requests! identifiers.forEach( ( identifier ) => { - return new Promise( () => - generateSiteGenMeta( siteInfo, identifier ) - .then( ( data ) => { - if ( data.body !== null ) { - currentData.sitegen.siteGenMetaStatus.currentStatus += 1; - setCurrentOnboardingData( currentData ); - } - } ) - /* eslint-disable no-console */ - .catch( ( err ) => console.log( err ) ) - ); + performSiteGenMetaGeneration( siteInfo, identifier ); } ); } diff --git a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss index b98d95405..dc0741ec5 100644 --- a/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss +++ b/src/OnboardingSPA/components/NewfoldInterfaceSkeleton/style.scss @@ -31,6 +31,13 @@ html.nfd-interface-interface-skeleton__html-container { top: 0; } } + + &.nfd-onboarding-skeleton--sitegen { + + .is-fullscreen-mode & { + top: 0; + } + } } .nfd-interface-interface-skeleton__editor { diff --git a/src/OnboardingSPA/static/icons/equalizer.svg b/src/OnboardingSPA/static/icons/equalizer.svg new file mode 100644 index 000000000..171f55051 --- /dev/null +++ b/src/OnboardingSPA/static/icons/equalizer.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/OnboardingSPA/static/icons/sitegen/heart-filled.svg b/src/OnboardingSPA/static/icons/sitegen/heart-filled.svg new file mode 100644 index 000000000..cf2506f55 --- /dev/null +++ b/src/OnboardingSPA/static/icons/sitegen/heart-filled.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/OnboardingSPA/static/icons/sitegen/heart-stroked.svg b/src/OnboardingSPA/static/icons/sitegen/heart-stroked.svg new file mode 100644 index 000000000..b9cfca95c --- /dev/null +++ b/src/OnboardingSPA/static/icons/sitegen/heart-stroked.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/OnboardingSPA/steps/SiteGen/Editor/Header/TextInput/index.js b/src/OnboardingSPA/steps/SiteGen/Editor/Header/TextInput/index.js new file mode 100644 index 000000000..571f64f6d --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Editor/Header/TextInput/index.js @@ -0,0 +1,32 @@ +import { useRef, useEffect } from '@wordpress/element'; + +const TextInputVersion = ( { + isInputDisabled, + versionName, + setVersionName, +} ) => { + const inputRef = useRef( null ); + + useEffect( () => { + if ( ! isInputDisabled && inputRef.current ) { + inputRef.current.focus(); + } + }, [ isInputDisabled ] ); + + const handleTextChange = ( e ) => { + setVersionName( e.target.value ); + }; + + return ( + + ); +}; + +export default TextInputVersion; diff --git a/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-center.js b/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-center.js new file mode 100644 index 000000000..172d4a31d --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-center.js @@ -0,0 +1,110 @@ +import { Icon, chevronDown, reusableBlock, settings } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; + +import { useViewportMatch } from '@wordpress/compose'; +import { useState } from '@wordpress/element'; +import { ReactComponent as FavouriteIcon } from '../../../../static/icons/sitegen/heart-stroked.svg'; +import { Dropdown, MenuGroup, MenuItem } from '@wordpress/components'; +import TextInputVersion from './TextInput'; +import { useSelect } from '@wordpress/data'; +import { store as nfdOnboardingStore } from '../../../../store'; + +/** + * Centre Step buttons presented in Header. + * + * @return {WPComponent} StepNavigation Component + */ +const StepNavigationCenter = () => { + const activeHomepage = useSelect( + ( select ) => select( nfdOnboardingStore ).getActiveHomepage(), + [] + ); + const [ isInputDisabled, setIsInputDisabled ] = useState( true ); + const [ versionName, setVersionName ] = useState( activeHomepage?.title ); + const isLargeViewport = useViewportMatch( 'medium' ); + + const handleRenameClick = () => { + setIsInputDisabled( false ); + }; + + /** + * Version step Navigation button. + * + * @return {WPComponent} VersionButton Component + */ + const VersionDropDownMenuItems = () => { + return ( + + { ! isLargeViewport && ( + <> + {} }> + + { __( 'Regenrate', 'wp-module-onboarding' ) } + + {} }> + + { __( 'Customize', 'wp-module-onboarding' ) } + + + ) } + + + { __( 'Rename', 'wp-module-onboarding' ) } + + {} }> + { __( 'View All', 'wp-module-onboarding' ) } + + + ); + }; + + /** + * Version step Navigation button. + * + * @param root0 + * @param root0.isInputDisabled + * @return {WPComponent} VersionButton Component + */ + + const VersionButton = () => { + return ( + ( +
+ + + { + if ( event.key === 'Enter' ) { + onToggle(); + } + } } + /> +
+ ) } + renderContent={ VersionDropDownMenuItems } + paddingSize="none" + /> + ); + }; + + return ( +
+ +
+ ); +}; + +export default StepNavigationCenter; diff --git a/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-left.js b/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-left.js new file mode 100644 index 000000000..8e566fa91 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-left.js @@ -0,0 +1,94 @@ +import { useSelect, useDispatch } from '@wordpress/data'; +import { useNavigate } from 'react-router-dom'; +import { Icon, chevronLeft, reusableBlock } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; +import { useViewportMatch } from '@wordpress/compose'; + +import { store as nfdOnboardingStore } from '../../../../store'; + +/** + * Step buttons presented in Header. + * + * @return {WPComponent} StepNavigation Component + */ +const StepNavigationLeft = () => { + const { previousStep, showErrorDialog } = useSelect( ( select ) => { + return { + previousStep: select( nfdOnboardingStore ).getPreviousStep(), + showErrorDialog: select( nfdOnboardingStore ).getShowErrorDialog(), + }; + }, [] ); + + const isLargeViewport = useViewportMatch( 'medium' ); + + /** + * Back step Navigation button. + * + * @param {*} param0 + * + * @return {WPComponent} Back Component + */ + const Back = ( { path } ) => { + const { setNavErrorContinuePath } = useDispatch( nfdOnboardingStore ); + const navigate = useNavigate(); + const navigateBack = () => { + if ( showErrorDialog !== false ) { + setNavErrorContinuePath( path ); + } else { + navigate( path, { state: { origin: 'header' } } ); + } + }; + return ( +
{ + if ( event.key === 'Enter' ) { + navigateBack(); + } + } } + aria-label="Back" + className="navigation-buttons-editor" + > + + { __( 'Back', 'wp-module-onboarding' ) } +
+ ); + }; + + const Regenerate = () => { + const regenerate = () => { + // alert( 'regenerate' ); + }; + return ( +
{ + if ( event.key === 'Enter' ) { + regenerate(); + } + } } + aria-label="Regenerate" + className="navigation-buttons-editor" + > + + { __( 'Regenerate', 'wp-module-onboarding' ) } +
+ ); + }; + + return ( +
+ + { isLargeViewport ? : '' } +
+ ); +}; + +export default StepNavigationLeft; diff --git a/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-right.js b/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-right.js new file mode 100644 index 000000000..3163d7656 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Editor/Header/step-navigation-right.js @@ -0,0 +1,86 @@ +import { useSelect } from '@wordpress/data'; +import { Icon, chevronRight, settings } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; +import { useViewportMatch } from '@wordpress/compose'; + +import { store as nfdOnboardingStore } from '../../../../store'; + +/** + * Step buttons presented in Header. + * + * @return {WPComponent} StepNavigation Component + */ +const StepNavigationRight = () => { + // eslint-disable-next-line no-unused-vars + const { previousStep, showErrorDialog } = useSelect( ( select ) => { + return { + previousStep: select( nfdOnboardingStore ).getPreviousStep(), + showErrorDialog: select( nfdOnboardingStore ).getShowErrorDialog(), + }; + }, [] ); + + const isLargeViewport = useViewportMatch( 'medium' ); + + /** + * Back step Navigation button. + * + * + * @return {WPComponent} Back Component + */ + const Customize = () => { + const customize = () => { + // alert('customize'); + }; + return ( +
{ + if ( event.key === 'Enter' ) { + customize(); + } + } } + aria-label="Customize" + className="navigation-buttons-editor" + > + + { __( 'Customize', 'wp-module-onboarding' ) } +
+ ); + }; + + const Save = () => { + const save = () => { + // alert( 'save' ); + }; + return ( +
{ + if ( event.key === 'Enter' ) { + save(); + } + } } + aria-label="Save" + className="navigation-buttons-editor" + > + { isLargeViewport + ? __( 'Save & Continue', 'wp-module-onboarding' ) + : __( 'Next', 'wp-module-onboarding' ) } + +
+ ); + }; + + return ( +
+ { isLargeViewport ? : '' } + +
+ ); +}; + +export default StepNavigationRight; diff --git a/src/OnboardingSPA/steps/SiteGen/Editor/stylesheet.scss b/src/OnboardingSPA/steps/SiteGen/Editor/stylesheet.scss index 421df366b..e4c544fc9 100644 --- a/src/OnboardingSPA/steps/SiteGen/Editor/stylesheet.scss +++ b/src/OnboardingSPA/steps/SiteGen/Editor/stylesheet.scss @@ -1,3 +1,45 @@ +.nfd-onboarding-header { + + &.nfd-onboarding-header-sg-editor { + background-color: var(--nfd-onboarding-admin-bar-background); + color: var(--nfd-onboarding-admin-bar-color); + } +} + +.nfd-onboarding-header__center { + + .nfd-onboarding-header__center-input { + font-size: 18px; + text-align: center; + border: none; + color: var(--nfd-onboarding-header-contrast); + background-color: var(--nfd-onboarding-header-base); + width: 150px; + } +} + +.components-dropdown__content { + + .components-popover__content { + padding: 0; + + .nfd-onboarding-header__version_dropdown-menu { + width: 240px; + + .components-menu-item__button { + + .components-menu-item__item { + gap: 8px; + } + + &:last-child { + border-top: 1px solid var(--nfd-onboarding-header-border); + } + } + } + } +} + .nfd-onboarding-step { &--site-gen { diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/contents.js b/src/OnboardingSPA/steps/SiteGen/Preview/contents.js index d35c4b3de..c6bc4b107 100644 --- a/src/OnboardingSPA/steps/SiteGen/Preview/contents.js +++ b/src/OnboardingSPA/steps/SiteGen/Preview/contents.js @@ -4,13 +4,15 @@ const getContents = () => { return { heading: __( 'Presto, here are 3 versions', 'wp-module-onboarding' ), subheading: __( - "We've created 3 unique website designs for you to start with, preview click around or start over.", + "We've created 3 unique website designs for you to start with, preview, click around or start over.", 'wp-module-onboarding' ), - favoriteInfo: __( + favouriteNote: __( 'Favorite a generated version to find and use again in the future.', 'wp-module-onboarding' ), + generating: __( 'Generating Site', 'wp-module-onboarding' ), + regenerating: __( 'Regenerating Site', 'wp-module-onboarding' ), }; }; diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/heartAnimation.js b/src/OnboardingSPA/steps/SiteGen/Preview/heartAnimation.js new file mode 100644 index 000000000..ea5d1c430 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Preview/heartAnimation.js @@ -0,0 +1,22 @@ +const HeartAnimation = () => { + return ( + + + + + + ); +}; + +export default HeartAnimation; diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/index.js b/src/OnboardingSPA/steps/SiteGen/Preview/index.js index 96d3635cd..fc47d324c 100644 --- a/src/OnboardingSPA/steps/SiteGen/Preview/index.js +++ b/src/OnboardingSPA/steps/SiteGen/Preview/index.js @@ -1,38 +1,35 @@ import CommonLayout from '../../../components/Layouts/Common'; -import { useNavigate } from 'react-router-dom'; - -import { useEffect, useState } from '@wordpress/element'; - -import { useDispatch, useSelect } from '@wordpress/data'; +import { useEffect, useState, useMemo } 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 LivePreviewSiteGenCard from '../../../components/LivePreview/SiteGenCard'; - +import { SiteGenLivePreview } from '../../../components/LivePreview'; import getContents from './contents'; +import HeartAnimation from './heartAnimation'; +import RegeneratingSiteCard from './regeneratingCard'; import { - getHomepages, - getRandom, -} from '../../../data/sitegen/homepages/homepages'; -import { getColorPalettes } from '../../../data/sitegen/sitemeta/siteMeta'; + getHomePagePreviews, + getRegeneratedHomePagePreviews, + toggleFavoriteHomepage, +} from '../../../utils/api/siteGen'; import { getGlobalStyles } from '../../../utils/api/themes'; -// eslint-disable-next-line import/no-extraneous-dependencies -import { isEmpty, cloneDeep } from 'lodash'; -import Grid from '../../../components/Grid'; - const SiteGenPreview = () => { const navigate = useNavigate(); - const [ homepages, setHomepages ] = useState(); + const [ homepages, setHomepages ] = useState( { active: {}, data: [] } ); + const [ isRegenerating, setIsRegenerating ] = useState( false ); + const [ isPreviewLoading, setIsPreviewLoading ] = useState( false ); const [ globalStyles, setGlobalStyles ] = useState( [] ); + const { setIsHeaderEnabled, setSidebarActiveView, setHeaderActiveView, setDrawerActiveView, setCurrentOnboardingData, - updateInitialize, } = useDispatch( nfdOnboardingStore ); const { currentData, nextStep } = useSelect( ( select ) => { @@ -43,52 +40,47 @@ const SiteGenPreview = () => { }; } ); - const loadData = async () => { - let homepagesObject = {}; - if ( isEmpty( currentData.sitegen.homepages.data ) ) { - const homepagesResponse = getHomepages(); - const colorsResponse = getColorPalettes(); - homepagesResponse.forEach( ( homepage, index ) => { - if ( ! homepage?.color ) { - const paletteKeys = Object.keys( colorsResponse ); - const paletteIndex = - paletteKeys[ index % paletteKeys.length ]; - homepage.color = { - slug: paletteIndex, - palette: colorsResponse[ paletteIndex ], - }; - } - } ); - homepagesResponse.forEach( ( homepage ) => { - homepagesObject[ homepage.slug ] = homepage; - } ); - currentData.sitegen.homepages.data = homepagesObject; - setCurrentOnboardingData( currentData ); - } else { - homepagesObject = currentData.sitegen.homepages.data; - } - const globalStylesResponse = await getGlobalStyles(); - setGlobalStyles( globalStylesResponse.body ); - - setHomepages( homepagesObject ); - }; - useEffect( () => { setIsHeaderEnabled( true ); setSidebarActiveView( false ); setHeaderActiveView( HEADER_SITEGEN ); setDrawerActiveView( false ); - updateInitialize( true ); - loadData(); + }, [ currentData ] ); + + useEffect( () => { + const fetchHomePagesPatterns = async () => { + setIsPreviewLoading( true ); + if ( currentData.sitegen.siteDetails?.prompt !== '' ) { + try { + const response = await getHomePagePreviews( + currentData.sitegen.siteDetails.prompt, + false + ); + + if ( response && response.body ) { + setHomepages( { ...homepages, data: response.body } ); + currentData.sitegen.homepages.data = response.body; + setCurrentOnboardingData( currentData ); + } else if ( response && response.error ) { + setHomepages( { ...homepages, data: [] } ); + } else { + /* Handle Error UI state */ + } + + setIsPreviewLoading( false ); + } catch ( error ) { + setIsPreviewLoading( false ); + } + } + }; + + fetchHomePagesPatterns(); + loadGlobalStyles(); }, [] ); - const handleFavorite = ( slug ) => { - if ( ! ( slug in homepages ) ) { - return false; - } - homepages[ slug ].favorite = ! homepages[ slug ].favorite; - currentData.sitegen.homepages.data = homepages; - setCurrentOnboardingData( currentData ); + const loadGlobalStyles = async () => { + const globalStylesResponse = await getGlobalStyles(); + setGlobalStyles( globalStylesResponse.body ); }; const handlePreview = ( slug ) => { @@ -101,67 +93,187 @@ const SiteGenPreview = () => { navigate( nextStep.path ); }; - const handleRegenerate = ( slug ) => { - if ( ! ( slug in homepages ) ) { - return false; + const scrollSelectionIntoView = () => { + if ( + document.getElementsByClassName( + 'nfd-onboarding-step--site-gen__preview__note' + ) + ) { + document + .getElementsByClassName( + 'nfd-onboarding-step--site-gen__preview__note' + )[ 0 ] + .scrollIntoView( { + behavior: 'smooth', + block: 'end', + } ); } - const page = { ...homepages }; - const newPage = getRandom( { ...page[ slug ] } ); - page[ newPage.slug ] = newPage; - setHomepages( page ); - currentData.sitegen.homepages.data = page; - setCurrentOnboardingData( currentData ); }; - const content = getContents(); + const updateFavoriteStatus = ( slug, homepagesList ) => { + homepagesList.forEach( ( homepageObj ) => { + if ( homepageObj.slug === slug ) { + homepageObj.isFavourited = ! homepageObj.isFavourited; + } + } ); + setCurrentOnboardingData( { ...currentData } ); + }; + + const handleToggleFavoriteSuccess = ( response, slug, homepagesList ) => { + if ( ! response ) { + updateFavoriteStatus( slug, homepagesList ); + } + }; + + const handleToggleFavoriteError = ( error, slug, homepagesList ) => { + updateFavoriteStatus( slug, homepagesList ); + // eslint-disable-next-line no-console + console.error( error ); + }; + + const handleFavorite = ( slug ) => { + const homepagesList = currentData.sitegen.homepages.data; + + if ( homepagesList && homepagesList.length > 0 ) { + updateFavoriteStatus( slug, homepagesList ); + } + + toggleFavoriteHomepage( slug ) + .then( ( response ) => + handleToggleFavoriteSuccess( response, slug, homepagesList ) + ) + .catch( ( error ) => + handleToggleFavoriteError( error, slug, homepagesList ) + ); + }; + + const handleRegenerate = async ( slug, colorPalattes, isFavourited ) => { + scrollSelectionIntoView(); + setIsRegenerating( true ); + if ( ! ( slug in homepages.data ) ) { + if ( currentData.sitegen.siteDetails?.prompt !== '' ) { + try { + const response = await getRegeneratedHomePagePreviews( + currentData.sitegen.siteDetails.prompt, + true, + slug, + colorPalattes, + isFavourited + ); + + if ( + response && + response.body && + response.body.length > 0 + ) { + setHomepages( { + ...homepages.data, + data: response.body, + } ); + currentData.sitegen.homepages.data = response.body; + setCurrentOnboardingData( currentData ); + } else if ( response && response.error ) { + /* Handle Error UI state */ + } else { + /* Handle Error UI state */ + } + + setIsRegenerating( false ); + } catch ( error ) { + setIsRegenerating( false ); + } + } + } + }; + + // Define the createPreviewSettings function inside your component + const createPreviewSettings = ( palette ) => { + let settings = {}; + if ( globalStyles.length > 0 ) { + settings = JSON.parse( JSON.stringify( globalStyles[ 0 ] ) ); + settings.settings.color.palette = palette; + } + return settings; + }; + + // Use useMemo to memoize the previewSettings + const previewSettings = useMemo( () => { + return homepages?.data.map( ( homepage ) => + createPreviewSettings( homepage?.color?.palette ) + ); + }, [ homepages.data, globalStyles ] ); const buildPreviews = () => { - return Object.keys( homepages ).map( ( homepage ) => { - const data = homepages[ homepage ]; - const newPreviewSettings = cloneDeep( globalStyles[ 0 ] ); - newPreviewSettings.settings.color.palette = data.color.palette; + if ( isPreviewLoading ) { return ( - + ); - } ); + } + + const designs = []; + designs.push( + homepages.data && + homepages.data.map( ( homepage, idx ) => { + let newPreviewSettings = {}; + if ( globalStyles.length > 0 ) { + newPreviewSettings = JSON.parse( + JSON.stringify( globalStyles && globalStyles[ 0 ] ) + ); + newPreviewSettings.settings.color.palette = + homepage.color.palette; + } + const isPreviewSettingsEmpty = + Object.keys( previewSettings[ idx ] ).length === 0; + if ( ! isPreviewSettingsEmpty ) { + return ( + + ); + } + return null; + } ) + ); + + return designs; }; + const content = getContents(); + return ( - -
-

- { content.heading } -

-
- { content.subheading } + +
+
+

+ { content.heading } +

+
+
+

+ { content.subheading } +

-
- { homepages && globalStyles && ( - { buildPreviews() } +
+ { buildPreviews() } + { isRegenerating && ( + ) }
-
-
-

- { content.favoriteInfo } -

+
+ + { content.favouriteNote }
); diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/pattern.js b/src/OnboardingSPA/steps/SiteGen/Preview/pattern.js new file mode 100644 index 000000000..46fd2d7fd --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Preview/pattern.js @@ -0,0 +1,636 @@ +/* eslint-disable jsdoc/newline-after-description */ +const pattern1 = `\n
\n \n\t
\n\t\t\n\t\t

Business Consulting Solutions

\n\t\t\n\t\t\n\t\t

Make your company
leader in the industry

\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n\t\n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n
\n \n\n\n
\n \n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n
\n \n\n\n
\n \n
\n \n

Area of Practice

\n \n
\n \n \n\t\n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

01

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Portfolio Management

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

We will work with you to create a personalised plan to help you achieve your financial goals.

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

02

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Performance Reviews

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

We will work with you to create a personalised plan to help you achieve your financial goals.

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

03

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Financial Planning

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

We will work with you to create a personalised plan to help you achieve your financial goals.

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

04

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Portfolio Management

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

We will work with you to create a personalised plan to help you achieve your financial goals.

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

05

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Performance Reviews

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

We will work with you to create a personalised plan to help you achieve your financial goals.

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

06

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Financial Planning

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

We will work with you to create a personalised plan to help you achieve your financial goals.

\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n
\n \n\n\n\n\n\n\n
\n\n \n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t

Explore Our Unmatched Consulting Solutions

\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t

Experience exceptional consulting services at our well-established agency. We offer comprehensive solutions for both established and emerging businesses, tailored to your unique needs.

\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n\n \n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t

Come and Experience Our Unforgettable Cuisine

\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t

Experience exquisite fine dining at our newly opened restaurant. Enjoy a delicious menu of classic and modern dishes, prepared with the freshest ingredients.

\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n\t\n\n
\n\n \n\n
\n \n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t

2.5k

\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t

Clients served

\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t

10k

\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t

Projects completed

\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t

1.2k

\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t

Marketing campaigns

\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t

15

\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t

Full-time consultants

\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n
\n \n\n\n
\n \n\t

★ ★ ★ ★ ★

\n\t\n\t\n\t
\n\t\t\n\t\t

My experience at the restaurant was great. The food was delicious, the service was excellent, and the atmosphere was cozy and inviting. Highly recommend this restaurant.

\n\t\t\n\t
\n\t\n\t\n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\t\n\t\t\t

\n\t\t\t\tAlex Martinez\n\t\t\t

\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t

/

\n\t\t\n\t\t\n\t\t

Customer

\n\t\t\n\t
\n\t\n
\n \n\n\n
\n \n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

We\'re meticulous in our approach
so you can focus on your business.

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Streamlined process

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Flawless events

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t
\n\t\n
\n \n\n\n
\n \n\t
\n\t\t\n\t\t
\n\t\t\t\n\t\t\t

Frequently Asked Questions

\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Is there a free trial available?

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Yes, we offer a free trial period of 14 days. During this period, you will have full access to all of our features and services.

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

How do I change my personal information?

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

You can update name, email address and other personal information from the "Settings" section.

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Can I change my plan later?

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Yes, you can upgrade or downgrade your plan at any time.

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

How does billing work?

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

We have a simple billing system which allows you to pay for services on a monthly basis.

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t

Can I get an invoice for my purchase?

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t

Yes, you can. Please contact our customer support and provide your purchase number.

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t
\n\t\n
\n \n\n\n
\n\n \n\t
\n\n\t\t\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t

Discover Our Consulting Services

\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t\n\t\t\n\t\t

Our Agency\'s Expertise

\n\t\t\n\t
\n\t\n\n \n\t
\n\t\t\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t\n\t\t\n\t\t\n\t
\n\t\n\n
\n`; + +const pattern2 = ` + +
+
+
+
+
+ + + +
+
+

Home

+ + + +

About

+ + + +

Services

+ + + +

Blog

+ + + +

Contact

+ + + + +
+
+
+ + + +
+

Lorem ipsum dolor sit amet, sitelit.

+ + + +

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.

+ + + + +
+
+ + + +
+
+
+
+
+ + + +
+

About Us

+ + + +

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).

+
+
+
+ + + +
+
+

Try Premium for free

+ + + +

We provide 15 days trial to all the users willing to try Rara permium services.

+ + + + +
+
+ + + +
+

Client’s Logo Section

+ + + +
+
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
+
+
+ + + +

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

+
+ + + +
+
Our Company
+ + + + + + + + + + + + + + + + + + + + +
+ + + +
+
Contact Us
+ + + +

Address
5th St, Winter Haven, Florida

+ + + + + + + + +
+ + + +
+
Subscribe Newsletter
+ + + + + +
FOLLow US
+ + + + +
+
+ + + +
+
+ +
+
+
+ +`; + +const pattern3 = ` +
+
+

Travelling on a shoestring

+ + + +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

+ + + + +
+
+ + + +
+
+
+
+
+ + + +
+
+
+ + + +
+
+

10

+ + + +

Years of Experience

+ + + +

We love Travelling

+ + + +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+ + + + +
+
+
+
+ + + +
+

Travelling is art on the move

+ + + +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+ + + + +
+ + + +
+

Your adventure begins here

+
+ + + +
+
+
+

Numbers speak for themselves

+ + + +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

+
+
+ + + +
+
+
+

62

+ + + +

Countries Visited

+
+ + + +
+

134

+ + + +

Adventure Trips

+
+ + + +
+

1500

+ + + +

Happy Customers

+
+
+
+
+ + + +
+
+
+
+

Our experienced travel agents are dedicated to providing top-quality service!

+
+
+ + + +
+ + + +

CONTACT

+ + + +

100 Main Street, Cape Town, 10278

+ + + +

0800 1234 1234

+ + + +

mail@example.com

+
+ + + +
+
+
+
+ + + +
+ +
+ + `; + +const pattern4 = ` +
+
+
+
+
+ + + +
+
+

Home

+ + + +

About

+ + + +

Team

+ + + +

Blog

+ + + +

Contact Us

+ + + + +
+
+
+
+ + + +
+

WP Support

+ + + +

Empowering Your WordPress Journey?

+ + + +

Welcome to WordPress Team, your go-to resource for all things WordPress. Our dedicated team of experts is passionate about empowering WordPress enthusiasts like you. Whether you're a beginner or an experienced user, we're here to provide valuable insights, tips, and tutorials to help you make the most out of your WordPress journey.

+ + + + +
+ + + +
+
+
+

wordpress

+ + + +

WordPress is open source software you can use to create a beautiful website, blog, or app.Beautiful designs, powerful features, and the freedom to build anything you want. WordPress is both free and priceless at the same time.

+ + + +

Email us - simple@gmail.com

+
+ + + +
+

Quick Link

+ + + +

Home

+ + + +

About Us

+ + + +

Shop

+ + + +

Team

+ + + +

Blog

+ + + +

Contact Us

+
+ + + +
+

Products

+ + + +

Home

+ + + +

About Us

+ + + +

Shop

+ + + +

Services

+ + + +

Blog

+ + + +

Contact Us

+
+ + + +
+

Subscribe Newsletter

+ + + +

Join 1,933,300 other subscribers

+ + + + + + +
+
+ + + +
+

+
+ + + +
+
+

Copyright © 2023 wordpress.org

+
+
+
+ +`; + +const homepageData = { + homepages: { + active: {}, + data: [ + { + slug: 'hash1', + title: 'Version 1', + favorite: false, + content: pattern1, + }, + { + slug: 'hash2', + title: 'Version 2', + favorite: false, + content: pattern2, + }, + { + slug: 'hash3', + title: 'Version 3', + favorite: false, + content: pattern3, + }, + { + slug: 'hash4', + title: 'Version 4', + favorite: false, + content: pattern4, + }, + ], + }, +}; + +export { homepageData }; diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/regeneratingCard.js b/src/OnboardingSPA/steps/SiteGen/Preview/regeneratingCard.js new file mode 100644 index 000000000..fc9e952d8 --- /dev/null +++ b/src/OnboardingSPA/steps/SiteGen/Preview/regeneratingCard.js @@ -0,0 +1,25 @@ +import { __ } from '@wordpress/i18n'; + +function RegeneratingSiteCard( { count = 1, isRegenerating } ) { + const cards = []; + for ( let i = 0; i < count; i++ ) { + cards.push( +
+
+

+ { isRegenerating + ? __( 'Regenerating Site', 'wp-module-onboarding' ) + : __( 'Generating Site', 'wp-module-onboarding' ) } +

+
+
+
+
+
+ ); + } + + return <>{ cards }; +} + +export default RegeneratingSiteCard; diff --git a/src/OnboardingSPA/steps/SiteGen/Preview/stylesheet.scss b/src/OnboardingSPA/steps/SiteGen/Preview/stylesheet.scss index 27a3ea8c7..95328801a 100644 --- a/src/OnboardingSPA/steps/SiteGen/Preview/stylesheet.scss +++ b/src/OnboardingSPA/steps/SiteGen/Preview/stylesheet.scss @@ -1,66 +1,176 @@ -.nfd-onboarding-step { +.nfd-onboarding-step--site-gen__preview { + display: flex; + flex-direction: column; + align-items: center; - &--site-gen { + &__container { + display: flex; + justify-content: center; + flex-wrap: wrap; + max-width: 500px; + padding-top: 32px; + margin-bottom: 15px; - &__preview { + &__heading { display: flex; - flex-direction: column; - justify-content: space-evenly; + flex-direction: row; + width: 100%; + padding: 0; + justify-content: center; align-items: center; - &__context { - text-align: center; + &__text { color: var(--nfd-onboarding-primary); - display: flex; - flex-direction: column; - width: 420px; - justify-content: center; - align-items: center; - margin: 16px 0; - - &__heading { - font-size: 28px; - } - - &__subheading { - font-size: 18px; - } + font-size: 28px; + margin-left: 15px; + white-space: normal; + line-height: 1; } + } - &__live_previews { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - width: 100%; - margin-top: 30px; - } + &__sub-heading { + width: 100%; + margin: 10px; + padding: 0; - &__favorite-info { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - background-color: var(--nfd-onboarding-preview-favorite-background); - border-radius: 12px; - margin: 16px 0; - height: 54px; - width: 559px; - - &__icon { - background-image: var(--sitegen-favorite); - height: 20px; - width: 20px; - margin-right: 18px; - } - - &__text { - color: var(--nfd-onboarding-primary); - font-size: 16px; - } + &__text { + text-align: center; + color: var(--nfd-onboarding-primary); + font-size: 18px; + margin: 0; + padding: 0; } + } + } + + &__options { + + @media (max-width: #{ ($break-xlarge) }) { + flex-direction: column; + } + display: flex; + flex-direction: row; + text-align: center; + margin: 10px 10px 55px 10px; + flex-wrap: wrap; + max-width: 1440px; + } + + &__note { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background: var(--nfd-onboarding-card-background); + color: var(--nfd-onboarding-primary); + gap: 18px; + padding: 16px; + font-size: 16px; + line-height: 22px; + text-align: center; + border-radius: 12px; + margin-bottom: 60px; + } + + @keyframes heartBeat { + + 0%, + 100% { + fill: transparent; + transform: scale(1); + transform-origin: center; + } + 33%, + 66% { + fill: var(--nfd-onboarding-heart-icon-fill); + transform: scale(1.1); + transform-origin: center; } + + 67% { + fill: var(--nfd-onboarding-heart-icon-fill); + transform: scale(1.3); + transform-origin: center; + } + + 87% { + fill: var(--nfd-onboarding-heart-icon-fill); + transform: scale(1.1); + transform-origin: center; + } + } + + .heart { + stroke: var(--nfd-onboarding-heart-icon-stroke); + stroke-width: 2px; + } + + .heart path { + animation: heartBeat 4s infinite; + } + +} + +@keyframes infiniteProgress { + + 0% { + width: 0; + } + + 100% { + width: 100%; + } +} + +.regenerating-site-card-wrap { + padding: 20px; + + .regenerating-site-card { + width: 420px; + height: 315px; + border-radius: 20px; + background-color: var(--nfd-onboarding-regenerating-card-background); + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border: 1px solid rgba(var(--nfd-onboarding-primary-rgb), 0.2); + margin-bottom: 20px; + padding: 60px; + box-sizing: border-box; + + &__title { + color: var(--nfd-onboarding-primary); + } + + &__progress-bar { + width: 100%; + background: var(--nfd-onboarding-progress-bar-background); + border-radius: 4px; + height: 8px; + overflow: hidden; + + &__fill { + background: var(--nfd-onboarding-progress-bar-fill); + height: 100%; + transition: width 0.3s ease; + animation: infiniteProgress 1.5s ease-in-out infinite; + } + } + } + + .regenerating-site-card__version { + text-align: left; + color: var(--nfd-onboarding-primary); + font-size: 18px; + display: flex; + gap: 8px; + } + + &.regenerating-site-card-skeleton { + padding: 0; } } diff --git a/src/OnboardingSPA/store/actions.js b/src/OnboardingSPA/store/actions.js index da6ae43d5..be52c9924 100644 --- a/src/OnboardingSPA/store/actions.js +++ b/src/OnboardingSPA/store/actions.js @@ -284,3 +284,24 @@ export function resetNavError() { type: 'RESET_NAV_ERROR', }; } + +export const setHomepagesData = ( homepagesData ) => { + return { + type: 'SET_HOMEPAGES_DATA', + homepagesData, + }; +}; + +export const setActiveHomepage = ( activeHomepage ) => { + return { + type: 'SET_ACTIVE_HOMEPAGE', + activeHomepage, + }; +}; + +export const toggleFavorite = ( slug ) => { + return { + type: 'TOGGLE_FAVORITE', + slug, + }; +}; diff --git a/src/OnboardingSPA/store/reducer.js b/src/OnboardingSPA/store/reducer.js index e475ad0e2..de6067be4 100644 --- a/src/OnboardingSPA/store/reducer.js +++ b/src/OnboardingSPA/store/reducer.js @@ -167,6 +167,32 @@ export function data( state = {}, action ) { ...action.socialData, }, }; + case 'SET_HOMEPAGES_DATA': + return { + ...state, + flowData: { + ...state.flowData, + sitegen: { + ...state.flowData.sitegen, + homepages: action.homepagesData, + }, + }, + }; + + case 'SET_ACTIVE_HOMEPAGE': + return { + ...state, + flowData: { + ...state.flowData, + sitegen: { + ...state.flowData.sitegen, + homepages: { + ...state.flowData.sitegen.homepages, + active: action.activeHomepage, + }, + }, + }, + }; } return state; diff --git a/src/OnboardingSPA/store/selectors.js b/src/OnboardingSPA/store/selectors.js index 1df9bb30e..6b1877429 100644 --- a/src/OnboardingSPA/store/selectors.js +++ b/src/OnboardingSPA/store/selectors.js @@ -426,3 +426,35 @@ export function getCurrentUserDetails( state ) { const currentUserInfo = state.runtime.currentUserDetails; return currentUserInfo; } + +/** + * Gets homepages + * + * @param {*} state + * @return {Object} homepages + */ +export const getHomepagesData = ( state ) => { + return state.data.flowData.sitegen.homepages; +}; + +/** + * Gets actove homepage + * + * @param {*} state + * @return {Object} active + */ + +export const getActiveHomepage = ( state ) => { + return state.data.flowData.sitegen.homepages.active; +}; + +/** + * Gets actove homepage + * + * @param {*} state + * @return {Object} data + */ + +export const getAllHomepages = ( state ) => { + return state.data.flowData.sitegen.homepages.data; +}; diff --git a/src/OnboardingSPA/styles/_branding.scss b/src/OnboardingSPA/styles/_branding.scss index 7cdd7221b..b28787025 100644 --- a/src/OnboardingSPA/styles/_branding.scss +++ b/src/OnboardingSPA/styles/_branding.scss @@ -320,6 +320,10 @@ body { --nfd-onboarding-navigation-back-background: rgba(54, 62, 68, 0.35); --nfd-onboarding-site-logo-border: var(--nfd-onboarding-primary); --nfd-onboarding-header-border: rgba(var(--nfd-onboarding-primary-rgb), 0.3); + --nfd-onboarding-card-overlay: rgba(0, 0, 0, 0.4); + --nfd-onboarding-heart-icon-fill: #ef4a71; + --nfd-onboarding-heart-icon-stroke: #9ca2a7; + --nfd-onboarding-regenerating-card-background: rgba(53, 58, 64, 0.1); --nfd-onboarding-preview-favorite-background: #1e2327; --nfd-onboarding-favorite-fill: #ef4a71; --nfd-onboarding-button-background: #363e4459; @@ -342,10 +346,14 @@ body { --nfd-onboarding-navigation-back-background: rgba(54, 62, 68, 0.35); --nfd-onboarding-site-logo-border: rgba(156, 162, 167, 1); --nfd-onboarding-header-border: rgba(var(--nfd-onboarding-primary-rgb), 0.3); + --nfd-onboarding-card-overlay: rgba(0, 0, 0, 0.4); + --nfd-onboarding-heart-icon-fill: #ef4a71; + --nfd-onboarding-heart-icon-stroke: #9ca2a7; --nfd-onboarding-preview-favorite-background: #1e2327; --nfd-onboarding-editor-header-background: var(--nfd-onboarding-preview-favorite-background); --nfd-onboarding-favorite-fill: #ef4a71; --nfd-onboarding-button-background: #363e4459; + --nfd-onboarding-regenerating-card-background: rgba(53, 58, 64, 0.1); --sitegen-background-low: url(../static/images/sitegen/ai_bg_low.png); } } diff --git a/src/OnboardingSPA/styles/_interface.scss b/src/OnboardingSPA/styles/_interface.scss index 98b81f65e..13b42dec5 100644 --- a/src/OnboardingSPA/styles/_interface.scss +++ b/src/OnboardingSPA/styles/_interface.scss @@ -14,6 +14,10 @@ body { &.is-fullscreen-mode { opacity: 1; transition: opacity ease-in 500ms; + + #wpadminbar { + display: none; + } } &.dashboard_page_nfd-onboarding { diff --git a/src/OnboardingSPA/styles/app.scss b/src/OnboardingSPA/styles/app.scss index 91fcbc046..58882e322 100644 --- a/src/OnboardingSPA/styles/app.scss +++ b/src/OnboardingSPA/styles/app.scss @@ -76,13 +76,13 @@ @import "../steps/DesignHeaderMenu/stylesheet"; @import "../steps/SiteFeatures/stylesheet"; @import "../steps/TheFork/stylesheet"; +@import "../steps/SiteGen/Preview/stylesheet"; @import "../steps/SiteGen/SiteDetails/stylesheet"; @import "../steps/SiteGen/Experience/stylesheet"; @import "../steps/SiteGen/SiteLogo/stylesheet"; @import "../steps/SiteGen/SocialMedia/stylesheet"; @import "../steps/SiteGen/Welcome/stylesheet"; @import "../steps/SiteGen/Building/stylesheet"; -@import "../steps/SiteGen/Preview/stylesheet"; @import "../steps/SiteGen/Editor/stylesheet"; @import "../steps/SiteGen/Editor/Header/stylesheet"; diff --git a/src/OnboardingSPA/utils/api/siteGen.js b/src/OnboardingSPA/utils/api/siteGen.js index 5a66f5da0..453f1f248 100644 --- a/src/OnboardingSPA/utils/api/siteGen.js +++ b/src/OnboardingSPA/utils/api/siteGen.js @@ -14,7 +14,7 @@ export async function getSiteGenIdentifiers() { export async function generateSiteGenMeta( siteInfo, identifier, - skipCache = false + skipCache = true ) { return await resolve( apiFetch( { @@ -28,3 +28,53 @@ export async function generateSiteGenMeta( } ).then() ); } + +export async function getHomePagePreviews( + siteDescription, + regenerate = false +) { + return await resolve( + apiFetch( { + url: onboardingRestURL( 'sitegen/get-homepages' ), + method: 'POST', + data: { + site_description: siteDescription, + regenerate, + }, + } ).then() + ); +} + +export async function getRegeneratedHomePagePreviews( + siteDescription, + regenerate = true, + slug, + colorPalettes, + isFavourited +) { + return await resolve( + apiFetch( { + url: onboardingRestURL( 'sitegen/get-homepages-regenerate' ), + method: 'POST', + data: { + site_description: siteDescription, + regenerate, + slug, + colorPalettes, + isFavourited, + }, + } ).then() + ); +} + +export async function toggleFavoriteHomepage( slug ) { + return await resolve( + apiFetch( { + url: onboardingRestURL( 'sitegen/favourites' ), + method: 'POST', + data: { + slug, + }, + } ).then() + ); +} diff --git a/src/constants.js b/src/constants.js index d5ceaab2e..5d0831e2e 100644 --- a/src/constants.js +++ b/src/constants.js @@ -51,6 +51,7 @@ export const HEADER_END = 'HeaderEnd'; export const FOOTER_START = 'FooterStart'; export const FOOTER_END = 'FooterEnd'; +export const MAX_RETRIES_SITE_GEN = 2; export const MAX_RETRIES_API_QUEUER = 2; export const MAX_RETRIES_SETTINGS_INIT = 2; export const MAX_RETRIES_FLOW_SWITCH = 2;