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

Homepage Plugins nudge - UI #350

Merged
merged 24 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
110884b
Home page plugins nudge
aratidgr8 Sep 11, 2024
0451508
Logic to display solutions banner component on Home page
aratidgr8 Sep 11, 2024
e05643e
Logic to display solutions banner component on Home page
aratidgr8 Sep 11, 2024
e291d37
Remove WPSolutionsBanner from Ecommerce and load directly in BlueHost
aratidgr8 Sep 11, 2024
50098e6
Export WPSolutionsBanner file as component from Ecommerce module to B…
aratidgr8 Sep 11, 2024
c3867d2
No existing plan view of Homepage - PRESS0-2229
aratidgr8 Sep 13, 2024
f6f3af3
No existing plan view of Homepage - PRESS0-2229
aratidgr8 Sep 13, 2024
aefdd57
PRESS0-2231: Store page CTA for Plugins & Tools page
aratidgr8 Sep 16, 2024
1db15d8
PRESS0-1719: Add WP entitlements to Next Steps
aratidgr8 Sep 24, 2024
e9517a1
Merge branch 'trunk' into PRESS0-2212
wpscholar Sep 26, 2024
405515f
Updated Solutions Banner Component as per entitlements API response
aratidgr8 Sep 30, 2024
3932eba
Updated code for homepage banner as per solutions API call response -…
aratidgr8 Oct 1, 2024
e253bc4
Remove whitespace difference change from src/components/OnboardingScr…
aratidgr8 Oct 1, 2024
2083610
Added TODO comment
aratidgr8 Oct 1, 2024
f1ff71a
Correct error text color
aratidgr8 Oct 1, 2024
6d279d4
Code updated as per Solutions Entitlement API
aratidgr8 Oct 4, 2024
316fbae
Remove commented code
aratidgr8 Oct 4, 2024
4903755
Add HTML tags handling for No existing plans features section
aratidgr8 Oct 4, 2024
13fbb5a
Remove commented code
aratidgr8 Oct 4, 2024
abc71ee
Organize Imports
aratidgr8 Oct 4, 2024
8e13162
Code review comments resolved
aratidgr8 Oct 8, 2024
fff299d
custom button text
aratidgr8 Oct 8, 2024
bcde247
Plugin install & activate logic added in Homepage Banner
aratidgr8 Oct 9, 2024
d634220
PR comments
aratidgr8 Oct 9, 2024
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 build/index.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '00a9f8f0dd08a3e19f74');
<?php return array('dependencies' => array('lodash', 'moment', 'react', 'react-dom', 'wp-api-fetch', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '85c8530ae08abcab339a');
81 changes: 81 additions & 0 deletions src/components/NoExistingPlan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Button } from "@newfold/ui-component-library";
import { useEffect, useState } from "@wordpress/element";
import { __ } from "@wordpress/i18n";
import { Section } from "./Section";
import { ReactComponent as GreenTick } from "../icons/green-tick.svg";
import { ReactComponent as FiftyOff } from "../icons/fifty-off.svg";


export function NoExistingPlan(props){
const { availableSolutions } = props;

const HtmlContent = ({ htmlString }) => {
return (
<div
dangerouslySetInnerHTML={{ __html: htmlString }}
/>
);
};


return(
<Section.Container className="nfd-container">
<Section.Header
title={__("Upgrade your plan for success.", "wp-module-ecommerce")}
subTitle={__("Choose the set of plugins and services that's right for your needs. You're covered by our no-quibble, 30 day money back guarantee.", "wp-module-ecommerce")}
/>
<Section.Content className="nfd-app-section-home">
{
availableSolutions?.map((solution) => {
return(
<div className="nfd-border nfd-border-[#E2E8F0] nfd-p-6 nfd-rounded-lg nfd-mb-6 nfd-relative">
<FiftyOff className="nfd-absolute nfd-top-0 nfd-right-0 nfd-hidden" />
<h2 className="nfd-text-[#0F172A] nfd-text-lg nfd-font-semibold nfd-mb-5">
{ __(`${solution.name.toUpperCase()}`, "wp-module-ecommerce") }
</h2>
<div className="nfd-flex nfd-flex-row">
<p className="nfd-text-[#0F172A] nfd-text-base nfd-mb-7 nfd-w-[480px]">
{__(`${solution.description}`, "wp-module-ecommerce")}
</p>
<p className="nfd-text-[#000000] nfd-text-4xl nfd-font-extrabold nfd-ml-auto nfd-mr-10">
{solution.price}
<span className="nfd-text-2xl nfd-text-[#000000] nfd-font-semibold nfd-relative nfd--top-[5px]">
{__("/mo", "wp-module-ecommerce")}
</span>
<br />
<span className="nfd-text-sm nfd-text-[#404040] nfd-font-normal nfd-relative nfd--top-9 nfd-hidden">
<span>{__("Original price ", "wp-module-ecommerce")}</span>
<span className="nfd-line-through">{solution.fullPrice}</span>
<span>{__("/mo", "wp-module-ecommerce")}</span>
</span>
</p>
</div>
<div className="nfd-flex nfd-flex-row nfd--mt-8">
<ul className="nfd-mr-6 nfd-grid nfd-grid-cols-2 nfd-gap-x-6 nfd-mt-6">
{
solution?.features.map((feat, index) => {
return(
<li className={classNames( "nfd-flex", "nfd-flex-row", "nfd-items-center",
{ 'nfd-border-b nfd-border-[#cccccc] nfd-mb-3 nfd-pb-3': (index === 0 || index === 1) })}
>
<GreenTick className="nfd-mt-1.5 nfd-mr-2" />
<span className="nfd-text-[#404040] nfd-text-base">
<HtmlContent htmlString={__(`${feat}`, "wp-module-ecommerce")} />
</span>
</li>
)
})
}
</ul>
<Button as="a" href={solution.url} className="nfd-button nfd-button--secondary nfd-self-end nfd-ml-auto">
{__("Learn more", "wp-module-ecommerce")}
</Button>
</div>
</div>
)
})
}
</Section.Content>
</Section.Container>
)
}
106 changes: 106 additions & 0 deletions src/components/WPSolutionsBanner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { NewfoldRuntime } from "../sdk/NewfoldRuntime";
import { Button, Spinner } from "@newfold/ui-component-library";
import apiFetch from '@wordpress/api-fetch';
import { useEffect, useState } from "@wordpress/element";
import { __ } from "@wordpress/i18n";
import { wpSolutionsPromotedPluginsList } from "../constants";
import { ReactComponent as RightArrow } from "../icons/right-arrow.svg";
import { NoExistingPlan } from "./NoExistingPlan";
import { Section } from "./Section";

