diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/templates/page/page.module.scss | 9 | ||||
| -rw-r--r-- | src/i18n/en.json | 30 | ||||
| -rw-r--r-- | src/i18n/fr.json | 30 | ||||
| -rw-r--r-- | src/pages/404.tsx | 194 | ||||
| -rw-r--r-- | src/styles/pages/blog.module.scss | 2 |
5 files changed, 162 insertions, 103 deletions
diff --git a/src/components/templates/page/page.module.scss b/src/components/templates/page/page.module.scss index e7d3587..91a1b58 100644 --- a/src/components/templates/page/page.module.scss +++ b/src/components/templates/page/page.module.scss @@ -78,9 +78,8 @@ padding-bottom: var(--spacing-md); } -.body > * + * { - margin-top: var(--spacing-sm); - margin-bottom: var(--spacing-sm); +.body > * { + margin-block: var(--spacing-sm); } .footer { @@ -143,6 +142,10 @@ } } +.body > *:first-child { + margin-block-start: var(--spacing-md); +} + :where(.footer) { .btn { margin-inline-end: var(--spacing-2xs); diff --git a/src/i18n/en.json b/src/i18n/en.json index 935dcdc..820902b 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -99,10 +99,6 @@ "defaultMessage": "Written by:", "description": "PostPreviewMeta: author label" }, - "310o3F": { - "defaultMessage": "Error 404: Page not found - {websiteName}", - "description": "404Page: SEO - Page title" - }, "3Pipok": { "defaultMessage": "Thanks. Your message was successfully sent. I will answer it as soon as possible.", "description": "Contact: success message" @@ -111,9 +107,9 @@ "defaultMessage": "Repositories:", "description": "ProjectOverview: repositories label" }, - "48Ww//": { - "defaultMessage": "Page not found.", - "description": "404Page: SEO - Meta description" + "3u29G5": { + "defaultMessage": "Query must be longer than one character.", + "description": "Error404Page: invalid query message" }, "4M71hp": { "defaultMessage": "{starsCount, plural, =0 {No stars} one {# star} other {# stars}}", @@ -135,6 +131,10 @@ "defaultMessage": "Copy", "description": "usePrism: copy button text (not clicked)" }, + "6IAJYx": { + "defaultMessage": "Thematics are loading...", + "description": "Error404Page: loading thematics message" + }, "701ggm": { "defaultMessage": "Illustration of {projectName}", "description": "ProjectOverview: cover accessible name" @@ -223,10 +223,6 @@ "defaultMessage": "Failed to load.", "description": "BlogPage: failed to load text" }, - "C6oK7h": { - "defaultMessage": "Query must be longer than one character.", - "description": "404Page: invalid query message" - }, "Dq6+WH": { "defaultMessage": "Thematics", "description": "SearchPage: thematics list widget title" @@ -263,6 +259,10 @@ "defaultMessage": "Share on Journal du Hacker", "description": "SharingWidget: Journal du Hacker sharing link" }, + "HnMf0i": { + "defaultMessage": "Topics are loading...", + "description": "Error404Page: loading topics message" + }, "HohQPh": { "defaultMessage": "Thematics", "description": "Error404Page: thematics list widget title" @@ -647,6 +647,10 @@ "defaultMessage": "Go to next page, page {number}", "description": "BlogPage: next page label" }, + "pNIIU1": { + "defaultMessage": "Error 404: Page not found - {websiteName}", + "description": "Error404Page: SEO - Page title" + }, "pT5nHk": { "defaultMessage": "Published on:", "description": "HomePage: publication date label" @@ -739,6 +743,10 @@ "defaultMessage": "Thematics are loading...", "description": "BlogPage: loading thematics message" }, + "yKoGqg": { + "defaultMessage": "Page not found.", + "description": "Error404Page: SEO - Meta description" + }, "yN5P+m": { "defaultMessage": "Message:", "description": "ContactForm: message label" diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 6e65be0..3628763 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -99,10 +99,6 @@ "defaultMessage": "Écrit par :", "description": "PostPreviewMeta: author label" }, - "310o3F": { - "defaultMessage": "Erreur 404 : Page non trouvée - {websiteName}", - "description": "404Page: SEO - Page title" - }, "3Pipok": { "defaultMessage": "Merci. Votre message a été envoyé avec succès. J’y répondrai dès que possible.", "description": "Contact: success message" @@ -111,9 +107,9 @@ "defaultMessage": "Dépôts :", "description": "ProjectOverview: repositories label" }, - "48Ww//": { - "defaultMessage": "Page non trouvée.", - "description": "404Page: SEO - Meta description" + "3u29G5": { + "defaultMessage": "Query must be longer than one character.", + "description": "Error404Page: invalid query message" }, "4M71hp": { "defaultMessage": "{starsCount, plural, =0 {0 étoile} one {# étoile} other {# étoiles}}", @@ -135,6 +131,10 @@ "defaultMessage": "Copier", "description": "usePrism: copy button text (not clicked)" }, + "6IAJYx": { + "defaultMessage": "Les thématiques sont en cours de chargement…", + "description": "Error404Page: loading thematics message" + }, "701ggm": { "defaultMessage": "Illustration de {projectName}", "description": "ProjectOverview: cover accessible name" @@ -223,10 +223,6 @@ "defaultMessage": "Échec du chargement.", "description": "BlogPage: failed to load text" }, - "C6oK7h": { - "defaultMessage": "Les mots-clés doivent être plus longs qu'un caractère.", - "description": "404Page: invalid query message" - }, "Dq6+WH": { "defaultMessage": "Thématiques", "description": "SearchPage: thematics list widget title" @@ -263,6 +259,10 @@ "defaultMessage": "Partager sur le Journal du Hacker", "description": "SharingWidget: Journal du Hacker sharing link" }, + "HnMf0i": { + "defaultMessage": "Les sujets sont en cours de chargement…", + "description": "Error404Page: loading topics message" + }, "HohQPh": { "defaultMessage": "Thématiques", "description": "Error404Page: thematics list widget title" @@ -647,6 +647,10 @@ "defaultMessage": "Aller à la page suivante, page {number}", "description": "BlogPage: next page label" }, + "pNIIU1": { + "defaultMessage": "Erreur 404 : Page non trouvée - {websiteName}", + "description": "Error404Page: SEO - Page title" + }, "pT5nHk": { "defaultMessage": "Publié le :", "description": "HomePage: publication date label" @@ -739,6 +743,10 @@ "defaultMessage": "Les thématiques sont en cours de chargement…", "description": "BlogPage: loading thematics message" }, + "yKoGqg": { + "defaultMessage": "Page non trouvée.", + "description": "Error404Page: SEO - Meta description" + }, "yN5P+m": { "defaultMessage": "Message :", "description": "ContactForm: message label" diff --git a/src/pages/404.tsx b/src/pages/404.tsx index a98931f..6ef0c55 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -1,4 +1,3 @@ -/* eslint-disable max-statements */ import type { GetStaticProps } from 'next'; import Head from 'next/head'; import { useRouter } from 'next/router'; @@ -15,6 +14,7 @@ import { PageHeader, PageSidebar, SearchForm, + Spinner, type SearchFormSubmit, } from '../components'; import { @@ -25,7 +25,9 @@ import { fetchTopicsCount, fetchTopicsList, } from '../services/graphql'; +import styles from '../styles/pages/blog.module.scss'; import type { + GraphQLConnection, NextPageWithLayout, WPThematicPreview, WPTopicPreview, @@ -34,82 +36,100 @@ import { CONFIG } from '../utils/config'; import { ROUTES } from '../utils/constants'; import { getLinksItemData } from '../utils/helpers'; import { loadTranslation, type Messages } from '../utils/helpers/server'; -import { useBreadcrumb } from '../utils/hooks'; +import { useBreadcrumb, useThematicsList, useTopicsList } from '../utils/hooks'; + +const link = (chunks: ReactNode) => <Link href={ROUTES.CONTACT}>{chunks}</Link>; type Error404PageProps = { - thematicsList: WPThematicPreview[]; - topicsList: WPTopicPreview[]; + data: { + thematics: GraphQLConnection<WPThematicPreview>; + topics: GraphQLConnection<WPTopicPreview>; + }; translation: Messages; }; /** * Error 404 page. */ -const Error404Page: NextPageWithLayout<Error404PageProps> = ({ - thematicsList, - topicsList, -}) => { +const Error404Page: NextPageWithLayout<Error404PageProps> = ({ data }) => { const router = useRouter(); const intl = useIntl(); - const title = intl.formatMessage({ - defaultMessage: 'Page not found', - description: 'Error404Page: page title', - id: 'KnWeKh', + const { isLoading: areThematicsLoading, thematics } = useThematicsList({ + fallback: data.thematics, + input: { first: data.thematics.pageInfo.total }, }); - const body = intl.formatMessage( - { - defaultMessage: - 'Sorry, it seems that the page your are looking for does not exist. If you think this path should work, feel free to <link>contact me</link> with the necessary information so that I can fix the problem.', - id: '9sGNKq', - description: 'Error404Page: page body', + const { isLoading: areTopicsLoading, topics } = useTopicsList({ + fallback: data.topics, + input: { first: data.topics.pageInfo.total }, + }); + const messages = { + loading: { + thematicsList: intl.formatMessage({ + defaultMessage: 'Thematics are loading...', + description: 'Error404Page: loading thematics message', + id: '6IAJYx', + }), + topicsList: intl.formatMessage({ + defaultMessage: 'Topics are loading...', + description: 'Error404Page: loading topics message', + id: 'HnMf0i', + }), }, - { - link: (chunks: ReactNode) => <Link href={ROUTES.CONTACT}>{chunks}</Link>, - } - ); + page: { + title: intl.formatMessage({ + defaultMessage: 'Page not found', + description: 'Error404Page: page title', + id: 'KnWeKh', + }), + }, + seo: { + title: intl.formatMessage( + { + defaultMessage: 'Error 404: Page not found - {websiteName}', + description: 'Error404Page: SEO - Page title', + id: 'pNIIU1', + }, + { websiteName: CONFIG.name } + ), + metaDesc: intl.formatMessage({ + defaultMessage: 'Page not found.', + description: 'Error404Page: SEO - Meta description', + id: 'yKoGqg', + }), + }, + widgets: { + thematicsListTitle: intl.formatMessage({ + defaultMessage: 'Thematics', + description: 'Error404Page: thematics list widget title', + id: 'HohQPh', + }), + topicsListTitle: intl.formatMessage({ + defaultMessage: 'Topics', + description: 'Error404Page: topics list widget title', + id: 'GVpTIl', + }), + }, + }; const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ - title, + title: messages.page.title, url: ROUTES.NOT_FOUND, }); - const pageTitle = intl.formatMessage( - { - defaultMessage: 'Error 404: Page not found - {websiteName}', - description: '404Page: SEO - Page title', - id: '310o3F', - }, - { websiteName: CONFIG.name } - ); - const pageDescription = intl.formatMessage({ - defaultMessage: 'Page not found.', - description: '404Page: SEO - Meta description', - id: '48Ww//', - }); - const thematicsListTitle = intl.formatMessage({ - defaultMessage: 'Thematics', - description: 'Error404Page: thematics list widget title', - id: 'HohQPh', - }); - const topicsListTitle = intl.formatMessage({ - defaultMessage: 'Topics', - description: 'Error404Page: topics list widget title', - id: 'GVpTIl', - }); const searchSubmitHandler: SearchFormSubmit = useCallback( - ({ query }) => { + async ({ query }) => { if (!query) return { messages: { error: intl.formatMessage({ defaultMessage: 'Query must be longer than one character.', - description: '404Page: invalid query message', - id: 'C6oK7h', + description: 'Error404Page: invalid query message', + id: '3u29G5', }), }, validator: (value) => value.query.length > 1, }; - router.push({ pathname: ROUTES.SEARCH, query: { s: query } }); + await router.push({ pathname: ROUTES.SEARCH, query: { s: query } }); return undefined; }, @@ -119,9 +139,9 @@ const Error404Page: NextPageWithLayout<Error404PageProps> = ({ return ( <Page breadcrumbs={breadcrumbItems}> <Head> - <title>{pageTitle}</title> + <title>{messages.seo.title}</title> {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */} - <meta name="description" content={pageDescription} /> + <meta name="description" content={messages.seo.metaDesc} /> </Head> <Script dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }} @@ -129,9 +149,19 @@ const Error404Page: NextPageWithLayout<Error404PageProps> = ({ id="schema-breadcrumb" type="application/ld+json" /> - <PageHeader heading={title} /> - <PageBody> - {body} + <PageHeader heading={messages.page.title} /> + <PageBody className={styles['no-results']}> + <p> + {intl.formatMessage( + { + defaultMessage: + 'Sorry, it seems that the page your are looking for does not exist. If you think this path should work, feel free to <link>contact me</link> with the necessary information so that I can fix the problem.', + id: '9sGNKq', + description: 'Error404Page: page body', + }, + { link } + )} + </p> <p> {intl.formatMessage({ defaultMessage: 'You can also try a search:', @@ -142,26 +172,34 @@ const Error404Page: NextPageWithLayout<Error404PageProps> = ({ <SearchForm isLabelHidden onSubmit={searchSubmitHandler} /> </PageBody> <PageSidebar> - <LinksWidget - heading={ - <Heading isFake level={3}> - {thematicsListTitle} - </Heading> - } - items={getLinksItemData( - thematicsList.map(convertWPThematicPreviewToPageLink) - )} - /> - <LinksWidget - heading={ - <Heading isFake level={3}> - {topicsListTitle} - </Heading> - } - items={getLinksItemData( - topicsList.map(convertWPTopicPreviewToPageLink) - )} - /> + {areThematicsLoading ? ( + <Spinner>{messages.loading.thematicsList}</Spinner> + ) : ( + <LinksWidget + heading={ + <Heading level={2}>{messages.widgets.thematicsListTitle}</Heading> + } + items={getLinksItemData( + thematics.edges.map((edge) => + convertWPThematicPreviewToPageLink(edge.node) + ) + )} + /> + )} + {areTopicsLoading ? ( + <Spinner>{messages.loading.topicsList}</Spinner> + ) : ( + <LinksWidget + heading={ + <Heading level={2}>{messages.widgets.topicsListTitle}</Heading> + } + items={getLinksItemData( + topics.edges.map((edge) => + convertWPTopicPreviewToPageLink(edge.node) + ) + )} + /> + )} </PageSidebar> </Page> ); @@ -180,8 +218,10 @@ export const getStaticProps: GetStaticProps<Error404PageProps> = async ({ return { props: { - thematicsList: thematics.edges.map((edge) => edge.node), - topicsList: topics.edges.map((edge) => edge.node), + data: { + thematics, + topics, + }, translation, }, }; diff --git a/src/styles/pages/blog.module.scss b/src/styles/pages/blog.module.scss index 6b68849..62bc6a5 100644 --- a/src/styles/pages/blog.module.scss +++ b/src/styles/pages/blog.module.scss @@ -20,7 +20,7 @@ } .no-results { - margin-block-start: var(--spacing-md); + margin-block-start: var(--spacing-sm); } .pagination { |
