Files
supabase/apps/studio/components/interfaces/Billing/Payment/AddNewPaymentMethodModal.tsx
T
Francesco SansalvadoreandJonathan Summers-Muir be9590c890 Multiple themes management (#18871)
* set up multiple themes in studio

* set up multiple themes in studio

* set up multiple themes in docs and www

* update all resolvedTheme to also include deep-dark

* update all resolvedTheme checks to also include deep-dark

* update tailwind.config.js tokens

* update tailwind.config.js tokens

* update leftover scale12 token

* update if resolvedTheme _doesn't_ include 'dark'

* update more styling tokens

* add dynamic themes to CmdK

* fix nav and footer for multi theme

* add data-theme selector output to transformTokens.js

* update code-hike.css to target data-theme css

* update tailwindcss to ^3.3.5

* ThemeImage with light and dark src for www and docs

* add brand-button styling token

* update old dark theme boolean

* update old dark theme boolean

* make homepage product visuals themeable

* update product page themed images

* update badge green with brand

* fix roles list appearance

* fix auth widget in auth page

* update more dark logic

* update more dark logic

* add button default bg and border

* update pricing page theme styling

* clean up Themeimage

* remove forceDark in homepage

* update dark:border-dark occurrences

* update dark:border-dark occurrences

* fix dark mode base colors

* remove foreground-strong

* fix notification badge bg

* remove some dark: selectors

* update dark: selectors

* update code-hike deep dark bg color

* fix comment typo

* update border-button-hover token

* fix customer story logo

* remove some more dark: selectors

* restore forceDark in www homepage

* fix auth react icon

* fix homepage product visuals

* remove theme

* add brand-link token

* fix checkbox bg

* npm install

* more visible EntityListItem active bg

* fix --background-alternative-default css vars

---------

Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
2023-11-16 16:41:53 +00:00

154 lines
4.5 KiB
TypeScript

import HCaptcha from '@hcaptcha/react-hcaptcha'
import { Elements } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
import { useTheme } from 'next-themes'
import { useCallback, useEffect, useState } from 'react'
import { Modal } from 'ui'
import { useOrganizationPaymentMethodSetupIntent } from 'data/organizations/organization-payment-method-setup-intent-mutation'
import { useSelectedOrganization, useStore } from 'hooks'
import { STRIPE_PUBLIC_KEY } from 'lib/constants'
import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store'
import AddPaymentMethodForm from './AddPaymentMethodForm'
interface AddNewPaymentMethodModalProps {
visible: boolean
returnUrl: string
onCancel: () => void
onConfirm: () => void
}
const stripePromise = loadStripe(STRIPE_PUBLIC_KEY)
const AddNewPaymentMethodModal = ({
visible,
returnUrl,
onCancel,
onConfirm,
}: AddNewPaymentMethodModalProps) => {
const { ui } = useStore()
const { resolvedTheme } = useTheme()
const [intent, setIntent] = useState<any>()
const selectedOrganization = useSelectedOrganization()
const captchaLoaded = useIsHCaptchaLoaded()
const [captchaToken, setCaptchaToken] = useState<string | null>(null)
const [captchaRef, setCaptchaRef] = useState<HCaptcha | null>(null)
const { mutate: setupIntent } = useOrganizationPaymentMethodSetupIntent({
onSuccess: (intent) => {
setIntent(intent)
},
onError: (error) => {
ui.setNotification({
category: 'error',
error: intent.error,
message: `Failed to setup intent: ${error.message}`,
})
},
})
const captchaRefCallback = useCallback((node: any) => {
setCaptchaRef(node)
}, [])
useEffect(() => {
const initSetupIntent = async (hcaptchaToken: string | undefined) => {
const slug = selectedOrganization?.slug
if (!slug) return console.error('Slug is required')
if (!hcaptchaToken) return console.error('HCaptcha token required')
setIntent(undefined)
setupIntent({ slug, hcaptchaToken })
}
const loadPaymentForm = async () => {
if (visible && captchaRef && captchaLoaded) {
let token = captchaToken
try {
if (!token) {
const captchaResponse = await captchaRef.execute({ async: true })
token = captchaResponse?.response ?? null
}
} catch (error) {
return
}
await initSetupIntent(token ?? undefined)
resetCaptcha()
}
}
loadPaymentForm()
}, [visible, captchaRef, captchaLoaded])
const resetCaptcha = () => {
setCaptchaToken(null)
captchaRef?.resetCaptcha()
}
const options = {
clientSecret: intent ? intent.client_secret : '',
appearance: { theme: resolvedTheme?.includes('dark') ? 'night' : 'flat', labels: 'floating' },
} as any
const onLocalCancel = () => {
setIntent(undefined)
return onCancel()
}
const onLocalConfirm = () => {
setIntent(undefined)
return onConfirm()
}
return (
// We cant display the hCaptcha in the modal, as the modal auto-closes when clicking the captcha
// So we only show the modal if the captcha has been executed successfully (intent loaded)
<>
<HCaptcha
ref={captchaRefCallback}
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
size="invisible"
onOpen={() => {
// [Joshen] This is to ensure that hCaptcha popup remains clickable
if (document !== undefined) document.body.classList.add('!pointer-events-auto')
}}
onClose={() => {
onLocalCancel()
if (document !== undefined) document.body.classList.remove('!pointer-events-auto')
}}
onVerify={(token) => {
setCaptchaToken(token)
if (document !== undefined) document.body.classList.remove('!pointer-events-auto')
}}
onExpire={() => {
setCaptchaToken(null)
}}
/>
<Modal
hideFooter
size="medium"
visible={visible && intent !== undefined}
header="Add new payment method"
onCancel={onLocalCancel}
className="PAYMENT"
>
<div className="py-4 space-y-4">
<Elements stripe={stripePromise} options={options}>
<AddPaymentMethodForm
returnUrl={returnUrl}
onCancel={onLocalCancel}
onConfirm={onLocalConfirm}
/>
</Elements>
</div>
</Modal>
</>
)
}
export default AddNewPaymentMethodModal