mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
show Stripe Projects errors inline (#48472)
## What kind of change does this PR introduce? Bug fix. ## What is the current behavior? Stripe Projects confirmation failures only appear in a toast. The existing **Unable to load authorization** Admonition is a separate error state shown when the account request itself cannot be loaded. ## What is the new behavior? Confirmation failures remain visible below the authorisation actions, clear on retry, and do not also trigger a toast. They use the shared `InterstitialActionError`. | Before | After | | --- | --- | | <img width="1024" height="759" alt="Authorize Stripe Projects Supabase" src="https://github.com/user-attachments/assets/83cb3144-9ddb-46b7-bfce-970497be61e2" /> | <img width="1024" height="759" alt="Authorize Stripe Projects Supabase" src="https://github.com/user-attachments/assets/bccedde9-9935-457b-a980-0c80499f2f27" /> | ## To test These instructions visually check the after state on this branch. Opening an invalid `ar_id` without the hardcodes only exercises the existing load-error Admonition, which this PR does not change. ### After on this branch 1. In `apps/studio/pages/partners/stripe/projects/login.tsx`, replace the `confirmationError` assignment with: ```tsx const confirmationError = 'Failed to authorize Stripe Projects: Test error' ``` 2. In the same file, replace the block beginning with `const linkedOrg` and ending with `interstitialDescription` with: ```tsx const linkedOrg = { name: 'Example Organization' } const emailMatches = true const displayName = primaryEmail ?? username ?? 'reviewer@example.com' const isPending = false const isConfirmed = false const isConfirming = false const isError = false const showAuthorizationState = true const interstitialDescription = 'This will create an organization on your behalf in Supabase' ``` 3. While signed in locally, open `http://localhost:8082/partners/stripe/projects/login?ar_id=test`. 4. Confirm the error appears below **Authorize Stripe Projects** and **Cancel**. No real Stripe request is required. 5. Revert both temporary edits. ### Before on master (optional) 1. Check out `master`. 2. In `apps/studio/pages/partners/stripe/projects/login.tsx`, replace the block beginning with `const linkedOrg` and ending with `interstitialDescription` with the same block from step 2 above. Do not add `confirmationError`. 3. While signed in locally, open `http://localhost:8082/partners/stripe/projects/login?ar_id=test`. 4. Click **Authorize Stripe Projects**. 5. Confirm the failed confirmation appears in a toast beginning **Failed to confirm account request**. 6. Revert the temporary edit before changing branches. ## Additional context Stacked on #48471. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **Bug Fixes** * Authorization errors during Stripe Projects login are now displayed inline in both authorization flows. * Authorization remains available after a failed confirmation attempt. * Failed authorization requests no longer trigger an additional toast notification. * Previous errors are cleared when retrying authorization. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
c0f1ef51fb
commit
022b374f2d
2 files changed
+94
-2
No files matched your search
@@ -10,6 +10,7 @@ import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import {
|
||||
InterstitialAccountRow,
|
||||
InterstitialActionError,
|
||||
InterstitialLayout,
|
||||
LogoPair,
|
||||
PartnerLogo,
|
||||
@@ -27,7 +28,7 @@ import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const PAGE_TITLE = buildStudioPageTitle({ section: 'Authorize Stripe Projects', brand: 'Supabase' })
|
||||
|
||||
const StripeProjectsLoginPage: NextPageWithLayout = () => {
|
||||
export const StripeProjectsLoginPage: NextPageWithLayout = () => {
|
||||
const router = useRouter()
|
||||
const { ar_id } = useParams()
|
||||
const signOut = useSignOut()
|
||||
@@ -48,7 +49,14 @@ const StripeProjectsLoginPage: NextPageWithLayout = () => {
|
||||
mutate: confirmAccountRequest,
|
||||
isPending: isConfirmationPending,
|
||||
isSuccess: isConfirmationSuccess,
|
||||
} = useConfirmAccountRequestMutation()
|
||||
error: confirmationMutationError,
|
||||
reset: resetConfirmationError,
|
||||
} = useConfirmAccountRequestMutation({
|
||||
onError: () => undefined,
|
||||
})
|
||||
const confirmationError = confirmationMutationError
|
||||
? `Failed to authorize Stripe Projects: ${confirmationMutationError.message}`
|
||||
: undefined
|
||||
|
||||
useEffect(() => {
|
||||
if (!router.isReady) return
|
||||
@@ -60,6 +68,7 @@ const StripeProjectsLoginPage: NextPageWithLayout = () => {
|
||||
|
||||
const handleApprove = async () => {
|
||||
if (!ar_id || isConfirmationPending) return
|
||||
resetConfirmationError()
|
||||
confirmAccountRequest({ arId: ar_id })
|
||||
}
|
||||
|
||||
@@ -155,6 +164,7 @@ const StripeProjectsLoginPage: NextPageWithLayout = () => {
|
||||
<Button variant="text" block onClick={() => router.push('/')}>
|
||||
Cancel
|
||||
</Button>
|
||||
<InterstitialActionError error={confirmationError} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -195,6 +205,7 @@ const StripeProjectsLoginPage: NextPageWithLayout = () => {
|
||||
<Button variant="text" onClick={() => router.push('/')}>
|
||||
Cancel
|
||||
</Button>
|
||||
<InterstitialActionError error={confirmationError} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { HttpResponse } from 'msw'
|
||||
import { toast } from 'sonner'
|
||||
import { beforeEach, expect, test, vi } from 'vitest'
|
||||
|
||||
import { StripeProjectsLoginPage } from '@/pages/partners/stripe/projects/login'
|
||||
import { customRender } from '@/tests/lib/custom-render'
|
||||
import { addAPIMock, type APIErrorBody } from '@/tests/lib/msw'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
routerPush: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@tanstack/react-query', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@tanstack/react-query')>()
|
||||
return {
|
||||
...actual,
|
||||
useQuery: () => ({
|
||||
data: {
|
||||
email: 'alex@example.com',
|
||||
email_matches: true,
|
||||
linked_organization: { name: 'Acme', slug: 'acme' },
|
||||
},
|
||||
isPending: false,
|
||||
isSuccess: true,
|
||||
isError: false,
|
||||
error: undefined,
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('common', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('common')>()
|
||||
return { ...actual, useParams: () => ({ ar_id: 'request-id' }) }
|
||||
})
|
||||
|
||||
vi.mock('next/router', () => ({
|
||||
useRouter: () => ({ isReady: true, push: mocks.routerPush }),
|
||||
}))
|
||||
|
||||
vi.mock('sonner', () => ({
|
||||
toast: { error: vi.fn() },
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/auth', () => ({
|
||||
useSignOut: () => vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/profile', () => ({
|
||||
useProfileNameAndPicture: () => ({
|
||||
username: 'alex',
|
||||
primaryEmail: 'alex@example.com',
|
||||
avatarUrl: undefined,
|
||||
}),
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
test('shows confirmation failures inline and keeps authorization available', async () => {
|
||||
const user = userEvent.setup()
|
||||
addAPIMock({
|
||||
method: 'post',
|
||||
path: '/platform/stripe/projects/provisioning/account_requests/:id/confirm',
|
||||
response: () =>
|
||||
HttpResponse.json<APIErrorBody>({ message: 'Confirmation failed' }, { status: 500 }),
|
||||
})
|
||||
|
||||
customRender(<StripeProjectsLoginPage dehydratedState={undefined} />)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Authorize Stripe Projects' }))
|
||||
|
||||
const errorMessage = await screen.findByText(
|
||||
'Failed to authorize Stripe Projects: Confirmation failed'
|
||||
)
|
||||
expect(errorMessage).toHaveAttribute('role', 'alert')
|
||||
expect(toast.error).not.toHaveBeenCalled()
|
||||
expect(screen.getByRole('button', { name: 'Authorize Stripe Projects' })).toBeEnabled()
|
||||
})
|
||||
Reference in new issue
Block a user