Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate APIs to AI Onboarding #370

Merged
merged 9 commits into from
Nov 28, 2023
Merged
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
1 change: 1 addition & 0 deletions includes/RestApi/RestApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ final class RestApi {
'NewfoldLabs\WP\\Module\\Onboarding\\RestApi\\Themes\\ThemeFontsController',
'NewfoldLabs\WP\\Module\\Onboarding\\RestApi\\Themes\\ThemeColorsController',
'NewfoldLabs\\WP\\Module\\Onboarding\\RestApi\\SiteClassificationController',
'NewfoldLabs\\WP\\Module\\Onboarding\\RestApi\\SiteGenController',
);

/**
Expand Down
109 changes: 109 additions & 0 deletions includes/RestApi/SiteGenController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

namespace NewfoldLabs\WP\Module\Onboarding\RestApi;

use NewfoldLabs\WP\Module\Onboarding\Permissions;
use NewfoldLabs\WP\Module\Onboarding\Data\Services\SiteGenService;

/**
* Class SiteGenController
*/
class SiteGenController {

/**
* The namespace of this controller's route.
*
* @var string
*/
protected $namespace = 'newfold-onboarding/v1';

/**
* The endpoint base
*
* @var string
*/
protected $rest_base = '/sitegen';

/**
* Registers rest routes for SiteGenController class.
*
* @return void
*/
public function register_routes() {
\register_rest_route(
$this->namespace,
$this->rest_base . '/get-identifiers',
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array( $this, 'get_valid_identifiers' ),
'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
)
);
\register_rest_route(
$this->namespace,
$this->rest_base . '/generate',
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'generate_sitegen_meta' ),
'permission_callback' => array( Permissions::class, 'rest_is_authorized_admin' ),
'args' => $this->sitegen_meta_args(),
)
);
}

/**
* Required Args for Generating Site Gen Meta.
*
* @return array
*/
public function sitegen_meta_args() {
return array(
'site_info' => array(
'required' => true,
'type' => 'object',
),
'identifier' => array(
'required' => true,
'type' => 'string',
),
'skip_cache' => array(
'required' => false,
'type' => 'boolean',
),
);
}

/**
* Gets all the valid Identifiers
*
* @return array
*/
public function get_valid_identifiers() {
return array_keys( array_filter( SiteGenService::get_identifiers() ) );
}

/**
* Generate Sitegen meta data.
*
* @param \WP_REST_Request $request Request model.
*
* @return array|WP_Error
*/
public function generate_sitegen_meta( \WP_REST_Request $request ) {

$site_info = $request->get_param( 'site_info' );
$identifier = $request->get_param( 'identifier' );
$skip_cache = $request->get_param( 'skip_cache' );

if ( SiteGenService::is_enabled() ) {
// TODO Implement the main function and do computations if required.
return SiteGenService::instantiate_site_meta( $site_info, $identifier, $skip_cache );
}

return new \WP_Error(
'sitegen-error',
'SiteGen is Disabled.',
array( 'status' => 404 )
);
}
}
11 changes: 10 additions & 1 deletion src/OnboardingSPA/components/Button/NextButtonSiteGen/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { Button } from '@wordpress/components';
import { Icon, chevronRight } from '@wordpress/icons';
import { store as nfdOnboardingStore } from '../../../store';

