Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/components/AppBody/AppBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const AppBody = () => {
}, [ location ] );

return (
<div className="nfd-onboarding-body nfd-flex nfd-justify-center nfd-pt-8 nfd-pb-16">
<div className="nfd-onboarding-body nfd-flex nfd-justify-center nfd-py-10 mobile:nfd-py-10">
<div className="nfd-onboarding-body-container nfd-w-full">
<AppErrorBoundary FallbackComponent={ ErrorBoundaryFallback }>{ boot() }</AppErrorBoundary>
</div>
Expand Down
30 changes: 30 additions & 0 deletions src/app/components/BackButton/BackButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
import Navigate from '../Navigate/Navigate';

/**
* BackButton component for header navigation.
*
* @param {Object} props - Component props
* @param {string} props.toRoute - The route to navigate back to
* @param {boolean} props.disabled - Whether the button is disabled
* @return {JSX.Element} A back button with icon and optional text
*/
const BackButton = ( { toRoute = '/', disabled = false } ) => {
return (
<Navigate
toRoute={ toRoute }
direction="backward"
variant="secondary"
disabled={ disabled }
className="!nfd-bg-transparent !nfd-border-0 !nfd-p-0 !nfd-shadow-none hover:!nfd-shadow-none !nfd-flex !nfd-items-center nfd-gap-2 !nfd-text-primary-500 hover:!nfd-text-primary-600 nfd-transition-colors"
>
<ArrowLeftIcon className="nfd-w-5 nfd-h-5 nfd-stroke-[2.5]" />
<span className="mobile:nfd-hidden nfd-text-base nfd-font-semibold">
{ __( 'Back', 'wp-module-onboarding' ) }
</span>
</Navigate>
);
};

export default BackButton;

2 changes: 2 additions & 0 deletions src/app/components/BackButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as BackButton } from './BackButton';

58 changes: 58 additions & 0 deletions src/app/components/BrandLoader/BrandLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useSelect } from '@wordpress/data';
import { nfdOnboardingStore } from '@/data/store';
import bluehostLogo from '../../../Brands/bluehost/step-loader-logo.svg';
import hostgatorLogo from '../../../Brands/hostgator/step-loader-logo.svg';
import crazyDomainsLogo from '../../../Brands/crazy-domains/step-loader-logo.svg';
import webcomLogo from '../../../Brands/webcom/logo.svg';
import wordpressLogo from '../../../Brands/wordpress/step-loader-logo.svg';
import './BrandLoader.scss';

/**
* BrandLoader component - displays brand-specific loading animation
*
* @param {Object} props Component props
* @param {string} props.width Width of the loader (default: '120px')
* @param {string} props.height Height of the loader (default: '120px')
* @param {string} props.alt Alt text for the loader image
* @return {JSX.Element} Brand loader component
*/
const BrandLoader = ( { width = '120px', height = '120px', alt = 'Loading animation' } ) => {
const brandName = useSelect( ( select ) => {
return select( nfdOnboardingStore ).getBrandName();
}, [] );

if ( ! brandName ) {
return null;
}

const normalizedBrand = brandName.toLowerCase().replace( / /g, '-' );

// Map brand names to their imported logos
const brandLogoMap = {
bluehost: bluehostLogo,
'bluehost-india': bluehostLogo,
hostgator: hostgatorLogo,
'hostgator-us': hostgatorLogo,
'hostgator-br': hostgatorLogo,
'crazy-domains': crazyDomainsLogo,
webcom: webcomLogo,
wordpress: wordpressLogo,
};

const logoUrl = brandLogoMap[ normalizedBrand ] || wordpressLogo;

return (
<div
className="brand-loader"
role="img"
aria-label={ alt }
style={ {
width,
height,
backgroundImage: `url(${ logoUrl })`,
} }
/>
);
};

export default BrandLoader;
6 changes: 6 additions & 0 deletions src/app/components/BrandLoader/BrandLoader.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.brand-loader {
display: inline-block;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
1 change: 1 addition & 0 deletions src/app/components/BrandLoader/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as BrandLoader } from './BrandLoader';
30 changes: 24 additions & 6 deletions src/app/components/Header/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { useLocation } from 'react-router-dom';
import { ReactComponent as BluehostLogo } from '@/assets/bluehost-logo.svg';
import { HeaderActions as CanvasStepHeaderActions } from '@/steps/Canvas';
import { HeaderActions as BlueprintCanvasStepHeaderActions } from '@/steps/BlueprintCanvas';
import { BackButton } from '@/components';

// Map of routes that should show a back button in the header
const BACK_NAVIGATION_MAP = {
'/intake': '/',
'/logo': '/intake',
'/generating': '/logo',
'/previews': '/logo',
'/blueprints': '/',
};

const Header = () => {
const [ isCanvasStep, setIsCanvasStep ] = useState( false );
Expand All @@ -23,6 +33,10 @@ const Header = () => {
}
}, [ location ] );

