diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-05-03 16:53:55 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-05-03 16:53:55 +0200 |
| commit | ee04742d1f0645908baa30e47845126c28848f50 (patch) | |
| tree | 1d80dca17437be6a351e932885e95c940833e571 /src/pages | |
| parent | 83a029084f1bbfd78b7099d9bea3371d4533c6d9 (diff) | |
chore: add a CV page
Diffstat (limited to 'src/pages')
| -rw-r--r-- | src/pages/cv.tsx | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx new file mode 100644 index 0000000..d47edc6 --- /dev/null +++ b/src/pages/cv.tsx @@ -0,0 +1,198 @@ +import Link from '@components/atoms/links/link'; +import { type BreadcrumbItem } from '@components/molecules/nav/breadcrumb'; +import ImageWidget from '@components/organisms/widgets/image-widget'; +import SocialMedia from '@components/organisms/widgets/social-media'; +import PageLayout, { + type PageLayoutProps, +} from '@components/templates/page/page-layout'; +import CVContent, { data, meta } from '@content/pages/cv.mdx'; +import styles from '@styles/pages/cv.module.scss'; +import { getFormattedDate } from '@utils/helpers/dates'; +import { loadTranslation } from '@utils/helpers/i18n'; +import useSettings from '@utils/hooks/use-settings'; +import { GetStaticProps, NextPage } from 'next'; +import Head from 'next/head'; +import { useRouter } from 'next/router'; +import Script from 'next/script'; +import { ReactNode } from 'react'; +import { useIntl } from 'react-intl'; +import { AboutPage, Graph, WebPage } from 'schema-dts'; + +/** + * CV page. + */ +const CVPage: NextPage = () => { + const intl = useIntl(); + const { file, image } = data; + const { dates, intro, seo, title } = meta; + const homeLabel = intl.formatMessage({ + defaultMessage: 'Home', + description: 'Breadcrumb: home label', + id: 'j5k9Fe', + }); + const breadcrumb: BreadcrumbItem[] = [ + { id: 'home', name: homeLabel, url: '/' }, + { id: 'cv', name: title, url: '/cv' }, + ]; + + const imageWidgetTitle = intl.formatMessage({ + defaultMessage: 'Others formats', + description: 'CVPage: cv preview widget title', + id: 'B9OCyV', + }); + const socialMediaTitle = intl.formatMessage({ + defaultMessage: 'Open-source projects', + description: 'CVPage: social media widget title', + id: '+Dre5J', + }); + + const publicationLabel = intl.formatMessage({ + defaultMessage: 'Published on:', + description: 'Meta: publication date label', + id: 'QGi5uD', + }); + + const updateLabel = intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'Meta: update date label', + id: 'tLC7bh', + }); + + const headerMeta: PageLayoutProps['headerMeta'] = { + publication: { + name: publicationLabel, + value: getFormattedDate(dates.publication), + }, + update: { name: updateLabel, value: getFormattedDate(dates.update) }, + }; + + const { website } = useSettings(); + const cvAlt = intl.formatMessage( + { + defaultMessage: '{name} CV', + description: 'CVPage: CV image alternative text', + id: 'KUowUk', + }, + { name: website.name } + ); + const cvCaption = intl.formatMessage( + { + defaultMessage: '<link>Download the CV in PDF</link>', + id: 'fN04AJ', + description: 'CVPage: download CV in PDF text', + }, + { + link: (chunks: ReactNode) => ( + <Link download={true} href={file}> + {chunks} + </Link> + ), + } + ); + + const widgets = [ + <ImageWidget + key="image-widget" + expanded={true} + title={imageWidgetTitle} + level={2} + image={{ alt: cvAlt, ...image }} + description={cvCaption} + imageClassName={styles.image} + />, + <SocialMedia + key="social-media" + title={socialMediaTitle} + level={2} + media={[ + { name: 'Github', url: 'https://github.com/ArmandPhilippot' }, + { name: 'Gitlab', url: 'https://gitlab.com/ArmandPhilippot' }, + { + name: 'LinkedIn', + url: 'https://www.linkedin.com/in/armandphilippot', + }, + ]} + />, + ]; + + const { asPath } = useRouter(); + const pageUrl = `${website.url}${asPath}`; + const pagePublicationDate = new Date(dates.publication); + const pageUpdateDate = new Date(dates.update); + + const webpageSchema: WebPage = { + '@id': `${pageUrl}`, + '@type': 'WebPage', + breadcrumb: { '@id': `${website.url}/#breadcrumb` }, + name: seo.title, + description: seo.description, + reviewedBy: { '@id': `${website.url}/#branding` }, + url: `${pageUrl}`, + isPartOf: { + '@id': `${website.url}`, + }, + }; + + const cvSchema: AboutPage = { + '@id': `${website.url}/#cv`, + '@type': 'AboutPage', + name: seo.title, + description: intro, + author: { '@id': `${website.url}/#branding` }, + creator: { '@id': `${website.url}/#branding` }, + dateCreated: pagePublicationDate.toISOString(), + dateModified: pageUpdateDate.toISOString(), + datePublished: pagePublicationDate.toISOString(), + editor: { '@id': `${website.url}/#branding` }, + image: image.src, + inLanguage: website.locales.default, + license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', + thumbnailUrl: image.src, + mainEntityOfPage: { '@id': `${pageUrl}` }, + }; + + const schemaJsonLd: Graph = { + '@context': 'https://schema.org', + '@graph': [webpageSchema, cvSchema], + }; + + return ( + <PageLayout + title={title} + intro={intro} + headerMeta={headerMeta} + breadcrumb={breadcrumb} + withToC={true} + widgets={widgets} + > + <Head> + <title>{`${seo.title} - ${website.name}`}</title> + <meta name="description" content={seo.description} /> + <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.src} /> + <meta property="og:image:alt" content={title} /> + </Head> + <Script + id="schema-cv" + type="application/ld+json" + dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} + /> + <CVContent /> + </PageLayout> + ); +}; + +export const getStaticProps: GetStaticProps = async ({ locale }) => { + const translation = await loadTranslation(locale); + + return { + props: { + translation, + }, + }; +}; + +export default CVPage; |
