diff --git a/CHANGELOG.md b/CHANGELOG.md index b616000cdc..e80e141c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,13 +10,14 @@ and this project adheres to - 🐛(backend) skip session creation for the liveness probe - 🐛(frontend) preserve page titles when adding an emoji #2586 +- 💄(frontend) add standalone 503 error page #2655 +- 💄(frontend) redesign email confirmation standalone page #2601 ## [v5.6.1] - 2026-09-04 ### Added - ✨(frontend) export presenter slides as PDF #2487 -- 💄(frontend) redesign email confirmation standalone page #2601 ### Fixed @@ -49,7 +50,7 @@ and this project adheres to - 🐛(backend) fix duplicating a document that has no content #2609 - 📄(frontend) allowed partially export when MIT #2551 - 🐛(backend) manage async support for Docs custom middleware #2619 -- 🐛(frontend) save the doc with a keepalive +- 🐛(frontend) save the doc with a keepalive request when leaving the page #2619 ### Removed diff --git a/src/frontend/apps/e2e/__tests__/app-impress/503.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/503.spec.ts new file mode 100644 index 0000000000..f3469f2093 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-impress/503.spec.ts @@ -0,0 +1,18 @@ +import { expect, test } from '@playwright/test'; + +test.describe('503', () => { + test('checks all the elements are visible', async ({ page }) => { + await page.goto('/503'); + + await expect( + page.getByRole('heading', { level: 1, name: 'Error 503' }), + ).toBeVisible(); + await expect( + page.getByText('The server is temporarily overloaded or unavailable'), + ).toBeVisible(); + await expect( + page.getByRole('button', { name: 'Refresh page' }), + ).toBeVisible(); + await expect(page.getByTestId('header-logo-link')).toBeVisible(); + }); +}); diff --git a/src/frontend/apps/impress/src/features/errors/assets/503.svg b/src/frontend/apps/impress/src/features/errors/assets/503.svg new file mode 100644 index 0000000000..1096259d36 --- /dev/null +++ b/src/frontend/apps/impress/src/features/errors/assets/503.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/apps/impress/src/features/errors/components/Error503.tsx b/src/frontend/apps/impress/src/features/errors/components/Error503.tsx new file mode 100644 index 0000000000..0894e3e354 --- /dev/null +++ b/src/frontend/apps/impress/src/features/errors/components/Error503.tsx @@ -0,0 +1,101 @@ +import { useTranslation } from 'react-i18next'; + +import { Box, BoxButton, Icon, Text } from '@/components'; + +import Error503Svg from '../assets/503.svg'; + +const getSafeRefreshUrl = (target?: string): string | undefined => { + if (!target) { + return undefined; + } + + if (typeof window === 'undefined') { + return target.startsWith('/') && !target.startsWith('//') + ? target + : undefined; + } + + try { + const url = new URL(target, window.location.origin); + if (url.origin !== window.location.origin) { + return undefined; + } + return url.pathname + url.search + url.hash; + } catch { + return undefined; + } +}; + +type Error503Props = { + refreshTarget?: string; +}; + +export const Error503 = ({ refreshTarget }: Error503Props) => { + const { t } = useTranslation(); + const safeTarget = getSafeRefreshUrl(refreshTarget); + + return ( + + + ); +}; diff --git a/src/frontend/apps/impress/src/features/errors/components/index.ts b/src/frontend/apps/impress/src/features/errors/components/index.ts new file mode 100644 index 0000000000..bc84ded58d --- /dev/null +++ b/src/frontend/apps/impress/src/features/errors/components/index.ts @@ -0,0 +1 @@ +export * from './Error503'; diff --git a/src/frontend/apps/impress/src/features/errors/index.ts b/src/frontend/apps/impress/src/features/errors/index.ts new file mode 100644 index 0000000000..07635cbbc8 --- /dev/null +++ b/src/frontend/apps/impress/src/features/errors/index.ts @@ -0,0 +1 @@ +export * from './components'; diff --git a/src/frontend/apps/impress/src/pages/503.tsx b/src/frontend/apps/impress/src/pages/503.tsx new file mode 100644 index 0000000000..6d9bbc9dec --- /dev/null +++ b/src/frontend/apps/impress/src/pages/503.tsx @@ -0,0 +1,37 @@ +import Head from 'next/head'; +import { useRouter } from 'next/router'; +import { ReactElement } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Error503 } from '@/features/errors'; +import { StandalonePageLayout } from '@/layouts'; +import { NextPageWithLayout } from '@/types/next'; + +const Page: NextPageWithLayout = () => { + const { t } = useTranslation(); + const { query } = useRouter(); + const from = Array.isArray(query.from) ? query.from[0] : query.from; + const refreshTarget = + from?.startsWith('/') && !from.startsWith('//') ? from : undefined; + + return ( + <> + + + {`${t('Error 503')} - ${t('Docs')}`} + + + + + ); +}; + +Page.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default Page;