diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-05-03 16:51:22 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-05-03 16:51:22 +0200 |
| commit | 83a029084f1bbfd78b7099d9bea3371d4533c6d9 (patch) | |
| tree | f99854e4cb430ccbdb725cb2e287423f80cb9791 | |
| parent | 732d0943f8041d76262222a092b014f2557085ef (diff) | |
chore: add a LegalNotice page
| -rw-r--r-- | mdx.d.ts | 10 | ||||
| -rw-r--r-- | src/components/molecules/buttons/heading-button.module.scss | 4 | ||||
| -rw-r--r-- | src/components/molecules/layout/meta.module.scss | 8 | ||||
| -rw-r--r-- | src/components/molecules/layout/page-header.module.scss | 7 | ||||
| -rw-r--r-- | src/components/organisms/widgets/links-list-widget.module.scss | 10 | ||||
| -rw-r--r-- | src/components/organisms/widgets/links-list-widget.tsx | 5 | ||||
| -rw-r--r-- | src/components/organisms/widgets/table-of-contents.module.scss | 4 | ||||
| -rw-r--r-- | src/components/organisms/widgets/table-of-contents.tsx | 2 | ||||
| -rw-r--r-- | src/pages/mentions-legales.tsx | 140 | ||||
| -rw-r--r-- | src/ts/types/app.ts | 16 | ||||
| -rw-r--r-- | src/ts/types/mdx.ts | 20 | ||||
| -rw-r--r-- | src/ts/types/raw-data.ts | 6 | ||||
| -rw-r--r-- | src/utils/helpers/author.ts | 10 |
13 files changed, 209 insertions, 33 deletions
@@ -1,13 +1,9 @@ declare module '*.mdx' { + import { MDXData, MDXPageMeta, MDXProjectMeta } from '@ts/types/mdx'; import { MDXProps } from 'mdx/types'; - import { Meta } from '@ts/types/app'; let MDXComponent: (props: MDXProps) => JSX.Element; export default MDXComponent; - export const cover: string; - export const image: string; - export const intro: string; - export const meta: Meta; - export const pdf: string; - export const seo: { title: string; description: string }; + export const data: MDXData; + export const meta: MDXPageMeta | MDXProjectMeta; } diff --git a/src/components/molecules/buttons/heading-button.module.scss b/src/components/molecules/buttons/heading-button.module.scss index 1d16410..9c278e4 100644 --- a/src/components/molecules/buttons/heading-button.module.scss +++ b/src/components/molecules/buttons/heading-button.module.scss @@ -36,6 +36,8 @@ } .heading { - background: none; padding: var(--spacing-2xs) 0; + background: none; + font-size: var(--font-size-xl); + text-align: left; } diff --git a/src/components/molecules/layout/meta.module.scss b/src/components/molecules/layout/meta.module.scss index f7cc55b..0485545 100644 --- a/src/components/molecules/layout/meta.module.scss +++ b/src/components/molecules/layout/meta.module.scss @@ -2,12 +2,18 @@ .list { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: repeat(1, minmax(0, 1fr)); + gap: var(--spacing-sm); @include mix.media("screen") { + @include mix.dimensions("2xs") { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + @include mix.dimensions("sm") { display: flex; flex-flow: column nowrap; + gap: var(--spacing-2xs); } } } diff --git a/src/components/molecules/layout/page-header.module.scss b/src/components/molecules/layout/page-header.module.scss index 93f7595..4c7df5f 100644 --- a/src/components/molecules/layout/page-header.module.scss +++ b/src/components/molecules/layout/page-header.module.scss @@ -1,5 +1,4 @@ @use "@styles/abstracts/functions" as fun; -@use "@styles/abstracts/mixins" as mix; @use "@styles/abstracts/placeholders"; .wrapper { @@ -55,9 +54,5 @@ } .meta { - @include mix.media("screen") { - @include mix.dimensions("xs") { - font-size: var(--font-size-sm); - } - } + font-size: var(--font-size-sm); } diff --git a/src/components/organisms/widgets/links-list-widget.module.scss b/src/components/organisms/widgets/links-list-widget.module.scss index cbad83e..4444df4 100644 --- a/src/components/organisms/widgets/links-list-widget.module.scss +++ b/src/components/organisms/widgets/links-list-widget.module.scss @@ -3,6 +3,12 @@ .widget { .list { + .list { + > *:first-child { + border-top: fun.convert-px(1) solid var(--color-primary); + } + } + &__link { display: block; padding: var(--spacing-2xs) var(--spacing-xs); @@ -50,9 +56,7 @@ &__item { &:not(:last-child) { - .list__link { - border-bottom: fun.convert-px(1) solid var(--color-primary); - } + border-bottom: fun.convert-px(1) solid var(--color-primary); } > .list { diff --git a/src/components/organisms/widgets/links-list-widget.tsx b/src/components/organisms/widgets/links-list-widget.tsx index 559d0b6..37a20fc 100644 --- a/src/components/organisms/widgets/links-list-widget.tsx +++ b/src/components/organisms/widgets/links-list-widget.tsx @@ -24,7 +24,7 @@ export type LinksListItems = { }; export type LinksListWidgetProps = Pick<WidgetProps, 'level' | 'title'> & - Pick<ListProps, 'kind'> & { + Pick<ListProps, 'className' | 'kind'> & { /** * An array of name/url couple. */ @@ -37,6 +37,7 @@ export type LinksListWidgetProps = Pick<WidgetProps, 'level' | 'title'> & * Render a list of links inside a widget. */ const LinksListWidget: FC<LinksListWidgetProps> = ({ + className = '', items, kind = 'unordered', ...props @@ -74,7 +75,7 @@ const LinksListWidget: FC<LinksListWidgetProps> = ({ items={getListItems(items)} kind={kind} withMargin={false} - className={`${styles.list} ${styles[listKindClass]}`} + className={`${styles.list} ${styles[listKindClass]} ${className}`} itemsClassName={styles.list__item} /> </Widget> diff --git a/src/components/organisms/widgets/table-of-contents.module.scss b/src/components/organisms/widgets/table-of-contents.module.scss new file mode 100644 index 0000000..36217ed --- /dev/null +++ b/src/components/organisms/widgets/table-of-contents.module.scss @@ -0,0 +1,4 @@ +.list { + font-size: var(--font-size-sm); + font-weight: 500; +} diff --git a/src/components/organisms/widgets/table-of-contents.tsx b/src/components/organisms/widgets/table-of-contents.tsx index 3778e02..800ff58 100644 --- a/src/components/organisms/widgets/table-of-contents.tsx +++ b/src/components/organisms/widgets/table-of-contents.tsx @@ -2,6 +2,7 @@ import useHeadingsTree, { type Heading } from '@utils/hooks/use-headings-tree'; import { FC } from 'react'; import { useIntl } from 'react-intl'; import LinksListWidget, { type LinksListItems } from './links-list-widget'; +import styles from './table-of-contents.module.scss'; type TableOfContentsProps = { /** @@ -46,6 +47,7 @@ const TableOfContents: FC<TableOfContentsProps> = ({ wrapper }) => { title={title} level={2} items={getItems(headingsTree)} + className={styles.list} /> ); }; diff --git a/src/pages/mentions-legales.tsx b/src/pages/mentions-legales.tsx new file mode 100644 index 0000000..8dd0a1d --- /dev/null +++ b/src/pages/mentions-legales.tsx @@ -0,0 +1,140 @@ +import Link from '@components/atoms/links/link'; +import ResponsiveImage from '@components/molecules/images/responsive-image'; +import { type BreadcrumbItem } from '@components/molecules/nav/breadcrumb'; +import PageLayout, { + type PageLayoutProps, +} from '@components/templates/page/page-layout'; +import LegalNoticeContent, { meta } from '@content/pages/legal-notice.mdx'; +import { getFormattedDate } from '@utils/helpers/dates'; +import { loadTranslation } 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 { useRouter } from 'next/router'; +import Script from 'next/script'; +import { useIntl } from 'react-intl'; +import { Article, Graph, WebPage } from 'schema-dts'; + +/** + * Legal Notice page. + */ +const LegalNoticePage: NextPage = () => { + const intl = useIntl(); + 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: 'legal-notice', name: title, url: '/mentions-legales' }, + ]; + + 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 components: NestedMDXComponents = { + Image: (props) => <ResponsiveImage {...props} />, + Link: (props) => <Link {...props} />, + }; + + const { website } = useSettings(); + 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, + inLanguage: website.locales.default, + license: 'https://creativecommons.org/licenses/by-sa/4.0/deed.fr', + reviewedBy: { '@id': `${website.url}/#branding` }, + url: `${pageUrl}`, + isPartOf: { + '@id': `${website.url}`, + }, + }; + + const articleSchema: Article = { + '@id': `${website.url}/#legal-notice`, + '@type': 'Article', + name: title, + description: intro, + author: { '@id': `${website.url}/#branding` }, + copyrightYear: pagePublicationDate.getFullYear(), + creator: { '@id': `${website.url}/#branding` }, + dateCreated: pagePublicationDate.toISOString(), + dateModified: pageUpdateDate.toISOString(), + datePublished: pagePublicationDate.toISOString(), + editor: { '@id': `${website.url}/#branding` }, + headline: title, + inLanguage: website.locales.default, + 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 ( + <PageLayout + title={title} + intro={intro} + headerMeta={headerMeta} + breadcrumb={breadcrumb} + withToC={true} + > + <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={`${seo.title} - ${website.name}`} /> + <meta property="og:description" content={intro} /> + </Head> + <Script + id="schema-legal-notice" + type="application/ld+json" + dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} + /> + <LegalNoticeContent components={components} /> + </PageLayout> + ); +}; + +export const getStaticProps: GetStaticProps = async ({ locale }) => { + const translation = await loadTranslation(locale); + + return { + props: { + translation, + }, + }; +}; + +export default LegalNoticePage; diff --git a/src/ts/types/app.ts b/src/ts/types/app.ts index b09f3d5..4d9c71c 100644 --- a/src/ts/types/app.ts +++ b/src/ts/types/app.ts @@ -1,8 +1,14 @@ -export type AuthorKind = 'page' | 'comment'; +export type ContentKind = + | 'article' + | 'comment' + | 'page' + | 'project' + | 'thematic' + | 'topic'; -export type Author<T extends AuthorKind> = { +export type Author<T extends ContentKind> = { avatar?: Image; - description?: T extends 'page' ? string | undefined : never; + description?: T extends 'comment' ? never : string; name: string; website?: string; }; @@ -44,11 +50,11 @@ export type SEO = { title: string; }; -export type PageKind = 'article' | 'project' | 'thematic' | 'topic'; +export type PageKind = Exclude<ContentKind, 'comment'>; export type Meta<T extends PageKind> = { articles?: T extends 'thematic' | 'topic' ? Article[] : never; - author: Author<'page'>; + author?: T extends 'article' | 'page' ? Author<T> : never; commentsCount?: T extends 'article' ? number : never; cover?: Image; dates: Dates; diff --git a/src/ts/types/mdx.ts b/src/ts/types/mdx.ts new file mode 100644 index 0000000..6b72f21 --- /dev/null +++ b/src/ts/types/mdx.ts @@ -0,0 +1,20 @@ +import { StaticImageData } from 'next/image'; +import { Meta } from './app'; + +export type MDXData = { + file: string; + image: StaticImageData; +}; + +export type MDXPageMeta = Pick<Meta<'page'>, 'cover' | 'dates' | 'seo'> & { + intro: string; + title: string; +}; + +export type MDXProjectMeta = Omit< + Meta<'project'>, + 'readingTime' | 'wordsCount' +> & { + intro: string; + title: string; +}; diff --git a/src/ts/types/raw-data.ts b/src/ts/types/raw-data.ts index 43a2453..7e12e7f 100644 --- a/src/ts/types/raw-data.ts +++ b/src/ts/types/raw-data.ts @@ -3,7 +3,7 @@ */ import { NodeResponse, PageInfo } from '@services/graphql/api'; -import { AuthorKind } from './app'; +import { ContentKind } from './app'; export type ACFPosts = { postsInThematic?: RawThematicPreview[]; @@ -29,8 +29,8 @@ export type Info = { wordsCount: number; }; -export type RawAuthor<T extends AuthorKind> = { - description?: T extends 'page' ? string | undefined : never; +export type RawAuthor<T extends ContentKind> = { + description?: T extends 'comment' ? never : string; gravatarUrl?: string; name: string; url?: string; diff --git a/src/utils/helpers/author.ts b/src/utils/helpers/author.ts index cf125fc..40743ca 100644 --- a/src/utils/helpers/author.ts +++ b/src/utils/helpers/author.ts @@ -1,17 +1,17 @@ -import { type Author, type AuthorKind } from '@ts/types/app'; +import { type Author, type ContentKind } from '@ts/types/app'; import { type RawAuthor } from '@ts/types/raw-data'; /** * Convert author raw data to regular data. * - * @param {RawAuthor<AuthorKind>} data - The author raw data. - * @param {AuthorKind} kind - The author kind. Either `page` or `comment`. + * @param {RawAuthor<ContentKind>} data - The author raw data. + * @param {ContentKind} kind - The author kind. Either `page` or `comment`. * @param {number} [avatarSize] - The author avatar size. - * @returns {Author<AuthorKind>} The author data. + * @returns {Author<ContentKind>} The author data. */ export const getAuthorFromRawData = ( data: RawAuthor<typeof kind>, - kind: AuthorKind, + kind: ContentKind, avatarSize: number = 80 ): Author<typeof kind> => { const { name, description, gravatarUrl, url } = data; |
