import { useCallback, useEffect } from 'react' import moment from 'moment' import { useTranslation, Trans } from 'react-i18next' import { SubscriptionChangePreview, AddOnPurchase, PremiumSubscriptionChange, } from '../../../../../../types/subscription/subscription-change-preview' import getMeta from '@/utils/meta' import { formatCurrency } from '@/shared/utils/currency' import useAsync from '@/shared/hooks/use-async' import { useLocation } from '@/shared/hooks/use-location' import { debugConsole } from '@/utils/debugging' import { FetchError, postJSON } from '@/infrastructure/fetch-json' import OLCard from '@/features/ui/components/ol/ol-card' import OLRow from '@/features/ui/components/ol/ol-row' import OLCol from '@/features/ui/components/ol/ol-col' import OLButton from '@/features/ui/components/ol/ol-button' import { subscriptionUpdateUrl } from '@/features/subscription/data/subscription-url' import * as eventTracking from '@/infrastructure/event-tracking' import sparkleText from '@/shared/svgs/ai-sparkle-text.svg' import { useFeatureFlag } from '@/shared/context/split-test-context' import PaymentErrorNotification from '@/features/subscription/components/shared/payment-error-notification' import handleStripePaymentAction from '../../util/handle-stripe-payment-action' function PreviewSubscriptionChange() { const preview = getMeta( 'ol-subscriptionChangePreview' ) as SubscriptionChangePreview const purchaseReferrer = getMeta('ol-purchaseReferrer') const { t } = useTranslation() const payNowTask = useAsync() const location = useLocation() const aiAssistEnabled = useFeatureFlag('overleaf-assist-bundle') useEffect(() => { if (preview.change.type === 'add-on-purchase') { eventTracking.sendMB('preview-subscription-change-view', { plan: preview.change.addOn.code, upgradeType: 'add-on', referrer: purchaseReferrer, }) } }, [preview.change, purchaseReferrer]) const handlePayNowClick = useCallback(() => { if (preview.change.type === 'add-on-purchase') { eventTracking.sendMB('subscription-change-form-submit', { plan: preview.change.addOn.code, upgradeType: 'add-on', referrer: purchaseReferrer, }) } eventTracking.sendMB('assistant-add-on-purchase') payNowTask .runAsync(payNow(preview)) .then(() => { if (preview.change.type === 'add-on-purchase') { eventTracking.sendMB('subscription-change-form-success', { plan: preview.change.addOn.code, upgradeType: 'add-on', referrer: purchaseReferrer, }) } location.replace('/user/subscription/thank-you') }) .catch(debugConsole.error) }, [purchaseReferrer, location, payNowTask, preview]) const aiAddOnChange = preview.change.type === 'add-on-purchase' && preview.change.addOn.code === 'assistant' // the driver of the change, which we can display as the immediate charge const changeName = preview.change.type === 'add-on-purchase' ? (preview.change as AddOnPurchase).addOn.name : (preview.change as PremiumSubscriptionChange).plan.name return (
{preview.change.type === 'add-on-purchase' ? (

{t('add_add_on_to_your_plan', { addOnName: preview.change.addOn.name, })}

) : preview.change.type === 'premium-subscription' ? (

{t('subscribe_to_plan', { planName: preview.change.plan.name })}

) : null} {payNowTask.isError && ( )} {aiAddOnChange && (
{aiAssistEnabled ? (
)}

{t('due_today')}:

{changeName} {formatCurrency( preview.immediateCharge.subtotal, preview.currency )} {preview.immediateCharge.tax > 0 && ( {t('vat')} {preview.nextInvoice.tax.rate * 100}% {formatCurrency( preview.immediateCharge.tax, preview.currency )} )} {t('total_today')} {formatCurrency( preview.immediateCharge.total, preview.currency )}
}} shouldUnescape tOptions={{ interpolation: { escapeValue: true } }} />{' '} }} shouldUnescape tOptions={{ interpolation: { escapeValue: true } }} />
{t('pay_now')}

{t('future_payments')}:

{preview.nextInvoice.plan.name} {formatCurrency( preview.nextInvoice.plan.amount, preview.currency )} {preview.nextInvoice.addOns.map(addOn => ( {addOn.name} {addOn.quantity > 1 ? ` ×${addOn.quantity}` : ''} {formatCurrency(addOn.amount, preview.currency)} ))} {preview.nextInvoice.tax.rate > 0 && ( {t('vat')} {preview.nextInvoice.tax.rate * 100}% {formatCurrency( preview.nextInvoice.tax.amount, preview.currency )} )} {preview.nextPlan.annual ? t('total_per_year') : t('total_per_month')} {formatCurrency(preview.nextInvoice.total, preview.currency)}
}} shouldUnescape tOptions={{ interpolation: { escapeValue: true } }} />
) } async function payNow(preview: SubscriptionChangePreview) { try { if (preview.change.type === 'add-on-purchase') { await postJSON( `/user/subscription/addon/${preview.change.addOn.code}/add` ) } else if (preview.change.type === 'premium-subscription') { await postJSON(subscriptionUpdateUrl, { body: { plan_code: preview.change.plan.code }, }) } else { throw new Error( `Unknown subscription change preview type: ${preview.change}` ) } } catch (e) { const { handled } = await handleStripePaymentAction(e as FetchError) if (!handled) { throw e } } } export default PreviewSubscriptionChange