diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-04-29 12:13:34 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-04-29 18:30:05 +0200 |
| commit | 7e16f500cb7bc0cfd8bafbf6bb1555704f771231 (patch) | |
| tree | bfc2b4a475cb06a787e2c4bdf284165644e82952 /src/pages | |
| parent | 5324664e87bedfaa01ba62c0c847ef5b861e69b3 (diff) | |
chore: remove old pages, components, helpers and types
Since I'm using new components, I will also rewrite the GraphQL queries
so it is easier to start from scratch.
Diffstat (limited to 'src/pages')
| -rw-r--r-- | src/pages/404.tsx | 85 | ||||
| -rw-r--r-- | src/pages/_app.tsx | 9 | ||||
| -rw-r--r-- | src/pages/article/[slug].tsx | 291 | ||||
| -rw-r--r-- | src/pages/blog/index.tsx | 269 | ||||
| -rw-r--r-- | src/pages/blog/page/[id].tsx | 205 | ||||
| -rw-r--r-- | src/pages/contact.tsx | 151 | ||||
| -rw-r--r-- | src/pages/cv.tsx | 181 | ||||
| -rw-r--r-- | src/pages/index.tsx | 241 | ||||
| -rw-r--r-- | src/pages/mentions-legales.tsx | 150 | ||||
| -rw-r--r-- | src/pages/projet/[slug].tsx | 186 | ||||
| -rw-r--r-- | src/pages/projets.tsx | 128 | ||||
| -rw-r--r-- | src/pages/recherche/index.tsx | 213 | ||||
| -rw-r--r-- | src/pages/sujet/[slug].tsx | 224 | ||||
| -rw-r--r-- | src/pages/thematique/[slug].tsx | 214 |
14 files changed, 4 insertions, 2543 deletions
diff --git a/src/pages/404.tsx b/src/pages/404.tsx deleted file mode 100644 index 24c6951..0000000 --- a/src/pages/404.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import PostHeader from '@components/PostHeader/PostHeader'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { settings } from '@utils/config'; -import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import Link from 'next/link'; -import { FormattedMessage, useIntl } from 'react-intl'; - -const Error404: NextPageWithLayout = () => { - const intl = useIntl(); - - const pageTitle = intl.formatMessage( - { - defaultMessage: 'Error 404: Page not found - {websiteName}', - description: '404Page: SEO - Page title', - id: '310o3F', - }, - { websiteName: settings.name } - ); - const pageDescription = intl.formatMessage({ - defaultMessage: 'Page not found.', - description: '404Page: SEO - Meta description', - id: '48Ww//', - }); - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - </Head> - <div className={`${styles.article} ${styles['article--no-comments']}`}> - <PostHeader - title={intl.formatMessage({ - defaultMessage: 'Page not found', - description: '404Page: page title', - id: 'OccTWi', - })} - /> - <div className={styles.body}> - <FormattedMessage - 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." - description="404Page: page body" - id="ZWh78Y" - values={{ - link: (chunks: string) => ( - <Link href="/contact/"> - <a>{chunks}</a> - </Link> - ), - }} - /> - </div> - </div> - </> - ); -}; - -Error404.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const intl = await getIntlInstance(); - const breadcrumbTitle = intl.formatMessage({ - defaultMessage: 'Error 404', - description: '404Page: breadcrumb item', - id: 'ywkCsK', - }); - const { locale } = context; - const translation = await loadTranslation(locale); - - return { - props: { - breadcrumbTitle, - locale, - translation, - }, - }; -}; - -export default Error404; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 84c2469..939b337 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,17 +1,16 @@ -import { AppPropsWithLayout } from '@ts/types/app'; import { settings } from '@utils/config'; import { AckeeProvider } from '@utils/providers/ackee'; import { PrismThemeProvider } from '@utils/providers/prism-theme'; import { ThemeProvider } from 'next-themes'; +import { AppProps } from 'next/app'; import { useRouter } from 'next/router'; import { IntlProvider } from 'react-intl'; import '../styles/globals.scss'; -const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => { +const App = ({ Component, pageProps }: AppProps) => { const { locale, defaultLocale } = useRouter(); const appLocale: string = locale || settings.locales.defaultLocale; - const getLayout = Component.getLayout ?? ((page) => page); return ( <AckeeProvider domain={settings.ackee.url} siteId={settings.ackee.siteId}> <IntlProvider @@ -25,7 +24,7 @@ const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => { enableSystem={true} > <PrismThemeProvider> - {getLayout(<Component {...pageProps} />)} + <Component {...pageProps} /> </PrismThemeProvider> </ThemeProvider> </IntlProvider> @@ -33,4 +32,4 @@ const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => { ); }; -export default MyApp; +export default App; diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx deleted file mode 100644 index 27a6f7b..0000000 --- a/src/pages/article/[slug].tsx +++ /dev/null @@ -1,291 +0,0 @@ -import CommentForm from '@components/CommentForm/CommentForm'; -import CommentsList from '@components/CommentsList/CommentsList'; -import { getLayout } from '@components/Layouts/Layout'; -import PostFooter from '@components/PostFooter/PostFooter'; -import PostHeader from '@components/PostHeader/PostHeader'; -import Sidebar from '@components/Sidebar/Sidebar'; -import Spinner from '@components/Spinner/Spinner'; -import { Sharing, ToC } from '@components/Widgets'; -import { - getAllPostsSlug, - getCommentsByPostId, - getPostBySlug, -} from '@services/graphql/queries'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { ArticleMeta, ArticleProps } from '@ts/types/articles'; -import { PrismDefaultPlugins, PrismPlugins } from '@ts/types/prism'; -import { settings } from '@utils/config'; -import { getFormattedPaths } from '@utils/helpers/format'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { addPrismClasses } from '@utils/helpers/prism'; -import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import Prism from 'prismjs'; -import { ParsedUrlQuery } from 'querystring'; -import { useCallback, useEffect, useMemo } from 'react'; -import { useIntl } from 'react-intl'; -import { Blog, BlogPosting, Graph, WebPage } from 'schema-dts'; - -const SingleArticle: NextPageWithLayout<ArticleProps> = ({ - comments, - post, -}) => { - const intl = useIntl(); - const router = useRouter(); - - const loadPrismPlugins = useCallback( - async (prismPlugins: (PrismDefaultPlugins | PrismPlugins)[]) => { - for (const plugin of prismPlugins) { - try { - if (plugin === 'color-scheme') { - await import(`@utils/plugins/prism-${plugin}`); - } else { - await import(`prismjs/plugins/${plugin}/prism-${plugin}.min.js`); - - if (plugin === 'autoloader') - Prism.plugins.autoloader.languages_path = '/prism/'; - } - } catch (error) { - console.error('Article: an error occurred with Prism.'); - console.error(error); - } - } - }, - [] - ); - - const plugins: (PrismDefaultPlugins | PrismPlugins)[] = useMemo( - () => [ - 'autoloader', - 'toolbar', - 'show-language', - 'copy-to-clipboard', - 'color-scheme', - 'command-line', - 'line-numbers', - 'match-braces', - 'normalize-whitespace', - ], - [] - ); - - useEffect(() => { - loadPrismPlugins(plugins).then(() => { - addPrismClasses(); - Prism.highlightAll(); - }); - }, [plugins, loadPrismPlugins]); - - if (router.isFallback) return <Spinner />; - - const { - author, - commentCount, - content, - databaseId, - dates, - featuredImage, - info, - intro, - seo, - topics, - thematics, - title, - } = post; - - const meta: ArticleMeta = { - author, - commentCount: commentCount || undefined, - dates, - readingTime: info.readingTime, - thematics, - wordsCount: info.wordsCount, - }; - - const articleUrl = `${settings.url}${router.asPath}`; - - const webpageSchema: WebPage = { - '@id': `${articleUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - lastReviewed: dates.update, - name: seo.title, - description: seo.metaDesc, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${articleUrl}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const blogSchema: Blog = { - '@id': `${settings.url}/#blog`, - '@type': 'Blog', - blogPost: { '@id': `${settings.url}/#article` }, - isPartOf: { - '@id': `${articleUrl}`, - }, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - }; - - const publicationDate = new Date(dates.publication); - const updateDate = new Date(dates.update); - - const blogPostSchema: BlogPosting = { - '@id': `${settings.url}/#article`, - '@type': 'BlogPosting', - name: title, - description: intro, - articleBody: content, - author: { '@id': `${settings.url}/#branding` }, - commentCount: commentCount || undefined, - copyrightYear: publicationDate.getFullYear(), - creator: { '@id': `${settings.url}/#branding` }, - dateCreated: publicationDate.toISOString(), - dateModified: updateDate.toISOString(), - datePublished: publicationDate.toISOString(), - discussionUrl: `${articleUrl}/#comments`, - editor: { '@id': `${settings.url}/#branding` }, - headline: title, - image: featuredImage?.sourceUrl, - inLanguage: settings.locales.defaultLocale, - isPartOf: { - '@id': `${settings.url}/blog`, - }, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${articleUrl}` }, - thumbnailUrl: featuredImage?.sourceUrl, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, blogSchema, blogPostSchema], - }; - - const copyText = intl.formatMessage({ - defaultMessage: 'Copy', - description: 'Prism: copy button text (no clicked)', - id: '/ly3AC', - }); - const copiedText = intl.formatMessage({ - defaultMessage: 'Copied!', - description: 'Prism: copy button text (clicked)', - id: 'OV9r1K', - }); - const errorText = intl.formatMessage({ - defaultMessage: 'Use Ctrl+c to copy', - description: 'Prism: error text', - id: 'z9qkcQ', - }); - const darkTheme = intl.formatMessage({ - defaultMessage: 'Dark Theme 🌙', - description: 'Prism: toggle dark theme button text', - id: 'nFMdWI', - }); - const lightTheme = intl.formatMessage({ - defaultMessage: 'Light Theme 🌞', - description: 'Prism: toggle light theme button text', - id: 'Ua2g2p', - }); - - return ( - <> - <Head> - <title>{seo.title}</title> - <meta name="description" content={seo.metaDesc} /> - <meta property="og:url" content={`${articleUrl}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={intro} /> - <meta property="og:image" content={featuredImage?.sourceUrl} /> - <meta property="og:image:alt" content={featuredImage?.altText} /> - </Head> - <Script - id="schema-article" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="article" - className={styles.article} - data-prismjs-copy={copyText} - data-prismjs-copy-success={copiedText} - data-prismjs-copy-error={errorText} - data-prismjs-color-scheme-dark={darkTheme} - data-prismjs-color-scheme-light={lightTheme} - > - <PostHeader intro={intro} meta={meta} title={title} /> - <Sidebar - position="left" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Table of Contents', - description: 'ArticlePage: ToC sidebar aria-label', - id: '9nhYRA', - })} - > - <ToC /> - </Sidebar> - <div - className={styles.body} - dangerouslySetInnerHTML={{ __html: content }} - ></div> - <PostFooter topics={topics} /> - <Sidebar - position="right" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Sidebar', - description: 'ArticlePage: right sidebar aria-label', - id: 'JeYOeA', - })} - > - <Sharing title={title} excerpt={intro} /> - </Sidebar> - <section id="comments" className={styles.comments}> - <CommentsList articleId={databaseId} comments={comments} /> - <CommentForm articleId={databaseId} /> - </section> - </article> - </> - ); -}; - -SingleArticle.getLayout = getLayout; - -interface PostParams extends ParsedUrlQuery { - slug: string; -} - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const { locale } = context; - const translation = await loadTranslation(locale); - const { slug } = context.params as PostParams; - const post = await getPostBySlug(slug); - const comments = await getCommentsByPostId(post.databaseId); - const breadcrumbTitle = post.title; - - return { - props: { - breadcrumbTitle, - comments, - post, - translation, - }, - }; -}; - -export const getStaticPaths: GetStaticPaths = async () => { - const allSlugs = await getAllPostsSlug(); - const paths = getFormattedPaths(allSlugs); - - return { - paths, - fallback: true, - }; -}; - -export default SingleArticle; diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx deleted file mode 100644 index b5ced07..0000000 --- a/src/pages/blog/index.tsx +++ /dev/null @@ -1,269 +0,0 @@ -import { Button } from '@components/Buttons'; -import { getLayout } from '@components/Layouts/Layout'; -import Pagination from '@components/Pagination/Pagination'; -import PaginationCursor from '@components/PaginationCursor/PaginationCursor'; -import PostHeader from '@components/PostHeader/PostHeader'; -import PostsList from '@components/PostsList/PostsList'; -import Sidebar from '@components/Sidebar/Sidebar'; -import Spinner from '@components/Spinner/Spinner'; -import { ThematicsList, TopicsList } from '@components/Widgets'; -import { - getAllThematics, - getAllTopics, - getPostsTotal, - getPublishedPosts, -} from '@services/graphql/queries'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { BlogPageProps, PostsList as PostsListData } from '@ts/types/blog'; -import { settings } from '@utils/config'; -import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { useEffect, useRef, useState } from 'react'; -import { useIntl } from 'react-intl'; -import { Blog as BlogSchema, Graph, WebPage } from 'schema-dts'; -import useSWRInfinite from 'swr/infinite'; - -const Blog: NextPageWithLayout<BlogPageProps> = ({ - allThematics, - allTopics, - posts, - totalPosts, -}) => { - const intl = useIntl(); - const lastPostRef = useRef<HTMLSpanElement>(null); - const router = useRouter(); - const [isMounted, setIsMounted] = useState<boolean>(false); - - useEffect(() => { - if (typeof window !== undefined) setIsMounted(true); - }, []); - - const getKey = (pageIndex: number, previousData: PostsListData) => { - if (previousData && !previousData.posts) return null; - - return pageIndex === 0 - ? { first: settings.postsPerPage } - : { - first: settings.postsPerPage, - after: previousData.pageInfo.endCursor, - }; - }; - - const { data, error, size, setSize } = useSWRInfinite( - getKey, - getPublishedPosts, - { fallbackData: [posts] } - ); - const [totalPostsCount, setTotalPostsCount] = useState<number>(totalPosts); - - useEffect(() => { - if (data) setTotalPostsCount(data[0].pageInfo.total); - }, [data]); - - const [loadedPostsCount, setLoadedPostsCount] = useState<number>( - settings.postsPerPage - ); - - useEffect(() => { - if (data && data.length > 0) { - const newCount = - settings.postsPerPage + - data[0].pageInfo.total - - data[data.length - 1].pageInfo.total; - setLoadedPostsCount(newCount); - } - }, [data]); - - const isLoadingInitialData = !data && !error; - const isLoadingMore: boolean = - isLoadingInitialData || - (size > 0 && data !== undefined && typeof data[size - 1] === 'undefined'); - - const hasNextPage = data && data[data.length - 1].pageInfo.hasNextPage; - - const loadMorePosts = () => { - if (lastPostRef.current) { - lastPostRef.current.focus(); - } - setSize(size + 1); - }; - - const getPostsList = () => { - if (error) - return intl.formatMessage({ - defaultMessage: 'Failed to load.', - description: 'BlogPage: failed to load text', - id: 'C/XGkH', - }); - if (!data) return <Spinner />; - - return <PostsList ref={lastPostRef} data={data} showYears={true} />; - }; - - const pageTitle = intl.formatMessage( - { - defaultMessage: 'Blog: development, open source - {websiteName}', - description: 'BlogPage: SEO - Page title', - id: '+Y+tLK', - }, - { websiteName: settings.name } - ); - const pageDescription = intl.formatMessage( - { - defaultMessage: - "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.", - description: 'BlogPage: SEO - Meta description', - id: '18h/t0', - }, - { websiteName: settings.name } - ); - const pageUrl = `${settings.url}${router.asPath}`; - - const webpageSchema: WebPage = { - '@id': `${pageUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: pageTitle, - description: pageDescription, - inLanguage: settings.locales.defaultLocale, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${settings.url}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const blogSchema: BlogSchema = { - '@id': `${settings.url}/#blog`, - '@type': 'Blog', - author: { '@id': `${settings.url}/#branding` }, - creator: { '@id': `${settings.url}/#branding` }, - editor: { '@id': `${settings.url}/#branding` }, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${pageUrl}` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, blogSchema], - }; - - const title = intl.formatMessage({ - defaultMessage: 'Blog', - description: 'BlogPage: page title', - id: '7TbbIk', - }); - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - <meta property="og:url" content={`${pageUrl}`} /> - <meta property="og:type" content="website" /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={pageDescription} /> - </Head> - <Script - id="schema-blog" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="blog" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader title={title} meta={{ results: totalPostsCount }} /> - <div className={styles.body}> - {getPostsList()} - {hasNextPage && - (isMounted ? ( - <> - <PaginationCursor - current={loadedPostsCount} - total={totalPostsCount} - /> - <Button - isDisabled={isLoadingMore} - clickHandler={loadMorePosts} - position="center" - spacing={true} - > - {intl.formatMessage({ - defaultMessage: 'Load more?', - description: 'BlogPage: load more text', - id: 'Kqq2cm', - })} - </Button> - </> - ) : ( - <Pagination baseUrl="/blog" total={totalPostsCount} /> - ))} - </div> - <Sidebar - position="right" - title={intl.formatMessage({ - defaultMessage: 'Filter by:', - description: 'BlogPage: sidebar title', - id: 'KERk7L', - })} - > - <ThematicsList - initialData={allThematics} - title={intl.formatMessage({ - defaultMessage: 'Thematics', - description: 'BlogPage: thematics list widget title', - id: 'HriY57', - })} - /> - <TopicsList - initialData={allTopics} - title={intl.formatMessage({ - defaultMessage: 'Topics', - description: 'BlogPage: topics list widget title', - id: '2D9tB5', - })} - /> - </Sidebar> - </article> - </> - ); -}; - -Blog.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const intl = await getIntlInstance(); - const breadcrumbTitle = intl.formatMessage({ - defaultMessage: 'Blog', - description: 'BlogPage: breadcrumb item', - id: 'R0eDmw', - }); - const firstPosts = await getPublishedPosts({ first: settings.postsPerPage }); - const totalPosts = await getPostsTotal(); - const allThematics = await getAllThematics(); - const allTopics = await getAllTopics(); - const { locale } = context; - const translation = await loadTranslation(locale); - - return { - props: { - allThematics, - allTopics, - breadcrumbTitle, - locale, - posts: firstPosts, - totalPosts, - translation, - }, - }; -}; - -export default Blog; diff --git a/src/pages/blog/page/[id].tsx b/src/pages/blog/page/[id].tsx deleted file mode 100644 index 6c4d2f8..0000000 --- a/src/pages/blog/page/[id].tsx +++ /dev/null @@ -1,205 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import Pagination from '@components/Pagination/Pagination'; -import PostHeader from '@components/PostHeader/PostHeader'; -import PostsList from '@components/PostsList/PostsList'; -import Sidebar from '@components/Sidebar/Sidebar'; -import { ThematicsList, TopicsList } from '@components/Widgets'; -import { - getAllThematics, - getAllTopics, - getEndCursor, - getPostsTotal, - getPublishedPosts, -} from '@services/graphql/queries'; -import { NextPageWithLayout } from '@ts/types/app'; -import { BlogPageProps } from '@ts/types/blog'; -import { settings } from '@utils/config'; -import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { useIntl } from 'react-intl'; -import { Blog, Graph, WebPage } from 'schema-dts'; -import styles from '@styles/pages/Page.module.scss'; -import { getFormattedPageNumbers } from '@utils/helpers/format'; -import { useEffect } from 'react'; -import Spinner from '@components/Spinner/Spinner'; - -const BlogPage: NextPageWithLayout<BlogPageProps> = ({ - allThematics, - allTopics, - posts, - totalPosts, -}) => { - const intl = useIntl(); - const router = useRouter(); - const pageNumber = Number(router.query.id); - - useEffect(() => { - if (router.query.id === '1') router.push('/blog'); - }, [router]); - - if (router.isFallback) return <Spinner />; - - const pageTitle = intl.formatMessage( - { - defaultMessage: 'Blog - Page {number} - {websiteName}', - description: 'BlogPage: SEO - Page title', - id: '8w+jnD', - }, - { number: pageNumber, websiteName: settings.name } - ); - const pageDescription = intl.formatMessage( - { - defaultMessage: - "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.", - description: 'BlogPage: SEO - Meta description', - id: '18h/t0', - }, - { websiteName: settings.name } - ); - const pageUrl = `${settings.url}${router.asPath}`; - - const webpageSchema: WebPage = { - '@id': `${pageUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: pageTitle, - description: pageDescription, - inLanguage: settings.locales.defaultLocale, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${settings.url}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const blogSchema: Blog = { - '@id': `${settings.url}/#blog`, - '@type': 'Blog', - author: { '@id': `${settings.url}/#branding` }, - creator: { '@id': `${settings.url}/#branding` }, - editor: { '@id': `${settings.url}/#branding` }, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${pageUrl}` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, blogSchema], - }; - - const title = intl.formatMessage({ - defaultMessage: 'Blog', - description: 'BlogPage: page title', - id: '7TbbIk', - }); - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - <meta property="og:url" content={`${pageUrl}`} /> - <meta property="og:type" content="website" /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={pageDescription} /> - </Head> - <Script - id="schema-blog" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="blog" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader title={title} meta={{ results: totalPosts }} /> - <div className={styles.body}> - <PostsList data={[posts]} showYears={true} /> - <Pagination baseUrl="/blog" total={totalPosts} /> - </div> - <Sidebar - position="right" - title={intl.formatMessage({ - defaultMessage: 'Filter by:', - description: 'BlogPage: sidebar title', - id: 'KERk7L', - })} - > - <ThematicsList - initialData={allThematics} - title={intl.formatMessage({ - defaultMessage: 'Thematics', - description: 'BlogPage: thematics list widget title', - id: 'HriY57', - })} - /> - <TopicsList - initialData={allTopics} - title={intl.formatMessage({ - defaultMessage: 'Topics', - description: 'BlogPage: topics list widget title', - id: '2D9tB5', - })} - /> - </Sidebar> - </article> - </> - ); -}; - -BlogPage.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const intl = await getIntlInstance(); - const breadcrumbTitle = intl.formatMessage({ - defaultMessage: 'Blog', - description: 'BlogPage: breadcrumb item', - id: 'R0eDmw', - }); - const { locale, params } = context; - const queriedPageNumber = params ? Number(params.id) : 1; - const queriedPostsNumber = settings.postsPerPage * queriedPageNumber; - const endCursor = - queriedPostsNumber === 1 - ? undefined - : await getEndCursor({ first: queriedPostsNumber }); - const posts = await getPublishedPosts({ - first: settings.postsPerPage, - after: endCursor, - }); - const totalPosts = await getPostsTotal(); - const allThematics = await getAllThematics(); - const allTopics = await getAllTopics(); - const translation = await loadTranslation(locale); - - return { - props: { - allThematics, - allTopics, - breadcrumbTitle, - locale, - posts, - totalPosts, - translation, - }, - }; -}; - -export default BlogPage; - -export const getStaticPaths: GetStaticPaths = async () => { - const totalPosts = await getPostsTotal(); - const totalPages = Math.floor(totalPosts / settings.postsPerPage); - const paths = getFormattedPageNumbers(totalPages); - - return { - paths, - fallback: true, - }; -}; diff --git a/src/pages/contact.tsx b/src/pages/contact.tsx deleted file mode 100644 index 5934dd9..0000000 --- a/src/pages/contact.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import ContactForm from '@components/ContactForm/ContactForm'; -import { getLayout } from '@components/Layouts/Layout'; -import PostHeader from '@components/PostHeader/PostHeader'; -import Sidebar from '@components/Sidebar/Sidebar'; -import { SocialMedia } from '@components/Widgets'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { settings } from '@utils/config'; -import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { useIntl } from 'react-intl'; -import { ContactPage as ContactPageSchema, Graph, WebPage } from 'schema-dts'; - -const ContactPage: NextPageWithLayout = () => { - const intl = useIntl(); - const router = useRouter(); - - const pageTitle = intl.formatMessage( - { - defaultMessage: 'Contact form - {websiteName}', - description: 'ContactPage: SEO - Page title', - id: 'Y3qRib', - }, - { websiteName: settings.name } - ); - const pageDescription = intl.formatMessage( - { - defaultMessage: - "Contact {websiteName} through its website. All you need to do it's to fill the contact form.", - description: 'ContactPage: SEO - Meta description', - id: 'OIffB4', - }, - { websiteName: settings.name } - ); - const pageUrl = `${settings.url}${router.asPath}`; - const title = intl.formatMessage({ - defaultMessage: 'Contact', - description: 'ContactPage: page title', - id: 'AN9iy7', - }); - const intro = intl.formatMessage({ - defaultMessage: 'Please fill the form to contact me.', - description: 'ContactPage: page introduction', - id: '8Ls2mD', - }); - - const webpageSchema: WebPage = { - '@id': `${pageUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: pageTitle, - description: pageDescription, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${pageUrl}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const contactSchema: ContactPageSchema = { - '@id': `${settings.url}/#contact`, - '@type': 'ContactPage', - name: title, - description: intro, - author: { '@id': `${settings.url}/#branding` }, - creator: { '@id': `${settings.url}/#branding` }, - editor: { '@id': `${settings.url}/#branding` }, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${pageUrl}` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, contactSchema], - }; - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - <meta property="og:url" content={`${pageUrl}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={intro} /> - </Head> - <Script - id="schema-contact" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="contact" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader title={title} intro={intro} /> - <div className={styles.body}> - <p> - {intl.formatMessage({ - defaultMessage: 'All fields marked with * are required.', - description: 'ContactPage: required fields text', - id: 'txusHd', - })} - </p> - <ContactForm /> - </div> - <Sidebar position="right"> - <SocialMedia - title={intl.formatMessage({ - defaultMessage: 'Find me elsewhere', - description: 'ContactPage: social media widget title', - id: 'Qh2CwH', - })} - github={true} - gitlab={true} - linkedin={true} - /> - </Sidebar> - </article> - </> - ); -}; - -ContactPage.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const intl = await getIntlInstance(); - const breadcrumbTitle = intl.formatMessage({ - defaultMessage: 'Contact', - description: 'ContactPage: breadcrumb item', - id: 'CzTbM4', - }); - const { locale } = context; - const translation = await loadTranslation(locale); - - return { - props: { - breadcrumbTitle, - locale, - translation, - }, - }; -}; - -export default ContactPage; diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx deleted file mode 100644 index 71eb449..0000000 --- a/src/pages/cv.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import PostHeader from '@components/PostHeader/PostHeader'; -import Sidebar from '@components/Sidebar/Sidebar'; -import { CVPreview, SocialMedia, ToC } from '@components/Widgets'; -import CVContent, { intro, meta, pdf, image } from '@content/pages/cv.mdx'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { ArticleMeta } from '@ts/types/articles'; -import { settings } from '@utils/config'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { useIntl } from 'react-intl'; -import { AboutPage, Graph, WebPage } from 'schema-dts'; - -const CV: NextPageWithLayout = () => { - const intl = useIntl(); - const router = useRouter(); - const dates = { - publication: meta.publishedOn, - update: meta.updatedOn, - }; - - const pageMeta: ArticleMeta = { - dates, - }; - const pageUrl = `${settings.url}${router.asPath}`; - const pageTitle = intl.formatMessage( - { - defaultMessage: 'CV Front-end developer - {websiteName}', - description: 'CVPage: SEO - Page title', - id: 'Y1ZdJ6', - }, - { websiteName: settings.name } - ); - const pageDescription = intl.formatMessage( - { - defaultMessage: - 'Discover the curriculum of {websiteName}, front-end developer located in France: skills, experiences and training.', - description: 'CVPage: SEO - Meta description', - id: 'bBdMGm', - }, - { websiteName: settings.name } - ); - - const webpageSchema: WebPage = { - '@id': `${pageUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: pageTitle, - description: pageDescription, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${pageUrl}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const publicationDate = new Date(dates.publication); - const updateDate = new Date(dates.update); - - const cvSchema: AboutPage = { - '@id': `${settings.url}/#cv`, - '@type': 'AboutPage', - name: pageTitle, - description: intro, - author: { '@id': `${settings.url}/#branding` }, - creator: { '@id': `${settings.url}/#branding` }, - dateCreated: publicationDate.toISOString(), - dateModified: updateDate.toISOString(), - datePublished: publicationDate.toISOString(), - editor: { '@id': `${settings.url}/#branding` }, - image, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - thumbnailUrl: image, - mainEntityOfPage: { '@id': `${pageUrl}` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, cvSchema], - }; - - const title = intl.formatMessage( - { - defaultMessage: "{name}'s CV", - description: 'CVPage: page title', - id: 'Mj2BQf', - }, - { name: settings.name } - ); - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - <meta property="og:url" content={`${pageUrl}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={intro} /> - <meta property="og:image" content={image} /> - <meta property="og:image:alt" content={title} /> - </Head> - <Script - id="schema-cv" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="cv" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader intro={intro} meta={pageMeta} title={meta.title} /> - <Sidebar - position="left" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Table of Contents', - description: 'CVPage: ToC sidebar aria-label', - id: 'g4DckL', - })} - > - <ToC /> - </Sidebar> - <div className={styles.body}> - <CVContent /> - </div> - <Sidebar - position="right" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Sidebar', - description: 'CVPage: right sidebar aria-label', - id: 'QHOm5t', - })} - > - <CVPreview - title={intl.formatMessage({ - defaultMessage: 'Others formats', - description: 'CVPage: cv preview widget title', - id: 'B9OCyV', - })} - imgSrc={image} - pdf={pdf} - /> - <SocialMedia - title={intl.formatMessage({ - defaultMessage: 'Open-source projects', - description: 'CVPage: social media widget title', - id: '+Dre5J', - })} - github={true} - gitlab={true} - /> - </Sidebar> - </article> - </> - ); -}; - -CV.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const breadcrumbTitle = meta.title; - const { locale } = context; - const translation = await loadTranslation(locale); - - return { - props: { - breadcrumbTitle, - locale, - translation, - }, - }; -}; - -export default CV; diff --git a/src/pages/index.tsx b/src/pages/index.tsx deleted file mode 100644 index ca0a809..0000000 --- a/src/pages/index.tsx +++ /dev/null @@ -1,241 +0,0 @@ -import FeedIcon from '@assets/images/icon-feed.svg'; -import { ButtonLink } from '@components/Buttons'; -import { ContactIcon } from '@components/Icons'; -import Layout from '@components/Layouts/Layout'; -import { ResponsiveImage } from '@components/MDX'; -import { RecentPosts } from '@components/Widgets'; -import HomePageContent from '@content/pages/homepage.mdx'; -import { getPublishedPosts } from '@services/graphql/queries'; -import styles from '@styles/pages/Home.module.scss'; -import { NextPageWithLayout, ResponsiveImageProps } from '@ts/types/app'; -import { PostsList } from '@ts/types/blog'; -import { settings } from '@utils/config'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { NestedMDXComponents } from 'mdx/types'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import Script from 'next/script'; -import type { ReactElement } from 'react'; -import { useIntl } from 'react-intl'; -import { Graph, WebPage } from 'schema-dts'; - -type HomePageProps = { - recentPosts: PostsList; -}; - -const Home: NextPageWithLayout<HomePageProps> = ({ - recentPosts, -}: { - recentPosts: PostsList; -}) => { - const intl = useIntl(); - - const CodingLinks = () => { - return ( - <ul className={styles['links-list']}> - <li> - <ButtonLink target="/thematique/developpement-web"> - {intl.formatMessage({ - defaultMessage: 'Web development', - description: 'HomePage: link to web development thematic', - id: 'vkF/RP', - })} - </ButtonLink> - </li> - <li> - <ButtonLink target="/projets"> - {intl.formatMessage({ - defaultMessage: 'Projects', - description: 'HomePage: link to projects', - id: 'N44SOc', - })} - </ButtonLink> - </li> - </ul> - ); - }; - - const ColdarkRepos = () => { - return ( - <ul className={styles['links-list']}> - <li> - <ButtonLink - target="https://github.com/ArmandPhilippot/coldark" - isExternal={true} - > - Github - </ButtonLink> - </li> - <li> - <ButtonLink - target="https://gitlab.com/ArmandPhilippot/coldark" - isExternal={true} - > - Gitlab - </ButtonLink> - </li> - </ul> - ); - }; - - const LibreLinks = () => { - return ( - <ul className={styles['links-list']}> - <li> - <ButtonLink target="/thematique/libre"> - {intl.formatMessage({ - defaultMessage: 'Free', - description: 'HomePage: link to free thematic', - id: 'w8GrOf', - })} - </ButtonLink> - </li> - <li> - <ButtonLink target="/thematique/linux"> - {intl.formatMessage({ - defaultMessage: 'Linux', - description: 'HomePage: link to Linux thematic', - id: 'jASD7k', - })} - </ButtonLink> - </li> - </ul> - ); - }; - - const ShaarliLink = () => { - return ( - <ul className={styles['links-list']}> - <li> - <ButtonLink target="https://shaarli.armandphilippot.com/"> - {intl.formatMessage({ - defaultMessage: 'Shaarli', - description: 'HomePage: link to Shaarli', - id: 'i5L19t', - })} - </ButtonLink> - </li> - </ul> - ); - }; - - const MoreLinks = () => { - return ( - <ul className={styles['links-list']}> - <li> - <ButtonLink target="/contact"> - <ContactIcon /> - {intl.formatMessage({ - defaultMessage: 'Contact me', - description: 'HomePage: contact button text', - id: 'sO/Iwj', - })} - </ButtonLink> - </li> - <li> - <ButtonLink target="/feed"> - <FeedIcon className={styles['icon--feed']} /> - {intl.formatMessage({ - defaultMessage: 'Subscribe', - description: 'HomePage: RSS feed subscription text', - id: 'T4YA64', - })} - </ButtonLink> - </li> - </ul> - ); - }; - - const getRecentPosts = () => { - return <RecentPosts posts={recentPosts} />; - }; - - const components: NestedMDXComponents = { - CodingLinks: CodingLinks, - ColdarkRepos: ColdarkRepos, - Image: (props: ResponsiveImageProps) => ResponsiveImage({ ...props }), - LibreLinks: LibreLinks, - MoreLinks: MoreLinks, - RecentPosts: getRecentPosts, - ShaarliLink: ShaarliLink, - }; - - const pageTitle = intl.formatMessage( - { - defaultMessage: '{websiteName} | Front-end developer: WordPress/React', - description: 'HomePage: SEO - Page title', - id: 'PXp2hv', - }, - { websiteName: settings.name } - ); - const pageDescription = intl.formatMessage( - { - defaultMessage: - '{websiteName} is a front-end developer located in France. He codes and he writes mostly about web development and open-source.', - description: 'HomePage: SEO - Meta description', - id: 'tMuNTy', - }, - { websiteName: settings.name } - ); - - const webpageSchema: WebPage = { - '@id': `${settings.url}/#home`, - '@type': 'WebPage', - name: pageTitle, - description: pageDescription, - author: { '@id': `${settings.url}/#branding` }, - creator: { '@id': `${settings.url}/#branding` }, - editor: { '@id': `${settings.url}/#branding` }, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${settings.url}`, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema], - }; - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - <meta property="og:type" content="website" /> - <meta property="og:url" content={`${settings.url}`} /> - <meta property="og:title" content={pageTitle} /> - <meta property="og:description" content={pageDescription} /> - </Head> - <Script - id="schema-homepage" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <div id="home"> - <HomePageContent components={components} /> - </div> - </> - ); -}; - -Home.getLayout = function getLayout(page: ReactElement) { - return <Layout isHome={true}>{page}</Layout>; -}; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const { locale } = context; - const translation = await loadTranslation(locale); - const recentPosts = await getPublishedPosts({ first: 3 }); - - return { - props: { - recentPosts, - translation, - }, - }; -}; - -export default Home; diff --git a/src/pages/mentions-legales.tsx b/src/pages/mentions-legales.tsx deleted file mode 100644 index b103b5e..0000000 --- a/src/pages/mentions-legales.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import { Link } from '@components/MDX'; -import PostHeader from '@components/PostHeader/PostHeader'; -import Sidebar from '@components/Sidebar/Sidebar'; -import { ToC } from '@components/Widgets'; -import LegalNoticeContent, { - intro, - meta, -} from '@content/pages/legal-notice.mdx'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { ArticleMeta } from '@ts/types/articles'; -import { settings } from '@utils/config'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { NestedMDXComponents } from 'mdx/types'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { useIntl } from 'react-intl'; -import { Article, Graph, WebPage } from 'schema-dts'; - -const LegalNotice: NextPageWithLayout = () => { - const intl = useIntl(); - const router = useRouter(); - const dates = { - publication: meta.publishedOn, - update: meta.updatedOn, - }; - - const pageMeta: ArticleMeta = { - dates, - }; - const pageTitle = intl.formatMessage( - { - defaultMessage: 'Legal notice - {websiteName}', - description: 'LegalNoticePage: SEO - Page title', - id: '4zAUSu', - }, - { websiteName: settings.name } - ); - const pageDescription = intl.formatMessage( - { - defaultMessage: "Discover the legal notice of {websiteName}'s website.", - description: 'LegalNoticePage: SEO - Meta description', - id: 'uvB+32', - }, - { websiteName: settings.name } - ); - const pageUrl = `${settings.url}${router.asPath}`; - const title = intl.formatMessage({ - defaultMessage: 'Legal notice', - description: 'LegalNoticePage: page title', - id: '/IirIt', - }); - const publicationDate = new Date(dates.publication); - const updateDate = new Date(dates.update); - - const webpageSchema: WebPage = { - '@id': `${pageUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: pageTitle, - description: pageDescription, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${pageUrl}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const articleSchema: Article = { - '@id': `${settings.url}/#legal-notice`, - '@type': 'Article', - name: title, - description: intro, - author: { '@id': `${settings.url}/#branding` }, - copyrightYear: publicationDate.getFullYear(), - creator: { '@id': `${settings.url}/#branding` }, - dateCreated: publicationDate.toISOString(), - dateModified: updateDate.toISOString(), - datePublished: publicationDate.toISOString(), - editor: { '@id': `${settings.url}/#branding` }, - headline: title, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${pageUrl}` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, articleSchema], - }; - - const components: NestedMDXComponents = { - Link: (props) => Link(props), - }; - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - <meta property="og:url" content={`${pageUrl}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={pageTitle} /> - <meta property="og:description" content={intro} /> - </Head> - <Script - id="schema-legal-notice" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="legal-notice" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader intro={intro} meta={pageMeta} title={meta.title} /> - <Sidebar position="left"> - <ToC /> - </Sidebar> - <div className={styles.body}> - <LegalNoticeContent components={components} /> - </div> - </article> - </> - ); -}; - -LegalNotice.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const breadcrumbTitle = meta.title; - const { locale } = context; - const translation = await loadTranslation(locale); - - return { - props: { - breadcrumbTitle, - locale, - translation, - }, - }; -}; - -export default LegalNotice; diff --git a/src/pages/projet/[slug].tsx b/src/pages/projet/[slug].tsx deleted file mode 100644 index 1f09fed..0000000 --- a/src/pages/projet/[slug].tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import { CodeBlock, Gallery, Link, ResponsiveImage } from '@components/MDX'; -import PostHeader from '@components/PostHeader/PostHeader'; -import ProjectSummary from '@components/ProjectSummary/ProjectSummary'; -import Sidebar from '@components/Sidebar/Sidebar'; -import { Sharing, ToC } from '@components/Widgets'; -import styles from '@styles/pages/Page.module.scss'; -import { - NextPageWithLayout, - Project as ProjectData, - ProjectProps, -} from '@ts/types/app'; -import { settings } from '@utils/config'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { - getAllProjectsFilename, - getProjectData, -} from '@utils/helpers/projects'; -import { MDXComponents, NestedMDXComponents } from 'mdx/types'; -import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { ParsedUrlQuery } from 'querystring'; -import { ComponentType } from 'react'; -import { useIntl } from 'react-intl'; -import { Article, Graph, WebPage } from 'schema-dts'; - -const Project: NextPageWithLayout<ProjectProps> = ({ - project, -}: { - project: ProjectData; -}) => { - const intl = useIntl(); - const router = useRouter(); - const projectUrl = `${settings.url}${router.asPath}`; - const { id, intro, meta, title, seo } = project; - const dates = { - publication: meta.publishedOn, - update: meta.updatedOn, - }; - - const components: NestedMDXComponents = { - CodeBlock: (props) => CodeBlock(props), - Gallery: (props) => Gallery(props), - Image: (props) => ResponsiveImage({ caption: props.caption, ...props }), - Link: (props) => Link(props), - pre: ({ children }) => CodeBlock(children.props), - }; - - const ProjectContent: ComponentType<MDXComponents> = - require(`../../content/projects/${id}.mdx`).default; - - const webpageSchema: WebPage = { - '@id': `${projectUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: seo.title, - description: seo.description, - inLanguage: settings.locales.defaultLocale, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${settings.url}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const publicationDate = new Date(dates.publication); - const updateDate = new Date(dates.update); - - const articleSchema: Article = { - '@id': `${settings.url}/project`, - '@type': 'Article', - name: title, - description: intro, - author: { '@id': `${settings.url}/#branding` }, - copyrightYear: publicationDate.getFullYear(), - creator: { '@id': `${settings.url}/#branding` }, - dateCreated: publicationDate.toISOString(), - dateModified: updateDate.toISOString(), - datePublished: publicationDate.toISOString(), - editor: { '@id': `${settings.url}/#branding` }, - headline: title, - thumbnailUrl: meta.hasCover ? `/projects/${id}.jpg` : '', - image: meta.hasCover ? `/projects/${id}.jpg` : '', - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${projectUrl}` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, articleSchema], - }; - - return ( - <> - <Head> - <title>{seo.title}</title> - <meta name="description" content={seo.description} /> - <meta property="og:url" content={`${projectUrl}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={title} /> - <meta property="og:description" content={intro} /> - </Head> - <Script - id="schema-project" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="project" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader title={title} intro={intro} meta={{ dates }} /> - <Sidebar - position="left" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Table of Contents', - description: 'ProjectPage: ToC sidebar aria-label', - id: '6dXfvr', - })} - > - <ToC /> - </Sidebar> - <div className={styles.body}> - <ProjectSummary id={id} title={title} meta={meta} /> - <ProjectContent components={components} /> - </div> - <Sidebar - position="right" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Sidebar', - description: 'ProjectPage: right sidebar aria-label', - id: 'hHrNd0', - })} - > - <Sharing title={title} excerpt={intro} /> - </Sidebar> - </article> - </> - ); -}; - -Project.getLayout = getLayout; - -interface ProjectParams extends ParsedUrlQuery { - slug: string; -} - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const { locale } = context; - const translation = await loadTranslation(locale); - const { slug } = context.params as ProjectParams; - const project = await getProjectData(slug); - const breadcrumbTitle = project.title; - - return { - props: { - breadcrumbTitle, - locale, - project, - translation, - }, - }; -}; - -export const getStaticPaths: GetStaticPaths = async () => { - const filenames = getAllProjectsFilename(); - const paths = filenames.map((filename) => { - return { - params: { - slug: filename, - }, - }; - }); - - return { - paths, - fallback: false, - }; -}; - -export default Project; diff --git a/src/pages/projets.tsx b/src/pages/projets.tsx deleted file mode 100644 index 8a81f39..0000000 --- a/src/pages/projets.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import PostHeader from '@components/PostHeader/PostHeader'; -import ProjectsList from '@components/ProjectsList/ProjectsList'; -import PageContent, { meta } from '@content/pages/projects.mdx'; -import styles from '@styles/pages/Projects.module.scss'; -import { Project } from '@ts/types/app'; -import { settings } from '@utils/config'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { getSortedProjects } from '@utils/helpers/projects'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { useIntl } from 'react-intl'; -import { Article, Graph, WebPage } from 'schema-dts'; - -const Projects = ({ projects }: { projects: Project[] }) => { - const intl = useIntl(); - const dates = { - publication: meta.publishedOn, - update: meta.updatedOn, - }; - const publicationDate = new Date(dates.publication); - const updateDate = new Date(dates.update); - const router = useRouter(); - const pageUrl = `${settings.url}${router.asPath}`; - const pageTitle = intl.formatMessage( - { - defaultMessage: 'Projects: open-source makings - {websiteName}', - description: 'ProjectsPage: SEO - Page title', - id: 'SX1z3t', - }, - { websiteName: settings.name } - ); - const pageDescription = intl.formatMessage( - { - defaultMessage: - 'Discover {websiteName} projects. Mostly related to web development and open source.', - description: 'ProjectsPage: SEO - Meta description', - id: 's6U1Xt', - }, - { websiteName: settings.name } - ); - - const webpageSchema: WebPage = { - '@id': `${pageUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: pageTitle, - description: pageDescription, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${pageUrl}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const articleSchema: Article = { - '@id': `${settings.url}/#projects`, - '@type': 'Article', - name: meta.title, - description: pageDescription, - author: { '@id': `${settings.url}/#branding` }, - copyrightYear: publicationDate.getFullYear(), - creator: { '@id': `${settings.url}/#branding` }, - dateCreated: publicationDate.toISOString(), - dateModified: updateDate.toISOString(), - datePublished: publicationDate.toISOString(), - editor: { '@id': `${settings.url}/#branding` }, - headline: meta.title, - inLanguage: settings.locales.defaultLocale, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${pageUrl}` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, articleSchema], - }; - - return ( - <> - <Head> - <title>{pageTitle}</title> - <meta name="description" content={pageDescription} /> - <meta property="og:url" content={`${pageUrl}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={meta.title} /> - <meta property="og:description" content={pageDescription} /> - </Head> - <Script - id="schema-projects" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article id="projects" className={styles.article}> - <PostHeader title={meta.title} intro={<PageContent />} /> - <div className={styles.body}> - {projects.length > 0 && <ProjectsList projects={projects} />} - </div> - </article> - </> - ); -}; - -Projects.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const breadcrumbTitle = meta.title; - const { locale } = context; - const projects: Project[] = await getSortedProjects(); - const translation = await loadTranslation(locale); - - return { - props: { - breadcrumbTitle, - locale, - projects, - translation, - }, - }; -}; - -export default Projects; diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx deleted file mode 100644 index b843f8d..0000000 --- a/src/pages/recherche/index.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { Button } from '@components/Buttons'; -import { getLayout } from '@components/Layouts/Layout'; -import PaginationCursor from '@components/PaginationCursor/PaginationCursor'; -import PostHeader from '@components/PostHeader/PostHeader'; -import PostsList from '@components/PostsList/PostsList'; -import Sidebar from '@components/Sidebar/Sidebar'; -import Spinner from '@components/Spinner/Spinner'; -import { ThematicsList, TopicsList } from '@components/Widgets'; -import { getPublishedPosts } from '@services/graphql/queries'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { PostsList as PostsListData } from '@ts/types/blog'; -import { settings } from '@utils/config'; -import { getIntlInstance, loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import { useEffect, useRef, useState } from 'react'; -import { useIntl } from 'react-intl'; -import useSWRInfinite from 'swr/infinite'; - -const Search: NextPageWithLayout = () => { - const intl = useIntl(); - const [query, setQuery] = useState(''); - const router = useRouter(); - const lastPostRef = useRef<HTMLSpanElement>(null); - - useEffect(() => { - if (!router.isReady) return; - - if (router.query?.s && typeof router.query.s === 'string') { - setQuery(router.query.s); - } - }, [router.isReady, router.query.s]); - - const getKey = (pageIndex: number, previousData: PostsListData) => { - if (previousData && !previousData.posts) return null; - - return pageIndex === 0 - ? { first: settings.postsPerPage, searchQuery: query } - : { - first: settings.postsPerPage, - after: previousData.pageInfo.endCursor, - searchQuery: query, - }; - }; - - const { data, error, size, setSize } = useSWRInfinite( - getKey, - getPublishedPosts - ); - const [totalPostsCount, setTotalPostsCount] = useState<number>(0); - - useEffect(() => { - if (data) setTotalPostsCount(data[0].pageInfo.total); - }, [data]); - - const [loadedPostsCount, setLoadedPostsCount] = useState<number>( - settings.postsPerPage - ); - - useEffect(() => { - if (data && data.length > 0) { - const newCount = - settings.postsPerPage + - data[0].pageInfo.total - - data[data.length - 1].pageInfo.total; - setLoadedPostsCount(newCount); - } - }, [data]); - - const isLoadingInitialData = !data && !error; - const isLoadingMore: boolean = - isLoadingInitialData || - (size > 0 && data !== undefined && typeof data[size - 1] === 'undefined'); - - const hasNextPage = data && data[data.length - 1].pageInfo.hasNextPage; - - const title = query - ? intl.formatMessage( - { - defaultMessage: 'Search results for {query}', - description: 'SearchPage: search results text', - id: 'VSGuGE', - }, - { query } - ) - : intl.formatMessage({ - defaultMessage: 'Search', - description: 'SearchPage: page title', - id: 'U+35YD', - }); - - const description = query - ? intl.formatMessage( - { - defaultMessage: 'Discover search results for {query}', - description: 'SearchPage: meta description with query', - id: 'A4LTGq', - }, - { query } - ) - : intl.formatMessage( - { - defaultMessage: 'Search for a post on {websiteName}', - description: 'SearchPage: meta description without query', - id: 'PrIz5o', - }, - { websiteName: settings.name } - ); - - const head = { - title: `${title} | ${settings.name}`, - description, - }; - - const loadMorePosts = () => { - if (lastPostRef.current) { - lastPostRef.current.focus(); - } - setSize(size + 1); - }; - - const getPostsList = () => { - if (error) - return intl.formatMessage({ - defaultMessage: 'Failed to load.', - description: 'SearchPage: failed to load text', - id: 'fOe8rH', - }); - if (!data) return <Spinner />; - - return <PostsList ref={lastPostRef} data={data} showYears={false} />; - }; - - return ( - <> - <Head> - <title>{head.title}</title> - <meta name="description" content={head.description} /> - </Head> - <article - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader title={title} meta={{ results: totalPostsCount }} /> - <div className={styles.body}> - {getPostsList()} - {hasNextPage && ( - <> - <PaginationCursor - current={loadedPostsCount} - total={totalPostsCount} - /> - <Button - isDisabled={isLoadingMore} - clickHandler={loadMorePosts} - position="center" - spacing={true} - > - {intl.formatMessage({ - defaultMessage: 'Load more?', - description: 'SearchPage: load more text', - id: 'pEtJik', - })} - </Button> - </> - )} - </div> - <Sidebar position="right"> - <ThematicsList - title={intl.formatMessage({ - defaultMessage: 'Thematics', - description: 'SearchPage: thematics list widget title', - id: 'Dq6+WH', - })} - /> - <TopicsList - title={intl.formatMessage({ - defaultMessage: 'Topics', - description: 'SearchPage: topics list widget title', - id: 'N804XO', - })} - /> - </Sidebar> - </article> - </> - ); -}; - -Search.getLayout = getLayout; - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const intl = await getIntlInstance(); - const breadcrumbTitle = intl.formatMessage({ - defaultMessage: 'Search', - description: 'SearchPage: breadcrumb item', - id: 'TfU6Qm', - }); - const { locale } = context; - const translation = await loadTranslation(locale); - - return { - props: { - breadcrumbTitle, - locale, - translation, - }, - }; -}; - -export default Search; diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx deleted file mode 100644 index 30dd36c..0000000 --- a/src/pages/sujet/[slug].tsx +++ /dev/null @@ -1,224 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import PostHeader from '@components/PostHeader/PostHeader'; -import PostPreview from '@components/PostPreview/PostPreview'; -import Sidebar from '@components/Sidebar/Sidebar'; -import Spinner from '@components/Spinner/Spinner'; -import { RelatedThematics, ToC, TopicsList } from '@components/Widgets'; -import { - getAllTopics, - getAllTopicsSlug, - getTopicBySlug, -} from '@services/graphql/queries'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { ArticleMeta } from '@ts/types/articles'; -import { TopicProps, ThematicPreview } from '@ts/types/taxonomies'; -import { settings } from '@utils/config'; -import { getFormattedPaths } from '@utils/helpers/format'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { ParsedUrlQuery } from 'querystring'; -import { useRef } from 'react'; -import { useIntl } from 'react-intl'; -import { Article as Article, Graph, WebPage } from 'schema-dts'; - -const Topic: NextPageWithLayout<TopicProps> = ({ topic, allTopics }) => { - const intl = useIntl(); - const relatedThematics = useRef<ThematicPreview[]>([]); - const router = useRouter(); - - if (router.isFallback) return <Spinner />; - - const updateRelatedThematics = (newThematics: ThematicPreview[]) => { - newThematics.forEach((thematic) => { - const thematicIndex = relatedThematics.current.findIndex( - (relatedThematic) => relatedThematic.id === thematic.id - ); - const hasThematic = thematicIndex === -1 ? false : true; - - if (!hasThematic) relatedThematics.current.push(thematic); - }); - }; - - const getPostsList = () => { - return [...topic.posts].reverse().map((post) => { - updateRelatedThematics(post.thematics); - - return ( - <li key={post.id} className={styles.item}> - <PostPreview post={post} titleLevel={3} /> - </li> - ); - }); - }; - - const meta: ArticleMeta = { - dates: topic.dates, - results: topic.posts.length, - website: topic.officialWebsite, - }; - const topicUrl = `${settings.url}${router.asPath}`; - - const webpageSchema: WebPage = { - '@id': `${topicUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: topic.seo.title, - description: topic.seo.metaDesc, - inLanguage: settings.locales.defaultLocale, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${settings.url}`, - isPartOf: { - '@id': `${settings.url}`, - }, - }; - - const publicationDate = new Date(topic.dates.publication); - const updateDate = new Date(topic.dates.update); - - const articleSchema: Article = { - '@id': `${settings.url}/#topic`, - '@type': 'Article', - name: topic.title, - description: topic.intro, - author: { '@id': `${settings.url}/#branding` }, - copyrightYear: publicationDate.getFullYear(), - creator: { '@id': `${settings.url}/#branding` }, - dateCreated: publicationDate.toISOString(), - dateModified: updateDate.toISOString(), - datePublished: publicationDate.toISOString(), - editor: { '@id': `${settings.url}/#branding` }, - headline: topic.title, - thumbnailUrl: topic.featuredImage?.sourceUrl, - image: topic.featuredImage?.sourceUrl, - inLanguage: settings.locales.defaultLocale, - isPartOf: { '@id': `${settings.url}/blog` }, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${topicUrl}` }, - subjectOf: { '@id': `${settings.url}/blog` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, articleSchema], - }; - - return ( - <> - <Head> - <title>{topic.seo.title}</title> - <meta name="description" content={topic.seo.metaDesc} /> - <meta property="og:url" content={`${topicUrl}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={topic.title} /> - <meta property="og:description" content={topic.intro} /> - <meta property="og:image" content={topic.featuredImage?.sourceUrl} /> - <meta property="og:image:alt" content={topic.featuredImage?.altText} /> - </Head> - <Script - id="schema-subject" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="topic" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader - cover={topic.featuredImage} - intro={topic.intro} - meta={meta} - title={topic.title} - /> - <Sidebar - position="left" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Table of Contents', - description: 'TopicPage: ToC sidebar aria-label', - id: 'lsDB5G', - })} - > - <ToC /> - </Sidebar> - <div className={styles.body}> - <div dangerouslySetInnerHTML={{ __html: topic.content }}></div> - {topic.posts.length > 0 && ( - <section className={styles.section}> - <h2> - {intl.formatMessage( - { - defaultMessage: 'All posts in {name}', - description: 'TopicPage: posts list title', - id: 'FLkF2R', - }, - { name: topic.title } - )} - </h2> - <ol className={styles.list}>{getPostsList()}</ol> - </section> - )} - </div> - <Sidebar - position="right" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Sidebar', - description: 'TopicPage: right sidebar aria-label', - id: 'eu3beS', - })} - > - <RelatedThematics thematics={relatedThematics.current} /> - <TopicsList - initialData={allTopics} - title={intl.formatMessage({ - defaultMessage: 'Others topics', - description: 'TopicPage: topics list widget title', - id: '+4tiVb', - })} - /> - </Sidebar> - </article> - </> - ); -}; - -Topic.getLayout = getLayout; - -interface PostParams extends ParsedUrlQuery { - slug: string; -} - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const { locale } = context; - const translation = await loadTranslation(locale); - const { slug } = context.params as PostParams; - const topic = await getTopicBySlug(slug); - const allTopics = await getAllTopics(); - const breadcrumbTitle = topic.title; - - return { - props: { - allTopics, - breadcrumbTitle, - locale, - topic, - translation, - }, - }; -}; - -export const getStaticPaths: GetStaticPaths = async () => { - const allTopics = await getAllTopicsSlug(); - const paths = getFormattedPaths(allTopics); - - return { - paths, - fallback: true, - }; -}; - -export default Topic; diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx deleted file mode 100644 index db22214..0000000 --- a/src/pages/thematique/[slug].tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { getLayout } from '@components/Layouts/Layout'; -import PostHeader from '@components/PostHeader/PostHeader'; -import PostPreview from '@components/PostPreview/PostPreview'; -import Sidebar from '@components/Sidebar/Sidebar'; -import Spinner from '@components/Spinner/Spinner'; -import { RelatedTopics, ThematicsList, ToC } from '@components/Widgets'; -import { - getAllThematics, - getAllThematicsSlug, - getThematicBySlug, -} from '@services/graphql/queries'; -import styles from '@styles/pages/Page.module.scss'; -import { NextPageWithLayout } from '@ts/types/app'; -import { ArticleMeta } from '@ts/types/articles'; -import { TopicPreview, ThematicProps } from '@ts/types/taxonomies'; -import { settings } from '@utils/config'; -import { getFormattedPaths } from '@utils/helpers/format'; -import { loadTranslation } from '@utils/helpers/i18n'; -import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import Script from 'next/script'; -import { ParsedUrlQuery } from 'querystring'; -import { useRef } from 'react'; -import { useIntl } from 'react-intl'; -import { Article, Graph, WebPage } from 'schema-dts'; - -const Thematic: NextPageWithLayout<ThematicProps> = ({ - thematic, - allThematics, -}) => { - const intl = useIntl(); - const relatedTopics = useRef<TopicPreview[]>([]); - const router = useRouter(); - - if (router.isFallback) return <Spinner />; - - const updateRelatedTopics = (newTopics: TopicPreview[]) => { - newTopics.forEach((topic) => { - const topicIndex = relatedTopics.current.findIndex( - (relatedTopic) => relatedTopic.id === topic.id - ); - const hasTopic = topicIndex === -1 ? false : true; - - if (!hasTopic) relatedTopics.current.push(topic); - }); - }; - - const getPostsList = () => { - return [...thematic.posts].reverse().map((post) => { - updateRelatedTopics(post.topics); - - return ( - <li key={post.id} className={styles.item}> - <PostPreview post={post} titleLevel={3} /> - </li> - ); - }); - }; - - const meta: ArticleMeta = { - dates: thematic.dates, - results: thematic.posts.length, - }; - const thematicUrl = `${settings.url}${router.asPath}`; - - const webpageSchema: WebPage = { - '@id': `${thematicUrl}`, - '@type': 'WebPage', - breadcrumb: { '@id': `${settings.url}/#breadcrumb` }, - name: thematic.seo.title, - description: thematic.seo.metaDesc, - inLanguage: settings.locales.defaultLocale, - reviewedBy: { '@id': `${settings.url}/#branding` }, - url: `${settings.url}`, - }; - - const publicationDate = new Date(thematic.dates.publication); - const updateDate = new Date(thematic.dates.update); - - const articleSchema: Article = { - '@id': `${settings.url}/#thematic`, - '@type': 'Article', - name: thematic.title, - description: thematic.intro, - author: { '@id': `${settings.url}/#branding` }, - copyrightYear: publicationDate.getFullYear(), - creator: { '@id': `${settings.url}/#branding` }, - dateCreated: publicationDate.toISOString(), - dateModified: updateDate.toISOString(), - datePublished: publicationDate.toISOString(), - editor: { '@id': `${settings.url}/#branding` }, - headline: thematic.title, - inLanguage: settings.locales.defaultLocale, - isPartOf: { '@id': `${settings.url}/blog` }, - license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', - mainEntityOfPage: { '@id': `${thematicUrl}` }, - subjectOf: { '@id': `${settings.url}/blog` }, - }; - - const schemaJsonLd: Graph = { - '@context': 'https://schema.org', - '@graph': [webpageSchema, articleSchema], - }; - - return ( - <> - <Head> - <title>{thematic.seo.title}</title> - <meta name="description" content={thematic.seo.metaDesc} /> - <meta property="og:url" content={`${thematic}`} /> - <meta property="og:type" content="article" /> - <meta property="og:title" content={thematic.title} /> - <meta property="og:description" content={thematic.intro} /> - </Head> - <Script - id="schema-thematic" - type="application/ld+json" - dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} - /> - <article - id="thematic" - className={`${styles.article} ${styles['article--no-comments']}`} - > - <PostHeader intro={thematic.intro} meta={meta} title={thematic.title} /> - <Sidebar - position="left" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Table of Contents', - description: 'ThematicPage: ToC sidebar aria-label', - id: 'YwvYfw', - })} - > - <ToC /> - </Sidebar> - <div className={styles.body}> - <div dangerouslySetInnerHTML={{ __html: thematic.content }}></div> - {thematic.posts.length > 0 && ( - <section className={styles.section}> - <h2> - {intl.formatMessage( - { - defaultMessage: 'All posts in {name}', - description: 'ThematicPage: posts list title', - id: 'P7fxX2', - }, - { name: thematic.title } - )} - </h2> - <ol className={styles.list}>{getPostsList()}</ol> - </section> - )} - </div> - <Sidebar - position="right" - ariaLabel={intl.formatMessage({ - defaultMessage: 'Sidebar', - description: 'ThematicPage: right sidebar aria-label', - id: 'syLgY9', - })} - > - <RelatedTopics topics={relatedTopics.current} /> - <ThematicsList - initialData={allThematics} - title={intl.formatMessage({ - defaultMessage: 'Others thematics', - description: 'ThematicPage: thematics list widget title', - id: 'norrGp', - })} - /> - </Sidebar> - </article> - </> - ); -}; - -Thematic.getLayout = getLayout; - -interface PostParams extends ParsedUrlQuery { - slug: string; -} - -export const getStaticProps: GetStaticProps = async ( - context: GetStaticPropsContext -) => { - const { locale } = context; - const translation = await loadTranslation(locale); - const { slug } = context.params as PostParams; - const thematic = await getThematicBySlug(slug); - const allThematics = await getAllThematics(); - const breadcrumbTitle = thematic.title; - - return { - props: { - allThematics, - breadcrumbTitle, - locale, - thematic, - translation, - }, - }; -}; - -export const getStaticPaths: GetStaticPaths = async () => { - const allSlugs = await getAllThematicsSlug(); - const paths = getFormattedPaths(allSlugs); - - return { - paths, - fallback: true, - }; -}; - -export default Thematic; |
