Skip to content

Commit 1f01600

Browse files
committed
Prevent onboarding re-entry
An error component will be shown to the user with an action to send them back to the dashboard.
1 parent 0ce55de commit 1f01600

File tree

6 files changed

+165
-25
lines changed

6 files changed

+165
-25
lines changed

includes/Services/StatusService.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ class StatusService {
1919
* @return bool True if the onboarding started was marked, false if it was already marked.
2020
*/
2121
public static function handle_started(): bool {
22-
if ( 'started' !== get_option( Options::get_option_name( 'status' ) ) ) {
22+
$status = get_option( Options::get_option_name( 'status' ) );
23+
if ( 'started' !== $status && 'completed' !== $status ) {
2324
update_option( Options::get_option_name( 'status' ), 'started' );
2425
do_action( 'newfold/onboarding/started' );
2526
return true;

src/app/components/AppBody/AppBody.js

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,36 @@
11
import { Routes, Route, useLocation } from 'react-router-dom';
2-
import { ErrorBoundary } from '@newfold/ui-component-library';
2+
import { ErrorBoundary as AppErrorBoundary } from '@newfold/ui-component-library';
33
import { STEPS } from '@/steps';
44
import { AnimateRoutes, ErrorBoundaryFallback } from '@/components';
55
import { OnboardingEvent, sendOnboardingEvent } from '@/utils/analytics/hiive';
66
import { ACTION_PAGEVIEW } from '@/utils/analytics/hiive/constants';
77

88
const AppBody = () => {
9+
/**
10+
* Check if any conditions prevent the onboarding from being accessed.
11+
* @return {boolean} True if the onboarding can be accessed, false otherwise.
12+
*/
13+
const canAccessOnboarding = () => {
14+
const response = {
15+
result: true,
16+
error: null,
17+
};
18+
19+
// Block if the onboarding has already been completed.
20+
if ( 'completed' === window.nfdOnboarding?.runtime?.status ) {
21+
response.result = false;
22+
response.error = 'onboarding_completed';
23+
}
24+
25+
// Block if missing 'hasAISiteGen' capability.
26+
if ( ! window.NewfoldRuntime?.capabilities?.hasAISiteGen ) {
27+
response.result = false;
28+
response.error = 'no_sitegen_capability';
29+
}
30+
31+
return response;
32+
};
33+
934
/**
1035
* Get the routes for the onboarding steps.
1136
* @return {Array} Array of <Route> components.
@@ -22,6 +47,26 @@ const AppBody = () => {
2247
} );
2348
};
2449

50+
/**
51+
* Boot the onboarding.
52+
* @return {React.ReactNode} The onboarding routes.
53+
*/
54+
const boot = () => {
55+
// Check if the onboarding can be accessed.
56+
const canBoot = canAccessOnboarding();
57+
if ( ! canBoot.result ) {
58+
throw new Error( canBoot.error );
59+
}
60+
61+
return (
62+
<AnimateRoutes>
63+
<Routes>
64+
{ getRoutes() }
65+
</Routes>
66+
</AnimateRoutes>
67+
);
68+
};
69+
2570
// Analytics: Track pageview events.
2671
const location = useLocation();
2772
useEffect( () => {
@@ -31,13 +76,9 @@ const AppBody = () => {
3176
return (
3277
<div className="nfd-onboarding-body nfd-flex nfd-justify-center nfd-py-20">
3378
<div className="nfd-onboarding-body-container">
34-
<ErrorBoundary FallbackComponent={ ErrorBoundaryFallback }>
35-
<AnimateRoutes>
36-
<Routes>
37-
{ getRoutes() }
38-
</Routes>
39-
</AnimateRoutes>
40-
</ErrorBoundary>
79+
<AppErrorBoundary FallbackComponent={ ErrorBoundaryFallback }>
80+
{ boot() }
81+
</AppErrorBoundary>
4182
</div>
4283
</div>
4384
);
Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
11
import { Button, Title } from '@newfold/ui-component-library';
2-
import errorImage from './nfd-err-boundary.png';
2+
import { pluginDashboardPage } from '@/data/constants';
3+
import errorImage from './nfd-err-boundary.svg';
34

4-
const ErrorBoundaryFallback = ( { error } ) => {
5+
const BootError = () => {
6+
return (
7+
<div className="nfd-onboarding-error-boot nfd-h-screen nfd-flex nfd-flex-col nfd-items-center nfd-justify-center nfd-gap-8 nfd-text-center nfd--mt-12 nfd-max-w-[425px]">
8+
<img src={ errorImage } alt="Error occurred" width="290" />
9+
<div>
10+
<Title className="nfd-text-[22px]">{ __( 'Whoops! Looks Like You Took a Wrong Turn' ) }</Title>
11+
<p className="nfd-mt-3 nfd-mb-5 nfd-text-tiny">{ __( "This page is not currently available. Let's send you back to your dashboard where all the good stuff is!" ) }</p>
12+
<Button
13+
size="large"
14+
onClick={ () => window.location.href = pluginDashboardPage }
15+
>
16+
{ __( 'Go to Dashboard' ) }
17+
</Button>
18+
</div>
19+
</div>
20+
);
21+
};
22+
23+
const UnspecifiedError = () => {
524
return (
6-
<div className="nfd-onboarding-error-boundary nfd-flex nfd-flex-col nfd-items-center nfd-gap-8 nfd-text-center nfd-pt-8">
7-
<img src={ errorImage } alt="Error occurred" width="325" />
25+
<div className="nfd-onboarding-error-unspecified nfd-flex nfd-flex-col nfd-items-center nfd-gap-8 nfd-text-center nfd-pt-8">
26+
<img src={ errorImage } alt="Error occurred" width="290" />
827
<div>
928
<Title>{ __( 'Oops! Something Unexpected Happened' ) }</Title>
1029
<p className="nfd-mt-2 nfd-mb-5">{ __( 'We encountered a temporary hiccup, please try again!' ) }</p>
@@ -14,4 +33,23 @@ const ErrorBoundaryFallback = ( { error } ) => {
1433
);
1534
};
1635

36+
const ErrorBoundaryFallback = ( { error } ) => {
37+
const getErrorComponent = () => {
38+
switch ( error.message ) {
39+
case 'onboarding_completed':
40+
return <BootError />;
41+
case 'no_sitegen_capability':
42+
return <BootError />;
43+
default:
44+
return <UnspecifiedError />;
45+
}
46+
};
47+
48+
return (
49+
<div className="nfd-onboarding-error-boundary nfd-flex nfd-flex-col nfd-items-center">
50+
{ getErrorComponent() }
51+
</div>
52+
);
53+
};
54+
1755
export default ErrorBoundaryFallback;

0 commit comments

Comments
 (0)