Skip to content
Merged
38 changes: 38 additions & 0 deletions src/app/(frontend)/api/editorial/import-oct11/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { importEditorialLaunchPack } from '@/endpoints/importEditorialLaunchPack'
import { october11EditorialArticles } from '@/editorial/october-11-2026'
import config from '@payload-config'
import { headers } from 'next/headers'
import { createLocalReq, getPayload } from 'payload'

export const maxDuration = 60

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: "Connecte-toi à Payload pour importer ces brouillons." }, { status: 403 })
}
try {
const req = await createLocalReq({ user }, payload)
const results = await importEditorialLaunchPack({
payload, req, onlySlugs: october11EditorialArticles.map(article => article.slug),
})
const created = results.filter(item => item.created).length
const translated = results.filter(item => item.englishCreated).length
return Response.json({
success: true,
results: results.map(item => ({ ...item, editUrl: '/admin/collections/posts/' + item.id })),
message: created > 0
? `${created} article(s) créé(s) en brouillon ; vérifier liens, traductions, graphiques et images avant publication.`
: translated > 0
? `${translated} traduction(s) anglaise(s) manquante(s) ajoutée(s), sans écraser les articles français.`
: "Les quatre articles existent déjà. Aucun texte existant n'a été écrasé.",
})
} catch (error) {
payload.logger.error({ err: error, message: 'Oct 11 editorial import failed' })
return Response.json({
success: false,
message: "Import interrompu. Certains brouillons peuvent avoir été créés : consulte Posts avant de relancer.",
}, { status: 500 })
}
}
33 changes: 33 additions & 0 deletions src/app/(frontend)/editorial/import-cybastion/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ImportCybastionButton } from '@/components/insights/ImportCybastionButton'
import { ImportEditorialLaunchPackButton } from '@/components/insights/ImportEditorialLaunchPackButton'
import { ImportOctober11Button } from '@/components/insights/ImportOctober11Button'
import { october11EditorialArticles } from '@/editorial/october-11-2026'
import { UpdateArticleReadabilityButton } from '@/components/insights/UpdateArticleReadabilityButton'
import { RefreshSeptemberEditorialButton } from '@/components/insights/RefreshSeptemberEditorialButton'
import { cybastionArticleMetadata, cybastionArticleSources } from '@/editorial/cybastion-data-center'
Expand Down Expand Up @@ -56,6 +58,37 @@ export default async function ImportCybastionPage() {
</h1>

<div className="grid gap-8">
<section className="rounded-md border border-emerald-200 bg-white p-8 shadow-[0_20px_50px_rgba(16,47,82,0.07)] md:p-10">
<p className="mb-3 text-xs font-extrabold uppercase tracking-[0.16em] text-[#1f6852]">
Édition du 11 octobre 2026 · Import ciblé
</p>
<h2 className="m-0 font-[var(--font-fraunces)] text-[clamp(28px,4vw,42px)] font-semibold leading-[1.08] tracking-[-0.035em] text-[#102f52]">
Quatre nouveaux articles pour Trigenys Insight.
</h2>
<p className="mt-4 max-w-[760px] text-[15px] leading-7 text-[#62686d]">
CITS 2026, transferts internationaux, Driivio et MORENA. Cet import ajoute uniquement
les quatre brouillons FR + EN avec les sources cliquables dans le texte,
les métadonnées SEO et les graphiques interactifs. Rien n&apos;est publié automatiquement.
</p>
<div className="mt-6 grid gap-3">
{october11EditorialArticles.map(article => (
<div key={article.slug} className="rounded-md border border-border bg-[#fafaf7] px-5 py-4">
<p className="m-0 text-[10px] font-extrabold uppercase tracking-[0.14em] text-[#8a8e91]">
{article.primaryCategory} · {article.readingTime} min · FR + EN
</p>
<p className="mb-0 mt-2 font-[var(--font-fraunces)] text-lg font-semibold text-[#102f52]">
{article.fr.title}
</p>
</div>
))}
</div>
<p className="mt-5 text-sm text-[#62686d]">
L&apos;import est idempotent : les articles existants ne seront pas écrasés.
Vérifie les données et ajoute les images de couverture dans Payload avant de publier.
</p>
<ImportOctober11Button />
</section>

<section className="rounded-md border border-border bg-white p-8 shadow-[0_20px_50px_rgba(16,47,82,0.07)] md:p-10">
<p className="eyebrow">Consommer camerounais · Comparatif FR + EN</p>
<h2 className="mt-3 font-[var(--font-fraunces)] text-3xl font-semibold text-[#102f52]">{whispArticle.fr.title}</h2>
Expand Down
52 changes: 52 additions & 0 deletions src/components/insights/ImportOctober11Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use client'

import Link from 'next/link'
import { useState } from 'react'

type Entry = { id: number; slug: string; title: string; editUrl: string; created: boolean; englishCreated: boolean }
type Result = { success: boolean; message?: string; results?: Entry[] }

export function ImportOctober11Button() {
const [pending, setPending] = useState(false)
const [result, setResult] = useState<Result | null>(null)

async function handleImport() {
setPending(true)
setResult(null)
try {
const res = await fetch('/api/editorial/import-oct11', { method: 'POST', credentials: 'include' })
const json = await res.json().catch(() => ({})) as Result
setResult({ ...json, success: res.ok && json.success })
} catch {
setResult({ success: false, message: "Erreur réseau. Vérifie Posts avant de relancer." })
} finally {
setPending(false)
}
}

return (
<div className="mt-7">
<button
className="inline-flex min-h-12 items-center justify-center rounded-md bg-[#102f52] px-6 py-3 text-sm font-bold text-white transition hover:bg-[#173f6c] disabled:opacity-60"
disabled={pending}
onClick={handleImport}
type="button"
>
{pending ? 'Import des 4 articles…' : 'Importer les 4 articles du 11 octobre (FR + EN)'}
</button>
{result && (
<div role="status" aria-live="polite" className={'mt-4 rounded-md border p-4 text-sm leading-6 ' +
(result.success ? 'border-emerald-200 bg-emerald-50 text-emerald-800' : 'border-red-200 bg-red-50 text-red-800')}>
<p>{result.message || (result.success ? 'Import terminé.' : 'Import impossible.')}</p>
{result.results?.map(item => (
<p key={item.slug} className="mt-2">
<Link className="font-semibold underline underline-offset-2" href={item.editUrl}>
Ouvrir « {item.title} » dans Payload →
</Link>
</p>
))}
</div>
)}
</div>
)
}
Loading
Loading