From d3b83b1abf870c1c5ce0c740ab984b4106689506 Mon Sep 17 00:00:00 2001 From: Abhijit Bhatnagar Date: Sat, 21 Jun 2025 06:33:49 +0530 Subject: [PATCH 1/4] update router logic - Prevent direct access to steps without required data - Validate data on component mount - Validation on route changes --- .../components/Step/StepValidationWrapper.js | 40 ++++++++++++ src/app/steps/index.js | 15 +++-- src/app/utils/helpers/stepValidation.js | 65 +++++++++++++++++++ 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 src/app/components/Step/StepValidationWrapper.js create mode 100644 src/app/utils/helpers/stepValidation.js diff --git a/src/app/components/Step/StepValidationWrapper.js b/src/app/components/Step/StepValidationWrapper.js new file mode 100644 index 000000000..ef26ee2f9 --- /dev/null +++ b/src/app/components/Step/StepValidationWrapper.js @@ -0,0 +1,40 @@ +import { useEffect } from '@wordpress/element'; +import { useNavigate, useLocation } from 'react-router-dom'; +import { shouldRedirectToFirstStep } from '../../utils/helpers/stepValidation'; + +/** + * Higher-order component that validates step data and redirects if necessary + * + * @param {React.Component} WrappedComponent - The step component to wrap + * @param {string} stepKey - The key of the step for validation + * @return {React.Component} - The wrapped component with validation logic + */ +export const withStepValidation = ( WrappedComponent, stepKey ) => { + const StepValidationWrapper = ( props ) => { + const navigate = useNavigate(); + const location = useLocation(); + + useEffect( () => { + // Check if we should redirect to the first step + if ( shouldRedirectToFirstStep( stepKey ) ) { + // Only redirect if we're not already on the first step + if ( location.pathname !== '/' ) { + navigate( '/', { replace: true } ); + } + } + }, [ navigate, location.pathname ] ); + + // If we should redirect, don't render the component + if ( shouldRedirectToFirstStep( stepKey ) ) { + return null; + } + + // Otherwise, render the wrapped component + return ; + }; + + // Set display name for debugging + StepValidationWrapper.displayName = `withStepValidation(${ WrappedComponent.displayName || WrappedComponent.name || 'Component' })`; + + return StepValidationWrapper; +}; diff --git a/src/app/steps/index.js b/src/app/steps/index.js index d574885d9..128a6afa3 100644 --- a/src/app/steps/index.js +++ b/src/app/steps/index.js @@ -5,49 +5,50 @@ import { GeneratingStep } from './Generating'; import { PreviewsStep } from './Previews'; import { CanvasStep } from './Canvas'; import { MigrationStep } from './Migration'; +import { withStepValidation } from '../components/Step/StepValidationWrapper'; const STEPS = { fork: { path: '/', order: 10, isRequired: true, - Component: ForkStep, + Component: withStepValidation( ForkStep, 'fork' ), }, intake: { path: '/intake', order: 20, isRequired: true, - Component: IntakeStep, + Component: withStepValidation( IntakeStep, 'intake' ), }, logo: { path: '/logo', order: 30, isRequired: true, - Component: LogoStep, + Component: withStepValidation( LogoStep, 'logo' ), }, generating: { path: '/generating', order: 50, isRequired: true, - Component: GeneratingStep, + Component: withStepValidation( GeneratingStep, 'generating' ), }, previews: { path: '/previews', order: 60, isRequired: true, - Component: PreviewsStep, + Component: withStepValidation( PreviewsStep, 'previews' ), }, design: { path: '/canvas', order: 70, isRequired: true, - Component: CanvasStep, + Component: withStepValidation( CanvasStep, 'design' ), }, migration: { path: '/migration', order: 80, isRequired: false, - Component: MigrationStep, + Component: withStepValidation( MigrationStep, 'migration' ), }, }; diff --git a/src/app/utils/helpers/stepValidation.js b/src/app/utils/helpers/stepValidation.js new file mode 100644 index 000000000..bf4a4e119 --- /dev/null +++ b/src/app/utils/helpers/stepValidation.js @@ -0,0 +1,65 @@ +import { select } from '@wordpress/data'; +import { nfdOnboardingStore } from '../../data/store'; + +/** + * Check if the required data is available for a specific step + * @param {string} stepKey - The key of the step to validate + * @return {boolean} - True if the step has required data, false otherwise + */ +export const hasRequiredDataForStep = ( stepKey ) => { + switch ( stepKey ) { + case 'fork': + // Fork step doesn't require any specific data - it's the entry point + return true; + + case 'intake': + // Intake step doesn't require any previous data - it's where data collection starts + return true; + + case 'logo': { + // Logo step requires site title and prompt from intake + const inputSlice = select( nfdOnboardingStore ).getInputSlice(); + return !! ( inputSlice.siteTitle && inputSlice.prompt ); + } + + case 'generating': { + // Generating step requires site title and prompt + const inputSlice = select( nfdOnboardingStore ).getInputSlice(); + return !! ( inputSlice.siteTitle && inputSlice.prompt ); + } + + case 'previews': { + // Previews step requires homepages to be generated + const sitegenSlice = select( nfdOnboardingStore ).getSiteGenSlice(); + return !! ( sitegenSlice.homepages && Object.keys( sitegenSlice.homepages ).length > 0 ); + } + + case 'design': { + // Canvas step requires a selected homepage + const sitegenSlice = select( nfdOnboardingStore ).getSiteGenSlice(); + return !! ( sitegenSlice.selectedHomepage && sitegenSlice.homepages && sitegenSlice.homepages[ sitegenSlice.selectedHomepage ] ); + } + + case 'migration': + // Migration step is optional and doesn't require specific data + return true; + + default: + return true; + } +}; + +/** + * Check if a step should redirect due to missing data + * @param {string} stepKey - The key of the step to check + * @return {boolean} - True if should redirect, false otherwise + */ +export const shouldRedirectToFirstStep = ( stepKey ) => { + // Don't redirect if we're already on the first step + if ( stepKey === 'fork' ) { + return false; + } + + // Check if the step has required data + return ! hasRequiredDataForStep( stepKey ); +}; From adcdded9d5e2e731298a40dba1a46b04849ad0aa Mon Sep 17 00:00:00 2001 From: Abhijit Bhatnagar Date: Sat, 21 Jun 2025 06:34:28 +0530 Subject: [PATCH 2/4] Update MigrationStep.js the prepareMigration() function was being used before being declared --- src/app/steps/Migration/MigrationStep.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/steps/Migration/MigrationStep.js b/src/app/steps/Migration/MigrationStep.js index 10da48501..700ff0f42 100644 --- a/src/app/steps/Migration/MigrationStep.js +++ b/src/app/steps/Migration/MigrationStep.js @@ -46,10 +46,6 @@ const MigrationStep = () => { ); }; - useEffect( () => { - prepareMigration(); - }, [ prepareMigration ] ); - const prepareMigration = useCallback( async () => { try { if ( ! canMigrateSite ) { @@ -89,6 +85,10 @@ const MigrationStep = () => { } }, [ canMigrateSite, setInstaWpMigrationUrl ] ); + useEffect( () => { + prepareMigration(); + }, [ prepareMigration ] ); + /** * @return { string } The title content */ From 4ff604b7e2b49b5579c6591d30af85c58823a37a Mon Sep 17 00:00:00 2001 From: Abhijit Bhatnagar Date: Sat, 21 Jun 2025 06:49:42 +0530 Subject: [PATCH 3/4] lint fixes --- src/app/steps/Migration/MigrationStep.js | 3 ++- src/app/utils/helpers/stepValidation.js | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/steps/Migration/MigrationStep.js b/src/app/steps/Migration/MigrationStep.js index 700ff0f42..6b3b4017a 100644 --- a/src/app/steps/Migration/MigrationStep.js +++ b/src/app/steps/Migration/MigrationStep.js @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { useCallback, useEffect, useState } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { Container, Title, Spinner } from '@newfold/ui-component-library'; import { ExclamationCircleIcon } from '@heroicons/react/24/solid'; @@ -25,6 +25,7 @@ const MigrationStep = () => { /** * Track migration initiated event + * * @param { string } instaWpMigrationUrl The migration url * @return { Promise } */ diff --git a/src/app/utils/helpers/stepValidation.js b/src/app/utils/helpers/stepValidation.js index bf4a4e119..4c535f14a 100644 --- a/src/app/utils/helpers/stepValidation.js +++ b/src/app/utils/helpers/stepValidation.js @@ -3,6 +3,7 @@ import { nfdOnboardingStore } from '../../data/store'; /** * Check if the required data is available for a specific step + * * @param {string} stepKey - The key of the step to validate * @return {boolean} - True if the step has required data, false otherwise */ @@ -51,6 +52,7 @@ export const hasRequiredDataForStep = ( stepKey ) => { /** * Check if a step should redirect due to missing data + * * @param {string} stepKey - The key of the step to check * @return {boolean} - True if should redirect, false otherwise */ From 120201a0dafe4c9b4904a26648c2c5b132aa9936 Mon Sep 17 00:00:00 2001 From: Abhijit Bhatnagar Date: Sat, 21 Jun 2025 06:53:04 +0530 Subject: [PATCH 4/4] lint fixes 2 --- src/app/steps/Migration/MigrationStep.js | 2 +- src/app/utils/helpers/stepValidation.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/steps/Migration/MigrationStep.js b/src/app/steps/Migration/MigrationStep.js index 6b3b4017a..6c3c3100e 100644 --- a/src/app/steps/Migration/MigrationStep.js +++ b/src/app/steps/Migration/MigrationStep.js @@ -25,7 +25,7 @@ const MigrationStep = () => { /** * Track migration initiated event - * + * * @param { string } instaWpMigrationUrl The migration url * @return { Promise } */ diff --git a/src/app/utils/helpers/stepValidation.js b/src/app/utils/helpers/stepValidation.js index 4c535f14a..e833743a7 100644 --- a/src/app/utils/helpers/stepValidation.js +++ b/src/app/utils/helpers/stepValidation.js @@ -3,7 +3,7 @@ import { nfdOnboardingStore } from '../../data/store'; /** * Check if the required data is available for a specific step - * + * * @param {string} stepKey - The key of the step to validate * @return {boolean} - True if the step has required data, false otherwise */ @@ -52,7 +52,7 @@ export const hasRequiredDataForStep = ( stepKey ) => { /** * Check if a step should redirect due to missing data - * + * * @param {string} stepKey - The key of the step to check * @return {boolean} - True if should redirect, false otherwise */