Your IP : 3.133.13.2


Current Path : /var/www/wptbox/wp-content/plugins/astra-sites/inc/lib/ai-builder/inc/assets/src/pages/
Upload File :
Current File : /var/www/wptbox/wp-content/plugins/astra-sites/inc/lib/ai-builder/inc/assets/src/pages/preview.js

import {
	useEffect,
	useState,
	useLayoutEffect,
	useRef,
} from '@wordpress/element';
import {
	ArrowRightIcon,
	ChevronLeftIcon,
	ExclamationCircleIcon,
} from '@heroicons/react/24/outline';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import SiteSkeleton from '../components/site-skeleton';
import Button from '../components/button';
import { STORE_KEY } from '../store';
import {
	addHttps,
	sendPostMessage as dispatchPostMessage,
} from '../utils/helpers';
import { classNames } from '../helpers';
import ColorPalettes from '../components/color-palettes';
import FontSelector from '../components/font-selector';
import SiteLogo from '../components/site-logo';
import AiBuilderExitButton from '../components/ai-builder-exit-button';
import ResponsiveButtons, {
	RESPONSIVE_MODES,
} from '../components/responsive-buttons';
import { getDataUri } from '../utils/functions';
import { motion, useAnimation } from 'framer-motion';
import { useNavigateSteps } from '../router';
import CustomColorPalette from '../components/custom-color-palette';
import withBuildSiteController from '../hoc/withBuildSiteController';
import LoadingSpinner from '../components/loading-spinner';
import Tooltip from '../components/tooltip';
import { PremiumCrownCircleIcon } from '../ui/icons';

const { logoUrlDark } = aiBuilderVars;

const getAnimationVariants = () => {
	if ( window.innerWidth < 1024 ) {
		return {
			collapsed: { width: '100%', marginLeft: 0 },
			expanded: { width: '100%', marginLeft: 0 },
		};
	}
	return {
		collapsed: { width: '100%', marginLeft: 0 },
		expanded: { width: 'calc(100% - 360px)', marginLeft: '360px' },
	};
};

const sidebarVariants = {
	collapsed: { x: '-100%' },
	expanded: { x: 0 },
};

const skipFeatures = !! aiBuilderVars?.skipFeatures;

