diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b14515173..e5d07b422f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to ### Added - ✨(backend) allow to create a new user in a marketing system +- 🥅(frontend) add boundary error page #1728 ### Changed diff --git a/src/frontend/apps/impress/src/assets/icons/error-planetes.png b/src/frontend/apps/impress/src/assets/icons/error-planetes.png new file mode 100644 index 0000000000..e8cffbdbb5 Binary files /dev/null and b/src/frontend/apps/impress/src/assets/icons/error-planetes.png differ diff --git a/src/frontend/apps/impress/src/components/Box.tsx b/src/frontend/apps/impress/src/components/Box.tsx index dd57c6fa05..3e9e907bf1 100644 --- a/src/frontend/apps/impress/src/components/Box.tsx +++ b/src/frontend/apps/impress/src/components/Box.tsx @@ -4,6 +4,8 @@ import { CSSProperties, RuleSet } from 'styled-components/dist/types'; import { MarginPadding, + Spacings, + spacingValue, stylesMargin, stylesPadding, } from '@/utils/styleBuilder'; @@ -22,7 +24,7 @@ export interface BoxProps { $display?: CSSProperties['display']; $effect?: 'show' | 'hide'; $flex?: CSSProperties['flex']; - $gap?: CSSProperties['gap']; + $gap?: Spacings; $hasTransition?: boolean | 'slow'; $height?: CSSProperties['height']; $justify?: CSSProperties['justifyContent']; @@ -70,7 +72,7 @@ export const Box = styled('div')` ${({ $display, as }) => `display: ${$display || (as?.match('span|input') ? 'inline-flex' : 'flex')};`} ${({ $flex }) => $flex && `flex: ${$flex};`} - ${({ $gap }) => $gap && `gap: ${$gap};`} + ${({ $gap }) => $gap && `gap: ${spacingValue($gap)};`} ${({ $height }) => $height && `height: ${$height};`} ${({ $hasTransition }) => $hasTransition && $hasTransition === 'slow' diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/BoutonShare.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/BoutonShare.tsx index 8a3c5055e4..6cad4ca850 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/BoutonShare.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/BoutonShare.tsx @@ -1,6 +1,6 @@ import { useTreeContext } from '@gouvfr-lasuite/ui-kit'; import { Button } from '@openfun/cunningham-react'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { css } from 'styled-components'; @@ -24,6 +24,7 @@ export const BoutonShare = ({ }: BoutonShareProps) => { const { t } = useTranslation(); const treeContext = useTreeContext(); + const [crash, setCrash] = useState(false); /** * Following the change where there is no default owner when adding a sub-page, @@ -74,14 +75,23 @@ export const BoutonShare = ({ } return ( - + <> + + {crash && } + ); }; + +const CrashComponent = () => { + throw new Error('Crash component error'); +}; diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelContent.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelContent.tsx index 98f04e8b25..112bf36ec5 100644 --- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelContent.tsx +++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelContent.tsx @@ -2,6 +2,7 @@ import { useRouter } from 'next/router'; import { css } from 'styled-components'; import { Box, SeparatedSection } from '@/components'; +import { useDocStore } from '@/docs/doc-management'; import { LeftPanelTargetFilters } from './LefPanelTargetFilters'; import { LeftPanelDocContent } from './LeftPanelDocContent'; @@ -11,6 +12,7 @@ export const LeftPanelContent = () => { const router = useRouter(); const isHome = router.pathname === '/'; const isDoc = router.pathname === '/docs/[id]'; + const { currentDoc } = useDocStore(); return ( <> @@ -36,7 +38,7 @@ export const LeftPanelContent = () => { )} - {isDoc && } + {isDoc && currentDoc && } ); }; diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelDocContent.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelDocContent.tsx index 6e67f8a987..3f7f25362b 100644 --- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelDocContent.tsx +++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelDocContent.tsx @@ -1,15 +1,13 @@ import { useTreeContext } from '@gouvfr-lasuite/ui-kit'; import { Box } from '@/components'; -import { Doc, useDocStore } from '@/docs/doc-management'; +import { Doc } from '@/docs/doc-management'; import { DocTree } from '@/docs/doc-tree/'; -export const LeftPanelDocContent = () => { - const { currentDoc } = useDocStore(); - +export const LeftPanelDocContent = ({ doc }: { doc: Doc }) => { const tree = useTreeContext(); - if (!currentDoc || !tree) { + if (!tree) { return null; } @@ -20,7 +18,7 @@ export const LeftPanelDocContent = () => { $css="width: 100%; overflow-y: auto; overflow-x: hidden;" className="--docs--left-panel-doc-content" > - + ); }; diff --git a/src/frontend/apps/impress/src/pages/404.tsx b/src/frontend/apps/impress/src/pages/404.tsx index 739fe9b6b0..a7b6109919 100644 --- a/src/frontend/apps/impress/src/pages/404.tsx +++ b/src/frontend/apps/impress/src/pages/404.tsx @@ -5,7 +5,7 @@ import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; -import img403 from '@/assets/icons/icon-403.png'; +import error_img from '@/assets/icons/error-planetes.png'; import { Box, Icon, StyledLink, Text } from '@/components'; import { PageLayout } from '@/layouts'; import { NextPageWithLayout } from '@/types/next'; @@ -32,32 +32,46 @@ const Page: NextPageWithLayout = () => { {t('Page Not Found - Error 404')} - {t('Docs')} - - - {t( - 'It seems that the page you are looking for does not exist or cannot be displayed correctly.', - )} - + + {t( + 'It seems that the page you are looking for does not exist or cannot be displayed correctly.', + )} + + - }> + + } + > {t('Home')} diff --git a/src/frontend/apps/impress/src/pages/_error.tsx b/src/frontend/apps/impress/src/pages/_error.tsx new file mode 100644 index 0000000000..65aa06472d --- /dev/null +++ b/src/frontend/apps/impress/src/pages/_error.tsx @@ -0,0 +1,126 @@ +import { Button } from '@openfun/cunningham-react'; +import * as Sentry from '@sentry/nextjs'; +import { NextPageContext } from 'next'; +import NextError from 'next/error'; +import Head from 'next/head'; +import Image from 'next/image'; +import { ReactElement } from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import error_img from '@/assets/icons/error-planetes.png'; +import { Box, Icon, StyledLink, Text } from '@/components'; +import { PageLayout } from '@/layouts'; + +const StyledButton = styled(Button)` + width: fit-content; +`; + +const Error = () => { + const { t } = useTranslation(); + + const errorTitle = t('An unexpected error occurred.'); + + return ( + <> + + + {errorTitle} - {t('Docs')} + + + + + + {errorTitle} - {t('Docs')} + + + + + {errorTitle} + + + + + + } + > + {t('Home')} + + + + + } + onClick={() => window.location.reload()} + > + {t('Refresh page')} + + + + + ); +}; + +Error.getInitialProps = async (contextData: NextPageContext) => { + const { res, err, asPath, pathname, query } = contextData; + + Sentry.captureException(err, { + contexts: { + nextjs: { + page: pathname, + path: asPath, + query: query, + statusCode: res?.statusCode || err?.statusCode, + }, + }, + tags: { + errorPage: '_error.tsx', + statusCode: String(res?.statusCode || err?.statusCode || 'unknown'), + }, + }); + + return NextError.getInitialProps(contextData); +}; + +Error.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default Error; diff --git a/src/helm/env.d/dev/values.impress.yaml.gotmpl b/src/helm/env.d/dev/values.impress.yaml.gotmpl index 129a4b89ba..5ea801d500 100644 --- a/src/helm/env.d/dev/values.impress.yaml.gotmpl +++ b/src/helm/env.d/dev/values.impress.yaml.gotmpl @@ -32,7 +32,7 @@ backend: LOGGING_LEVEL_LOGGERS_ROOT: INFO LOGGING_LEVEL_LOGGERS_APP: INFO OIDC_USERINFO_SHORTNAME_FIELD: "given_name" - OIDC_USERINFO_FULLNAME_FIELDS: "given_name,usual_name" + OIDC_USERINFO_FULLNAME_FIELDS: "given_name,family_name" OIDC_OP_JWKS_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/certs OIDC_OP_AUTHORIZATION_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/auth OIDC_OP_TOKEN_ENDPOINT: https://docs-keycloak.127.0.0.1.nip.io/realms/docs/protocol/openid-connect/token @@ -41,7 +41,7 @@ backend: OIDC_RP_CLIENT_ID: docs OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly OIDC_RP_SIGN_ALGO: RS256 - OIDC_RP_SCOPES: "openid email given_name usual_name" + OIDC_RP_SCOPES: "openid email profile" LOGIN_REDIRECT_URL: https://docs.127.0.0.1.nip.io LOGIN_REDIRECT_URL_FAILURE: https://docs.127.0.0.1.nip.io LOGOUT_REDIRECT_URL: https://docs.127.0.0.1.nip.io diff --git a/src/helm/env.d/feature/values.impress.yaml.gotmpl b/src/helm/env.d/feature/values.impress.yaml.gotmpl index 050c35ece2..b35b617d0a 100644 --- a/src/helm/env.d/feature/values.impress.yaml.gotmpl +++ b/src/helm/env.d/feature/values.impress.yaml.gotmpl @@ -33,7 +33,7 @@ backend: LOGGING_LEVEL_LOGGERS_ROOT: INFO LOGGING_LEVEL_LOGGERS_APP: INFO OIDC_USERINFO_SHORTNAME_FIELD: "given_name" - OIDC_USERINFO_FULLNAME_FIELDS: "given_name,usual_name" + OIDC_USERINFO_FULLNAME_FIELDS: "given_name,family_name" OIDC_OP_JWKS_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/certs OIDC_OP_AUTHORIZATION_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/auth OIDC_OP_TOKEN_ENDPOINT: https://{{ .Values.feature }}-docs-keycloak.{{ .Values.domain }}/realms/docs/protocol/openid-connect/token @@ -42,7 +42,7 @@ backend: OIDC_RP_CLIENT_ID: docs OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly OIDC_RP_SIGN_ALGO: RS256 - OIDC_RP_SCOPES: "openid email given_name usual_name" + OIDC_RP_SCOPES: "openid email profile" LOGIN_REDIRECT_URL: https://{{ .Values.feature }}-docs.{{ .Values.domain }} LOGIN_REDIRECT_URL_FAILURE: https://{{ .Values.feature }}-docs.{{ .Values.domain }} LOGOUT_REDIRECT_URL: https://{{ .Values.feature }}-docs.{{ .Values.domain }} @@ -66,6 +66,7 @@ backend: AWS_S3_ACCESS_KEY_ID: dinum AWS_S3_SECRET_ACCESS_KEY: password AWS_STORAGE_BUCKET_NAME: docs-media-storage + SENTRY_DSN: https://0b77e8f3658c6eec0008ec4acb1874b9@sentry.incubateur.net/197 STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage Y_PROVIDER_API_BASE_URL: http://impress-y-provider:443/api/ Y_PROVIDER_API_KEY: my-secret