const NextButtonSiteGen = ( { text, className, callback = null } ) => {
const NextButtonSiteGen = ( {
text,
className,
callback = null,
disabled = false,
} ) => {
const navigate = useNavigate();
const { nextStep } = useSelect( ( select ) => {
return {
Expand All @@ -16,9 +21,13 @@ const NextButtonSiteGen = ( { text, className, callback = null } ) => {
<Button
className={ classNames(
'nfd-onboarding-button--site-gen-next',
{ 'nfd-onboarding-button--site-gen-next--disabled': disabled },
className
) }
onClick={ () => {
if ( disabled ) {
return;
}
if ( callback && typeof callback === 'function' ) {
callback();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@
height: 30px;
margin-top: 10px;
}

&--disabled {
background-color: rgba(var(--nfd-onboarding-primary-rgb), 0.4);

&:hover {
cursor: not-allowed;
color: var(--nfd-onboarding-secondary);
}
}
}

&--icon {
Expand Down
18 changes: 11 additions & 7 deletions src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,17 @@
const [ percentage, setPercentage ] = useState( 0 );
const [ status, setStatus ] = useState( content.status[ statusIdx ].title );

const { nextStep } = useSelect( ( select ) => {
const { currentData, nextStep } = useSelect( ( select ) => {
return {
currentData:
select( nfdOnboardingStore ).getCurrentOnboardingData(),
nextStep: select( nfdOnboardingStore ).getNextStep(),
};
} );

const checkStatus = async () => {
// Make fake API Call to get the status.
if ( percentage !== 100 ) setPercentage( ( t ) => t + 10 );
};

useEffect( () => {
const statusTimer = setInterval( () => {
checkStatus();
statusIdx += 1;

Check warning on line 24 in src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js

View workflow job for this annotation

GitHub Actions / Run Lint Checks

Assignments to the 'statusIdx' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect
if ( statusIdx === content.status.length ) statusIdx = 0;
setStatus( content.status[ statusIdx ].title );
}, 3000 );
Expand All @@ -34,13 +30,21 @@
};
}, [] );

useEffect( () => {
const percentageValue =
( currentData.sitegen.siteGenMetaStatus.currentStatus /
currentData.sitegen.siteGenMetaStatus.totalCount ) *
100;
setPercentage( percentageValue );
}, [ currentData.sitegen.siteGenMetaStatus.currentStatus ] );

Check warning on line 39 in src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js

View workflow job for this annotation

GitHub Actions / Run Lint Checks

React Hook useEffect has a missing dependency: 'currentData.sitegen.siteGenMetaStatus.totalCount'. Either include it or remove the dependency array

useEffect( () => {
if ( percentage === 100 ) {
if ( nextStep && autoNavigate ) {
navigate( nextStep.path );
}
}
}, [ percentage ] );

Check warning on line 47 in src/OnboardingSPA/components/Loaders/SiteGenLoader/index.js

View workflow job for this annotation

GitHub Actions / Run Lint Checks

React Hook useEffect has missing dependencies: 'autoNavigate', 'navigate', and 'nextStep'. Either include them or remove the dependency array

return (
<div className={ 'nfd-sg-loader' }>
Expand Down
103 changes: 101 additions & 2 deletions src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import NewfoldInterfaceSkeleton from '../index';
import { useEffect } from '@wordpress/element';
import { useLocation } from 'react-router-dom';
import { useSelect, useDispatch } from '@wordpress/data';

import Header from '../../Header';
import Content from '../../Content';
import Sidebar from '../../Sidebar';
import themeToggleHOC from '../themeToggleHOC';
import NewfoldInterfaceSkeleton from '../index';
import ToggleDarkMode from '../../ToggleDarkMode';
import { ThemeProvider } from '../../ThemeContextProvider';
import themeToggleHOC from '../themeToggleHOC';
import { store as nfdOnboardingStore } from '../../../store';
import { setFlow } from '../../../utils/api/flow';
import {
generateSiteGenMeta,
getSiteGenIdentifiers,
} from '../../../utils/api/siteGen';

// Wrapping the NewfoldInterfaceSkeleton with the HOC to make theme available
const ThemedNewfoldInterfaceSkeleton = themeToggleHOC(
Expand All @@ -14,6 +24,95 @@
);

const SiteGen = () => {
const location = useLocation();

const { currentData } = useSelect( ( select ) => {
return {
currentData:
select( nfdOnboardingStore ).getCurrentOnboardingData(),
};
} );

const { setCurrentOnboardingData } = useDispatch( nfdOnboardingStore );

async function syncStoreToDB() {
if ( currentData ) {
//Set the Flow Data and sync store and DB
setFlow( currentData );
}
}

async function generateSiteGenData() {
// Start the API Requests when the loader is shown.
if (
! (
location.pathname.includes( 'experience' ) ||
location.pathname.includes( 'building' )
)
) {
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 );
} else if ( location.pathname.includes( 'building' ) ) {
identifiers = identifiers.slice( midIndex );
}

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 ) )
);
} );
}

const handlePreviousStepTracking = () => {
const previousStep = window.nfdOnboarding?.previousStep;
if ( typeof previousStep !== 'object' ) {
window.nfdOnboarding.previousStep = {
path: location.pathname,
url: window.location.href,
};
return;
}

window.nfdOnboarding.previousStep = {
path: location.pathname,
url: window.location.href,
};
};

useEffect( () => {
syncStoreToDB();
generateSiteGenData();
handlePreviousStepTracking();
}, [ location.pathname ] );

Check warning on line 114 in src/OnboardingSPA/components/NewfoldInterfaceSkeleton/SiteGen/index.js

View workflow job for this annotation

GitHub Actions / Run Lint Checks

React Hook useEffect has missing dependencies: 'generateSiteGenData', 'handlePreviousStepTracking', and 'syncStoreToDB'. Either include them or remove the dependency array

return (
<ThemeProvider>
<ThemedNewfoldInterfaceSkeleton
Expand Down
4 changes: 4 additions & 0 deletions src/OnboardingSPA/steps/SiteGen/SiteDetails/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
if ( currentData.sitegen.siteDetails?.prompt !== '' ) {
setCustomerInput( currentData.sitegen.siteDetails.prompt );
}
}, [] );

Check warning on line 41 in src/OnboardingSPA/steps/SiteGen/SiteDetails/index.js

View workflow job for this annotation

GitHub Actions / Run Lint Checks

React Hook useEffect has missing dependencies: 'currentData.sitegen.siteDetails.prompt', 'setDrawerActiveView', 'setHeaderActiveView', 'setIsHeaderEnabled', and 'setSidebarActiveView'. Either include them or remove the dependency array

const checkAndNavigate = () => {
currentData.sitegen.siteDetails.prompt = customerInput;
Expand All @@ -63,6 +63,10 @@
className={ 'nfd-sg-site-details--next-btn' }
text={ content.buttonText }
callback={ checkAndNavigate }
disabled={
customerInput === undefined ||
customerInput === ''
}
/>
</div>
</div>
Expand Down
30 changes: 30 additions & 0 deletions src/OnboardingSPA/utils/api/siteGen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import apiFetch from '@wordpress/api-fetch';

import { resolve } from './resolve.js';
import { onboardingRestURL } from './common';

export async function getSiteGenIdentifiers() {
return await resolve(
apiFetch( {
url: onboardingRestURL( 'sitegen/get-identifiers' ),
} ).then()
);
}

export async function generateSiteGenMeta(
siteInfo,
identifier,
skipCache = false
) {
return await resolve(
apiFetch( {
url: onboardingRestURL( 'sitegen/generate' ),
method: 'POST',
data: {
site_info: siteInfo,
identifier,
skip_cache: skipCache,
},
} ).then()
);
}