// Determine if the current route should show a back button
const backRoute = BACK_NAVIGATION_MAP[ location.pathname ];
const showBackButton = !! backRoute;

return (
<header
className={ classNames(
Expand All @@ -31,12 +45,16 @@ const Header = () => {
) }
>
<div className="nfd-onboarding-header-container nfd-flex nfd-justify-between nfd-items-center nfd-min-h-16 nfd-px-6 mobile:nfd-px-0 mobile:nfd-max-w-[90%] mobile:nfd-mx-auto">
<BluehostLogo
id="nfd-onboarding-header-logo"
className={ classNames(
( isCanvasStep || isBlueprintCanvasStep ) && 'mobile:nfd-hidden'
) }
/>
{ showBackButton ? (
<BackButton toRoute={ backRoute } />
) : (
<BluehostLogo
id="nfd-onboarding-header-logo"
className={ classNames(
( isCanvasStep || isBlueprintCanvasStep ) && 'mobile:nfd-hidden'
) }
/>
) }
{ isCanvasStep && <CanvasStepHeaderActions /> }
{ isBlueprintCanvasStep && <BlueprintCanvasStepHeaderActions /> }
</div>
Expand Down
5 changes: 2 additions & 3 deletions src/app/components/Iframe/Iframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { forwardRef } from '@wordpress/element';
const Iframe = forwardRef( ( {
title,
src,
width = 400,
width = 400, // eslint-disable-line no-unused-vars
height = 400,
viewportScale = 1,
viewportWidth = 1440,
Expand All @@ -18,8 +18,7 @@ const Iframe = forwardRef( ( {
'nfd-onboarding-iframe nfd-relative nfd-flex nfd-flex-col nfd-overflow-hidden',
) }
style={ {
minWidth: width,
maxWidth: width,
width: '100%',
minHeight: height,
maxHeight: height,
} }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Spinner } from '@newfold/ui-component-library';
import { BrandLoader } from '@/components';

const InteractionBlockingOverlay = ( {
hasLoadingSpinner = false,
Expand All @@ -8,11 +8,9 @@ const InteractionBlockingOverlay = ( {
return (
<div className="nfd-onboarding-ibo nfd-fixed nfd-inset-0 nfd-z-50 nfd-flex nfd-flex-col nfd-items-center nfd-justify-center nfd-gap-5 nfd-opacity-100 nfd-transition-opacity nfd-duration-200 nfd-ease-in-out">
{ hasLoadingSpinner && (
<Spinner
variant="white"
size="8"
className="!nfd-w-12 !nfd-h-12 nfd-z-40"
/>
<div className="nfd-bg-white nfd-rounded-full nfd-p-8 nfd-shadow-lg nfd-z-40">
<BrandLoader width="80px" height="80px" />
</div>
) }
{ hasBackground && (
<div className="nfd-onboarding-ibo-background nfd-absolute nfd-inset-0 nfd-bg-slate-800 nfd-bg-opacity-90" />
Expand Down
110 changes: 60 additions & 50 deletions src/app/components/SiteGenPreviewCard/SiteGenPreviewCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { Button, Spinner } from '@newfold/ui-component-library';
import classNames from 'classnames';
import { Iframe } from '@/components';
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { useState } from '@wordpress/element';

const SiteGenPreviewCard = ( {
screenshot,
frameName,
frameSrc,
onFrameLoad = () => {},
width = '300px',
height = '360px',
width = '280px',
height = '350px',
viewportScale = 0.2,
viewportWidth = 1500,
viewportHeight = 2500,
Expand All @@ -19,6 +20,8 @@ const SiteGenPreviewCard = ( {
tabIndex = 0,
onPreview,
className,
title = '',
isNew = false,
...props
} ) => {
const handleOnPreview = () => {
Expand All @@ -40,12 +43,7 @@ const SiteGenPreviewCard = ( {
</span>
</div>
) }
{ isLoading && (
<Spinner
variant="primary"
size="8"
/>
) }
{ isLoading && <Spinner variant="primary" size="8" /> }
</div>
);
};
Expand All @@ -63,7 +61,7 @@ const SiteGenPreviewCard = ( {
variant="primary"
className={ classNames(
'nfd-z-20 nfd-transition-opacity',
isHovered ? 'nfd-opacity-100' : 'nfd-opacity-0',
isHovered ? 'nfd-opacity-100' : 'nfd-opacity-0'
) }
tabIndex="-1"
>
Expand All @@ -74,47 +72,59 @@ const SiteGenPreviewCard = ( {
};

return (
<div
className={ classNames(
'nfd-onboarding-sitegen-preview-card nfd-relative nfd-bg-cover nfd-bg-top nfd-bg-no-repeat nfd-border nfd-border-slate-30 nfd-rounded nfd-overflow-hidden focus:nfd-outline-none focus:nfd-ring-2 focus:nfd-ring-primary focus:nfd-ring-offset-2 hover:nfd-bg-bottom nfd-transition-[background-position] nfd-duration-[1500ms] hover:nfd-duration-[5000ms]',
! isLoading && ! isError && 'nfd-cursor-pointer',
( isLoading || isError ) && 'nfd-cursor-default',
className,
) }
style={ {
backgroundImage: screenshot ? `url(${ screenshot })` : 'none',
minWidth: width,
maxWidth: width,
minHeight: height,
maxHeight: height,
} }
onClick={ handleOnPreview }
onKeyDown={ ( event ) => {
if ( event.key === 'Enter' ) {
handleOnPreview();
}
} }
role="button"
tabIndex={ tabIndex }
{ ...props }
>
{ ( isLoading || isError ) && <StatusOverlay /> }
{ overlay && ! isLoading && ! isError && <ActionOverlay /> }
{ ! screenshot && frameSrc && (
<Iframe
title={ frameName }
name={ `nfd-onboarding-${ frameName }` }
src={ frameSrc }
width={ width }
height={ height }
viewportWidth={ viewportWidth }
viewportHeight={ viewportHeight }
viewportScale={ viewportScale }
className="nfd-basis-full nfd-absolute nfd-origin-top-left nfd-z-10"
onLoad={ onFrameLoad }
tabIndex="-1"
inert
/>
<div className="nfd-flex nfd-flex-col nfd-gap-5 nfd-w-full">
<div
className={ classNames(
'nfd-onboarding-sitegen-preview-card nfd-relative nfd-bg-cover nfd-bg-top nfd-bg-no-repeat nfd-bg-white nfd-border nfd-border-[#E5E7EB] nfd-overflow-hidden nfd-shadow-sm focus:nfd-outline-none focus:nfd-ring-2 focus:nfd-ring-primary focus:nfd-ring-offset-2 hover:nfd-bg-bottom nfd-transition-[background-position] nfd-duration-[1500ms] hover:nfd-duration-[5000ms]',
! isLoading && ! isError && 'nfd-cursor-pointer hover:nfd-shadow-md',
( isLoading || isError ) && 'nfd-cursor-default',
className
) }
style={ {
backgroundImage: screenshot ? `url(${ screenshot })` : 'none',
width: '100%',
minHeight: height,
maxHeight: height,
borderRadius: '15px',
} }
onClick={ handleOnPreview }
onKeyDown={ ( event ) => {
if ( event.key === 'Enter' ) {
handleOnPreview();
}
} }
role="button"
tabIndex={ tabIndex }
{ ...props }
>
{ ( isLoading || isError ) && <StatusOverlay /> }
{ overlay && ! isLoading && ! isError && <ActionOverlay /> }
{ ! screenshot && frameSrc && (
<Iframe
title={ frameName }
name={ `nfd-onboarding-${ frameName }` }
src={ frameSrc }
width={ width }
height={ height }
viewportWidth={ viewportWidth }
viewportHeight={ viewportHeight }
viewportScale={ viewportScale }
className="nfd-basis-full nfd-absolute nfd-origin-top-left nfd-z-10"
onLoad={ onFrameLoad }
tabIndex="-1"
inert
/>
) }
</div>
{ title && (
<div className="nfd-flex nfd-items-center nfd-gap-2">
<h3 className="nfd-text-base nfd-font-semibold nfd-text-[#111827]">{ title }</h3>
{ isNew && (
<span className="nfd-bg-[#E8E9FF] nfd-text-[#3239CB] nfd-px-3 nfd-py-1 nfd-rounded-full nfd-font-bold nfd-uppercase nfd-letter-spacing-tighter nfd-text-xxs">
{ __( 'New', 'wp-module-onboarding' ) }
</span>
) }
</div>
) }
</div>
);
Expand Down
2 changes: 2 additions & 0 deletions src/app/components/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export { ActionCard as ActionCard } from './ActionCard';
export { AnimateRoutes as AnimateRoutes } from './AnimateRoutes';
export { AppBody as AppBody } from './AppBody';
export { BackButton as BackButton } from './BackButton';
export { BrandLoader as BrandLoader } from './BrandLoader';
export { ErrorBoundaryFallback as ErrorBoundaryFallback } from './ErrorBoundaryFallback';
export { Header as Header } from './Header';
export { Iframe as Iframe } from './Iframe';
Expand Down
8 changes: 2 additions & 6 deletions src/app/steps/BlueprintCanvas/Preview.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useRef, useState, useEffect } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { nfdOnboardingStore } from '@/data/store';
import { Iframe } from '@/components';
import { Spinner } from '@newfold/ui-component-library';
import { Iframe, BrandLoader } from '@/components';

const Preview = () => {
const [ preview, setPreview ] = useState( null );
Expand Down Expand Up @@ -36,10 +35,7 @@ const Preview = () => {
height: `calc(100dvh - ${ appHeaderHeight }px)`,
} }
>
<Spinner
variant="primary"
size="8"
/>
<BrandLoader width="80px" height="80px" />
</div>
);
}
Expand Down
Loading
Loading