Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion redirects.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { editorialRubrics, localizedRubricPath } from './src/editorial/rubrics'
import type { NextConfig } from 'next'

export const redirects: NextConfig['redirects'] = async () => {
Expand Down Expand Up @@ -32,5 +33,25 @@ export const redirects: NextConfig['redirects'] = async () => {
source: '/:path((?!ie-incompatible.html$).*)', // all pages except the incompatibility page
}

return [legacyVercelHostRedirect, homepageRedirect, internetExplorerRedirect]
const legacyEditorialPaths = [
{ old: 'technology', rubric: 'tech-ai' },
{ old: 'business', rubric: 'business-finance' },
{ old: 'information-systems', rubric: 'tech-ai' },
{ old: 'africa', rubric: 'society-future' },
{ old: 'consommer-local', rubric: 'made-in-africa' },
] as const

const editorialRedirects = (['fr', 'en'] as const).flatMap((locale) =>
legacyEditorialPaths.map(({ old, rubric }) => {
const target = editorialRubrics.find((section) => section.id === rubric)
if (!target) throw new Error(`Unknown canonical rubric: ${rubric}`)
return {
source: `/${locale}/${old}`,
destination: localizedRubricPath(target, locale),
permanent: true,
}
}),
)

return [legacyVercelHostRedirect, homepageRedirect, ...editorialRedirects, internetExplorerRedirect]
}
36 changes: 26 additions & 10 deletions src/Header/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ import { BrandMark } from '@/components/insights/BrandMark'
import { LanguageSwitcher } from '@/components/insights/LanguageSwitcher'
import { localeFromPathname, withLocale } from '@/i18n/config'
import { getMessages } from '@/i18n/messages'
import { ArrowUpRight } from 'lucide-react'
import { ArrowUpRight, Menu, X } from 'lucide-react'
import { editorialRubrics, localizedRubricPath } from '@/editorial/rubrics'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import React from 'react'
import React, { useState } from 'react'

