mirror of
https://github.com/suitenumerique/docs.git
synced 2026-09-28 04:25:09 +02:00
✨(frontend) add error page header and footer layout
Use Docs logo header and bottom actions without sidebar on error pages.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<svg width="51" height="16" viewBox="0 0 51 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.2 15.95C0.0895427 15.95 0 15.8605 0 15.75V0.199999C0 0.0895425 0.0895431 0 0.2 0H6.2205C7.48131 0 8.62819 0.212667 9.66114 0.638C10.6941 1.06333 11.5827 1.64817 12.3271 2.3925C13.0866 3.12164 13.6638 3.96471 14.0588 4.92171C14.4689 5.87871 14.674 6.89648 14.674 7.975C14.674 9.05352 14.4689 10.0713 14.0588 11.0283C13.6638 11.9853 13.0866 12.836 12.3271 13.5803C11.5827 14.3094 10.6941 14.8867 9.66114 15.312C8.62819 15.7373 7.48131 15.95 6.2205 15.95H0.2ZM6.24329 2.52921H3.00264C2.89219 2.52921 2.80264 2.61876 2.80264 2.72921V13.2208C2.80264 13.3312 2.89219 13.4208 3.00264 13.4208H6.24329C7.04838 13.4208 7.78512 13.2841 8.4535 13.0106C9.13707 12.722 9.7219 12.3271 10.208 11.8258C10.7093 11.3245 11.0966 10.7473 11.3701 10.0941C11.6435 9.44088 11.7802 8.73452 11.7802 7.975C11.7802 7.21548 11.6435 6.50912 11.3701 5.85593C11.0966 5.18755 10.7093 4.61031 10.208 4.12421C9.7219 3.62293 9.13707 3.23557 8.4535 2.96214C7.78512 2.67352 7.04838 2.52921 6.24329 2.52921Z" fill="#2845C1"/>
|
||||
<path d="M22.3939 13.6031C22.9104 13.6031 23.3889 13.5044 23.8294 13.3069C24.2699 13.0943 24.6497 12.8132 24.9687 12.4639C25.3029 12.1145 25.5611 11.7119 25.7434 11.2562C25.9257 10.7853 26.0168 10.284 26.0168 9.75235C26.0168 9.02321 25.8573 8.37002 25.5383 7.79278C25.2193 7.21554 24.7864 6.75983 24.2395 6.42564C23.6927 6.07626 23.0775 5.90157 22.3939 5.90157C21.8622 5.90157 21.3686 6.00031 20.9128 6.19778C20.4723 6.39526 20.085 6.66869 19.7508 7.01807C19.4318 7.36745 19.1811 7.77759 18.9988 8.2485C18.8166 8.7194 18.7254 9.22069 18.7254 9.75235C18.7254 10.4663 18.8849 11.1195 19.2039 11.7119C19.5229 12.2892 19.9558 12.7525 20.5027 13.1019C21.0647 13.436 21.6951 13.6031 22.3939 13.6031ZM22.3711 3.55464C23.2977 3.55464 24.1408 3.71414 24.9003 4.03314C25.675 4.35214 26.3358 4.80026 26.8827 5.3775C27.4447 5.93954 27.8701 6.60033 28.1587 7.35985C28.4625 8.10419 28.6144 8.90169 28.6144 9.75235C28.6144 10.603 28.4625 11.4081 28.1587 12.1676C27.8701 12.912 27.4447 13.5728 26.8827 14.15C26.3358 14.712 25.675 15.1526 24.9003 15.4716C24.1408 15.7906 23.2977 15.9501 22.3711 15.9501C21.4445 15.9501 20.5938 15.7906 19.8191 15.4716C19.0596 15.1526 18.3988 14.712 17.8368 14.15C17.2747 13.5728 16.8418 12.912 16.538 12.1676C16.2494 11.4081 16.1051 10.603 16.1051 9.75235C16.1051 8.90169 16.2494 8.10419 16.538 7.35985C16.8418 6.60033 17.2747 5.93954 17.8368 5.3775C18.3988 4.80026 19.0596 4.35214 19.8191 4.03314C20.5938 3.71414 21.4445 3.55464 22.3711 3.55464Z" fill="#2845C1"/>
|
||||
<path d="M36.2852 3.55464C37.3333 3.55464 38.2675 3.76731 39.0878 4.19264C39.8611 4.57226 40.5043 5.08202 41.0173 5.72192C41.086 5.80756 41.0681 5.93225 40.9806 5.99859L39.3269 7.25312C39.2346 7.32316 39.1027 7.29989 39.0326 7.20762C38.7582 6.84661 38.4045 6.54797 37.9713 6.31171C37.4852 6.03828 36.9156 5.90157 36.2624 5.90157C35.7307 5.90157 35.237 6.00031 34.7813 6.19778C34.3408 6.39526 33.961 6.66869 33.642 7.01807C33.323 7.36745 33.0724 7.77759 32.8901 8.2485C32.7078 8.70421 32.6167 9.2055 32.6167 9.75235C32.6167 10.4815 32.7762 11.1423 33.0952 11.7347C33.4142 12.3119 33.8471 12.7677 34.394 13.1019C34.9408 13.436 35.5788 13.6031 36.308 13.6031C36.9308 13.6031 37.4852 13.4664 37.9713 13.193C38.4045 12.9567 38.7582 12.6581 39.0326 12.2971C39.1027 12.2048 39.2346 12.1815 39.3269 12.2516L40.979 13.5049C41.0671 13.5717 41.0845 13.6976 41.0145 13.7831C40.5019 14.4095 39.8597 14.9191 39.0878 15.3121C38.2675 15.7374 37.3333 15.9501 36.2852 15.9501C34.994 15.9501 33.8775 15.669 32.9357 15.107C31.9939 14.5298 31.2647 13.7702 30.7483 12.8284C30.247 11.8866 29.9963 10.8613 29.9963 9.75235C29.9963 8.91688 30.1406 8.12697 30.4293 7.38264C30.7179 6.63831 31.1356 5.97752 31.6825 5.40028C32.2293 4.82304 32.8901 4.37492 33.6648 4.05592C34.4395 3.72173 35.313 3.55464 36.2852 3.55464Z" fill="#2845C1"/>
|
||||
<path d="M44.9175 6.90414C44.9175 7.26871 45.0466 7.57252 45.3049 7.81557C45.5783 8.04342 45.9277 8.2409 46.353 8.408C46.7783 8.57509 47.2265 8.75738 47.6974 8.95485C48.1835 9.13714 48.6392 9.365 49.0645 9.63843C49.4898 9.89666 49.8316 10.246 50.0899 10.6866C50.3633 11.1119 50.5 11.6512 50.5 12.3044C50.5 13.0791 50.3101 13.7399 49.9304 14.2867C49.5658 14.8184 49.0797 15.2285 48.4721 15.5171C47.8644 15.8058 47.1961 15.9501 46.4669 15.9501C45.4795 15.9501 44.6213 15.7678 43.8921 15.4032C43.2203 15.0533 42.6259 14.5938 42.1088 14.0246C42.0352 13.9436 42.0442 13.8185 42.1263 13.7462L43.5297 12.5112C43.6154 12.4358 43.7464 12.4475 43.821 12.534C44.1512 12.917 44.5168 13.2354 44.9175 13.4892C45.3732 13.7778 45.8821 13.9221 46.4441 13.9221C46.9758 13.9221 47.3784 13.7854 47.6518 13.512C47.9404 13.2234 48.0847 12.874 48.0847 12.4639C48.0847 12.0841 47.948 11.7803 47.6746 11.5524C47.4163 11.3246 47.0745 11.1271 46.6492 10.96C46.2239 10.7929 45.7682 10.6182 45.2821 10.4359C44.8112 10.2384 44.363 10.003 43.9377 9.72957C43.5124 9.45614 43.163 9.10676 42.8896 8.68143C42.6313 8.2409 42.5022 7.67885 42.5022 6.99528C42.5022 6.35728 42.6693 5.78004 43.0035 5.26357C43.3377 4.7319 43.801 4.31416 44.3934 4.01035C45.001 3.70654 45.6922 3.55464 46.4669 3.55464C47.3024 3.55464 48.0771 3.73693 48.7911 4.1015C49.4556 4.43377 49.9877 4.84806 50.3872 5.34436C50.4528 5.42581 50.4385 5.54386 50.359 5.61174L48.9551 6.80972C48.8666 6.88527 48.7328 6.86922 48.6595 6.77888C48.4001 6.4596 48.1022 6.18995 47.7657 5.96993C47.3708 5.71169 46.9378 5.58257 46.4669 5.58257C46.1327 5.58257 45.8441 5.64333 45.6011 5.76485C45.3732 5.88638 45.1985 6.04588 45.077 6.24335C44.9707 6.44083 44.9175 6.66109 44.9175 6.90414Z" fill="#2845C1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,59 @@
|
||||
import { UserMenu } from '@gouvfr-lasuite/ui-kit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { Waffle } from '@/components/Waffle';
|
||||
import { useAuth } from '@/features/auth/hooks/useAuth';
|
||||
import { gotoLogout } from '@/features/auth/utils';
|
||||
import { HelpMenu } from '@/features/help';
|
||||
|
||||
const footerButtonStyles = css`
|
||||
display: flex;
|
||||
height: var(--Height, 32px);
|
||||
min-width: var(--Height, 32px);
|
||||
padding: 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
border-radius: 4px;
|
||||
color: var(--c--contextuals--content--semantic--neutral--tertiary);
|
||||
`;
|
||||
|
||||
export const ErrorPageFooter = () => {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
|
||||
const userMenu = user || {
|
||||
full_name: t('Guest'),
|
||||
email: '',
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
as="footer"
|
||||
$direction="row"
|
||||
$align="center"
|
||||
$width="100%"
|
||||
$css={css`
|
||||
align-self: stretch;
|
||||
padding: var(--sm, 12px);
|
||||
margin-top: auto;
|
||||
gap: 0.2rem;
|
||||
border-top: 1px solid
|
||||
var(--c--contextuals--border--semantic--neutral--default);
|
||||
|
||||
.c__button--neutral--tertiary,
|
||||
.c__button--brand--tertiary {
|
||||
${footerButtonStyles}
|
||||
}
|
||||
`}
|
||||
>
|
||||
{user && (
|
||||
<UserMenu user={userMenu} logout={gotoLogout} withMobileView={false} />
|
||||
)}
|
||||
<Waffle />
|
||||
<HelpMenu colorButton="neutral" />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
import Image from 'next/image';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import DocsTitleSvg from '@/assets/icons/DOCS.svg';
|
||||
import { Box, StyledLink } from '@/components';
|
||||
import { useConfig } from '@/core';
|
||||
|
||||
export const ErrorPageHeader = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: config } = useConfig();
|
||||
const icon = config?.theme_customization?.header?.icon;
|
||||
|
||||
return (
|
||||
<Box
|
||||
as="header"
|
||||
$direction="row"
|
||||
$align="center"
|
||||
$justify="space-between"
|
||||
$width="100%"
|
||||
$css={css`
|
||||
height: var(--xxl, 48px);
|
||||
padding: 0 var(--xs, 8px);
|
||||
align-self: stretch;
|
||||
border-bottom: 1px solid
|
||||
var(--c--contextuals--border--semantic--neutral--default);
|
||||
`}
|
||||
>
|
||||
<StyledLink
|
||||
href="/"
|
||||
data-testid="header-logo-link"
|
||||
aria-label={t('Docs') + ' - ' + t('Home')}
|
||||
$css={css`
|
||||
padding: var(--xxxs, 4px);
|
||||
outline: none;
|
||||
&:focus-visible {
|
||||
box-shadow: 0 0 0 2px var(--c--globals--colors--brand-400) !important;
|
||||
border-radius: var(--c--globals--spacings--st);
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Box $align="center" $gap="5px" $direction="row" $height="fit-content">
|
||||
{icon && (
|
||||
<Image
|
||||
data-testid="header-icon-docs"
|
||||
width={32}
|
||||
height={32}
|
||||
priority
|
||||
src={icon.src}
|
||||
alt=""
|
||||
style={{ width: '32px', height: '32px', flexShrink: 0 }}
|
||||
/>
|
||||
)}
|
||||
<DocsTitleSvg
|
||||
width={50.5}
|
||||
height={16}
|
||||
aria-hidden="true"
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
</Box>
|
||||
</StyledLink>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -5,6 +5,8 @@ export * from './DropButton';
|
||||
export * from './dropdown-menu/DropdownMenu';
|
||||
export * from './Emoji/EmojiPicker';
|
||||
export * from './ErrorPage';
|
||||
export * from './ErrorPageFooter';
|
||||
export * from './ErrorPageHeader';
|
||||
export * from './quick-search';
|
||||
export * from './Icon';
|
||||
export * from './InfiniteScroll';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { Footer } from '@/features/footer';
|
||||
@@ -10,19 +10,24 @@ import { MainContent } from './MainLayout';
|
||||
interface PageLayoutProps {
|
||||
withFooter?: boolean;
|
||||
withLeftPanel?: boolean;
|
||||
headerSlot?: ReactNode;
|
||||
footerSlot?: ReactNode;
|
||||
}
|
||||
|
||||
export function PageLayout({
|
||||
children,
|
||||
withFooter = true,
|
||||
withLeftPanel = true,
|
||||
headerSlot,
|
||||
footerSlot,
|
||||
}: PropsWithChildren<PageLayoutProps>) {
|
||||
return (
|
||||
<Box className="--docs--page-layout" $direction="row" $minHeight="100vh">
|
||||
{withLeftPanel && <LeftPanel />}
|
||||
<MainContent>
|
||||
<HeaderFloatingBar />
|
||||
{headerSlot !== undefined ? headerSlot : <HeaderFloatingBar />}
|
||||
{children}
|
||||
{footerSlot}
|
||||
</MainContent>
|
||||
{withFooter && <Footer />}
|
||||
</Box>
|
||||
|
||||
@@ -5,7 +5,13 @@ import { ReactElement, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import ErrorAccessDeniedSvg from '@/assets/icons/Docs Locked.svg';
|
||||
import { Box, Text, errorDescriptionStyles } from '@/components';
|
||||
import {
|
||||
Box,
|
||||
ErrorPageFooter,
|
||||
ErrorPageHeader,
|
||||
Text,
|
||||
errorDescriptionStyles,
|
||||
} from '@/components';
|
||||
import { gotoLogin, useAuth } from '@/features/auth';
|
||||
import { PageLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
@@ -75,7 +81,16 @@ const Page: NextPageWithLayout = () => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <PageLayout withFooter={false}>{page}</PageLayout>;
|
||||
return (
|
||||
<PageLayout
|
||||
withFooter={false}
|
||||
withLeftPanel={false}
|
||||
headerSlot={<ErrorPageHeader />}
|
||||
footerSlot={<ErrorPageFooter />}
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Error404Svg from '@/assets/icons/404.svg';
|
||||
import { ErrorPage } from '@/components';
|
||||
import { ErrorPage, ErrorPageFooter, ErrorPageHeader } from '@/components';
|
||||
import { PageLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
@@ -22,7 +22,16 @@ const Page: NextPageWithLayout = () => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <PageLayout withFooter={false}>{page}</PageLayout>;
|
||||
return (
|
||||
<PageLayout
|
||||
withFooter={false}
|
||||
withLeftPanel={false}
|
||||
headerSlot={<ErrorPageHeader />}
|
||||
footerSlot={<ErrorPageFooter />}
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Error500Svg from '@/assets/icons/500.svg';
|
||||
import { ErrorPage } from '@/components';
|
||||
import { ErrorPage, ErrorPageFooter, ErrorPageHeader } from '@/components';
|
||||
import { PageLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
@@ -28,7 +28,16 @@ const Page: NextPageWithLayout = () => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <PageLayout withFooter={false}>{page}</PageLayout>;
|
||||
return (
|
||||
<PageLayout
|
||||
withFooter={false}
|
||||
withLeftPanel={false}
|
||||
headerSlot={<ErrorPageHeader />}
|
||||
footerSlot={<ErrorPageFooter />}
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Error500Svg from '@/assets/icons/500.svg';
|
||||
import { ErrorPage } from '@/components';
|
||||
import { ErrorPage, ErrorPageFooter, ErrorPageHeader } from '@/components';
|
||||
import { PageLayout } from '@/layouts';
|
||||
|
||||
const Error = () => {
|
||||
@@ -44,7 +44,12 @@ Error.getInitialProps = async (contextData: NextPageContext) => {
|
||||
|
||||
Error.getLayout = function getLayout(page: ReactElement) {
|
||||
return (
|
||||
<PageLayout withFooter={false} withLeftPanel={false}>
|
||||
<PageLayout
|
||||
withFooter={false}
|
||||
withLeftPanel={false}
|
||||
headerSlot={<ErrorPageHeader />}
|
||||
footerSlot={<ErrorPageFooter />}
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Error503Svg from '@/assets/icons/503.svg';
|
||||
import { ErrorPage } from '@/components';
|
||||
import { ErrorPage, ErrorPageFooter, ErrorPageHeader } from '@/components';
|
||||
import { PageLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
@@ -21,7 +21,16 @@ const Page: NextPageWithLayout = () => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <PageLayout withFooter={false}>{page}</PageLayout>;
|
||||
return (
|
||||
<PageLayout
|
||||
withFooter={false}
|
||||
withLeftPanel={false}
|
||||
headerSlot={<ErrorPageHeader />}
|
||||
footerSlot={<ErrorPageFooter />}
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRouter } from 'next/router';
|
||||
import { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ErrorPageFooter, ErrorPageHeader } from '@/components';
|
||||
import { UserReconciliation } from '@/features/auth/components/UserReconciliation';
|
||||
import { PageLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
@@ -34,7 +35,16 @@ const Page: NextPageWithLayout = () => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <PageLayout withFooter={false}>{page}</PageLayout>;
|
||||
return (
|
||||
<PageLayout
|
||||
withFooter={false}
|
||||
withLeftPanel={false}
|
||||
headerSlot={<ErrorPageHeader />}
|
||||
footerSlot={<ErrorPageFooter />}
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRouter } from 'next/router';
|
||||
import { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ErrorPageFooter, ErrorPageHeader } from '@/components';
|
||||
import { UserReconciliation } from '@/features/auth/components/UserReconciliation';
|
||||
import { PageLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
@@ -34,7 +35,16 @@ const Page: NextPageWithLayout = () => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <PageLayout withFooter={false}>{page}</PageLayout>;
|
||||
return (
|
||||
<PageLayout
|
||||
withFooter={false}
|
||||
withLeftPanel={false}
|
||||
headerSlot={<ErrorPageHeader />}
|
||||
footerSlot={<ErrorPageFooter />}
|
||||
>
|
||||
{page}
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
Reference in New Issue
Block a user