Cleaned site and product helper usage

no refs

- cleans up helpers for site and products to more consistent and predictable usage
This commit is contained in:
Rishabh 2021-06-23 22:09:14 +05:30
parent c7a3fdc639
commit 21d1c1b9e8
5 changed files with 108 additions and 122 deletions

View File

@ -93,13 +93,12 @@ export default class App extends React.Component {
const target = event.currentTarget; const target = event.currentTarget;
const pagePath = (target && target.dataset.portal); const pagePath = (target && target.dataset.portal);
const {page, pageQuery} = this.getPageFromLinkPath(pagePath) || {}; const {page, pageQuery} = this.getPageFromLinkPath(pagePath) || {};
if (this.state.initStatus === 'success') { if (this.state.initStatus === 'success') {
this.handleSignupQuery({site: this.state.site, pageQuery}); if (pageQuery && pageQuery !== 'free') {
} this.handleSignupQuery({site: this.state.site, pageQuery});
} else {
if (page) { this.dispatchAction('openPopup', {page, pageQuery});
this.dispatchAction('openPopup', {page, pageQuery}); }
} }
}; };
const customTriggerSelector = '[data-portal]'; const customTriggerSelector = '[data-portal]';
@ -485,11 +484,11 @@ export default class App extends React.Component {
handleSignupQuery({site, pageQuery}) { handleSignupQuery({site, pageQuery}) {
const queryPrice = getQueryPrice({site: site, priceId: pageQuery}); const queryPrice = getQueryPrice({site: site, priceId: pageQuery});
if (!this.state.member if (!this.state.member
&& pageQuery
&& pageQuery !== 'free' && pageQuery !== 'free'
&& queryPrice
) { ) {
removePortalLinkFromUrl(); removePortalLinkFromUrl();
this.dispatchAction('signup', {plan: queryPrice.id}); this.dispatchAction('signup', {plan: queryPrice?.id || pageQuery});
} }
} }

View File

@ -1,11 +1,11 @@
import React, {useContext, useEffect, useState} from 'react'; import React, {useContext, useEffect, useState} from 'react';
import Switch from '../common/Switch'; import Switch from '../common/Switch';
import {ReactComponent as CheckmarkIcon} from '../../images/icons/checkmark.svg'; import {ReactComponent as CheckmarkIcon} from '../../images/icons/checkmark.svg';
import {getAllProducts, getCurrencySymbol, getPriceString, getStripeAmount, isCookiesDisabled} from '../../utils/helpers'; import {getSiteProducts, getCurrencySymbol, getPriceString, getStripeAmount, isCookiesDisabled} from '../../utils/helpers';
import AppContext from '../../AppContext'; import AppContext from '../../AppContext';
export const ProductsSectionStyles = ({site}) => { export const ProductsSectionStyles = ({site}) => {
const products = getAllProducts({site}); const products = getSiteProducts({site});
const noOfProducts = products.length; const noOfProducts = products.length;
return ` return `
.gh-portal-products { .gh-portal-products {

View File

@ -3,9 +3,9 @@ import AppContext from '../../AppContext';
import ActionButton from '../common/ActionButton'; import ActionButton from '../common/ActionButton';
import CloseButton from '../common/CloseButton'; import CloseButton from '../common/CloseButton';
import BackButton from '../common/BackButton'; import BackButton from '../common/BackButton';
import PlansSection from '../common/PlansSection'; import PlansSection, {MultipleProductsPlansSection} from '../common/PlansSection';
import {getDateString} from '../../utils/date-time'; import {getDateString} from '../../utils/date-time';
import {formatNumber, getFilteredPrices, getMemberActivePrice, getMemberSubscription, getPriceFromSubscription, getSitePrices, getSubscriptionFromId, isPaidMember} from '../../utils/helpers'; import {formatNumber, getAvailablePrices, getFilteredPrices, getMemberActivePrice, getMemberSubscription, getPriceFromSubscription, getSubscriptionFromId, getUpgradeProducts, hasMultipleProducts, isPaidMember} from '../../utils/helpers';
export const AccountPlanPageStyles = ` export const AccountPlanPageStyles = `
.gh-portal-accountplans-main { .gh-portal-accountplans-main {
@ -188,7 +188,7 @@ const ChangePlanSection = ({plans, selectedPlan, onPlanSelect, onCancelSubscript
return ( return (
<section> <section>
<div className='gh-portal-section gh-portal-accountplans-main'> <div className='gh-portal-section gh-portal-accountplans-main'>
<PlansSection <PlansOrProductSection
showLabel={false} showLabel={false}
plans={plans} plans={plans}
selectedPlan={selectedPlan} selectedPlan={selectedPlan}
@ -264,6 +264,7 @@ const PlansContainer = ({
/> />
); );
}; };
export default class AccountPlanPage extends React.Component { export default class AccountPlanPage extends React.Component {
static contextType = AppContext; static contextType = AppContext;
@ -286,15 +287,19 @@ export default class AccountPlanPage extends React.Component {
} }
getInitialState() { getInitialState() {
const {member, site, pageQuery} = this.context; const {member, site} = this.context;
this.prices = getSitePrices({site, pageQuery, includeFree: false});
this.prices = getAvailablePrices({site});
let activePrice = getMemberActivePrice({member}); let activePrice = getMemberActivePrice({member});
if (activePrice) {
this.prices = getFilteredPrices({prices: this.prices, currency: activePrice.currency});
}
let selectedPrice = activePrice ? this.prices.find((d) => { let selectedPrice = activePrice ? this.prices.find((d) => {
return (d.id === activePrice.id); return (d.id === activePrice.id);
}) : null; }) : null;
if (selectedPrice) {
this.prices = getFilteredPrices({prices: this.prices, currency: selectedPrice.currency});
}
// Select first plan as default for free member // Select first plan as default for free member
if (!isPaidMember({member}) && this.prices.length > 0) { if (!isPaidMember({member}) && this.prices.length > 0) {
selectedPrice = this.prices[0]; selectedPrice = this.prices[0];

View File

@ -5,7 +5,7 @@ import PlansSection from '../common/PlansSection';
import ProductsSection from '../common/ProductsSection'; import ProductsSection from '../common/ProductsSection';
import InputForm from '../common/InputForm'; import InputForm from '../common/InputForm';
import {ValidateInputForm} from '../../utils/form'; import {ValidateInputForm} from '../../utils/form';
import {getAllProducts, getSitePrices, hasMultipleProducts, hasOnlyFreePlan, isInviteOnlySite} from '../../utils/helpers'; import {getSiteProducts, getSitePrices, hasMultipleProducts, hasOnlyFreePlan, isInviteOnlySite} from '../../utils/helpers';
import {ReactComponent as InvitationIcon} from '../../images/icons/invitation.svg'; import {ReactComponent as InvitationIcon} from '../../images/icons/invitation.svg';
const React = require('react'); const React = require('react');
@ -391,7 +391,7 @@ class SignupPage extends React.Component {
renderProducts() { renderProducts() {
const {site} = this.context; const {site} = this.context;
const products = getAllProducts({site}); const products = getSiteProducts({site});
return ( return (
<> <>
<ProductsSection <ProductsSection

View File

@ -8,12 +8,8 @@ export function removePortalLinkFromUrl() {
export function getPortalLinkPath({page}) { export function getPortalLinkPath({page}) {
const Links = { const Links = {
default: '#/portal',
signin: '#/portal/signin', signin: '#/portal/signin',
signup: '#/portal/signup', signup: '#/portal/signup'
account: '#/portal/account',
'account-plans': '#/portal/account/plans',
'account-profile': '#/portal/account/profile'
}; };
if (Object.keys(Links).includes(page)) { if (Object.keys(Links).includes(page)) {
return Links[page]; return Links[page];
@ -55,6 +51,34 @@ export function isPaidMember({member = {}}) {
return (member && member.paid); return (member && member.paid);
} }
export function getUpgradePrices({site, member}) {
const activePrice = getMemberActivePrice({member});
if (activePrice) {
return getFilteredPrices({prices: this.prices, currency: activePrice.currency});
}
return getAvailablePrices({site});
}
export function getProductCurrency({product}) {
if (!product?.monthlyPrice) {
return null;
}
return product.monthlyPrice.currency;
}
export function getUpgradeProducts({site, member}) {
const activePrice = getMemberActivePrice({member});
const activePriceCurrency = activePrice?.currency;
const availableProducts = getAvailableProducts({site});
if (!activePrice) {
return availableProducts;
}
return availableProducts.filter((product) => {
return (getProductCurrency({product}) === activePriceCurrency);
});
}
export function getFilteredPrices({prices, currency}) { export function getFilteredPrices({prices, currency}) {
return prices.filter((d) => { return prices.filter((d) => {
return (d.currency || '').toLowerCase() === (currency || '').toLowerCase(); return (d.currency || '').toLowerCase() === (currency || '').toLowerCase();
@ -69,6 +93,7 @@ export function getPriceFromSubscription({subscription}) {
id: subscription.price.price_id, id: subscription.price.price_id,
price: subscription.price.amount / 100, price: subscription.price.amount / 100,
name: subscription.price.nickname, name: subscription.price.nickname,
currency: subscription.price.currency.toLowerCase(),
currency_symbol: getCurrencySymbol(subscription.price.currency) currency_symbol: getCurrencySymbol(subscription.price.currency)
}; };
} }
@ -138,43 +163,44 @@ export function isInviteOnlySite({site = {}, pageQuery = ''}) {
} }
export function hasMultipleProducts({site = {}}) { export function hasMultipleProducts({site = {}}) {
const { const products = getAvailableProducts({site});
products = []
} = site || {};
if (site.portal_plans && !site.portal_plans.includes('monthly') && !site.portal_plans.includes('yearly')) {
return false;
}
if (site.portal_products && site.portal_products.length < 2) {
return false;
}
if (products?.length > 1) { if (products?.length > 1) {
return true; return true;
} }
return false; return false;
} }
export function getSiteProducts({site = {}}) {
const products = site?.products || [];
return products.filter(product => !!product).sort((productA, productB) => {
return productA?.monthlyPrice?.amount - productB?.monthlyPrice.amount;
});
}
export function getAvailableProducts({site}) { export function getAvailableProducts({site}) {
const {portal_products: portalProducts} = site; const {portal_products: portalProducts, products = [], portal_plans: portalPlans = []} = site || {};
const products = getSiteProducts({site}).filter((product) => {
if (!portalPlans.includes('monthly') && !portalPlans.includes('yearly')) {
return [];
}
return products.filter(product => !!product).filter((product) => {
if (portalProducts) { if (portalProducts) {
return portalProducts.includes(product.id); return portalProducts.includes(product.id);
} }
return true; return true;
}).sort((productA, productB) => {
return productA?.monthlyPrice?.amount - productB?.monthlyPrice.amount;
}).map((product) => {
product.monthlyPrice = {
...product.monthlyPrice,
currency_symbol: getCurrencySymbol(product.monthlyPrice.currency)
};
product.yearlyPrice = {
...product.yearlyPrice,
currency_symbol: getCurrencySymbol(product.yearlyPrice.currency)
};
return product;
}); });
return products;
} }
export function getAllProducts({site}) { export function getSiteProducts({site}) {
const products = getAvailableProducts({site}); const products = getAvailableProducts({site});
if (hasFreeProduct({site}) && products.length > 0) { if (hasFreeProductPrice({site}) && products.length > 0) {
products.unshift({ products.unshift({
id: 'free' id: 'free'
}); });
@ -182,7 +208,7 @@ export function getAllProducts({site}) {
return products; return products;
} }
export function getProductPrices({site}) { export function getPricesFromProducts({site}) {
const products = getAvailableProducts({site}) || []; const products = getAvailableProducts({site}) || [];
const prices = products.reduce((accumPrices, product) => { const prices = products.reduce((accumPrices, product) => {
if (product.monthlyPrice && product.yearlyPrice) { if (product.monthlyPrice && product.yearlyPrice) {
@ -194,63 +220,7 @@ export function getProductPrices({site}) {
return prices; return prices;
} }
export function getAvailablePrices({site = {}, includeFree = true} = {}) { export function hasFreeProductPrice({site}) {
let {
prices,
products,
allow_self_signup: allowSelfSignup,
is_stripe_configured: isStripeConfigured
} = site || {};
if (!prices) {
prices = [];
}
if (products) {
prices = [];
products.forEach((product) => {
if (product.prices) {
prices = prices.concat(product.prices);
}
});
}
const plansData = [];
const stripePrices = prices.filter((d) => {
return !!(d && d.id);
}).map((d) => {
return {
...d,
price_id: d.id,
price: d.amount / 100,
name: d.nickname,
currency_symbol: getCurrencySymbol(d.currency)
};
}).filter((price) => {
return price.amount !== 0 && price.type === 'recurring';
});
if (allowSelfSignup && includeFree) {
plansData.push({
id: 'free',
type: 'free',
price: 0,
currency: 'usd',
currency_symbol: '$',
name: 'Free'
});
}
if (isStripeConfigured) {
stripePrices.forEach((price) => {
plansData.push(price);
});
}
return plansData;
}
export function hasFreeProduct({site}) {
const { const {
allow_self_signup: allowSelfSignup, allow_self_signup: allowSelfSignup,
portal_plans: portalPlans portal_plans: portalPlans
@ -258,22 +228,19 @@ export function hasFreeProduct({site}) {
return allowSelfSignup && portalPlans.includes('free'); return allowSelfSignup && portalPlans.includes('free');
} }
export function getSitePrices({site = {}, includeFree = true, pageQuery = ''} = {}) { export function getAvailablePrices({site}) {
const { const {
prices = [], portal_plans: portalPlans = [],
allow_self_signup: allowSelfSignup, is_stripe_configured: isStripeConfigured
is_stripe_configured: isStripeConfigured,
portal_plans: portalPlans
} = site || {}; } = site || {};
if (!prices) { if (!isStripeConfigured) {
return []; return [];
} }
const availablePrices = getProductPrices({site});
const plansData = []; const productPrices = getPricesFromProducts({site});
const stripePrices = availablePrices.filter((d) => { return productPrices.filter((d) => {
return !!(d && d.id); return !!(d && d.id);
}).map((d) => { }).map((d) => {
return { return {
@ -287,10 +254,10 @@ export function getSitePrices({site = {}, includeFree = true, pageQuery = ''} =
return price.amount !== 0 && price.type === 'recurring'; return price.amount !== 0 && price.type === 'recurring';
}).filter((price) => { }).filter((price) => {
if (price.interval === 'month') { if (price.interval === 'month') {
return (portalPlans || []).includes('monthly'); return portalPlans.includes('monthly');
} }
if (price.interval === 'year') { if (price.interval === 'year') {
return (portalPlans || []).includes('yearly'); return portalPlans.includes('yearly');
} }
return false; return false;
}).sort((a, b) => { }).sort((a, b) => {
@ -300,19 +267,33 @@ export function getSitePrices({site = {}, includeFree = true, pageQuery = ''} =
return 0; return 0;
} }
return a.currency.localeCompare(b.currency, undefined, {ignorePunctuation: true}); return a.currency.localeCompare(b.currency, undefined, {ignorePunctuation: true});
}).sort((a, b) => {
return (a.active === b.active) ? 0 : (a.active ? -1 : 1);
}); });
}
export function getFreePriceCurrency({site}) {
const stripePrices = getAvailablePrices({site});
let freePriceCurrencyDetail = { let freePriceCurrencyDetail = {
currency: 'usd', currency: 'usd',
currency_symbol: '$' currency_symbol: '$'
}; };
if (stripePrices && stripePrices.length > 0) { if (stripePrices?.length > 0) {
freePriceCurrencyDetail.currency = stripePrices[0].currency; freePriceCurrencyDetail.currency = stripePrices[0].currency;
freePriceCurrencyDetail.currency_symbol = stripePrices[0].currency_symbol; freePriceCurrencyDetail.currency_symbol = stripePrices[0].currency_symbol;
} }
return freePriceCurrencyDetail;
}
if (allowSelfSignup && portalPlans.includes('free') && includeFree) { export function getSitePrices({site = {}, pageQuery = ''} = {}) {
const {
allow_self_signup: allowSelfSignup,
portal_plans: portalPlans
} = site || {};
const plansData = [];
if (allowSelfSignup && portalPlans.includes('free')) {
const freePriceCurrencyDetail = getFreePriceCurrency({site});
plansData.push({ plansData.push({
id: 'free', id: 'free',
type: 'free', type: 'free',
@ -323,9 +304,10 @@ export function getSitePrices({site = {}, includeFree = true, pageQuery = ''} =
}); });
} }
const showOnlyFree = pageQuery === 'free' && hasPrice({site, plan: 'free'}); const showOnlyFree = pageQuery === 'free' && hasFreeProductPrice({site});
if (isStripeConfigured && !showOnlyFree) { if (!showOnlyFree) {
const stripePrices = getAvailablePrices({site});
stripePrices.forEach((price) => { stripePrices.forEach((price) => {
plansData.push(price); plansData.push(price);
}); });