import type {ComponentType} from 'react'; import styled from '@emotion/styled'; import type {Location} from 'history'; import {openModal} from 'sentry/actionCreators/modal'; import {promptsUpdate} from 'sentry/actionCreators/prompts'; import {Client} from 'sentry/api'; import {openConfirmModal} from 'sentry/components/confirm'; import {t} from 'sentry/locale'; import type {Organization} from 'sentry/types/organization'; import type {PromotionModalBodyProps} from 'getsentry/components/promotionModal'; import type {Reservations} from 'getsentry/components/upgradeNowModal/types'; import SubscriptionStore from 'getsentry/stores/subscriptionStore'; import type { Invoice, Plan, PreviewData, Promotion, PromotionClaimed, Subscription, } from 'getsentry/types'; import type {AM2UpdateSurfaces} from 'getsentry/utils/trackGetsentryAnalytics'; type UpsellModalOptions = { organization: Organization; source: string; defaultSelection?: string; }; export async function openUpsellModal(options: UpsellModalOptions) { const {default: Modal, modalCss} = await import('getsentry/components/upsellModal'); openModal(deps => , {modalCss}); } type TrialModalProps = { organization: Organization; }; type PartnerPlanModalProps = { organization: Organization; subscription: Subscription; }; function genTrialModalOnClose( options: TrialModalProps, type: 'trialEnd' | 'forcedTrial' ) { let feature: string, subField: string; switch (type) { case 'trialEnd': feature = 'trial_ended_notice'; subField = 'hasDismissedTrialEndingNotice'; break; case 'forcedTrial': feature = 'forced_trial_notice'; subField = 'hasDismissedForcedTrialNotice'; break; default: throw new Error('Unexpected type'); } const api = new Client(); const promptParams = { organization: options.organization, feature, status: 'dismissed', } as const; const subUpdate = { [subField]: true, } as const; // Handle marking the feature prompt as seen when the modal is // closed return () => { promptsUpdate(api, promptParams); SubscriptionStore.set(options.organization.slug, subUpdate); }; } export async function openTrialEndingModal(options: TrialModalProps) { const {default: Modal, modalCss} = await import( 'getsentry/components/trialEndingModal' ); const onClose = genTrialModalOnClose(options, 'trialEnd'); openModal(deps => , {modalCss, onClose}); } export async function openForcedTrialModal(options: TrialModalProps) { const {default: Modal, modalCss} = await import( 'getsentry/components/forcedTrialModal' ); const onClose = genTrialModalOnClose(options, 'forcedTrial'); openModal(deps => , { modalCss, onClose, }); } export async function openPartnerPlanEndingModal(options: PartnerPlanModalProps) { const {default: Modal, modalCss} = await import( 'getsentry/components/partnerPlanEndingModal' ); const api = new Client(); const promptParams = { organization: options.organization, feature: 'partner_plan_ending_modal', status: 'dismissed', } as const; const onClose = () => { promptsUpdate(api, promptParams); }; openModal(deps => , {modalCss, onClose}); } type EditCreditCardOptions = { onSuccess: (data: Subscription) => void; organization: Organization; location?: Location; }; export async function openEditCreditCard(options: EditCreditCardOptions) { const {default: Modal} = await import('getsentry/components/creditCardEditModal'); openModal(deps => ); } type OpenInvoicePaymentOptions = { invoice: Invoice; organization: Organization; reloadInvoice: () => void; }; export async function openInvoicePaymentModal(options: OpenInvoicePaymentOptions) { const {default: Modal} = await import('getsentry/views/invoiceDetails/paymentForm'); openModal(deps => ); } type UpsellModalProps = { organization: Organization; plan: Plan; previewData: PreviewData; reservations: Reservations; subscription: Subscription; surface: AM2UpdateSurfaces; isActionDisabled?: boolean; onComplete?: () => void; }; export async function openAM2UpsellModal(options: UpsellModalProps) { const {default: Modal, modalCss} = await import( 'getsentry/components/upgradeNowModal/index' ); openModal(deps => , {modalCss}); } export type UpsellModalSamePriceProps = { organization: Organization; plan: Plan; previewData: PreviewData; reservations: Reservations; subscription: Subscription; surface: AM2UpdateSurfaces; onComplete?: () => void; }; export async function openAM2UpsellModalSamePrice(options: UpsellModalSamePriceProps) { const {default: Modal, modalCss} = await import( 'getsentry/components/upgradeNowModal/modalSamePrice' ); openModal(deps => , {modalCss}); } type ProfilingUpsellModalProps = { organization: Organization; subscription: Subscription; isActionDisabled?: boolean; onComplete?: () => void; }; export async function openAM2ProfilingUpsellModal(options: ProfilingUpsellModalProps) { const {default: Modal, modalCss} = await import( 'getsentry/components/profiling/profilingUpgradeModal' ); openModal(deps => , {modalCss}); } type PromotionModalOptions = { organization: Organization; price: number; promotion: Promotion; promptFeature: string; PromotionModalBody?: ComponentType; acceptButtonText?: string; api?: Client; declineButtonText?: string; onAccept?: () => void; }; export async function openPromotionModal(options: PromotionModalOptions) { const {default: Modal, modalCss} = await import('getsentry/components/promotionModal'); openModal(deps => , {closeEvents: 'none', modalCss}); } export function openPromotionReminderModal( promotionClaimed: PromotionClaimed, onCancel?: () => void, onConfirm?: () => void ) { const {dateCompleted} = promotionClaimed; const promo = promotionClaimed.promotion; const {amount, billingInterval, billingPeriods, maxCentsPerPeriod, reminderText} = promo.discountInfo; const date = new Date(dateCompleted); const percentOff = amount / 100; const interval = billingInterval === 'monthly' ? t('months') : t('years'); const intervalSingular = interval.slice(0, -1); /** * Removed translation because of complicated pluralization and lots of changing * parameters from the different promotions we can use this for */ openConfirmModal({ message: (

{reminderText}

{t('Current Promotion:')}

{percentOff}% off (up to ${maxCentsPerPeriod / 100} per {intervalSingular}) for{' '} {billingPeriods} {interval} starting on {date.toLocaleDateString('en-US')}

), header: Promotion Conflict, priority: 'danger', confirmText: 'Downgrade Anyway', onCancel: () => onCancel?.(), onConfirm: () => onConfirm?.(), }); } export async function openCodecovModal(options: {organization: Organization}) { const {default: Modal, modalCss} = await import( 'getsentry/components/codecovPromotionModal' ); openModal(deps => , {modalCss, closeEvents: 'none'}); } const HeaderText = styled('div')` font-size: ${p => p.theme.fontSizeExtraLarge}; font-weight: bold; `; const Subheader = styled('div')` font-weight: bold; font-size: ${p => p.theme.fontSizeMedium}; `; export async function openDataConsentModal() { const {default: Modal} = await import('getsentry/components/dataConsentModal'); openModal(deps => ); }