diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-05-02 18:57:29 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-05-03 15:22:24 +0200 |
| commit | 732d0943f8041d76262222a092b014f2557085ef (patch) | |
| tree | 16f6f76648b479a9591400ab15bb3e9c914f2226 /src/pages/index.tsx | |
| parent | ca921d7536cfe950b5a7d442977bbf900b48faf4 (diff) | |
chore: add homepage
Diffstat (limited to 'src/pages/index.tsx')
| -rw-r--r-- | src/pages/index.tsx | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/src/pages/index.tsx b/src/pages/index.tsx new file mode 100644 index 0000000..c965320 --- /dev/null +++ b/src/pages/index.tsx @@ -0,0 +1,365 @@ +import FeedIcon from '@assets/images/icon-feed.svg'; +import ButtonLink from '@components/atoms/buttons/button-link'; +import Envelop from '@components/atoms/icons/envelop'; +import Column, { type ColumnProps } from '@components/atoms/layout/column'; +import Section, { type SectionProps } from '@components/atoms/layout/section'; +import List, { type ListItem } from '@components/atoms/lists/list'; +import ResponsiveImage, { + type ResponsiveImageProps, +} from '@components/molecules/images/responsive-image'; +import Columns, { + type ColumnsProps, +} from '@components/molecules/layout/columns'; +import CardsList, { + type CardsListItem, +} from '@components/organisms/layout/cards-list'; +import Layout from '@components/templates/layout/layout'; +import HomePageContent from '@content/pages/homepage.mdx'; +import { getArticlesCard } from '@services/graphql/articles'; +import styles from '@styles/pages/home.module.scss'; +import { ArticleCard } from '@ts/types/app'; +import { loadTranslation, type Messages } from '@utils/helpers/i18n'; +import useSettings from '@utils/hooks/use-settings'; +import { NestedMDXComponents } from 'mdx/types'; +import { GetStaticProps, NextPage } from 'next'; +import Head from 'next/head'; +import Script from 'next/script'; +import { ReactElement } from 'react'; +import { useIntl } from 'react-intl'; +import { Graph, WebPage } from 'schema-dts'; + +type HomeProps = { + recentPosts: ArticleCard[]; + translation?: Messages; +}; + +/** + * Home page. + */ +const HomePage: NextPage<HomeProps> = ({ recentPosts }) => { + const intl = useIntl(); + + /** + * Retrieve a list of coding links. + * + * @returns {JSX.Element} - A list of links. + */ + const CodingLinks = (): JSX.Element => { + const links: ListItem[] = [ + { + id: 'web-development', + value: ( + <ButtonLink target="/thematique/developpement-web"> + {intl.formatMessage({ + defaultMessage: 'Web development', + description: 'HomePage: link to web development thematic', + id: 'vkF/RP', + })} + </ButtonLink> + ), + }, + { + id: 'projects', + value: ( + <ButtonLink target="/projets"> + {intl.formatMessage({ + defaultMessage: 'Projects', + description: 'HomePage: link to projects', + id: 'N44SOc', + })} + </ButtonLink> + ), + }, + ]; + + return <List kind="flex" items={links} className={styles.list} />; + }; + + /** + * Retrieve a list of Coldark repositories. + * + * @returns {JSX.Element} - A list of links. + */ + const ColdarkRepos = (): JSX.Element => { + const links: ListItem[] = [ + { + id: 'coldark-github', + value: ( + <ButtonLink + target="https://github.com/ArmandPhilippot/coldark" + external={true} + > + {intl.formatMessage({ + defaultMessage: 'Github', + description: 'HomePage: Github link', + id: '3f3PzH', + })} + </ButtonLink> + ), + }, + { + id: 'coldark-gitlab', + value: ( + <ButtonLink + target="https://gitlab.com/ArmandPhilippot/coldark" + external={true} + > + {intl.formatMessage({ + defaultMessage: 'Gitlab', + description: 'HomePage: Gitlab link', + id: '7AnwZ7', + })} + </ButtonLink> + ), + }, + ]; + + return <List kind="flex" items={links} className={styles.list} />; + }; + + /** + * Retrieve a list of links related to Free thematic. + * + * @returns {JSX.Element} - A list of links. + */ + const LibreLinks = (): JSX.Element => { + const links: ListItem[] = [ + { + id: 'free', + value: ( + <ButtonLink target="/thematique/libre"> + {intl.formatMessage({ + defaultMessage: 'Free', + description: 'HomePage: link to free thematic', + id: 'w8GrOf', + })} + </ButtonLink> + ), + }, + { + id: 'linux', + value: ( + <ButtonLink target="/thematique/linux"> + {intl.formatMessage({ + defaultMessage: 'Linux', + description: 'HomePage: link to Linux thematic', + id: 'jASD7k', + })} + </ButtonLink> + ), + }, + ]; + + return <List kind="flex" items={links} className={styles.list} />; + }; + + /** + * Retrieve the Shaarli link. + * + * @returns {JSX.Element} - A list of links + */ + const ShaarliLink = (): JSX.Element => { + const links: ListItem[] = [ + { + id: 'shaarli', + value: ( + <ButtonLink target="https://shaarli.armandphilippot.com/"> + {intl.formatMessage({ + defaultMessage: 'Shaarli', + description: 'HomePage: link to Shaarli', + id: 'i5L19t', + })} + </ButtonLink> + ), + }, + ]; + + return <List kind="flex" items={links} className={styles.list} />; + }; + + /** + * Retrieve the additional links. + * + * @returns {JSX.Element} - A list of links. + */ + const MoreLinks = (): JSX.Element => { + const links: ListItem[] = [ + { + id: 'contact-me', + value: ( + <ButtonLink target="/contact"> + <Envelop className={styles.icon} /> + {intl.formatMessage({ + defaultMessage: 'Contact me', + description: 'HomePage: contact button text', + id: 'sO/Iwj', + })} + </ButtonLink> + ), + }, + { + id: 'rss-feed', + value: ( + <ButtonLink target="/feed"> + <FeedIcon className={`${styles.icon} ${styles['icon--feed']}`} /> + {intl.formatMessage({ + defaultMessage: 'Subscribe', + description: 'HomePage: RSS feed subscription text', + id: 'T4YA64', + })} + </ButtonLink> + ), + }, + ]; + + return <List kind="flex" items={links} className={styles.list} />; + }; + + /** + * Get a cards list of recent posts. + * + * @returns {JSX.Element} - The cards list. + */ + const getRecentPosts = (): JSX.Element => { + const posts: CardsListItem[] = recentPosts.map((post) => { + return { + cover: post.cover, + id: post.slug, + meta: [ + { + id: 'publication', + term: intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'HomePage: publication date label', + id: 'pT5nHk', + }), + value: [post.dates.publication], + }, + ], + title: post.title, + url: `/article/${post.slug}`, + }; + }); + + return ( + <CardsList + items={posts} + titleLevel={3} + className={`${styles.list} ${styles['list--cards']}`} + /> + ); + }; + + /** + * Create the page sections. + * + * @param {object} obj - An object containing the section body. + * @param {ReactElement[]} obj.children - The section body. + * @returns {JSX.Element} A section element. + */ + const getSection = ({ + children, + variant, + }: { + children: ReactElement[]; + variant: SectionProps['variant']; + }): JSX.Element => { + const [headingEl, ...content] = children; + const title = headingEl.props.children; + + return ( + <Section + title={title} + content={content} + variant={variant} + className={styles.section} + /> + ); + }; + + const components: NestedMDXComponents = { + CodingLinks: CodingLinks, + ColdarkRepos: ColdarkRepos, + Column: (props: ColumnProps) => <Column {...props} />, + Columns: (props: ColumnsProps) => ( + <Columns className={styles.columns} {...props} /> + ), + Image: (props: ResponsiveImageProps) => <ResponsiveImage {...props} />, + LibreLinks: LibreLinks, + MoreLinks: MoreLinks, + RecentPosts: getRecentPosts, + Section: getSection, + ShaarliLink: ShaarliLink, + }; + + const { website } = useSettings(); + + const pageTitle = intl.formatMessage( + { + defaultMessage: '{websiteName} | Front-end developer: WordPress/React', + description: 'HomePage: SEO - Page title', + id: 'PXp2hv', + }, + { websiteName: website.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: website.name } + ); + + const webpageSchema: WebPage = { + '@id': `${website.url}/#home`, + '@type': 'WebPage', + name: pageTitle, + description: pageDescription, + author: { '@id': `${website.url}/#branding` }, + creator: { '@id': `${website.url}/#branding` }, + editor: { '@id': `${website.url}/#branding` }, + inLanguage: website.locales.default, + license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', + reviewedBy: { '@id': `${website.url}/#branding` }, + url: `${website.url}`, + }; + + const schemaJsonLd: Graph = { + '@context': 'https://schema.org', + '@graph': [webpageSchema], + }; + + return ( + <Layout> + <Head> + <title>{pageTitle}</title> + <meta name="description" content={pageDescription} /> + <meta property="og:url" content={website.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) }} + /> + <HomePageContent components={components} /> + </Layout> + ); +}; + +export const getStaticProps: GetStaticProps = async ({ locale }) => { + const translation = await loadTranslation(locale); + const recentPosts = await getArticlesCard({ first: 3 }); + + return { + props: { + recentPosts, + translation, + }, + }; +}; + +export default HomePage; |
