import "./index.scss"; import { FXA_SIGNIN_URL, MDN_PLUS_SUBSCRIBE_10M_URL, MDN_PLUS_SUBSCRIBE_10M_URL_SP3, MDN_PLUS_SUBSCRIBE_10Y_URL, MDN_PLUS_SUBSCRIBE_10Y_URL_SP3, MDN_PLUS_SUBSCRIBE_5M_URL, MDN_PLUS_SUBSCRIBE_5M_URL_SP3, MDN_PLUS_SUBSCRIBE_5Y_URL, MDN_PLUS_SUBSCRIBE_5Y_URL_SP3, MDN_PLUS_SUBSCRIBE_BASE, MDN_PLUS_USE_SP3, } from "../../../env"; import { SubscriptionType, UserData, useUserData } from "../../../user-context"; import { Switch } from "../../../ui/atoms/switch"; import { useEffect, useState } from "react"; import { getStripePlans } from "../../common/api"; import { useOnlineStatus } from "../../../hooks"; import { useGleanClick } from "../../../telemetry/glean-context"; import { OFFER_OVERVIEW_CLICK } from "../../../telemetry/constants"; import LogInLink from "../../../ui/atoms/login-link"; import React from "react"; const Stripe = React.lazy(() => import("./stripe")); export enum Period { Month, Year, } const SUBSCRIPTIONS = { [SubscriptionType.MDN_CORE]: { order: 0, period: Period.Month }, [SubscriptionType.MDN_PLUS_5M]: { order: 1, period: Period.Month, }, [SubscriptionType.MDN_PLUS_5Y]: { order: 2, period: Period.Year, }, [SubscriptionType.MDN_PLUS_10M]: { order: 3, period: Period.Month, }, [SubscriptionType.MDN_PLUS_10Y]: { order: 4, period: Period.Year, }, }; export type OfferDetailsPlanProps = { subscriptionType: SubscriptionType; monthlyPrice?: number; ctaLink: string; }; export type PlanInfo = { id: string; monthlyPriceInCents: number; }; export type StripePlans = { currency: string; plans: { [key: string]: PlanInfo }; }; export type OfferDetailsProps = { id: string; name: string; price?: number; currency?: string; features: (string | null)[][]; includes: string; cta: string; upgradeCta?: string; discounted: OfferDetailsPlanProps; regular: OfferDetailsPlanProps; }; const PLUS_FEATURES = [ ["afree", "Ads free"], ["updates", "Filter and sort updates"], ["collections", "Collections of articles"], ["ai-help", "AI Help"], ]; const CORE: OfferDetailsProps = { id: "core", name: "Core", features: [ ["updates", "Filter and sort updates"], ["collections", "Up to 3 collections"], ["playground", "Share playgrounds"], ["ai-help", "AI Help: 5 questions per day"], ], includes: "Includes:", cta: "Start with Core", regular: { subscriptionType: SubscriptionType.MDN_CORE, ctaLink: `${FXA_SIGNIN_URL}?next=/en-US/plus`, }, discounted: { subscriptionType: SubscriptionType.MDN_CORE, ctaLink: `${FXA_SIGNIN_URL}?next=/en-US/plus`, }, }; const PLUS_5: OfferDetailsProps = { id: "plus5", name: "MDN Plus 5", currency: "USD", features: PLUS_FEATURES, includes: "Includes unlimited access to:", cta: "Start with Plus 5", upgradeCta: "Upgrade to Plus 5", regular: { subscriptionType: SubscriptionType.MDN_PLUS_5M, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_5M_URL_SP3 : MDN_PLUS_SUBSCRIBE_5M_URL, monthlyPrice: 500, }, discounted: { subscriptionType: SubscriptionType.MDN_PLUS_5Y, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_5Y_URL_SP3 : MDN_PLUS_SUBSCRIBE_5Y_URL, monthlyPrice: 417, }, }; const PLUS_10: OfferDetailsProps = { id: "plus10", name: "MDN Supporter 10", currency: "USD", features: [ ...PLUS_FEATURES, [null, "Early access to new features"], [null, "Pride and joy"], ], includes: "Includes unlimited access to:", cta: "Start with Supporter 10", upgradeCta: "Upgrade to Supporter 10", regular: { subscriptionType: SubscriptionType.MDN_PLUS_10M, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_10M_URL_SP3 : MDN_PLUS_SUBSCRIBE_10M_URL, monthlyPrice: 1000, }, discounted: { subscriptionType: SubscriptionType.MDN_PLUS_10Y, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_10Y_URL_SP3 : MDN_PLUS_SUBSCRIBE_10Y_URL, monthlyPrice: 833, }, }; function OfferDetails({ offerDetails, period, }: { offerDetails: OfferDetailsProps; period: Period; }) { const discounted = period === Period.Year; const { subscriptionType, ctaLink, monthlyPrice } = period === Period.Year && offerDetails.id !== "core" ? offerDetails.discounted : offerDetails.regular; const userData = useUserData(); const current = isCurrent(userData, subscriptionType); const upgrade = canUpgrade(userData, subscriptionType); const gleanClick = useGleanClick(); const displayMonthlyPrice = monthlyPrice && new Intl.NumberFormat(undefined, { style: "currency", currency: offerDetails.currency, }).format(monthlyPrice / 100); return (

{offerDetails.name}

{(displayMonthlyPrice && (

{displayMonthlyPrice} /month
{`Billed ${discounted ? "annually" : "monthly"}`}

)) || (

Free

)} {(current && Current plan) || (upgrade === null && ( gleanClick(`${OFFER_OVERVIEW_CLICK}: ${offerDetails.id}`) } > {offerDetails.cta} )) || (upgrade && ( {offerDetails.upgradeCta} )) || ( Not available )}

{offerDetails.includes}

See terms and conditions
); } function isCurrent(user: UserData, subscriptionType: SubscriptionType) { if (!user?.isAuthenticated) { return false; } return user.subscriptionType === subscriptionType; } function canUpgrade(user: UserData, subscriptionType: SubscriptionType) { if (!user?.isAuthenticated) { return null; } if (!user.isSubscriber || !user.subscriptionType) { return true; } return ( SUBSCRIPTIONS[user.subscriptionType]?.order < SUBSCRIPTIONS[subscriptionType]?.order ); } function getLocalizedPlans(countrySpecific: StripePlans): { CORE: OfferDetailsProps; PLUS_5: OfferDetailsProps; PLUS_10: OfferDetailsProps; } { return { CORE: CORE, PLUS_5: { ...PLUS_5, currency: countrySpecific.currency, regular: { ...PLUS_5.regular, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_5M_URL_SP3 : `${MDN_PLUS_SUBSCRIBE_BASE}?plan=${countrySpecific.plans["mdn_plus_5m"].id}`, monthlyPrice: countrySpecific.plans["mdn_plus_5m"].monthlyPriceInCents, }, discounted: { ...PLUS_5.discounted, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_5Y_URL_SP3 : `${MDN_PLUS_SUBSCRIBE_BASE}?plan=${countrySpecific.plans["mdn_plus_5y"].id}`, monthlyPrice: countrySpecific.plans["mdn_plus_5y"].monthlyPriceInCents, }, }, PLUS_10: { ...PLUS_10, currency: countrySpecific.currency, regular: { ...PLUS_10.regular, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_10M_URL_SP3 : `${MDN_PLUS_SUBSCRIBE_BASE}?plan=${countrySpecific.plans["mdn_plus_10m"].id}`, monthlyPrice: countrySpecific.plans["mdn_plus_10m"].monthlyPriceInCents, }, discounted: { ...PLUS_10.discounted, ctaLink: MDN_PLUS_USE_SP3 ? MDN_PLUS_SUBSCRIBE_10Y_URL_SP3 : `${MDN_PLUS_SUBSCRIBE_BASE}?plan=${countrySpecific.plans["mdn_plus_10y"].id}`, monthlyPrice: countrySpecific.plans["mdn_plus_10y"].monthlyPriceInCents, }, }, }; } function OfferOverviewSubscribe() { const userData = useUserData(); const [offerDetails, setOfferDetails] = useState(null); const { isOnline } = useOnlineStatus(); useEffect(() => { (async () => { if (isOnline) { try { const plans: StripePlans = await getStripePlans(); setOfferDetails(getLocalizedPlans(plans)); } catch (error) { //Paid subs Not supported by region just display Free subscription setOfferDetails({ CORE: CORE, PLUS_5: null, PLUS_10: null }); } } })(); }, [isOnline]); const activeSubscription = userData?.subscriptionType; const activeSubscriptionPeriod = (activeSubscription && SUBSCRIPTIONS[activeSubscription]?.period) || Period.Month; let [period, setPeriod] = useState(activeSubscriptionPeriod); const wrapperClass = !isOnline ? "wrapper-offline" : "wrapper"; return (
{!isOnline && (

You are currently offline. Please go online to view the plans for MDN Plus

)} {isOnline && ( <> {(offerDetails && (

Choose a plan {!activeSubscription && ( <> {" "} or )}

)) ||

Loading available plans…

} {offerDetails && /** Only display discount switch if paid plans available */ offerDetails.PLUS_5 && ( { const period = e.target.checked ? Period.Year : Period.Month; setPeriod(period); }} > Pay yearly and get 2 months for free )} )} {offerDetails && (
{offerDetails.PLUS_5 && ( )} {offerDetails.PLUS_10 && ( )}
)}

* Do you need MDN Plus for your company?{" "} Let us know {" "} and we’ll get back to you when it becomes available.

); } export default OfferOverviewSubscribe;