/** * WordPress dependencies */ import { Popover, Button, Spinner, RadioControl, } from '@safe-wordpress/components'; import { useSelect, useDispatch } from '@safe-wordpress/data'; import { useCallback, useMemo, useState } from '@safe-wordpress/element'; import { sprintf, _x } from '@safe-wordpress/i18n'; /** * External dependencies */ import { mapValues, trim } from 'lodash'; import { store as NAB_DATA } from '@nab/data'; import { getLocale } from '@nab/date'; import type { Currency, FSLocalizedString, FastSpringProduct, FastSpringProductId, } from '@nab/types'; /** * Internal dependencies */ import { store as NAB_ACCOUNT } from '../../store'; import type { UpgradePopoverProps } from './props'; type Props = UpgradePopoverProps & { readonly showEnterprise: () => void; }; export const RegularUpgradePopover = ( { isOpen, placement, onClick, onFocusOutside, showEnterprise, }: Props ): JSX.Element | null => { const isLoading = useIsLoading(); const products = useProducts(); const currency = useCurrency(); const { upgradeSubscription } = useDispatch( NAB_ACCOUNT ); const [ selectedOption, setSelectedOption ] = useState( '' as FastSpringProductId ); if ( ! isOpen ) { return null; } if ( isLoading ) { return ( ); } return (

{ _x( 'Subscription Plans', 'text', 'nelio-ab-testing' ) }

( { label: (
{ localize( displayName ) } { formatPrice( id, ( price[ currency ] || 0 ) + enabledAddons.reduce( ( acc, a ) => acc + ( a.price[ currency ] || 0 ), 0 ), currency ) } { localize( description ) }
) as unknown as string, value: id, } ) ) } onChange={ ( option ) => { if ( ! option ) { return; } if ( 'nab-enterprise-placeholder' === option ) { showEnterprise(); return; } setSelectedOption( option ); } } />
); }; // ===== // HOOKS // ===== const useIsLoading = () => useSelect( ( select ) => ! select( NAB_DATA ).hasFinishedResolution( 'getProducts' ), [] ); const useProducts = (): ReadonlyArray< Pick< FastSpringProduct, 'id' | 'displayName' | 'price' | 'description' > & { readonly enabledAddons: ReadonlyArray< FastSpringProduct >; } > => { const { currentProduct, products, enabledAddons } = useSelect( ( select ) => ( { products: select( NAB_DATA ).getProducts(), currentProduct: select( NAB_ACCOUNT ).getSubscriptionProduct(), enabledAddons: select( NAB_ACCOUNT ).getSubscriptionAddons(), } ), [] ); const allUpgradeableProducts = products.filter( ( { upgradeableFrom } ) => upgradeableFrom.includes( currentProduct ) ); const upgradeableProducts = allUpgradeableProducts.filter( ( p ) => ! p.id.includes( 'enterprise' ) ); const enterpriseProduct = allUpgradeableProducts.find( ( p ) => p.id.includes( 'enterprise-monthly' ) ) || allUpgradeableProducts.find( ( p ) => p.id.includes( 'enterprise-yearly' ) ); const addons = useMemo( () => products.filter( ( p ) => p.isAddon ), [ products ] ); const getEnabledAddons = useCallback( ( p?: FastSpringProduct ) => ( p?.allowedAddons ?? [] ) .filter( ( addonId ) => !! enabledAddons.find( ( id ) => areSameAddon( addonId, id ) ) ) .map( ( addonId ) => addons.find( ( a ) => a.id === addonId ) ) .filter( ( a ) => !! a ) as ReadonlyArray< FastSpringProduct >, [ addons, enabledAddons ] ); return useMemo( () => [ ...upgradeableProducts.map( ( p ) => ( { ...p, enabledAddons: getEnabledAddons( p ), } ) ), { price: { USD: 0, EUR: 0 }, description: { en: '', es: '' }, enabledAddons: getEnabledAddons( enterpriseProduct ), ...enterpriseProduct, id: 'nab-enterprise-placeholder' as FastSpringProductId, displayName: mapValues( enterpriseProduct?.displayName ?? { en: '', es: '' }, ( name ) => trim( name.replace( /\([^)]*\)/, '' ) ) ), }, ], [ upgradeableProducts, enterpriseProduct, getEnabledAddons ] ); }; const useCurrency = () => useSelect( ( select ) => select( NAB_ACCOUNT ).getCurrency() || 'USD', [] ); // ======= // HELPERS // ======= const formatter = ( () => { try { return new Intl.NumberFormat( getLocale().replace( '_', '-' ) ); } catch ( _ ) { return new Intl.NumberFormat( 'en-US' ); } } )(); const formatPrice = ( product: string, price: number, currency: Currency ): string => { const result = 'EUR' === currency ? `${ formatter.format( Math.ceil( price ) ) }€` : `USD${ formatter.format( Math.ceil( price ) ) }`; if ( 'nab-enterprise-placeholder' === product ) { return sprintf( /* translators: %s: Price. */ _x( 'starting from %s', 'text (money)', 'nelio-ab-testing' ), result ); } return result; }; const localize = ( str: FSLocalizedString ) => { const locale = getLocale(); return locale.startsWith( 'es' ) ? str.es.replace( 'de Nelio A/B Testing', '' ) : str.en.replace( 'Nelio A/B Testing', '' ); }; const areSameAddon = ( a: string, b: string ): boolean => { const aprefix = a.substring( 0, a.indexOf( 'addon-' ) ); const bprefix = b.substring( 0, b.indexOf( 'addon-' ) ); return !! aprefix && !! bprefix && aprefix === bprefix; };