From ad58d3badde06b8cfbe88bbccdbad1318e12ea19 Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Sun, 2 Feb 2020 03:52:10 -0300 Subject: [PATCH] [Landing] Support "custom" pricing plan --- landing/src/components/common/Tabs.tsx | 22 +++--- .../sections/pricing/PricingPlanBlock.tsx | 60 ++++++++++------ .../sections/pricing/PricingPlans.tsx | 68 +++++++++++++------ packages/core/src/types/devhub.ts | 2 +- 4 files changed, 100 insertions(+), 52 deletions(-) diff --git a/landing/src/components/common/Tabs.tsx b/landing/src/components/common/Tabs.tsx index 57639ae50..05ebfcc70 100644 --- a/landing/src/components/common/Tabs.tsx +++ b/landing/src/components/common/Tabs.tsx @@ -3,7 +3,7 @@ import React, { ReactElement } from 'react' export interface TabsProps { className?: string - children: Array>> + children: Array> | false | null> onTabChange: (id: TabId) => void } @@ -18,14 +18,18 @@ export function Tabs(props: TabsProps) { className, )} > - {React.Children.map(children, child => ( -
onTabChange(child.props.id)} - > - {child} -
- ))} + {React.Children.map( + children, + child => + !!child && ( +
onTabChange(child.props.id)} + > + {child} +
+ ), + )} ) diff --git a/landing/src/components/sections/pricing/PricingPlanBlock.tsx b/landing/src/components/sections/pricing/PricingPlanBlock.tsx index cfe160d11..5360c1ddf 100644 --- a/landing/src/components/sections/pricing/PricingPlanBlock.tsx +++ b/landing/src/components/sections/pricing/PricingPlanBlock.tsx @@ -89,7 +89,9 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) { : _priceLabel const subtitle = `${ - forceShowAsMonthly + localizedPlan.type === 'custom' && !Number(priceLabelWithoutCents) + ? ' ' + : forceShowAsMonthly ? `${plan.type === 'team' ? '/user' : ''}/month` : localizedPlan.interval ? formatInterval(localizedPlan) @@ -125,11 +127,6 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) { `*Billed ${ localizedPlan.amount % 100 > 50 ? '~' : '' }${_roundedPriceLabelWithInterval}` - } else if ( - forceShowAsMonthly && - plans.find(p => p && p.interval !== 'month') - ) { - footerText = `${footerText || ' '}\n` } if (!localizedPlan.interval && localizedPlan.amount) { @@ -138,6 +135,17 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) { 'One-time payment (no subscription)' } + if ( + !footerText && + localizedPlan.type === 'custom' && + buttonLink && + buttonLink.startsWith('mailto:') + ) { + footerText = + (footerText ? `${footerText}\n` : footerText) + + `Contact us: ${buttonLink.replace('mailto:', '')}` + } + if (!footerText && localizedPlan.interval && localizedPlan.amount) { footerText = (footerText ? `${footerText}\n` : footerText) + localizedPlan.interval === @@ -146,6 +154,15 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) { : 'Cancel anytime with one click' } + if ( + !footerText && + forceShowAsMonthly && + plans.find(p => p && p.interval !== 'month') && + modifiedAmount === localizedPlan.amount + ) { + footerText = `${footerText || ' '}\n` + } + return (
- {`${priceLabelWithoutCents}`} - {!!priceLabelCents && ( - - + {localizedPlan.type === 'custom' && + !Number(priceLabelWithoutCents) ? ( + '$?' + ) : ( + <> + {priceLabelWithoutCents} + {!!priceLabelCents && ( - {priceLabelCents} + + + + {priceLabelCents} + + + - - + )} + )}
@@ -213,14 +239,6 @@ export function PricingPlanBlock(props: PricingPlanBlockProps) {
 {subtitle} 
- {/* {localizedPlan.interval ? ( -
{`/${ - localizedPlan.intervalCount > 1 ? `${localizedPlan.intervalCount}-` : '' - }${localizedPlan.interval}`}
- ) : ( -
 
- )} */} -
)} diff --git a/landing/src/components/sections/pricing/PricingPlans.tsx b/landing/src/components/sections/pricing/PricingPlans.tsx index 1f397709c..6014998ad 100644 --- a/landing/src/components/sections/pricing/PricingPlans.tsx +++ b/landing/src/components/sections/pricing/PricingPlans.tsx @@ -14,17 +14,25 @@ export function PricingPlans(_props: PricingPlansProps) { const { authData } = useAuth() const { dealCode, plans } = usePlans() - const shouldShowPlanTypeTabs = - plans.some(plan => plan && plan.type !== 'team') && - plans.some(plan => plan && plan.type === 'team') + const planTypesCounters = plans.reduce< + Partial, number>> + >((obj, plan) => { + if (!(plan && plan.type)) return obj + obj[plan.type] = (obj[plan.type] || 0) + 1 + return obj + }, {}) + + const hasMultiplePlanTypes = Object.keys(planTypesCounters).length > 1 const [_tab, setTab] = useState(undefined) const tab = _tab || - (shouldShowPlanTypeTabs && + (hasMultiplePlanTypes && (authData && authData.plan && authData.plan.type) === 'team' ? 'team' - : shouldShowPlanTypeTabs + : (authData && authData.plan && authData.plan.type) === 'custom' + ? 'custom' + : hasMultiplePlanTypes ? 'individual' : undefined) @@ -38,7 +46,7 @@ export function PricingPlans(_props: PricingPlansProps) { plan => !!( plan && - ((!tab && !shouldShowPlanTypeTabs) || + ((!tab && !hasMultiplePlanTypes) || (plan.type === tab || (tab === 'individual' && !plan.type))) ), ) @@ -57,31 +65,49 @@ export function PricingPlans(_props: PricingPlansProps) { totalNumberOfVisiblePlans={filteredPlans.length} /> ) : plan ? ( - + plan.type === 'custom' ? ( + + ) : ( + + ) ) : null, ) }, [plans, tab]) return (
- {!!shouldShowPlanTypeTabs && ( + {!!hasMultiplePlanTypes && ( > className="mb-6" onTabChange={id => setTab(id)} > - - + {!!planTypesCounters.individual && ( + + )} + {!!planTypesCounters.team && ( + + )} + {!!planTypesCounters.custom && ( + + )} )} diff --git a/packages/core/src/types/devhub.ts b/packages/core/src/types/devhub.ts index cfc08cd07..4ac35989a 100644 --- a/packages/core/src/types/devhub.ts +++ b/packages/core/src/types/devhub.ts @@ -557,7 +557,7 @@ export type PlatformCategory = DownloadOption['category'] export type Platform = DownloadOption['platform'] export type PlanSource = 'stripe' | 'paddle' | 'none' // | 'github_marketplace' | 'opencollective' | 'appstore' | 'playstore' -export type PlanType = 'individual' | 'team' | undefined +export type PlanType = 'individual' | 'team' | 'custom' | undefined export interface Plan { id: PlanID