From 3920657e6498c9e2a6319f25ca3130223f107cff Mon Sep 17 00:00:00 2001 From: Legrand Thomas Date: Tue, 30 Sep 2025 21:58:59 +0200 Subject: [PATCH] feat: Add thematics block to the thematic pages #313 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the "Nos thématiques" block to the thematic pages by fetching data from the /thematics endpoint. --- frontend/messages/fr/climate.json | 3 +- frontend/messages/fr/democracy.json | 3 +- frontend/messages/fr/social.json | 3 +- .../climate-and-biodiversity/climate.tsx | 14 +++- .../climate-and-biodiversity/page.tsx | 40 +++++++++- .../src/app/[locale]/democracy/democracy.tsx | 14 +++- frontend/src/app/[locale]/democracy/page.tsx | 18 ++++- .../src/app/[locale]/social-justice/page.tsx | 18 ++++- .../app/[locale]/social-justice/social.tsx | 15 +++- frontend/src/lib/formatters/thematics.ts | 22 ++++++ frontend/src/lib/toc.ts | 78 ------------------- 11 files changed, 131 insertions(+), 97 deletions(-) delete mode 100644 frontend/src/lib/toc.ts diff --git a/frontend/messages/fr/climate.json b/frontend/messages/fr/climate.json index c59f2ade..3c965e54 100644 --- a/frontend/messages/fr/climate.json +++ b/frontend/messages/fr/climate.json @@ -2,6 +2,7 @@ "climate": { "title": "Climat et biodiversité", "projectsTitle": "Nos projets", - "partners": "Partenaires" + "partners": "Partenaires", + "thematics": "Nos thématiques" } } diff --git a/frontend/messages/fr/democracy.json b/frontend/messages/fr/democracy.json index 1f089916..89489e2b 100644 --- a/frontend/messages/fr/democracy.json +++ b/frontend/messages/fr/democracy.json @@ -2,6 +2,7 @@ "democracy": { "title": "Démocratie", "projectsTitle": "Nos projets", - "partners": "Partenaires" + "partners": "Partenaires", + "thematics": "Nos thématiques" } } diff --git a/frontend/messages/fr/social.json b/frontend/messages/fr/social.json index 4fc8482a..3975d640 100644 --- a/frontend/messages/fr/social.json +++ b/frontend/messages/fr/social.json @@ -2,6 +2,7 @@ "social": { "title": "Justice sociale", "projectsTitle": "Nos projets", - "partners": "Partenaires" + "partners": "Partenaires", + "thematics": "Nos thématiques" } } diff --git a/frontend/src/app/[locale]/climate-and-biodiversity/climate.tsx b/frontend/src/app/[locale]/climate-and-biodiversity/climate.tsx index c850d2cd..6ce2db91 100644 --- a/frontend/src/app/[locale]/climate-and-biodiversity/climate.tsx +++ b/frontend/src/app/[locale]/climate-and-biodiversity/climate.tsx @@ -5,25 +5,29 @@ import { Kpis, PartnersBlock, ThematicHeroBlock, + ThematicsBlock, ThumbnailProjectsBlock, } from '@/components'; import { transformProjects, transformKpis, transformPartners, + transformThematics, } from '@/lib/formatters/thematics'; import { useTranslations } from 'next-intl'; -import { ThematicPageData } from './page'; +import { ThematicPageData, ThematicsData } from './page'; type ThematicsProps = { data: ThematicPageData; + thematicsData: ThematicsData; } -export default function SocialPage({ data }: ThematicsProps) { +export default function SocialPage({ data , thematicsData }: ThematicsProps) { const t = useTranslations('climate'); const partners = transformPartners(data.thematic?.funders); const kpis = transformKpis(data.thematic?.kpis); const projects = transformProjects(data.thematic?.projects); + const thematics = transformThematics(thematicsData); return ( <> @@ -64,6 +68,12 @@ export default function SocialPage({ data }: ThematicsProps) { partners={partners} className="my-lg" /> + + ); } diff --git a/frontend/src/app/[locale]/climate-and-biodiversity/page.tsx b/frontend/src/app/[locale]/climate-and-biodiversity/page.tsx index 33283dc0..7e0f57d6 100644 --- a/frontend/src/app/[locale]/climate-and-biodiversity/page.tsx +++ b/frontend/src/app/[locale]/climate-and-biodiversity/page.tsx @@ -56,14 +56,48 @@ async function fetchThematicPageData() { }); } +async function fetchThematics() { + return await client.GET('/thematics', { + params: { + query: { + populate: { + projects: { + populate: '*' + }, + partners: { + populate: '*' + }, + funders: { + populate: '*' + }, + kpis: { + populate: '*' + }, + image_1: { + fields: ["url"] + }, + image_2: { + fields: ["url"] + }, + thumbnail: { + fields: ["url"] + }, + } + }, + }, + }); +} + export type ThematicPageData = NonNullable>["data"]>["data"]>; +export type ThematicsData = NonNullable>["data"]>["data"]>; export default async function Page() { const { data } = await fetchThematicPageData(); + const { data: thematicsData } = await fetchThematics(); - if (!data?.data) { + if (!data?.data || !thematicsData?.data) { return null; } - return ; -}; \ No newline at end of file + return ; +}; diff --git a/frontend/src/app/[locale]/democracy/democracy.tsx b/frontend/src/app/[locale]/democracy/democracy.tsx index 3318cda4..60cf4f08 100644 --- a/frontend/src/app/[locale]/democracy/democracy.tsx +++ b/frontend/src/app/[locale]/democracy/democracy.tsx @@ -5,25 +5,29 @@ import { Kpis, PartnersBlock, ThematicHeroBlock, + ThematicsBlock, ThumbnailProjectsBlock, } from '@/components'; import { transformProjects, transformKpis, transformPartners, + transformThematics, } from '@/lib/formatters/thematics'; import { useTranslations } from 'next-intl'; -import { ThematicPageData } from './page'; +import { ThematicPageData , ThematicsData} from './page'; type ThematicsProps = { data: ThematicPageData; + thematicsData: ThematicsData; } -export default function SocialPage({ data }: ThematicsProps) { +export default function SocialPage({ data, thematicsData }: ThematicsProps) { const t = useTranslations('democracy'); const partners = transformPartners(data.thematic?.funders); const kpis = transformKpis(data.thematic?.kpis); const projects = transformProjects(data.thematic?.projects); + const thematics = transformThematics(thematicsData); return ( <> @@ -64,6 +68,12 @@ export default function SocialPage({ data }: ThematicsProps) { partners={partners} className="my-lg" /> + + ); } diff --git a/frontend/src/app/[locale]/democracy/page.tsx b/frontend/src/app/[locale]/democracy/page.tsx index d3e046e6..3fbfcf7d 100644 --- a/frontend/src/app/[locale]/democracy/page.tsx +++ b/frontend/src/app/[locale]/democracy/page.tsx @@ -56,14 +56,26 @@ async function fetchThematicPageData() { }); } +async function fetchThematics() { + return await client.GET('/thematics', { + params: { + query: { + populate: '*' + }, + }, + }); +} + export type ThematicPageData = NonNullable>["data"]>["data"]>; +export type ThematicsData = NonNullable>["data"]>["data"]>; export default async function Page() { const { data } = await fetchThematicPageData(); + const { data: thematicsData } = await fetchThematics(); - if (!data?.data) { + if (!data?.data || !thematicsData?.data) { return null; } - return ; -}; \ No newline at end of file + return ; +}; diff --git a/frontend/src/app/[locale]/social-justice/page.tsx b/frontend/src/app/[locale]/social-justice/page.tsx index 2428c5fd..99366d90 100644 --- a/frontend/src/app/[locale]/social-justice/page.tsx +++ b/frontend/src/app/[locale]/social-justice/page.tsx @@ -56,14 +56,26 @@ async function fetchThematicPageData() { }); } +async function fetchThematics() { + return await client.GET('/thematics', { + params: { + query: { + populate: '*' + }, + }, + }); +} + export type ThematicPageData = NonNullable>["data"]>["data"]>; +export type ThematicsData = NonNullable>["data"]>["data"]>; export default async function Page() { const { data } = await fetchThematicPageData(); + const { data: thematicsData } = await fetchThematics(); - if (!data?.data) { + if (!data?.data || !thematicsData?.data) { return null; } - return ; -}; \ No newline at end of file + return ; +}; diff --git a/frontend/src/app/[locale]/social-justice/social.tsx b/frontend/src/app/[locale]/social-justice/social.tsx index f7a3521d..8f612050 100644 --- a/frontend/src/app/[locale]/social-justice/social.tsx +++ b/frontend/src/app/[locale]/social-justice/social.tsx @@ -5,26 +5,29 @@ import { Kpis, PartnersBlock, ThematicHeroBlock, + ThematicsBlock, ThumbnailProjectsBlock, } from '@/components'; import { transformProjects, transformKpis, transformPartners, + transformThematics, } from '@/lib/formatters/thematics'; import { useTranslations } from 'next-intl'; -import { ThematicPageData } from './page'; +import { ThematicPageData, ThematicsData } from './page'; type ThematicsProps = { data: ThematicPageData; + thematicsData: ThematicsData; } - -export default function SocialPage({ data }: ThematicsProps) { +export default function SocialPage({ data, thematicsData }: ThematicsProps) { const t = useTranslations('social'); const partners = transformPartners(data.thematic?.funders); const kpis = transformKpis(data.thematic?.kpis); const projects = transformProjects(data.thematic?.projects); + const thematics = transformThematics(thematicsData); return ( <> @@ -65,6 +68,12 @@ export default function SocialPage({ data }: ThematicsProps) { partners={partners} className="my-lg" /> + + ({ ...t, id: t.id?.toString() || '' }))} + className="my-lg" + /> ); } diff --git a/frontend/src/lib/formatters/thematics.ts b/frontend/src/lib/formatters/thematics.ts index 43b8c31b..bc80afdb 100644 --- a/frontend/src/lib/formatters/thematics.ts +++ b/frontend/src/lib/formatters/thematics.ts @@ -1,4 +1,5 @@ import { ThematicPageData } from '@/app/[locale]/climate-and-biodiversity/page'; +import { ThematicsData } from '@/app/[locale]/social-justice/page'; export function transformPartners( partners: NonNullable @@ -47,3 +48,24 @@ export function transformProjects( ], })) ?? []; } + +export function transformThematics( + thematics: NonNullable +) { + return thematics?.map(thematic => ({ + title: { + children: thematic.name || '', + props: { + colors: `text-black bg-${thematic.color}`, + className: "drop-shadow-3 drop-shadow-black before:-z-1", + rotation: -2.58, + } + }, + id: thematic.id?.toString() || '', + talk: thematic.short_description || '', + talkOffset: 10, + image: thematic.thumbnail?.url || '', + ctaText: thematic.cta_text, + ctaLink: thematic.cta_link, + })).filter(thematic => thematic.talk) ?? []; +} diff --git a/frontend/src/lib/toc.ts b/frontend/src/lib/toc.ts deleted file mode 100644 index 9447491e..00000000 --- a/frontend/src/lib/toc.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { toc } from 'mdast-util-toc'; -import { remark } from 'remark'; -import { visit } from 'unist-util-visit'; - -const textTypes = ['text', 'emphasis', 'strong', 'inlineCode']; - -function flattenNode(node) { - const p = []; - visit(node, node => { - if (!textTypes.includes(node.type)) { - return; - } - p.push(node.value); - }); - return p.join(``); -} - -type Item = { - title: string; - url: string; - items?: Item[]; -}; - -type Items = { - items?: Item[]; -}; - -function getItems(node, current): Items { - if (!node) { - return {}; - } - - if (node.type === 'paragraph') { - visit(node, item => { - if (item.type === 'link') { - current.url = item.url; - current.title = flattenNode(node); - } - - if (item.type === 'text') { - current.title = flattenNode(node); - } - }); - - return current; - } - - if (node.type === 'list') { - current.items = node.children.map(i => getItems(i, {})); - - return current; - } else if (node.type === 'listItem') { - const heading = getItems(node.children[0], {}); - - if (node.children.length > 1) { - getItems(node.children[1], heading); - } - - return heading; - } - - return {}; -} - -const getToc = () => (node, file) => { - const table = toc(node); - file.data = getItems(table.map, {}); -}; - -export type TableOfContents = Items; - -export async function getTableOfContents( - content: string -): Promise { - const result = await remark().use(getToc).process(content); - - return result.data; -}