diff --git a/ghost/portal/src/components/common/ProductsSection.js b/ghost/portal/src/components/common/ProductsSection.js index 95d6abe8f5..9594af28cc 100644 --- a/ghost/portal/src/components/common/ProductsSection.js +++ b/ghost/portal/src/components/common/ProductsSection.js @@ -1,6 +1,7 @@ -import React from 'react'; +import React, {useContext, useState} from 'react'; import Switch from '../common/Switch'; -import {isCookiesDisabled} from '../../utils/helpers'; +import {getCurrencySymbol, getPriceString, getProducts, getStripeAmount, isCookiesDisabled} from '../../utils/helpers'; +import AppContext from '../../AppContext'; export const ProductsSectionStyles = ` .gh-portal-products { @@ -28,7 +29,7 @@ export const ProductsSectionStyles = ` } .gh-portal-products-priceswitch .gh-portal-for-switch .input-toggle-component, - .gh-portal-products-priceswitch .gh-portal-for-switch label:hover input:not(:checked) + .input-toggle-component, + .gh-portal-products-priceswitch .gh-portal-for-switch label:hover input:not(:checked) + .input-toggle-component, .gh-portal-products-priceswitch .gh-portal-for-switch .container:hover input:not(:checked) + .input-toggle-component { background: var(--grey1); border-color: var(--grey1); @@ -60,7 +61,7 @@ export const ProductsSectionStyles = ` padding: 32px 5vw; } - + .gh-portal-product-card { position: relative; display: flex; @@ -222,7 +223,7 @@ export const ProductsSectionStyles = ` grid-row: 1; } - .gh-portal-popup-container.fullscreen footer.gh-portal-signup-footer, + .gh-portal-popup-container.fullscreen footer.gh-portal-signup-footer, .gh-portal-popup-container.fullscreen footer.gh-portal-signin-footer { padding: 0 32px !important; margin-top: 32px; @@ -234,12 +235,18 @@ export const ProductsSectionStyles = ` } `; +const ProductsContext = React.createContext({ + selectedInterval: 'month', + selectedProduct: 'free', + setSelectedProduct: null +}); + function productColumns() { const noOfProducts = 4; return noOfProducts > 5 ? 5 : noOfProducts; } -function Checkbox({name, id, onPlanSelect, isChecked, disabled = false}) { +function Checkbox({name, id, onProductSelect, isChecked, disabled = false}) { if (isCookiesDisabled()) { disabled = true; } @@ -250,87 +257,169 @@ function Checkbox({name, id, onPlanSelect, isChecked, disabled = false}) { key={id} type="checkbox" checked={isChecked} + onChange={(e) => { + onProductSelect(e, id); + }} aria-label={name} - onChange={e => onPlanSelect(e, id)} disabled={disabled} /> - + { + if (!disabled) { + onProductSelect(e, id); + } + }}> + + ); +} + +function ProductCardFooter({product}) { + const {selectedInterval} = useContext(ProductsContext); + const monthlyPrice = product.monthlyPrice; + const yearlyPrice = product.yearlyPrice; + const activePrice = selectedInterval === 'month' ? monthlyPrice : yearlyPrice; + const alternatePrice = selectedInterval === 'month' ? yearlyPrice : monthlyPrice; + if (!monthlyPrice || !yearlyPrice) { + return null; + } + return ( +
+
+ {getCurrencySymbol(activePrice.currency)} + {getStripeAmount(activePrice.amount)} + /{activePrice.interval} +
+
{getPriceString(alternatePrice)}
+
+ ); +} + +function ProductCard({product}) { + const {selectedProduct, setSelectedProduct} = useContext(ProductsContext); + + return ( +
+
+ { + setSelectedProduct(product.id); + }} /> +

{product.name}

+
{product.description}
+
+ +
+ ); +} + +function ProductCards({products}) { + return products.map((product) => { + return ( + + ); + }); +} + +function DummyProductCards({products}) { + return ( + <> +
+
+ +

Bronze

+
Access to all members articles
+
+
+
+ $ + 70 + /year +
+
$7/month
+
+
+
+
+ +

Silver

+
Access to all members articles and weekly podcast
+
+
+
+ $ + 120 + /year +
+
$12/month
+
+
+
+
+ +

Gold

+
Access to all members articles, weekly podcast and exclusive interviews
+
+
+
+ $ + 1000 + /year +
+
$12/month
+
+
+ + ); +} + +function FreeProductCard() { + const {selectedProduct, setSelectedProduct} = useContext(ProductsContext); + return ( +
+
+ { + setSelectedProduct('free'); + }} /> +

Free

+
Free preview
+
+
+
+ $ + 0 +
+
+
); } function ProductsSection() { + const {site} = useContext(AppContext); + const products = getProducts({site}); + const [selectedInterval, setSelectedInterval] = useState('month'); + const [selectedProduct, setSelectedProduct] = useState('free'); + const checked = selectedInterval === 'year'; return ( -
+ +
+
+ Monthly + { + const interval = selectedInterval === 'month' ? 'year' : 'month'; + setSelectedInterval(interval); + }} checked={checked} /> + Yearly +
-
- Monthly - - Yearly -
- -
-
-
- -

Free

-
Free preview
-
-
-
- $ - 0 -
-
-
+
+ +
-
-
- -

Bronze

-
Access to all members articles
-
-
-
- $ - 70 - /year -
-
$7/month
-
-
-
-
- -

Silver

-
Access to all members articles and weekly podcast
-
-
-
- $ - 120 - /year -
-
$12/month
-
-
-
-
- -

Gold

-
Access to all members articles, weekly podcast and exclusive interviews
-
-
-
- $ - 1000 - /year -
-
$12/month
-
-
-
-
+
+ ); } diff --git a/ghost/portal/src/components/common/Switch.js b/ghost/portal/src/components/common/Switch.js index 8b8a5197a1..19b2881962 100644 --- a/ghost/portal/src/components/common/Switch.js +++ b/ghost/portal/src/components/common/Switch.js @@ -74,7 +74,7 @@ export const SwitchStyles = ` } `; -function Switch({id, label, onToggle, checked = false}) { +function Switch({id, label='', onToggle, checked = false}) { const {action} = useContext(AppContext); const [isChecked, setIsChecked] = useState(checked); const isActionChanged = ['updateNewsletter:failed', 'updateNewsletter:success'].includes(action); diff --git a/ghost/portal/src/utils/helpers.js b/ghost/portal/src/utils/helpers.js index cb69d3cf11..2dfe35ebf2 100644 --- a/ghost/portal/src/utils/helpers.js +++ b/ghost/portal/src/utils/helpers.js @@ -316,6 +316,19 @@ export const getCurrencySymbol = (currency) => { return Intl.NumberFormat('en', {currency, style: 'currency'}).format(0).replace(/[\d\s.]/g, ''); }; +export const getStripeAmount = (amount) => { + if (isNaN(amount)) { + return 0; + } + return (amount / 100); +}; + +export const getPriceString = (price = {}) => { + const symbol = getCurrencySymbol(price.currency); + const amount = getStripeAmount(price.amount); + return `${symbol}${amount}/${price.interval}`; +}; + export const formatNumber = (amount) => { if (amount === undefined || amount === null) { return '';