aboutsummaryrefslogtreecommitdiffstats
path: root/src/pages
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-12-01 13:26:44 +0100
committerArmand Philippot <git@armandphilippot.com>2023-12-01 17:23:19 +0100
commitdfdbf6cac1fe3719dc71e130129d28e04ba4e225 (patch)
treef865bdad53cef95bdfb10fc04174a0173ab36f15 /src/pages
parent5b762b1b669454a89899c4bdf6008027d9615acf (diff)
refactor(pages): refine Thematic pages
* add a table of contents (however posts heading are not included) * rename posts list section title * add a useThematic hook to refresh thematic contents * add a useThematicLists hook to refresh thematics list * add a `notIn` filter in thematics list fetcher to directly remove unwanted thematics * add Cypress tests
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/sujet/[slug].tsx2
-rw-r--r--src/pages/thematique/[slug].tsx181
2 files changed, 120 insertions, 63 deletions
diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx
index 483df48..185756b 100644
--- a/src/pages/sujet/[slug].tsx
+++ b/src/pages/sujet/[slug].tsx
@@ -97,7 +97,7 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
const getPageHeading = () => (
<>
- {cover ? <NextImage {...cover} className={styles.logo} /> : null}
+ {cover ? <NextImage {...cover} className={styles['topic-logo']} /> : null}
{title}
</>
);
diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx
index 487b18b..e290782 100644
--- a/src/pages/thematique/[slug].tsx
+++ b/src/pages/thematique/[slug].tsx
@@ -14,17 +14,24 @@ import {
PageHeader,
PageSidebar,
PageBody,
+ LoadingPage,
+ TocWidget,
+ Spinner,
} from '../../components';
import {
convertWPThematicPreviewToPageLink,
- convertWPThematicToThematic,
fetchAllThematicsSlugs,
fetchThematic,
fetchThematicsCount,
fetchThematicsList,
} from '../../services/graphql';
import styles from '../../styles/pages/blog.module.scss';
-import type { NextPageWithLayout, PageLink, Thematic } from '../../types';
+import type {
+ GraphQLConnection,
+ NextPageWithLayout,
+ WPThematic,
+ WPThematicPreview,
+} from '../../types';
import { CONFIG } from '../../utils/config';
import { ROUTES } from '../../utils/constants';
import {
@@ -33,29 +40,47 @@ import {
getSchemaJson,
getSinglePageSchema,
getWebPageSchema,
+ slugify,
} from '../../utils/helpers';
import { loadTranslation, type Messages } from '../../utils/helpers/server';
-import { useBreadcrumb } from '../../utils/hooks';
+import {
+ useBreadcrumb,
+ useHeadingsTree,
+ useThematic,
+ useThematicsList,
+} from '../../utils/hooks';
export type ThematicPageProps = {
- currentThematic: Thematic;
- thematics: PageLink[];
+ data: {
+ currentThematic: WPThematic;
+ otherThematics: GraphQLConnection<WPThematicPreview>;
+ totalThematics: number;
+ };
translation: Messages;
};
-const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({
- currentThematic,
- thematics,
-}) => {
- const { content, intro, meta, slug, title } = currentThematic;
- const { articles, dates, seo, relatedTopics } = meta;
+const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ data }) => {
const intl = useIntl();
+ const { isFallback } = useRouter();
+ const { isLoading, thematic } = useThematic(
+ data.currentThematic.slug,
+ data.currentThematic
+ );
+ const { isLoading: areThematicsLoading, thematics } = useThematicsList({
+ fallback: data.otherThematics,
+ input: { first: data.totalThematics, where: { notIn: [thematic.id] } },
+ });
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
- title,
- url: `${ROUTES.THEMATICS}/${slug}`,
+ title: data.currentThematic.title,
+ url: `${ROUTES.THEMATICS}/${data.currentThematic.slug}`,
});
+ const { ref, tree } = useHeadingsTree({ fromLevel: 2 });
+
+ if (isFallback || isLoading) return <LoadingPage />;
+
+ const { content, intro, meta, slug, title } = thematic;
+ const { articles, dates, seo, relatedTopics } = meta;
- const { asPath } = useRouter();
const webpageSchema = getWebPageSchema({
description: seo.description,
locale: CONFIG.locales.defaultLocale,
@@ -74,18 +99,41 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({
});
const schemaJsonLd = getSchemaJson([webpageSchema, articleSchema]);
- const thematicsListTitle = intl.formatMessage({
- defaultMessage: 'Other thematics',
- description: 'ThematicPage: other thematics list widget title',
- id: 'KVSWGP',
- });
+ const messages = {
+ widgets: {
+ loadingThematicsList: intl.formatMessage({
+ defaultMessage: 'Thematics are loading...',
+ description: 'ThematicPage: loading thematics message',
+ id: 'rVoW4G',
+ }),
+ thematicsListTitle: intl.formatMessage({
+ defaultMessage: 'Other thematics',
+ description: 'ThematicPage: other thematics list widget title',
+ id: 'KVSWGP',
+ }),
+ tocTitle: intl.formatMessage({
+ defaultMessage: 'Table of Contents',
+ description: 'PageLayout: table of contents title',
+ id: 'eys2uX',
+ }),
+ topicsListTitle: intl.formatMessage({
+ defaultMessage: 'Related topics',
+ description: 'ThematicPage: related topics list widget title',
+ id: '/42Z0z',
+ }),
+ },
+ browsePostsTitle: intl.formatMessage(
+ {
+ defaultMessage: 'Browse posts in {thematicName} thematic',
+ description: 'ThematicPage: posts list heading',
+ id: 'jrRBeb',
+ },
+ { thematicName: title }
+ ),
+ };
- const topicsListTitle = intl.formatMessage({
- defaultMessage: 'Related topics',
- description: 'ThematicPage: related topics list widget title',
- id: '/42Z0z',
- });
- const pageUrl = `${CONFIG.url}${asPath}`;
+ const pageUrl = `${CONFIG.url}${slug}`;
+ const browsePostHeadingId = slugify(messages.browsePostsTitle);
return (
<Page breadcrumbs={breadcrumbItems}>
@@ -121,23 +169,33 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({
updateDate: dates.update,
}}
/>
- <PageBody className={styles.body}>
- {/*eslint-disable-next-line react/no-danger -- Necessary for content*/}
- <div dangerouslySetInnerHTML={{ __html: content }} />
+ <PageSidebar>
+ <TocWidget
+ heading={<Heading level={2}>{messages.widgets.tocTitle}</Heading>}
+ tree={[
+ ...tree,
+ {
+ children: [],
+ depth: 2,
+ id: browsePostHeadingId,
+ label: messages.browsePostsTitle,
+ },
+ ]}
+ />
+ </PageSidebar>
+ <PageBody>
+ <div
+ className={styles.body}
+ // eslint-disable-next-line react/no-danger -- Necessary for content
+ dangerouslySetInnerHTML={{ __html: content }}
+ ref={ref}
+ />
{articles ? (
<>
- <Heading level={2}>
- {intl.formatMessage(
- {
- defaultMessage: 'All posts in {thematicName}',
- description: 'ThematicPage: posts list heading',
- id: 'LszkU6',
- },
- { thematicName: title }
- )}
+ <Heading id={browsePostHeadingId} level={2}>
+ {messages.browsePostsTitle}
</Heading>
<PostsList
- className={styles.list}
posts={getPostsWithUrl(articles)}
headingLvl={3}
sortByYear
@@ -146,20 +204,24 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({
) : null}
</PageBody>
<PageSidebar>
- <LinksWidget
- heading={
- <Heading isFake level={3}>
- {thematicsListTitle}
- </Heading>
- }
- items={getLinksItemData(thematics)}
- />
+ {areThematicsLoading ? (
+ <Spinner>{messages.widgets.loadingThematicsList}</Spinner>
+ ) : (
+ <LinksWidget
+ heading={
+ <Heading level={2}>{messages.widgets.thematicsListTitle}</Heading>
+ }
+ items={getLinksItemData(
+ thematics.edges.map((edge) =>
+ convertWPThematicPreviewToPageLink(edge.node)
+ )
+ )}
+ />
+ )}
{relatedTopics ? (
<LinksWidget
heading={
- <Heading isFake level={3}>
- {topicsListTitle}
- </Heading>
+ <Heading level={2}>{messages.widgets.topicsListTitle}</Heading>
}
items={getLinksItemData(relatedTopics)}
/>
@@ -179,26 +241,21 @@ export const getStaticProps: GetStaticProps<ThematicPageProps> = async ({
locale,
params,
}) => {
- const currentThematic = await fetchThematic((params as ThematicParams).slug);
+ const thematic = await fetchThematic((params as ThematicParams).slug);
const totalThematics = await fetchThematicsCount();
- const allThematicsEdges = await fetchThematicsList({
+ const otherThematics = await fetchThematicsList({
first: totalThematics,
+ where: { notIn: [thematic.databaseId] },
});
- const allThematics = allThematicsEdges.edges.map((edge) =>
- convertWPThematicPreviewToPageLink(edge.node)
- );
- const allThematicsLinks = allThematics.filter(
- (thematic) =>
- thematic.url !== `${ROUTES.THEMATICS}/${(params as ThematicParams).slug}`
- );
const translation = await loadTranslation(locale);
return {
props: {
- currentThematic: JSON.parse(
- JSON.stringify(convertWPThematicToThematic(currentThematic))
- ),
- thematics: JSON.parse(JSON.stringify(allThematicsLinks)),
+ data: {
+ currentThematic: JSON.parse(JSON.stringify(thematic)),
+ otherThematics: JSON.parse(JSON.stringify(otherThematics)),
+ totalThematics,
+ },
translation,
},
};