export function Header() {
const pathname = usePathname() || '/fr'
const locale = localeFromPathname(pathname)
const t = getMessages(locale)

const navItems = [
{ href: withLocale(locale, '/technology'), label: t.header.tech },
{ href: withLocale(locale, '/business'), label: t.header.business },
{ href: withLocale(locale, '/information-systems'), label: t.header.systems },
{ href: withLocale(locale, '/africa'), label: t.header.africa },
{ href: withLocale(locale, '/consommer-local'), label: t.header.consumeLocal },
{ href: withLocale(locale, '/methodology'), label: t.header.methodology },
]
const [openedFromPath, setOpenedFromPath] = useState<string | null>(null)
const menuOpen = openedFromPath === pathname
const navItems = editorialRubrics.map((rubric) => ({
href: localizedRubricPath(rubric, locale),
label: rubric.title[locale],
}))


return (
<header className="site-header">
Expand All @@ -36,11 +36,27 @@ export function Header() {
</nav>
<div className="site-header__actions">
<LanguageSwitcher locale={locale} />
<button
aria-controls="editorial-mobile-nav"
aria-expanded={menuOpen}
aria-label={menuOpen ? (locale === 'fr' ? 'Fermer le menu' : 'Close menu') : (locale === 'fr' ? 'Ouvrir le menu' : 'Open menu')}
className="site-header__menu-toggle"
onClick={() => setOpenedFromPath(menuOpen ? null : pathname)}
type="button"
>
{menuOpen ? <X aria-hidden="true" size={22} /> : <Menu aria-hidden="true" size={22} />}
</button>
<Link className="header-cta" href={withLocale(locale, '/#newsletter')}>
{t.header.brief} <ArrowUpRight aria-hidden="true" size={14} />
</Link>
</div>
</div>
<nav aria-label={t.header.navLabel} className={`site-header__mobile-nav${menuOpen ? ' is-open' : ''}`} id="editorial-mobile-nav">
{navItems.map((item) => (
<Link href={item.href} key={item.href} onClick={() => setOpenedFromPath(null)}>{item.label}</Link>
))}
<Link href={withLocale(locale, '/methodology')} onClick={() => setOpenedFromPath(null)}>{t.header.methodology}</Link>
</nav>
</header>
)
}
19 changes: 10 additions & 9 deletions src/app/(frontend)/(sitemaps)/pages-sitemap.xml/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ import config from '@payload-config'
import { unstable_cache } from 'next/cache'

import { SITE_IDENTITY } from '@/seo/structuredData'
import { editorialRubrics, localizedRubricPath } from '@/editorial/rubrics'

const localizedEditorialPaths = [
'',
'/posts',
'/technology',
'/business',
'/information-systems',
'/africa',
'/consommer-local',
'/consommer-local/cameroun',
'/consommer-local/afrique',
'/methodology',
Expand Down Expand Up @@ -47,12 +43,16 @@ const getPagesSitemap = unstable_cache(
const dateFallback = new Date().toISOString()
const locales = ['fr', 'en'] as const

const localizedEditorialPages = locales.flatMap((locale) =>
localizedEditorialPaths.map((path) => ({
const localizedEditorialPages = locales.flatMap((locale) => [
...localizedEditorialPaths.map((path) => ({
loc: `${SITE_URL}/${locale}${path}`,
lastmod: dateFallback,
})),
)
...editorialRubrics.map((rubric) => ({
loc: `${SITE_URL}${localizedRubricPath(rubric, locale)}`,
lastmod: dateFallback,
})),
])

// Generic CMS pages are not localized yet, so keep their canonical legacy URLs.
const cmsPages = results.docs
Expand All @@ -66,9 +66,10 @@ const getPagesSitemap = unstable_cache(

return [...localizedEditorialPages, ...cmsPages]
},
['pages-sitemap-v3'],
['pages-sitemap-v4'],
{
tags: ['pages-sitemap'],
revalidate: 86400,
},
)

Expand Down
75 changes: 75 additions & 0 deletions src/app/(frontend)/api/editorial/setup-rubriques/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import config from '@payload-config'
import { headers } from 'next/headers'
import { createLocalReq, getPayload } from 'payload'

import { editorialRubrics } from '@/editorial/rubrics'

// Authenticated, idempotent opt-in. No mutation happens on a frontend GET/build.
// Keeps legacy CMS categories and existing article relationships intact.
export async function POST(): Promise<Response> {
const payload = await getPayload({ config })
const { user } = await payload.auth({ headers: await headers() })
if (!user) {
return Response.json({ success: false, message: 'Authentification Payload requise.' }, { status: 403 })
}

try {
const req = await createLocalReq({ user }, payload)
const results = []

for (const rubric of editorialRubrics) {
const existingResult = await payload.find({
collection: 'categories',
depth: 0,
fallbackLocale: false,
locale: 'fr',
limit: 1,
overrideAccess: false,
req,
where: { slug: { equals: rubric.id } },
})
const existing = existingResult.docs[0]

if (existing) {
results.push({ slug: rubric.id, id: existing.id, action: 'already-exists' })
continue
}

const doc = await payload.create({
collection: 'categories',
locale: 'fr',
depth: 0,
overrideAccess: false,
req,
data: {
slug: rubric.id,
color: rubric.color,
title: rubric.title.fr,
manifesto: rubric.description.fr,
},
})

await payload.update({
collection: 'categories',
id: doc.id,
locale: 'en',
fallbackLocale: false,
depth: 0,
overrideAccess: false,
req,
data: { title: rubric.title.en, manifesto: rubric.description.en },
})

results.push({ slug: rubric.id, id: doc.id, action: 'created' })
}

return Response.json({
success: true,
results,
message: 'Les six rubriques FR/EN sont prêtes dans Payload. Aucun article existant n’a été modifié.',
})
} catch (error) {
payload.logger.error({ err: error, message: 'Editorial rubric setup failed' })
return Response.json({ success: false, message: 'Installation des rubriques impossible.' }, { status: 500 })
}
}
7 changes: 3 additions & 4 deletions src/app/(frontend)/en/africa/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'
import { permanentRedirect } from 'next/navigation'

// Backstop for old links; next.config also installs a 308 at the edge.
export default function Page() {
return <LocalizedDeskPage locale="en" desk="africa" />
permanentRedirect('/en/society-future')
}

export const metadata = localizedDeskMetadata('en', 'africa')
7 changes: 7 additions & 0 deletions src/app/(frontend)/en/built-in-africa/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="en" desk="made-in-africa" />
}

export const metadata = localizedDeskMetadata('en', 'made-in-africa')
7 changes: 7 additions & 0 deletions src/app/(frontend)/en/business-finance/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="en" desk="business-finance" />
}

export const metadata = localizedDeskMetadata('en', 'business-finance')
7 changes: 3 additions & 4 deletions src/app/(frontend)/en/business/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'
import { permanentRedirect } from 'next/navigation'

// Backstop for old links; next.config also installs a 308 at the edge.
export default function Page() {
return <LocalizedDeskPage locale="en" desk="business" />
permanentRedirect('/en/business-finance')
}

export const metadata = localizedDeskMetadata('en', 'business')
7 changes: 7 additions & 0 deletions src/app/(frontend)/en/education-careers/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="en" desk="education-careers" />
}

export const metadata = localizedDeskMetadata('en', 'education-careers')
7 changes: 7 additions & 0 deletions src/app/(frontend)/en/gaming-culture/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="en" desk="gaming-culture" />
}

export const metadata = localizedDeskMetadata('en', 'gaming-culture')
7 changes: 3 additions & 4 deletions src/app/(frontend)/en/information-systems/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'
import { permanentRedirect } from 'next/navigation'

// Backstop for old links; next.config also installs a 308 at the edge.
export default function Page() {
return <LocalizedDeskPage locale="en" desk="information-systems" />
permanentRedirect('/en/tech-ai')
}

export const metadata = localizedDeskMetadata('en', 'information-systems')
7 changes: 7 additions & 0 deletions src/app/(frontend)/en/society-future/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="en" desk="society-future" />
}

