import {useState} from 'react'; import {addErrorMessage} from 'sentry/actionCreators/indicator'; import type {Client} from 'sentry/api'; import Confirm from 'sentry/components/confirm'; import {Button} from 'sentry/components/core/button'; import {t, tct} from 'sentry/locale'; import type {Organization} from 'sentry/types/organization'; import {browserHistory} from 'sentry/utils/browserHistory'; import normalizeUrl from 'sentry/utils/url/normalizeUrl'; import withApi from 'sentry/utils/withApi'; import withOrganization from 'sentry/utils/withOrganization'; import {openUpsellModal} from 'getsentry/actionCreators/modal'; import {sendTrialRequest, sendUpgradeRequest} from 'getsentry/actionCreators/upsell'; import TrialStarter from 'getsentry/components/trialStarter'; import withSubscription from 'getsentry/components/withSubscription'; import type {Subscription} from 'getsentry/types'; import {getTrialLength} from 'getsentry/utils/billing'; import trackGetsentryAnalytics, { type GetsentryEventKey, } from 'getsentry/utils/trackGetsentryAnalytics'; type ChildRenderProps = { action: | 'start_trial' | 'send_to_checkout' | 'request_trial' | 'request_upgrade' | 'open_upsell_modal'; canTrial: boolean; defaultButtonText: string; hasBillingScope: boolean; onClick: (e?: React.MouseEvent) => void; }; type Props = { api: Client; children: (opts: ChildRenderProps) => React.ReactNode; organization: Organization; source: string; subscription: Subscription; extraAnalyticsParams?: Record; onTrialStarted?: () => void; /** * When this is true, a Confirm modal will be displayed before activating the trial */ showConfirmation?: boolean; /** * if true, non-billing users clicking will trigger trial and plan upgrade requests */ triggerMemberRequests?: boolean; upsellDefaultSelection?: string; }; function LoadingButton(props: { defaultOnClick: () => void; startTrial: () => Promise; }) { const {startTrial, defaultOnClick} = props; const [busy, setBusy] = useState(false); return ( ); } function UpsellProvider({ api, onTrialStarted, organization, subscription, source, extraAnalyticsParams, triggerMemberRequests, showConfirmation, upsellDefaultSelection, children, }: Props) { // if the org or subscription isn't loaded yet, don't render anything if (!organization || !subscription) { return null; } const hasBillingScope = organization.access?.includes('org:billing'); // don't render any request trial/upgrade CTAs for non-self serve customers if (!hasBillingScope && !subscription.canSelfServe && triggerMemberRequests) { return null; } const canTrial = subscription.canTrial && !subscription.isTrial; const handleRequest = () => { const args = { api, organization, }; if (canTrial) { return sendTrialRequest(args); } return sendUpgradeRequest(args); }; let defaultButtonText: string; if (hasBillingScope || !triggerMemberRequests) { defaultButtonText = canTrial ? t('Start Trial') : t('Upgrade Plan'); } else { defaultButtonText = canTrial ? t('Request Trial') : t('Request Upgrade'); } const getAction = () => { if (hasBillingScope) { return canTrial ? 'start_trial' : 'send_to_checkout'; } if (triggerMemberRequests) { return canTrial ? 'request_trial' : 'request_upgrade'; } return 'open_upsell_modal'; }; showConfirmation = showConfirmation && hasBillingScope && canTrial; const collectAnalytics = (eventKey: GetsentryEventKey) => { trackGetsentryAnalytics(eventKey, { source, can_trial: canTrial, has_billing_scope: hasBillingScope, showed_confirmation: !!showConfirmation, action: getAction(), organization, subscription, ...extraAnalyticsParams, }); }; const trialStartProps = { organization, source, onTrialFailed: () => { addErrorMessage(t('Error starting trial. Please try again.')); }, onTrialStarted, }; if (showConfirmation) { const confirmContent = () => { const trialLength = getTrialLength(organization); return (
{tct( `Your organization is about to start a [trialLength]-day free trial. Click confirm to start your trial.`, { trialLength, } )}
); }; return ( collectAnalytics('growth.upsell_feature.confirmed')} onCancel={() => collectAnalytics('growth.upsell_feature.cancelled')} renderConfirmButton={({defaultOnClick}) => ( {({startTrial}) => ( )} )} header={

{t('Your trial is about to start')}

} > {({open}) => children({ canTrial, hasBillingScope, defaultButtonText, action: getAction(), onClick: () => { // When the user clicks, open the modal collectAnalytics('growth.upsell_feature.clicked'); open(); }, }) }
); } return ( {({startTrial}) => children({ canTrial, hasBillingScope, defaultButtonText, action: getAction(), onClick: e => { e?.preventDefault(); // Direct start the trial. collectAnalytics('growth.upsell_feature.clicked'); if (hasBillingScope) { if (canTrial) { startTrial(); } else { // for self-serve can send them to checkout const baseUrl = subscription.canSelfServe ? `/settings/${organization.slug}/billing/checkout/` : `/settings/${organization.slug}/billing/overview/`; browserHistory.push(`${normalizeUrl(baseUrl)}?referrer=upsell-${source}`); } } else { if (triggerMemberRequests) { handleRequest(); } else { openUpsellModal({ organization, source, defaultSelection: upsellDefaultSelection, }); } } }, }) } ); } export default withApi( withOrganization(withSubscription(UpsellProvider, {noLoader: true})) );