This repository was archived by the owner on Sep 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathNewCodyProSubscriptionPage.tsx
More file actions
131 lines (110 loc) · 5.43 KB
/
NewCodyProSubscriptionPage.tsx
File metadata and controls
131 lines (110 loc) · 5.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import React, { useEffect, type FunctionComponent } from 'react'
import { Elements } from '@stripe/react-stripe-js'
// NOTE: A side effect of loading this library will update the DOM and
// fetch stripe.js. This is a subtle detail but means that the Stripe
// functionality won't be loaded until this actual module does, via
// the lazily loaded router module.
import { loadStripe } from '@stripe/stripe-js'
import classNames from 'classnames'
import { Navigate, useSearchParams } from 'react-router-dom'
import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
import { PageHeader, Text, LoadingSpinner, Alert, logger } from '@sourcegraph/wildcard'
import type { AuthenticatedUser } from '../../../../auth'
import { withAuthenticatedUser } from '../../../../auth/withAuthenticatedUser'
import { Page } from '../../../../components/Page'
import { PageTitle } from '../../../../components/PageTitle'
import { CodySubscriptionPlan } from '../../../../graphql-operations'
import { CodyProRoutes } from '../../../codyProRoutes'
import { PageHeaderIcon } from '../../../components/PageHeaderIcon'
import type { UserCodySubscription } from '../../../subscription/useUserCodySubscription'
import { defaultCodyProApiClientContext, CodyProApiClientContext } from '../../api/components/CodyProApiClient'
import { useCurrentSubscription } from '../../api/react-query/subscriptions'
import { useBillingAddressStripeElementsOptions } from '../manage/BillingAddress'
import { CodyProCheckoutForm } from './CodyProCheckoutForm'
import styles from './NewCodyProSubscriptionPage.module.scss'
// NOTE: Call loadStripe outside a component’s render to avoid recreating the object.
// We do it here, meaning that "stripe.js" will get loaded lazily, when the user
// routes to this page.
const publishableKey = window.context.frontendCodyProConfig?.stripePublishableKey
const stripe = await loadStripe(publishableKey || '')
interface NewCodyProSubscriptionPageProps extends TelemetryV2Props {
authenticatedUser: AuthenticatedUser
codySubscription: UserCodySubscription
}
const AuthenticatedNewCodyProSubscriptionPage: FunctionComponent<NewCodyProSubscriptionPageProps> = ({
authenticatedUser,
telemetryRecorder,
codySubscription,
}) => {
const [urlSearchParams] = useSearchParams()
const addSeats = !!urlSearchParams.get('addSeats')
const isTeam = addSeats || parseInt(urlSearchParams.get('seats') || '', 10) > 1
const stripeElementsOptions = useBillingAddressStripeElementsOptions()
const subscriptionQueryResult = useCurrentSubscription()
const subscription = subscriptionQueryResult?.data
useEffect(() => {
telemetryRecorder.recordEvent('cody.new-subscription-checkout', 'view')
}, [telemetryRecorder])
useEffect(() => {
if (subscriptionQueryResult.error) {
logger.error('Failed to fetch subscription data', subscriptionQueryResult.error)
}
}, [subscriptionQueryResult.error])
// If the user already has a Cody Pro subscription, direct them back to the Cody Management page.
if (!addSeats && codySubscription.plan === CodySubscriptionPlan.PRO) {
return <Navigate to={CodyProRoutes.Manage} replace={true} />
}
// Display spinner without page header because without the subscription, we don't know which header to show
if (subscriptionQueryResult.isLoading) {
return <LoadingSpinner className="mx-auto" />
}
const PageWithHeader = ({ children }: { children: React.ReactNode }): React.ReactElement => (
<Page className={classNames('d-flex flex-column', styles.page)}>
<PageTitle title={addSeats ? 'Add seats' : 'New subscription'} />
<PageHeader className="my-4 d-inline-flex align-items-center">
<PageHeader.Heading as="h1" className="text-3xl font-medium">
<PageHeaderIcon
name={isTeam ? 'mdi-account-multiple-plus-gradient' : 'cody-logo'}
className="mr-3"
/>{' '}
<Text as="span">{isTeam ? 'Give your team Cody Pro' : 'Upgrade to Cody Pro'}</Text>
</PageHeader.Heading>
</PageHeader>
{children}
</Page>
)
if (subscriptionQueryResult.isLoading) {
return (
<PageWithHeader>
<LoadingSpinner className="mx-auto" />
</PageWithHeader>
)
}
if (addSeats && subscriptionQueryResult.isError) {
return (
<PageWithHeader>
<Alert variant="danger">Failed to fetch subscription data</Alert>
</PageWithHeader>
)
}
if (addSeats && !subscriptionQueryResult.isLoading && !subscription) {
return (
<PageWithHeader>
<Alert variant="danger">Subscription data is not available</Alert>
</PageWithHeader>
)
}
return (
<PageWithHeader>
<CodyProApiClientContext.Provider value={defaultCodyProApiClientContext}>
<Elements stripe={stripe} options={stripeElementsOptions}>
<CodyProCheckoutForm
subscription={subscription}
customerEmail={authenticatedUser?.emails[0].email || ''}
/>
</Elements>
</CodyProApiClientContext.Provider>
</PageWithHeader>
)
}
export const NewCodyProSubscriptionPage = withAuthenticatedUser(AuthenticatedNewCodyProSubscriptionPage)