export const metadata = localizedDeskMetadata('en', 'society-future')
7 changes: 7 additions & 0 deletions src/app/(frontend)/en/tech-ai/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="en" desk="tech-ai" />
}

export const metadata = localizedDeskMetadata('en', 'tech-ai')
7 changes: 3 additions & 4 deletions src/app/(frontend)/en/technology/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'
import { permanentRedirect } from 'next/navigation'

// Backstop for old links; next.config also installs a 308 at the edge.
export default function Page() {
return <LocalizedDeskPage locale="en" desk="technology" />
permanentRedirect('/en/tech-ai')
}

export const metadata = localizedDeskMetadata('en', 'technology')
7 changes: 3 additions & 4 deletions src/app/(frontend)/fr/africa/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'
import { permanentRedirect } from 'next/navigation'

// Backstop for old links; next.config also installs a 308 at the edge.
export default function Page() {
return <LocalizedDeskPage locale="fr" desk="africa" />
permanentRedirect('/fr/societe-avenir')
}

export const metadata = localizedDeskMetadata('fr', 'africa')
7 changes: 7 additions & 0 deletions src/app/(frontend)/fr/business-finance/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="fr" desk="business-finance" />
}

export const metadata = localizedDeskMetadata('fr', 'business-finance')
7 changes: 3 additions & 4 deletions src/app/(frontend)/fr/business/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'
import { permanentRedirect } from 'next/navigation'

// Backstop for old links; next.config also installs a 308 at the edge.
export default function Page() {
return <LocalizedDeskPage locale="fr" desk="business" />
permanentRedirect('/fr/business-finance')
}

export const metadata = localizedDeskMetadata('fr', 'business')
7 changes: 7 additions & 0 deletions src/app/(frontend)/fr/etudes-carrieres/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="fr" desk="education-careers" />
}

export const metadata = localizedDeskMetadata('fr', 'education-careers')
7 changes: 7 additions & 0 deletions src/app/(frontend)/fr/gaming-culture/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="fr" desk="gaming-culture" />
}

export const metadata = localizedDeskMetadata('fr', 'gaming-culture')
7 changes: 3 additions & 4 deletions src/app/(frontend)/fr/information-systems/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'
import { permanentRedirect } from 'next/navigation'

// Backstop for old links; next.config also installs a 308 at the edge.
export default function Page() {
return <LocalizedDeskPage locale="fr" desk="information-systems" />
permanentRedirect('/fr/tech-ia')
}

export const metadata = localizedDeskMetadata('fr', 'information-systems')
7 changes: 7 additions & 0 deletions src/app/(frontend)/fr/made-in-africa/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="fr" desk="made-in-africa" />
}

export const metadata = localizedDeskMetadata('fr', 'made-in-africa')
7 changes: 7 additions & 0 deletions src/app/(frontend)/fr/societe-avenir/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="fr" desk="society-future" />
}

export const metadata = localizedDeskMetadata('fr', 'society-future')
7 changes: 7 additions & 0 deletions src/app/(frontend)/fr/tech-ia/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { LocalizedDeskPage, localizedDeskMetadata } from '@/components/insights/LocalizedDeskPage'

export default function Page() {
return <LocalizedDeskPage locale="fr" desk="tech-ai" />
}

export const metadata = localizedDeskMetadata('fr', 'tech-ai')
Loading
Loading