diff --git a/Assets/WebsiteAssets/css/site.css b/Assets/WebsiteAssets/css/site.css index 67742b1618..ad54a6ffff 100644 --- a/Assets/WebsiteAssets/css/site.css +++ b/Assets/WebsiteAssets/css/site.css @@ -1212,6 +1212,13 @@ footer .bottom-links-row p { margin-top: 2em; } +.price-container p.pricing-table-billed-annually { + font-size: 14px; + font-style: italic; + opacity: 0.7; + margin-top: 0.5em; +} + .price-row { display: flex; flex-direction: row; diff --git a/Assets/WebsiteAssets/templates/partials/plan.mustache b/Assets/WebsiteAssets/templates/partials/plan.mustache index 2145a78ae3..b784ff4a42 100644 --- a/Assets/WebsiteAssets/templates/partials/plan.mustache +++ b/Assets/WebsiteAssets/templates/partials/plan.mustache @@ -40,12 +40,16 @@ {{#pricingTable}} Pricing + + + {{#rows}} - + {{/rows}}
UsersPrice
{{condition}}{{priceYearly}}
{{condition}}{{priceMonthly}}
+

{{billedAnnually}}

{{/pricingTable}}

diff --git a/packages/lib/utils/joplinCloud/index.ts b/packages/lib/utils/joplinCloud/index.ts index 3b0d61432d..120aded0df 100644 --- a/packages/lib/utils/joplinCloud/index.ts +++ b/packages/lib/utils/joplinCloud/index.ts @@ -40,7 +40,7 @@ enum PlanHostingType { export interface PlanTieredPricingTableRow { condition: string; - priceYearly: string; + priceMonthly: string; } export interface Plan { @@ -48,7 +48,7 @@ export interface Plan { title: string; priceMonthly?: StripePublicConfigPrice; priceYearly?: StripePublicConfigPrice; - pricingTable?: { rows: PlanTieredPricingTableRow[] }; + pricingTable?: { rows: PlanTieredPricingTableRow[]; billedAnnually: string }; featured: boolean; iconName: string; featuresOn: FeatureId[]; @@ -578,24 +578,32 @@ export const createFeatureTableMd = () => { const getTieredPricingTable = (price: StripePublicConfigPrice) => { if (!isTieredPrice(price)) throw new Error(`Not a tiered price: ${price.id}`); + const formatUserCount = (count: number) => { + if (count === Number.POSITIVE_INFINITY) { + return '∞'; + } + return String(count); + }; + const rows: PlanTieredPricingTableRow[] = []; for (const amount of price.amounts) { - const formatUserCount = (count: number) => { - if (count === Number.POSITIVE_INFINITY) { - return '∞'; - } - return String(count); - }; + // The tiered amounts are yearly per-user prices, so derive the monthly + // price shown in the table by dividing by 12. + const monthlyAmount = formatPrice(Math.round(Number(amount.amount) / 12 * 100) / 100, price.currency); rows.push({ condition: `${ formatUserCount(amount.userRange.min) }—${ formatUserCount(amount.userRange.max) } users`, - priceYearly: `${amount.formattedAmount} / user / year`, + priceMonthly: `${monthlyAmount} / user / month`, }); } - return rows; + + const yearlyAmounts = price.amounts.map(amount => amount.formattedAmount).join(' / '); + const billedAnnually = _('Billed annually (%s per user per year respectively).', yearlyAmounts); + + return { rows, billedAnnually }; }; export function getPlans(stripeConfig: StripePublicConfig): Record { @@ -705,12 +713,10 @@ export function getPlans(stripeConfig: StripePublicConfig): Record