diff --git a/docs/src/app/(main)/pricing/_components/pricing.tsx b/docs/src/app/(main)/pricing/_components/pricing.tsx index 477d0430..3b10a080 100644 --- a/docs/src/app/(main)/pricing/_components/pricing.tsx +++ b/docs/src/app/(main)/pricing/_components/pricing.tsx @@ -8,9 +8,14 @@ import { CampaignCountdown } from './campaign-countdown'; export async function Pricing() { const pricingInfo = await fetchPricingInfo(); - const { defaultPricing, campaignPricing, campaign } = pricingInfo; + const { defaultPricing, campaignPricing, upcomingPricing, campaign } = + pricingInfo; - const pricingBlocks = buildPricingBlocks(defaultPricing, campaignPricing); + const pricingBlocks = buildPricingBlocks( + defaultPricing, + campaignPricing, + upcomingPricing, + ); return (
@@ -44,7 +49,7 @@ function PricingBlock(props: { className?: string; }) { const { - item: { id, title, description, price, features, action }, + item: { id, title, description, price, upcoming, features, action }, isMain, className, } = props; @@ -85,6 +90,11 @@ function PricingBlock(props: {
)} + {upcoming && ( +
+ {`$${upcoming.price}/month for new subscribers from ${formatDate(upcoming.activeFrom)}. Subscribe before then to keep $${price}.`} +
+ )}
{features.map((feature, i) => (
@@ -149,6 +159,7 @@ type PricingBlockItem = { title: string; description: string | React.ReactNode; price?: number; + upcoming?: { price: number; activeFrom: string }; features: (string | React.ReactNode)[]; action?: React.ReactNode; }; @@ -156,6 +167,7 @@ type PricingBlockItem = { function buildPricingBlocks( defaultPricing: PricingInfo['defaultPricing'], campaignPricing: PricingInfo['campaignPricing'], + upcomingPricing: PricingInfo['upcomingPricing'], ) { return [ { @@ -213,7 +225,8 @@ function buildPricingBlocks( title: 'Starter', description: 'Best for start-ups and businesses who build commercial products with Edge Store.', - price: 5, + price: toDollars(defaultPricing.STARTER.basePrice, 5), + upcoming: toUpcoming(upcomingPricing?.STARTER), features: [ 'Everything in Free', @@ -274,7 +287,8 @@ function buildPricingBlocks( title: 'Pro', description: 'Best for businesses who build larger commercial products with Edge Store.', - price: 35, + price: toDollars(defaultPricing.PRO.basePrice, 35), + upcoming: toUpcoming(upcomingPricing?.PRO), features: [ 'Everything in Starter', @@ -351,6 +365,25 @@ function buildPricingBlocks( ] as const satisfies PricingBlockItem[]; } +// `fallback` covers dashboards that don't return prices yet. +function toDollars(cents: number | undefined, fallback: number) { + return cents === undefined ? fallback : cents / 100; +} + +function toUpcoming(plan: UpcomingPlanPricing | undefined) { + return plan?.basePrice === undefined + ? undefined + : { price: plan.basePrice / 100, activeFrom: plan.activeFrom }; +} + +function formatDate(value: string) { + return new Date(value).toLocaleDateString('en-US', { + month: 'long', + day: 'numeric', + timeZone: 'UTC', + }); +} + function PlanLimitItem(props: { defaultValue: string | number; campaignValue?: string | number; @@ -407,11 +440,17 @@ export type PlanPricing = { aggRangeLimit: number; canRestore: number; campaignId: string | null; + /** Monthly base price in cents. */ + basePrice?: number; }; +type UpcomingPlanPricing = PlanPricing & { activeFrom: string }; + export type PricingInfo = { defaultPricing: Record; campaignPricing?: Record; + /** Price changes scheduled for new subscribers. */ + upcomingPricing?: Partial>; campaign?: { id: string; name: string; diff --git a/docs/src/app/(main)/pricing/page.tsx b/docs/src/app/(main)/pricing/page.tsx index 4ecea7a9..e540ec2a 100644 --- a/docs/src/app/(main)/pricing/page.tsx +++ b/docs/src/app/(main)/pricing/page.tsx @@ -6,6 +6,8 @@ export const metadata: Metadata = { }; export const dynamic = 'force-static'; +// Pick up scheduled price changes without a redeploy. +export const revalidate = 3600; export default function Page() { return ;