const SitePreview = ( { handleClickStartBuilding, isInProgress } ) => {
	const { nextStep } = useNavigateSteps();
	const handleStartBuilding = () => {
		window.open(
			`https://app.zipwp.com/founders-deal?source=${ wpApiSettings?.zipwp_auth?.source }`,
			'_blank'
		);
	};

	const [ loadingIframe, setLoadingIframe ] = useState( true );
	const [ responsiveMode, setResponsiveMode ] = useState(
		RESPONSIVE_MODES.desktop
	);
	const [ collapsed, setCollapsed ] = useState( window.innerWidth < 1024 );
	const { setWebsiteSelectedTemplateAIStep, setSelectedTemplateIsPremium } =
		useDispatch( STORE_KEY );
	const previewAnimationControl = useAnimation();

	const previewContainer = useRef( null );

	const {
		stepData: {
			selectedTemplate,
			templateList,
			businessName,
			selectedImages = [],
			businessContact,
		},
		aiSiteLogo,
		aiSiteTitleVisible,
		aiActiveTypography,
		aiActivePallette,
	} = useSelect( ( select ) => {
		const {
			getWebsiteInfo,
			getAIStepData,
			getSiteLogo,
			getSiteTitleVisible,
			getActiveTypography,
			getActiveColorPalette,
		} = select( STORE_KEY );

		return {
			websiteInfo: getWebsiteInfo(),
			stepData: getAIStepData(),
			aiSiteLogo: getSiteLogo(),
			aiSiteTitleVisible: getSiteTitleVisible(),
			aiActiveTypography: getActiveTypography(),
			aiActivePallette: getActiveColorPalette(),
		};
	}, [] );
	const selectedTemplateItem = templateList?.find(
		( item ) => item?.uuid === selectedTemplate
	);

	const sendPostMessage = ( data ) => {
		dispatchPostMessage( data, 'astra-starter-templates-preview' );
	};

	const isTemplateRestricted =
		selectedTemplateItem?.is_premium &&
		aiBuilderVars?.zip_plans?.active_plan?.slug === 'free';

	const updateScaling = () => {
		const container = previewContainer.current;
		if ( ! container ) {
			return;
		}

		const iframe = container.children[ 1 ];
		const containerWidth = container.clientWidth;
		const containerHeight = container.clientHeight - 44;
		const iframeWidth = iframe.clientWidth;
		const scaleX = containerWidth / iframeWidth;
		const scaleValue = scaleX;

		if ( responsiveMode.name !== 'desktop' ) {
			// Check if overflowing.
			iframe.removeAttribute( 'style' );
			if ( containerWidth > iframeWidth ) {
				return;
			}
		}

		// Set the scale for both width and height
		iframe.style.transform = `scale(${ scaleValue })`;
		iframe.style.transformOrigin = 'top left';
		iframe.style.height = `${ containerHeight / scaleValue }px`;
	};

	const handleIframeLoading = () => {
		if ( ! selectedImages?.length ) {
			selectedImages.push( aiBuilderVars?.placeholder_images[ 0 ] );
			selectedImages.push( aiBuilderVars?.placeholder_images[ 1 ] );
		}

		if ( aiSiteLogo?.url ) {
			const mediaData = { ...aiSiteLogo };
			if ( window.location.protocol === 'http:' ) {
				getDataUri( mediaData.url, function ( data ) {
					mediaData.dataUri = data;
				} );
			}
			setTimeout( () => {
				sendPostMessage( {
					param: 'siteLogo',
					data: mediaData,
				} );
				sendPostMessage( {
					param: 'siteTitle',
					data: aiSiteTitleVisible,
				} );
			}, 100 );
		}

		if ( ! aiActivePallette?.slug?.includes( 'default' ) ) {
			sendPostMessage( {
				param: 'colorPalette',
				data: aiActivePallette,
			} );
		}

		if ( ! aiActiveTypography?.default ) {
			sendPostMessage( {
				param: 'siteTypography',
				data: aiActiveTypography,
			} );
		}

		if ( Object.values( businessContact ).some( Boolean ) ) {
			const updatedData = [
				{
					type: 'phone',
					value: businessContact.phone,
					fallback: '202-555-0188',
				},
				{
					type: 'email',
					value: businessContact.email,
					fallback: 'contact@example.com',
				},
				{
					type: 'address',
					value: businessContact.address,
					fallback: '2360 Hood Avenue, San Diego, CA, 92123',
				},
			];
			sendPostMessage( {
				param: 'contactInfo',
				data: updatedData,
			} );
		}

		sendPostMessage( {
			param: 'images',
			data: {
				...selectedImages,
			},
			preview_type: 'full',
		} );

		if ( selectedTemplateItem?.content ) {
			sendPostMessage( {
				param: 'content',
				data: selectedTemplateItem.content,
				businessName,
			} );
		}

		setLoadingIframe( false );
		updateScaling();
	};

	const updateZipPlanData = async () => {
		await apiFetch( {
			path: 'zipwp/v1/zip-plan',
			method: 'POST',
			headers: {
				'X-WP-Nonce': aiBuilderVars.rest_api_nonce,
			},
		} ).then( ( response ) => {
			if ( response.success ) {
				// setZipPlans( response.data );
			} else {
				//  Handle error.
			}
		} );
	};

	useEffect( () => {
		updateZipPlanData();
	}, [] );

	const handleResize = () => {
		if ( window.innerWidth < 1024 ) {
			setCollapsed( true );
		} else {
			setCollapsed( false );
		}
	};

	// Check for window resize.
	useLayoutEffect( () => {
		const resizeObserver = new ResizeObserver( handleResize );
		resizeObserver.observe( window.document.body );
		return () => {
			resizeObserver.unobserve( window.document.body );
		};
	}, [] );

	useLayoutEffect( () => {
		requestAnimationFrame( updateScaling );
	}, [ responsiveMode, collapsed ] );

	useLayoutEffect( () => {
		const resizeObserver = new ResizeObserver( updateScaling );
		resizeObserver.observe( window.document.body );
		return () => {
			resizeObserver.unobserve( window.document.body );
		};
	}, [ responsiveMode ] );

	const toggleCollapse = async () => {
		setCollapsed( ( prev ) => ! prev );
		await previewAnimationControl.start( {
			opacity: [ 0, 0, 1 ],
			transition: {
				type: 'spring',
				mass: 0.4,
				damping: 600,
				stiffness: 600,
				duration: 0.6,
				ease: 'easeOut',
			},
		} );
	};

	const renderBrowserFrame = () => {
		const isDesktopOrTablet =
			responsiveMode?.name === 'desktop' ||
			responsiveMode?.name === 'tablet';
		const isMobile = responsiveMode?.name === 'mobile';

		const message = __(
			'This is just a sneak peek. The actual website and its content will be created in the next step.',
			'ai-builder'
		);

		const TooltipContent = () => <p>{ message }</p>;

		return (
			<div
				className={ classNames(
					'flex items-center py-3 px-4 bg-white shadow-sm rounded-t-lg mx-auto h-[44px] z-[1] group',
					responsiveMode?.name === 'desktop' &&
						'w-full mx-0 relative justify-between md:justify-start',
					responsiveMode?.name === 'tablet' &&
						'w-[800px] justify-between md:justify-start',
					responsiveMode?.name === 'mobile' &&
						'w-[400px] justify-between'
				) }
			>
				<div className="flex gap-2 py-[3px] w-20">
					<div className="w-[14px] h-[14px] border border-solid border-border-primary rounded-full" />
					<div className="w-[14px] h-[14px] border border-solid border-border-primary rounded-full" />
					<div className="w-[14px] h-[14px] border border-solid border-border-primary rounded-full" />
				</div>
				{ isDesktopOrTablet && (
					<div className="flex-grow flex justify-end md:justify-center items-center relative">
						<p className="absolute md:static top-1 sm:top-2 right-10 sm:right md:right-80 lg:right-24 xl:right-52 px-2 py-1 bg-white rounded-md max-md:shadow-lg text-center !text-sm !text-zip-body-text !m-0 max-md:hidden">
							{ message }
						</p>
						<Tooltip
							content={ <TooltipContent /> }
							placement="right"
							offset={ [ 10, 0 ] }
							className="zw-tooltip__material"
							arrow={ false }
						>
							<ExclamationCircleIcon className="w-[18px] text-gray-600 cursor-pointer block md:hidden" />
						</Tooltip>
					</div>
				) }
				{ isMobile && (
					<>
						<Tooltip
							content={ <TooltipContent /> }
							placement="right"
							offset={ [ 10, 0 ] }
							className="zw-tooltip__material"
						>
							<ExclamationCircleIcon className="w-[18px] text-gray-600 cursor-pointer block" />
						</Tooltip>
					</>
				) }
			</div>
		);
	};

	return (
		<motion.div
			id="spectra-onboarding-ai"
			key="spectra-onboarding-ai"
			className="relative font-sans flex flex-wrap h-screen w-screen"
			initial={ { opacity: 0, scale: 0.95 } }
			animate={ { opacity: 1, scale: 1, transition: { duration: 0.2 } } }
			exit={ { opacity: 1 } }
			transition={ { type: 'tween' } }
		>
			<motion.div
				className={ classNames(
					'absolute top-0 left-0 flex w-[360px] lg:flex-col z-10 h-screen'
				) }
				onTransitionEnd={ updateScaling }
				initial={ false }
				animate={ collapsed ? 'collapsed' : 'expanded' }
				transition={ {
					duration: 0.25,
					ease: 'easeInOut',
					delay: 0.05,
				} }
				variants={ sidebarVariants }
			>
				<div className="flex grow flex-col gap-y-5 overflow-y-auto border-r border-gray-200 bg-zip-dark-theme-bg px-6">
					<div className="mt-3 flex h-16 shrink-0 items-center relative">
						<img
							className="h-10"
							src={ logoUrlDark }
							alt={ __( 'Build with AI', 'ai-builder' ) }
						/>
						{ /* Exit button */ }
						<div className="absolute top-3 right-0">
							<AiBuilderExitButton exitButtonClassName="text-icon-tertiary" />
						</div>
					</div>
					<nav className="flex flex-1 flex-col gap-y-1">
						<div className="w-full mt-2">
							<div
								className="space-y-5"
								data-disabled={ isInProgress }
							>
								<div>
									<SiteLogo />
								</div>
								<div>
									<FontSelector />
								</div>
								<div>
									<ColorPalettes />
								</div>
								{ aiActivePallette?.slug === 'custom' && (
									<div>
										<CustomColorPalette />
									</div>
								) }
							</div>
						</div>
						{ isTemplateRestricted ? (
							<div className="mt-5">
								<div className="flex flex-col p-4 rounded-md border border-solid border-alert-info-text gap-4 bg-transparent text-white">
									<div className="flex gap-4 flex-col">
										<div className="flex gap-3 items-center leading-[1em]">
											<span>
												<PremiumCrownCircleIcon
													className={ 'w-5 h-5' }
												/>
											</span>
											<h4 className="text-base font-semibold leading-[1em] tracking-normal text-left text-white">
												{ __(
													'Premium Design',
													'ai-builder'
												) }
											</h4>
										</div>
										<p className="text-brand-secondary-200 text-white text-sm">
											{ __(
												"You've chosen a Premium Design. Access this design and all others with our paid plans starting at just $79.",
												'ai-builder'
											) }
										</p>
									</div>

									<Button
										className="w-full h-10 font-semibold text-sm leading-5"
										variant="primary"
										size="l"
										onClick={ handleStartBuilding }
									>
										{ __(
											'Unlock the Access',
											'ai-builder'
										) }
									</Button>
								</div>
							</div>
						) : (
							<></>
						) }
						<div className="mt-8 mb-5 space-y-5">
							{ ! isTemplateRestricted && (
								<Button
									className="h-10 w-full font-semibold text-sm leading-5"
									onClick={
										skipFeatures
											? handleClickStartBuilding( true )
											: nextStep
									}
									variant="primary"
									hasSuffixIcon={ ! isInProgress }
								>
									{ isInProgress ? (
										<LoadingSpinner className="w-5 h-5" />
									) : (
										<>
											<span>
												{ skipFeatures ? (
													<span>
														{ __(
															'Start Building',
															'ai-builder'
														) }
													</span>
												) : (
													__(
														'Continue',
														'ai-builder'
													)
												) }
											</span>
											<ArrowRightIcon className="w-5 h-5" />
										</>
									) }
								</Button>
							) }
							<Button
								className="mx-auto text-white h-10 w-full font-semibold text-sm leading-5 bg-zip-dark-theme-content-background"
								variant="blank"
								onClick={ () => {
									setWebsiteSelectedTemplateAIStep( '' );
									setSelectedTemplateIsPremium( '' );
								} }
								disabled={ isInProgress }
							>
								<span>
									{ __(
										'Back to Other Designs',
										'ai-builder'
									) }
								</span>
							</Button>
						</div>

						{ /* Responsive preview buttons */ }
						<div
							className="mt-auto mb-6 flex items-center justify-between gap-3"
							data-disabled={ isInProgress }
						>
							<span className="text-zip-dark-theme-body text-sm font-semibold">
								{ __( 'Responsive Preview', 'ai-builder' ) }
							</span>
							<ResponsiveButtons
								onChange={ setResponsiveMode }
								value={ responsiveMode }
							/>
						</div>
					</nav>
				</div>
				<button
					className="absolute top-[45%] left-full flex items-center justify-center w-4 h-14 bg-zip-dark-theme-bg shadow-sm rounded-tr rounded-br border border-solid border-zip-dark-theme-border cursor-pointer focus:outline-none"
					onClick={ toggleCollapse }
				>
					<ChevronLeftIcon
						className={ classNames(
							'w-4 h-4 text-white scale-110 stroke-2 !shrink-0 transform transition-transform duration-200 ease-in-out',
							collapsed ? 'rotate-180' : 'rotate-0'
						) }
					/>
				</button>
			</motion.div>

			<motion.main
				id="sp-onboarding-content-wrapper"
				className="flex-1 overflow-hidden h-screen max-w-full bg-white transition-all duration-200 ease-in-out"
				initial={ false }
				animate={ collapsed ? 'collapsed' : 'expanded' }
				transition={ { duration: 0.1, ease: 'easeInOut' } }
				variants={ getAnimationVariants() }
				onUpdate={ () => requestAnimationFrame( updateScaling ) }
				onTransitionEnd={ () => requestAnimationFrame( updateScaling ) }
			>
				<div className="h-full w-full relative flex">
					<div
						className={ `w-full max-h-full flex flex-col flex-auto items-center bg-preview-background overflow-hidden` }
					>
						<div className="w-full h-full flex-1">
							{ loadingIframe && (
								<div className="w-full h-full p-8 overflow-y-hidden bg-zip-app-light-bg text-center">
									{ renderBrowserFrame() }
									<SiteSkeleton className="shadow-template-preview !h-[calc(100%_-_44px)]" />
								</div>
							) }

							{ selectedTemplateItem?.domain && (
								<motion.div
									className={ classNames(
										'w-full h-full p-8',
										responsiveMode?.name !== 'desktop' &&
											'relative'
									) }
									animate={ previewAnimationControl }
								>
									<div
										ref={ previewContainer }
										className={ classNames(
											'h-full mx-auto shadow-template-preview',
											responsiveMode?.name ===
												'desktop' && 'w-full mx-0',
											responsiveMode?.name === 'tablet' &&
												'w-[800px]',
											responsiveMode?.name === 'mobile' &&
												'w-[400px]',
											responsiveMode?.name ===
												'desktop' &&
												'overflow-hidden relative'
										) }
									>
										{ renderBrowserFrame() }
										<div
											className={ classNames(
												'h-full bg-zip-app-light-bg mx-auto',
												responsiveMode?.name ===
													'desktop' &&
													'w-[1700px] mx-0',
												responsiveMode?.name ===
													'tablet' && 'w-[800px]',
												responsiveMode?.name ===
													'mobile' && 'w-[400px]'
											) }
										>
											<motion.iframe
												className={ classNames(
													'h-full z-[1]',
													responsiveMode?.name ===
														'desktop' &&
														'w-[1700px]',
													responsiveMode?.name ===
														'tablet' && 'w-[800px]',
													responsiveMode?.name ===
														'mobile' && 'w-[400px]'
												) }
												id="astra-starter-templates-preview"
												title="Website Preview"
												height="100%"
												width={
													responsiveMode?.width ??
													'100%'
												}
												src={
													addHttps(
														selectedTemplateItem.domain
													) + '?preview_demo=yes'
												}
												onLoad={ handleIframeLoading }
											/>
										</div>
									</div>
								</motion.div>
							) }
						</div>
					</div>
				</div>
			</motion.main>
		</motion.div>
	);
};

export default withBuildSiteController( SitePreview );