Skip to content

Commit 5e48a4e

Browse files
wip
1 parent 747f183 commit 5e48a4e

File tree

8 files changed

+336
-123
lines changed

8 files changed

+336
-123
lines changed

src/pages/PlanPage/PlanPage.jsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import config from 'config'
88

99
import { SentryRoute } from 'sentry'
1010

11+
import { useStripeSetupIntent } from 'services/account/useStripeSetupIntent'
1112
import LoadingLogo from 'ui/LoadingLogo'
1213

1314
import { PlanProvider } from './context'
@@ -37,6 +38,12 @@ function PlanPage() {
3738
const { data: ownerData } = useSuspenseQueryV5(
3839
PlanPageDataQueryOpts({ owner, provider })
3940
)
41+
// const { data: setupIntent } = useStripeSetupIntent({ owner, provider })
42+
43+
const setupIntent = {
44+
clientSecret:
45+
'seti_1QfCiSGlVGuVgOrkPhA3FjTZ_secret_RYJLn86FhD6Co4PXYqdSkDYCgMcgZN0',
46+
}
4047

4148
if (config.IS_SELF_HOSTED || !ownerData?.isCurrentUserPartOfOrg) {
4249
return <Redirect to={`/${provider}/${owner}`} />
@@ -45,7 +52,10 @@ function PlanPage() {
4552
return (
4653
<div className="flex flex-col gap-4">
4754
<Tabs />
48-
<Elements stripe={stripePromise}>
55+
<Elements
56+
stripe={stripePromise}
57+
options={{ clientSecret: setupIntent?.clientSecret }}
58+
>
4959
<PlanProvider>
5060
<PlanBreadcrumb />
5161
<Suspense fallback={<Loader />}>

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/BillingDetails.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import PaymentMethod from './PaymentMethod'
77
import Button from 'ui/Button'
88
import { useState } from 'react'
99
import A from 'ui/A'
10-
import EditablePaymentMethod from './EditPaymentMethod'
10+
import EditPaymentMethod from './EditPaymentMethod'
1111

1212
interface URLParams {
1313
provider: string
@@ -29,8 +29,6 @@ function BillingDetails() {
2929
return null
3030
}
3131

32-
console.log('iseditmode', isEditMode)
33-
3432
return (
3533
<div className="flex flex-col divide-y border">
3634
{/* Billing Details Section */}
@@ -68,7 +66,12 @@ function BillingDetails() {
6866
)}
6967
</div>
7068
{isEditMode ? (
71-
<EditablePaymentMethod />
69+
<EditPaymentMethod
70+
isEditMode={isEditMode}
71+
setEditMode={setEditMode}
72+
provider={provider}
73+
owner={owner}
74+
/>
7275
) : (
7376
<>
7477
<EmailAddress />

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/EditPaymentMethod/EditPaymentMethod.tsx

+24-113
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,22 @@
1-
import {
2-
Elements,
3-
PaymentElement,
4-
useElements,
5-
useStripe,
6-
} from '@stripe/react-stripe-js'
7-
import { loadStripe } from '@stripe/stripe-js'
81
import React, { useState } from 'react'
92

10-
import Button from 'ui/Button'
11-
123
import AddressForm from '../Address/AddressForm'
134

14-
// TODO - fetch from API
15-
const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY || ''
16-
const MANUALLY_FETCHED_CLIENT_SECRET = process.env.STRIPE_CLIENT_SECRET || ''
17-
18-
const stripePromise = loadStripe(STRIPE_PUBLISHABLE_KEY)
19-
20-
interface PaymentFormProps {
21-
clientSecret: string
22-
}
23-
24-
const PaymentForm: React.FC<PaymentFormProps> = () => {
25-
const stripe = useStripe()
26-
const elements = useElements()
27-
const [isSubmitting, setIsSubmitting] = useState(false)
28-
const [errorMessage, setErrorMessage] = useState<string | null>(null)
29-
30-
const handleSubmit = async (event: React.FormEvent) => {
31-
event.preventDefault()
32-
setIsSubmitting(true)
33-
setErrorMessage(null)
34-
35-
if (!stripe || !elements) {
36-
setErrorMessage('Stripe has not loaded yet. Please try again.')
37-
setIsSubmitting(false)
38-
return
39-
}
40-
41-
const { error } = await stripe.confirmPayment({
42-
elements,
43-
confirmParams: {
44-
// eslint-disable-next-line camelcase
45-
return_url: 'https://codecov.io',
46-
},
47-
})
48-
49-
if (error) {
50-
setErrorMessage(error.message || 'An unexpected error occurred.')
51-
setIsSubmitting(false)
52-
} else {
53-
setIsSubmitting(false)
54-
}
55-
}
56-
57-
return (
58-
<div>
59-
<PaymentElement
60-
options={{
61-
layout: 'tabs',
62-
defaultValues: {
63-
billingDetails: {
64-
name: 'John Doe',
65-
},
66-
},
67-
}}
68-
/>
69-
<div className="mb-8 mt-4 flex gap-1">
70-
<Button
71-
hook="submit-address-update"
72-
type="submit"
73-
variant="primary"
74-
disabled={isSubmitting} // TODO - handle
75-
onClick={handleSubmit}
76-
to={undefined}
77-
>
78-
Save
79-
</Button>
80-
<Button
81-
type="button"
82-
hook="cancel-address-update"
83-
variant="plain"
84-
// disabled={isLoading}
85-
onClick={() => console.log('TODO - implement me')} // TODO - implement me
86-
to={undefined}
87-
>
88-
Cancel
89-
</Button>
90-
</div>
91-
92-
{errorMessage && <div className="text-red-500">{errorMessage}</div>}
93-
</div>
94-
)
95-
}
96-
97-
const PaymentPage: React.FC<{ clientSecret: string }> = ({ clientSecret }) => {
98-
const options = {
99-
clientSecret,
100-
appearance: {
101-
theme: 'stripe' as const,
102-
},
103-
}
5+
import EditPaymentMethodForm from './EditPaymentMethodForm'
1046

105-
return (
106-
<Elements stripe={stripePromise} options={options}>
107-
<PaymentForm clientSecret={clientSecret} />
108-
</Elements>
109-
)
7+
interface EditPaymentMethodProps {
8+
isEditMode: boolean
9+
setEditMode: (isEditMode: boolean) => void
10+
provider: string
11+
owner: string
11012
}
11113

112-
interface EditablePaymentMethodProps {
113-
clientSecret: string
114-
}
115-
116-
const EditPaymentMethod: React.FC<EditablePaymentMethodProps> = () => {
117-
const clientSecret = MANUALLY_FETCHED_CLIENT_SECRET // TODO - fetch from API
118-
14+
const EditPaymentMethod = ({
15+
isEditMode,
16+
setEditMode,
17+
provider,
18+
owner,
19+
}: EditPaymentMethodProps) => {
11920
const [activeTab, setActiveTab] = useState<'primary' | 'secondary'>('primary')
12021

12122
return (
@@ -143,13 +44,23 @@ const EditPaymentMethod: React.FC<EditablePaymentMethodProps> = () => {
14344
<div className="m-4">
14445
{activeTab === 'primary' && (
14546
<div>
146-
<PaymentPage clientSecret={clientSecret} />
47+
<EditPaymentMethodForm
48+
isEditMode={isEditMode}
49+
setEditMode={setEditMode}
50+
provider={provider}
51+
owner={owner}
52+
/>
14753
<AddressForm closeForm={() => {}} provider={''} owner={''} />
14854
</div>
14955
)}
15056
{activeTab === 'secondary' && (
15157
<div>
152-
<PaymentPage clientSecret={clientSecret} />
58+
<EditPaymentMethodForm
59+
isEditMode={isEditMode}
60+
setEditMode={setEditMode}
61+
provider={provider}
62+
owner={owner}
63+
/>
15364
<AddressForm closeForm={() => {}} provider={''} owner={''} />
15465
</div>
15566
)}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { PaymentElement, useElements } from '@stripe/react-stripe-js'
2+
import cs from 'classnames'
3+
import { useState } from 'react'
4+
5+
import { useUpdatePaymentMethod } from 'services/account/useUpdatePaymentMethod'
6+
import { Theme, useThemeContext } from 'shared/ThemeContext'
7+
import Button from 'ui/Button'
8+
9+
interface PaymentMethodFormProps {
10+
closeForm: () => void
11+
provider: string
12+
owner: string
13+
}
14+
15+
const EditPaymentMethodForm = ({
16+
closeForm,
17+
provider,
18+
owner,
19+
}: PaymentMethodFormProps) => {
20+
const [errorState, setErrorState] = useState('')
21+
const { theme } = useThemeContext()
22+
const isDarkMode = theme === Theme.DARK
23+
24+
const elements = useElements()
25+
const {
26+
mutate: updatePaymentMethod,
27+
isLoading,
28+
error,
29+
reset,
30+
} = useUpdatePaymentMethod({
31+
provider,
32+
owner,
33+
})
34+
35+
function submit(e: React.FormEvent) {
36+
e.preventDefault()
37+
38+
if (!elements) {
39+
return null
40+
}
41+
42+
updatePaymentMethod(elements.getElement(PaymentElement), {
43+
onSuccess: closeForm,
44+
})
45+
}
46+
47+
const showError = (error && !reset) || errorState
48+
49+
return (
50+
<form onSubmit={submit} aria-label="form">
51+
<div className={cs('flex flex-col gap-3')}>
52+
<div className="mt-2 flex flex-col gap-2">
53+
<PaymentElement
54+
options={{
55+
layout: 'tabs',
56+
defaultValues: {
57+
billingDetails: {
58+
name: 'John Doe',
59+
},
60+
},
61+
}}
62+
/>
63+
<p className="mt-1 text-ds-primary-red">
64+
{showError && (error?.message || errorState)}
65+
</p>
66+
<div className="mb-8 mt-4 flex gap-1">
67+
<Button
68+
hook="update-payment"
69+
type="submit"
70+
variant="primary"
71+
disabled={isLoading}
72+
to={undefined}
73+
>
74+
Update
75+
</Button>
76+
<Button
77+
type="button"
78+
hook="cancel-payment"
79+
variant="plain"
80+
disabled={isLoading}
81+
onClick={closeForm}
82+
to={undefined}
83+
>
84+
Cancel
85+
</Button>
86+
</div>
87+
</div>
88+
</div>
89+
</form>
90+
)
91+
}
92+
93+
export default EditPaymentMethodForm

src/pages/PlanPage/subRoutes/CurrentOrgPlan/BillingDetails/PaymentCard/PaymentCard.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import A from 'ui/A'
1010
import Button from 'ui/Button'
1111
import Icon from 'ui/Icon'
1212

13-
import CardInformation from './CardInformation'
13+
import PaymentMethodInformation from '../PaymentMethod/PaymentMethodInformation'
1414
import CreditCardForm from './CreditCardForm'
1515
import { cn } from 'shared/utils/cn'
1616

@@ -29,7 +29,7 @@ function PaymentCard({
2929
owner: string
3030
className?: string
3131
}) {
32-
const card = subscriptionDetail?.defaultPaymentMethod?.card
32+
const isPaymentMethodSet = !!subscriptionDetail?.defaultPaymentMethod
3333

3434
return (
3535
<div className={cn("flex flex-col gap-2", className)}>
@@ -42,12 +42,12 @@ function PaymentCard({
4242
owner={owner}
4343
closeForm={() => setEditMode(false)}
4444
/>
45-
) : card ? (
46-
<CardInformation card={card} subscriptionDetail={subscriptionDetail} />
45+
) : isPaymentMethodSet ? (
46+
<PaymentMethodInformation subscriptionDetail={subscriptionDetail} />
4747
) : (
4848
<div className="flex flex-col gap-4 text-ds-gray-quinary">
4949
<p className="mt-4">
50-
No credit card set. Please contact support if you think it’s an
50+
No payment method set. Please contact support if you think it’s an
5151
error or set it yourself.
5252
</p>
5353
<div className="flex self-start">

0 commit comments

Comments
 (0)