export function WPSolutionsBanner() {

const entitlementsEndPoint = NewfoldRuntime.createApiUrl("/newfold-solutions/v1/entitlements");
const [ error, setError ] = useState(null);
const [ apiResponse, setApiResponse ] = useState(null);
const [ isLoaded, setIsLoaded ] = useState( false );
const [ purchasedSolution, setPurchasedSolution] = useState(null)
const [ availableSolutions, setAvailableSolutions] = useState([]);
let currentSolution = [];


const myPluginsAndToolsPageLink = `${window.location.href.split('#')[0]}#/my_plugins_and_tools`;
const routeChange = () =>{
location.href = myPluginsAndToolsPageLink;
}

useEffect( () => {
apiFetch( { url: `${ entitlementsEndPoint }` } ).then(
( result ) => {
setIsLoaded(true);
setApiResponse(result)
setPurchasedSolution(result['solution'])
setAvailableSolutions(result['solutions'])
},
( error ) => {
setIsLoaded(true);
setError(error);
}
);
}, [] );

if ( error ) {
console.log(error.message, "error");
return (
<div className="nfd-flex nfd-p-6 nfd-bg-white nfd-w-full nfd-rounded-lg nfd-text-red-700">
<ExclamationTriangleIcon className="nfd-w-[24px] nfd-h-[24px]" />
<span className="nfd-ml-1.5">{__("Oops! something went wrong. Please try again later", "wp-module-ecommerce")}</span>
</div>
);
}
else if (!isLoaded){
return (
<div className="nfd-flex nfd-items-center nfd-text-center nfd-justify-center nfd-h-full">
<Spinner size="8" className="nfd-text-primary" />
</div>
);
}
else if (apiResponse) {
if (purchasedSolution === null) {
return (<NoExistingPlan availableSolutions={availableSolutions} />);
}
else{
currentSolution = purchasedSolution === "WP_SOLUTION_CREATOR" ?
wpSolutionsPromotedPluginsList[0]['WP_SOLUTION_CREATOR'] :
purchasedSolution === "WP_SOLUTION_SERVICE" ?
wpSolutionsPromotedPluginsList[0]['WP_SOLUTION_SERVICE'] :
purchasedSolution === "WP_SOLUTION_COMMERCE" ?
wpSolutionsPromotedPluginsList[0]['WP_SOLUTION_COMMERCE'] : wpSolutionsPromotedPluginsList[0]['none'];
let solutionsCards = Object.values(currentSolution);
return(
<Section.Container className="nfd-container">
<Section.Header
title={__("Explore Your Plugins and Tools", "wp-module-ecommerce")}
subTitle={__("Improve your site with the tools and services included in your plan.", "wp-module-ecommerce")}
secondaryAction={{title : __( "View all your plugins and tools", "wp-module-ecommerce" ), className: false, onClick: routeChange }}
/>
<Section.Content className="nfd-app-section-home">
<div className="nfd-flex nfd-flex-row nfd-flex-wrap">
{
solutionsCards?.map((details, index) => {
return (<div className={"nfd-flex nfd-flex-col nfd-bg-[#F1F5F7] nfd-m-3 nfd-p-6 nfd-rounded-lg nfd-border nfd-border-[#E2E8F0] nfd-box-content "+ (index === 0 || index === 3 ? "nfd-w-[474px]" : "nfd-w-[300px]") }>
<h2 className="nfd-text-[#0F172A] nfd-text-lg nfd-leading-5 nfd-font-semibold nfd-mb-4">
{ __(`${details['title']}`,"wp-module-ecommerce") }
</h2>
<p className="nfd-text-[#0F172A] nfd-text-lg nfd-leading-5 nfd-font-normal nfd-mb-10">
{ __(`${details['description']}`,"wp-module-ecommerce") }
</p>
<Button className="nfd-button nfd-button--primary nfd-mt-9 nfd-mt-auto nfd-self-start" as="a" href="">
{ __(`${details['buttonText']}`,"wp-module-ecommerce") }
<RightArrow className="nfd-mt-2.5" />
</Button>
</div>)
})
}
</div>
<Button as="a" href={myPluginsAndToolsPageLink} className="nfd-button nfd-button--secondary nfd-flex nfd-w-56 nfd-mx-auto nfd-mt-3">
{__("View all your plugins and tools", "wp-module-ecommerce")}
</Button>
</Section.Content>
</Section.Container>
)
}

}
}
6 changes: 5 additions & 1 deletion src/components/YITHPlugins.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { __ } from "@wordpress/i18n";
import { Modal, Spinner } from "@newfold/ui-component-library";
import { Button, Modal, Spinner } from "@newfold/ui-component-library";
import { YITHPluginsDefinitions } from "../configs/YITHPlugins.config";
import { Section } from "./Section";
import { useCardManager } from "./useCardManager";
Expand All @@ -15,6 +15,7 @@ export function YITHPlugins({ wpModules }) {
const [isOpen, setIsOpen] = useState(false);
const [pluginName, setPluginName] = useState("");
const [yithProducts, setYithProducts] = useState([]);
const myPluginsAndToolsPageLink = `${window.location.href.split('#')[0]}#/my_plugins_and_tools`;
let anyPluginActive = 0;
const [yithPluginsMap, setYithPluginsMap] = useState(new Map([
[
Expand Down Expand Up @@ -127,6 +128,9 @@ export function YITHPlugins({ wpModules }) {
"Unlock the full power of your plan with access to a range of exclusive WooCommerce tools powered by \nYITH. Enhance your store and keep your customers coming back for more!",
"wp-module-ecommerce"
)}
<Button as="a" href={myPluginsAndToolsPageLink} className="nfd-button nfd-button--secondary nfd-flex nfd-self-end nfd-ml-auto">
{__( "View all your plugin and tools", "wp-module-ecommerce" )}
</Button>
</div>
</div>
<Section.Content className={"nfd-pt-4 nfd-pl-4 nfd-pr-4"}>
Expand Down
24 changes: 23 additions & 1 deletion src/configs/OnboardingList.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
yithOnboardingStoreParser,
getOrderList,
get_tax_configured,
get_settings_list
get_settings_list,
getMyPluginAndToolsDetails
} from "./selectors";
import { brandName, check_url_match } from "./Utility";
import { VIEW_GUIDE_LINK } from "../constants";
Expand Down Expand Up @@ -67,6 +68,10 @@ const signUpYoastSEOAcademy = () => {
AnalyticsSdk.track("next_step", "next_step_yoast_academy_clicked", data);
};

const clickMyPluginAndTools = () => {
AnalyticsSdk.track("next_step", "next_step_my_plugins_and_tools_clicked", data);
};

export function OnboardingListDefinition(props) {
const installJetpack = createPluginInstallAction("jetpack", 20, props);
return {
Expand All @@ -79,6 +84,23 @@ export function OnboardingListDefinition(props) {
orders: WooCommerceSdk.orders.get
},
cards: [
{
name:"Explore the features included with your solution",
id: "mypluginsandtools",
text: __("Explore the features included with your solution", "wp-module-ecommerce"),
state: {
isAvailable: (queries) => queries?.settings?.hasBrandSignedUp,
isCompleted: (queries) => !(queries?.settings?.hasBrandSignedUp),
url: () =>
`${window.location.href.split('#')[0]}#/my_plugins_and_tools`,
target: () => "_blank",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @aratidgr8 / @searunt , can you please confirm , in the next steps when user clicks on Explore the features included with your solution are we expecting it to open it in new tab ??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will open in same tab as discussed with @wpscholar

},
shouldRender: () => (state) => state.isAvailable,
actions: {
manage: () => clickMyPluginAndTools(),
},
queries: [{ key: "settings", selector: getMyPluginAndToolsDetails() }],
},
{
name: "Update your website nameservers",
id: "nameservers",
Expand Down
8 changes: 8 additions & 0 deletions src/configs/selectors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { NewfoldRuntime } from "@newfold-labs/wp-module-runtime";

export const wcTasksParser = (title) => (data) => {
const isCompleted = (data?.[0]?.tasks ?? []).find(
(task) => task.id === title
Expand Down Expand Up @@ -65,6 +67,12 @@ export const getAcademyEnrollmentDetails = () => (data) => {
};
};

export const getMyPluginAndToolsDetails = () => (data) => {
return {
hasBrandSignedUp: NewfoldRuntime['plugin']['brand'].includes('bluehost'),
};
};

export const getOrderList = () => (ordersList) => {
return {
pendingOrders: ordersList.filter(order => (order.status === 'processing') || (order.status === 'on-hold') || (order.status === 'pending')),
Expand Down
Loading