| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import '../../../../stylesheets/components/compile-time-paywall-modal.scss'
- import {
- OLModal,
- OLModalBody,
- OLModalHeader,
- OLModalTitle,
- } from '@/shared/components/ol/ol-modal'
- import OLButton from '@/shared/components/ol/ol-button'
- import MaterialIcon from '@/shared/components/material-icon'
- import BillingPeriodToggle, {
- type BillingPeriod,
- } from '@/shared/components/billing-period-toggle'
- import getMeta from '@/utils/meta'
- import { sendMB } from '@/infrastructure/event-tracking'
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
- import { useTranslation } from 'react-i18next'
- const PLAN_CODE = {
- monthly: 'collaborator',
- annual: 'collaborator-annual',
- } as const
- export type CompileTimeoutPaywallModalProps = {
- show: boolean
- onHide: () => void
- }
- export function CompileTimeoutPaywallModal({
- show,
- onHide,
- }: CompileTimeoutPaywallModalProps) {
- const { t } = useTranslation()
- const standardPlanPricing = getMeta('ol-standardPlanPricing')
- const currency = getMeta('ol-recommendedCurrency')
- const supportsAnnualPricing = Boolean(standardPlanPricing?.annual)
- const [annual, setAnnual] = useState(false)
- useEffect(() => {
- if (!show) {
- setAnnual(false)
- }
- }, [show])
- const billingPeriod: BillingPeriod = annual ? 'annual' : 'monthly'
- const planCode = PLAN_CODE[billingPeriod]
- const hasShownRef = useRef(false)
- useEffect(() => {
- if (show && !hasShownRef.current) {
- const countryCode = getMeta('ol-countryCode')
- sendMB('paywall-plans-page-view', {
- currency,
- countryCode,
- version: 'compile-timeout',
- })
- hasShownRef.current = true
- } else if (!show) {
- hasShownRef.current = false
- }
- }, [show, currency])
- const handleToggleBilling = useCallback(() => {
- const nextAnnual = !annual
- setAnnual(nextAnnual)
- sendMB('paywall-plans-page-toggle', {
- 'billing-period': nextAnnual ? 'annual' : 'monthly',
- checked: nextAnnual ? 'checked' : 'unchecked',
- version: 'compile-timeout',
- })
- }, [annual])
- const openCheckout = useCallback(() => {
- const params = new URLSearchParams({
- itm_campaign: 'compile-timeout',
- })
- window.open(
- `/user/subscription/new?planCode=${planCode}&${params.toString()}`,
- '_blank',
- 'noopener,noreferrer'
- )
- }, [planCode])
- const handleUpgrade = useCallback(() => {
- sendMB('paywall-plans-page-click', {
- plan: 'collaborator',
- 'billing-period': billingPeriod,
- currency,
- version: 'compile-timeout',
- button: 'buy',
- })
- openCheckout()
- }, [billingPeriod, currency, openCheckout])
- const viewAllPlansHref = useMemo(() => {
- const params = new URLSearchParams({
- itm_campaign: 'compile-timeout',
- })
- return `/user/subscription/choose-your-plan?${params.toString()}`
- }, [])
- const handleViewAllPlans = useCallback(() => {
- sendMB('paywall-plans-page-click', {
- button: 'plans',
- 'billing-period': billingPeriod,
- currency,
- version: 'compile-timeout',
- })
- }, [billingPeriod, currency])
- const price = annual
- ? standardPlanPricing?.annual
- : standardPlanPricing?.monthly
- const priceSubtext = annual ? t('per_year') : t('per_month')
- const strikethroughPrice = annual
- ? standardPlanPricing?.monthlyTimesTwelve
- : undefined
- return (
- <OLModal
- className="compile-time-paywall-modal"
- size="sm"
- show={show}
- onHide={onHide}
- centered
- >
- <OLModalHeader className="border-0 pb-0" closeButton>
- <div className="w-100 text-center">
- <OLModalTitle>{t('get_more_compile_time')}</OLModalTitle>
- </div>
- </OLModalHeader>
- <OLModalBody>
- <div className="compile-time-paywall-card">
- <div className="compile-time-paywall-header">
- <div className="compile-time-paywall-plan-meta">
- <p className="compile-time-paywall-plan-label">
- {t('standard')} {t('plan')}
- </p>
- <div className="compile-time-paywall-price-container">
- {strikethroughPrice ? (
- <span className="compile-time-paywall-price-strikethrough">
- {strikethroughPrice}
- </span>
- ) : (
- <span
- className="compile-time-paywall-price-strikethrough"
- style={{ visibility: 'hidden' }}
- aria-hidden="true"
- >
-
- </span>
- )}
- <span className="compile-time-paywall-price">{price}</span>
- {priceSubtext && (
- <span className="compile-time-paywall-price-subtext">
- {priceSubtext}
- </span>
- )}
- </div>
- </div>
- {supportsAnnualPricing && (
- <div className="compile-time-paywall-period-toggle">
- <BillingPeriodToggle
- id="compile-timeout-period"
- value={annual ? 'annual' : 'monthly'}
- onChange={(period: BillingPeriod) => {
- if ((period === 'annual') !== annual) {
- handleToggleBilling()
- }
- }}
- variant="premium"
- />
- </div>
- )}
- </div>
- <div className="compile-time-paywall-body">
- <div className="compile-time-paywall-illustration">
- <MaterialIcon type="rocket_launch" />
- </div>
- <div className="compile-time-paywall-content">
- <p className="compile-time-paywall-intro">
- <strong>{t('compile_timeout_modal_intro')}</strong>
- </p>
- <div className="compile-time-paywall-feature-list">
- <ListItem>
- {t('collabs_per_proj', { collabcount: 10 })}
- </ListItem>
- <ListItem>{t('track_changes')}</ListItem>
- <ListItem>{t('github_integration')}</ListItem>
- <ListItem>{t('and_much_more')}</ListItem>
- </div>
- </div>
- </div>
- <div className="compile-time-paywall-actions">
- <OLButton
- variant="premium"
- type="button"
- size="lg"
- onClick={handleUpgrade}
- >
- {t('buy_now_no_exclamation_mark')}
- </OLButton>
- <OLButton
- className="compile-time-paywall-view-plans"
- variant="ghost"
- href={viewAllPlansHref}
- target="_blank"
- rel="noreferrer"
- onClick={handleViewAllPlans}
- >
- {t('view_all_plans')}
- </OLButton>
- </div>
- </div>
- </OLModalBody>
- </OLModal>
- )
- }
- function ListItem({ children }: { children: React.ReactNode }) {
- return (
- <div className="compile-time-paywall-list-item">
- <MaterialIcon type="check_circle" />
- <span>{children}</span>
- </div>
- )
- }
- export default CompileTimeoutPaywallModal
|