diff --git a/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.stories.tsx b/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.stories.tsx index 120d8c2872..533ab90a3b 100644 --- a/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.stories.tsx +++ b/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn } from '@storybook/react' +import { http, HttpResponse } from 'msw' import { FormResponseMode, FormSettings } from 'formsg-shared/types/form' @@ -75,6 +76,22 @@ Loading.parameters = { msw: { handlers: { default: buildMswRoutes({ delay: 'infinite' }) } }, } +export const Error = Template.bind({}) +Error.parameters = { + msw: { + handlers: { + default: [ + http.get('/api/v3/admin/forms/:formId/settings', () => + HttpResponse.json( + { message: 'Internal Server Error' }, + { status: 500 }, + ), + ), + ], + }, + }, +} + export const Mobile = Template.bind({}) Mobile.parameters = { ...StorageModeRetryEnabled.parameters, diff --git a/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.test.tsx b/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.test.tsx new file mode 100644 index 0000000000..cf1f0feae3 --- /dev/null +++ b/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.test.tsx @@ -0,0 +1,34 @@ +import { composeStories } from '@storybook/react' +import { act, render, screen } from '@testing-library/react' + +import * as stories from './SettingsWebhooksPage.stories' + +const { Error: ErrorStory, UnsupportedEmailMode } = composeStories(stories) + +const UNSUPPORTED_MSG = /webhooks are only available in storage mode/i +const ERROR_MSG = /couldn't load webhook settings/i + +describe('SettingsWebhooksPage', () => { + it('shows an error state, not the unsupported-mode message, when the settings fetch fails', async () => { + await act(async () => { + render() + }) + + await screen.findByText(ERROR_MSG) + // The error is announced to assistive tech (it appears after an async failure). + expect(screen.getByRole('alert')).toBeInTheDocument() + expect( + screen.getByRole('button', { name: /try again/i }), + ).toBeInTheDocument() + expect(screen.queryByText(UNSUPPORTED_MSG)).not.toBeInTheDocument() + }) + + it('still shows the unsupported-mode message for a form whose mode genuinely lacks webhook support', async () => { + await act(async () => { + render() + }) + + await screen.findByText(UNSUPPORTED_MSG) + expect(screen.queryByText(ERROR_MSG)).not.toBeInTheDocument() + }) +}) diff --git a/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.tsx b/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.tsx index e05844a8df..0a9f50b510 100644 --- a/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.tsx +++ b/apps/frontend/src/features/admin-form/settings/SettingsWebhooksPage.tsx @@ -10,13 +10,20 @@ import { useUser } from '~features/user/queries' import { CategoryHeader } from './components/CategoryHeader' import { WebhooksSection } from './components/WebhooksSection' +import { WebhooksErrorMsg } from './components/WebhooksSection/WebhooksErrorMsg' import { WebhooksUnsupportedMsg } from './components/WebhooksSection/WebhooksUnsupportedMsg' import { useAdminFormSettings } from './queries' export const SettingsWebhooksPage = (): JSX.Element => { const { t } = useTranslation() const gb = useGrowthBook() - const { data: settings, isLoading } = useAdminFormSettings() + const { + data: settings, + isLoading, + isError, + isRefetching, + refetch, + } = useAdminFormSettings() const userRes = useUser() useEffect(() => { @@ -27,9 +34,13 @@ export const SettingsWebhooksPage = (): JSX.Element => { const enableMrfWebhooks = useFeatureIsOn(featureFlags.enableMrfWebhooks) + if (isError) { + return + } + const enableWebhooks = !isLoading && - (settings?.responseMode == FormResponseMode.Encrypt || + (settings?.responseMode === FormResponseMode.Encrypt || (settings?.responseMode === FormResponseMode.Multirespondent && enableMrfWebhooks)) diff --git a/apps/frontend/src/features/admin-form/settings/components/WebhooksSection/WebhooksErrorMsg.tsx b/apps/frontend/src/features/admin-form/settings/components/WebhooksSection/WebhooksErrorMsg.tsx new file mode 100644 index 0000000000..411de32705 --- /dev/null +++ b/apps/frontend/src/features/admin-form/settings/components/WebhooksSection/WebhooksErrorMsg.tsx @@ -0,0 +1,41 @@ +import { useTranslation } from 'react-i18next' +import { Flex, Text } from '@chakra-ui/react' + +import Button from '~components/Button' + +export interface WebhooksErrorMsgProps { + onRetry: () => void + isRetrying?: boolean +} + +export const WebhooksErrorMsg = ({ + onRetry, + isRetrying = false, +}: WebhooksErrorMsgProps): JSX.Element => { + const { t } = useTranslation() + const { title, body, button } = t( + 'features.adminForm.settings.webhooks.error', + { + returnObjects: true, + }, + ) + return ( + + + {title} + + + {body} + + + + + + ) +} diff --git a/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/en-sg.ts b/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/en-sg.ts index 89daa00739..4ac0b70eb9 100644 --- a/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/en-sg.ts +++ b/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/en-sg.ts @@ -9,4 +9,12 @@ export const enSG = { label: 'Enable retries', description: `Your system must meet certain requirements before retries can be safely enabled. [Learn more]({url})`, }, + error: { + title: "Couldn't load webhook settings", + body: "Something went wrong while loading this form's settings. This does not affect your form or its responses. Please try again.", + button: { + label: 'Try again', + loadingText: 'Trying again…', + }, + }, } diff --git a/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/index.ts b/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/index.ts index 67d797b927..e5583d2151 100644 --- a/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/index.ts +++ b/apps/frontend/src/i18n/locales/features/admin-form/settings/webhooks/index.ts @@ -11,4 +11,12 @@ export interface Webhooks extends HasTitle { label: string description: string } + error: { + title: string + body: string + button: { + label: string + loadingText: string + } + } }