feat: complete MDX migration, stabilize environment & verify via E2E tests
Former-commit-id: ec3e64156a2e182535cbdcf0d975cd54603a517d
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
export default function DatenschutzPage() {
|
||||
return (
|
||||
<div className="pt-32 pb-24">
|
||||
<div className="container max-w-3xl">
|
||||
<h1 className="text-5xl font-heading font-extrabold text-neutral-dark mb-8">Datenschutzerklärung</h1>
|
||||
<div className="prose prose-neutral max-w-none">
|
||||
<h3>1. Datenschutz auf einen Blick</h3>
|
||||
<p>
|
||||
Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen.
|
||||
Die vollständige Datenschutzerklärung der E-TIB GmbH wird aktuell überarbeitet und an dieser Stelle zur Verfügung gestellt.
|
||||
</p>
|
||||
<h3>2. Allgemeine Hinweise und Pflichtinformationen</h3>
|
||||
<p>
|
||||
Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend der gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerklärung.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
export default function ImpressumPage() {
|
||||
return (
|
||||
<div className="pt-32 pb-24">
|
||||
<div className="container max-w-3xl">
|
||||
<h1 className="text-5xl font-heading font-extrabold text-neutral-dark mb-8">Impressum</h1>
|
||||
<div className="prose prose-neutral max-w-none">
|
||||
<p><strong>Angaben gemäß § 5 TMG</strong></p>
|
||||
<p>
|
||||
E-TIB GmbH<br />
|
||||
Gewerbestraße 22<br />
|
||||
03172 Guben
|
||||
</p>
|
||||
<p>
|
||||
<strong>Vertreten durch:</strong><br />
|
||||
Geschäftsführer: Danny Joseph
|
||||
</p>
|
||||
<p>
|
||||
<strong>Kontakt:</strong><br />
|
||||
Telefon: +49 15207230518<br />
|
||||
E-Mail: d.joseph@e-tib.com
|
||||
</p>
|
||||
<p>
|
||||
<strong>Registereintrag:</strong><br />
|
||||
Eintragung im Handelsregister.<br />
|
||||
Registergericht: Amtsgericht Cottbus<br />
|
||||
Registernummer: HRB 12403 CB
|
||||
</p>
|
||||
<p>
|
||||
<strong>Umsatzsteuer-ID:</strong><br />
|
||||
Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:<br />
|
||||
DE304799919
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,10 +4,23 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import { getPageBySlug } from '@/lib/pages';
|
||||
import { mapSlugToFileSlug, mapFileSlugToTranslated } from '@/lib/slugs';
|
||||
import PayloadRichText from '@/components/PayloadRichText';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { SITE_URL } from '@/lib/schema';
|
||||
import TrackedLink from '@/components/analytics/TrackedLink';
|
||||
|
||||
// Import components used in MDX
|
||||
import { HeroVideo as HomeHero } from '@/components/blocks/HeroVideo';
|
||||
import { SubCompanyTiles as HomeSubCompanyTiles } from '@/components/blocks/SubCompanyTiles';
|
||||
import { CompetenceBentoGrid as HomeCompetenceBentoGrid } from '@/components/blocks/CompetenceBentoGrid';
|
||||
import { ReferencesSlider as HomeReferencesSlider } from '@/components/blocks/ReferencesSlider';
|
||||
|
||||
const mdxComponents = {
|
||||
HomeHero,
|
||||
HomeSubCompanyTiles,
|
||||
HomeCompetenceBentoGrid,
|
||||
HomeReferencesSlider,
|
||||
};
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{
|
||||
locale: string;
|
||||
@@ -72,18 +85,20 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Redirect if accessed via a different locale's slug
|
||||
// Redirect logic removed to simplify for now, as slugs match file names
|
||||
/*
|
||||
const fileSlug = await mapSlugToFileSlug(pageData.slug || slug, locale);
|
||||
const correctSlug = await mapFileSlugToTranslated(fileSlug, locale);
|
||||
if (correctSlug && correctSlug !== slug) {
|
||||
redirect(`/${locale}/${correctSlug}`);
|
||||
}
|
||||
*/
|
||||
|
||||
// Full-bleed pages render blocks edge-to-edge without the generic article wrapper
|
||||
if (pageData.frontmatter.layout === 'fullBleed') {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<PayloadRichText data={pageData.content} className="" />
|
||||
<MDXRemote source={pageData.content} components={mdxComponents} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -120,9 +135,9 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main content with shared blog components */}
|
||||
{/* Main content */}
|
||||
<div className="prose prose-lg md:prose-xl max-w-none prose-headings:font-bold prose-headings:text-text-primary prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-img:rounded-2xl prose-img:shadow-2xl prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:rounded-r-2xl prose-strong:text-primary">
|
||||
<PayloadRichText data={pageData.content} />
|
||||
<MDXRemote source={pageData.content} components={mdxComponents} />
|
||||
</div>
|
||||
|
||||
{/* Support Section */}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
getPostBySlug,
|
||||
getAdjacentPosts,
|
||||
getReadingTime,
|
||||
extractLexicalHeadings,
|
||||
getHeadings,
|
||||
} from '@/lib/blog';
|
||||
import { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
@@ -16,9 +16,7 @@ import TableOfContents from '@/components/blog/TableOfContents';
|
||||
import { Heading } from '@/components/ui';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import BlogEngagementTracker from '@/components/analytics/BlogEngagementTracker';
|
||||
|
||||
// Payload CMS Imports
|
||||
import PayloadRichText from '@/components/PayloadRichText';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
|
||||
interface BlogPostProps {
|
||||
params: Promise<{
|
||||
@@ -65,20 +63,9 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// If the user accessed this post using a slug from a different locale
|
||||
// (e.g. via the generic language switcher), redirect them to the correct localized slug URL
|
||||
if (post.slug && post.slug !== slug) {
|
||||
redirect(`/${locale}/blog/${post.slug}`);
|
||||
}
|
||||
|
||||
const { prev, next, isPrevRandom, isNextRandom } = await getAdjacentPosts(post.slug, locale);
|
||||
|
||||
// Convert Lexical content into a plain string to estimate reading time roughly
|
||||
// Extract headings for TOC
|
||||
const headings = extractLexicalHeadings(post.content?.root || post.content);
|
||||
|
||||
// Convert Lexical content into a plain string to estimate reading time roughly
|
||||
const rawTextContent = JSON.stringify(post.content);
|
||||
const headings = getHeadings(post.content);
|
||||
const readingTime = getReadingTime(post.content);
|
||||
|
||||
return (
|
||||
<article className="bg-white min-h-screen font-sans selection:bg-primary/10 selection:text-primary">
|
||||
@@ -86,7 +73,7 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
title={post.frontmatter.title}
|
||||
slug={slug}
|
||||
category={post.frontmatter.category}
|
||||
readingTime={getReadingTime(rawTextContent)}
|
||||
readingTime={readingTime}
|
||||
/>
|
||||
|
||||
{/* Featured Image Header */}
|
||||
@@ -131,7 +118,7 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
})}
|
||||
</time>
|
||||
<span className="w-1 h-1 bg-white/30 rounded-full" />
|
||||
<span>{getReadingTime(rawTextContent)} min read</span>
|
||||
<span>{readingTime} min read</span>
|
||||
{(new Date(post.frontmatter.date) > new Date() ||
|
||||
post.frontmatter.public === false) && (
|
||||
<>
|
||||
@@ -168,7 +155,7 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
})}
|
||||
</time>
|
||||
<span className="w-1 h-1 bg-neutral-400 rounded-full" />
|
||||
<span>{getReadingTime(rawTextContent)} min read</span>
|
||||
<span>{readingTime} min read</span>
|
||||
{(new Date(post.frontmatter.date) > new Date() ||
|
||||
post.frontmatter.public === false) && (
|
||||
<>
|
||||
@@ -197,9 +184,9 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main content with enhanced styling rendering Payload Lexical */}
|
||||
{/* Main content */}
|
||||
<div className="prose prose-lg md:prose-xl max-w-none prose-headings:font-bold prose-headings:text-text-primary prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-img:rounded-2xl prose-img:shadow-2xl prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:rounded-r-2xl prose-strong:text-primary">
|
||||
<PayloadRichText data={post.content} />
|
||||
<MDXRemote source={post.content} />
|
||||
</div>
|
||||
|
||||
{/* Power CTA */}
|
||||
@@ -284,8 +271,8 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
||||
'@id': `${SITE_URL}/${locale}/blog/${slug}`,
|
||||
},
|
||||
articleSection: post.frontmatter.category,
|
||||
wordCount: rawTextContent.split(/\s+/).length,
|
||||
timeRequired: `PT${getReadingTime(rawTextContent)}M`,
|
||||
wordCount: post.content.split(/\s+/).length,
|
||||
timeRequired: `PT${readingTime}M`,
|
||||
} as any
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -62,7 +62,8 @@ export default async function ContactPage({ params }: ContactPageProps) {
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale, namespace: 'Contact' });
|
||||
|
||||
// Get translated slug to redirect if user used incorrect static slug
|
||||
// Get translated slug - redirect logic removed to prevent loop with next.config.js rewrites
|
||||
/*
|
||||
const { headers } = await import('next/headers');
|
||||
const headersList = await headers();
|
||||
const urlPath = headersList.get('x-invoke-path') || '';
|
||||
@@ -75,6 +76,7 @@ export default async function ContactPage({ params }: ContactPageProps) {
|
||||
redirect(`/${locale}/${contactSlugDe}`);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-neutral-light">
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Footer } from '@/components/layout/Footer';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import JsonLd from '@/components/JsonLd';
|
||||
import SkipLink from '@/components/SkipLink';
|
||||
import CMSConnectivityNotice from '@/components/CMSConnectivityNotice';
|
||||
import AnalyticsShell from '@/components/analytics/AnalyticsShell';
|
||||
import { Metadata, Viewport } from 'next';
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
@@ -12,8 +11,7 @@ import { SITE_URL } from '@/lib/schema';
|
||||
import FeedbackClientWrapper from '@/components/FeedbackClientWrapper';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import { mapFileSlugToTranslated } from '@/lib/slugs';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
@@ -76,36 +74,34 @@ export default async function Layout(props: {
|
||||
const safeLocale = supportedLocales.includes(localeStr) ? localeStr : 'en';
|
||||
|
||||
setRequestLocale(safeLocale);
|
||||
|
||||
|
||||
let messages: Record<string, any> = {};
|
||||
try {
|
||||
messages = await getMessages();
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error('[Layout] Failed to load messages:', err);
|
||||
messages = {};
|
||||
}
|
||||
|
||||
// Fetch navigation and company info from Payload Global Settings
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
const settings = await payload.findGlobal({
|
||||
slug: 'settings',
|
||||
locale: safeLocale as any,
|
||||
}).catch(() => null);
|
||||
|
||||
const navLinks = (settings?.mainNav && settings.mainNav.length > 0)
|
||||
? settings.mainNav.map((n) => ({
|
||||
label: n.label,
|
||||
url: n.url,
|
||||
}))
|
||||
: [
|
||||
{ label: 'Kompetenzen', url: `/${safeLocale}/kompetenzen` },
|
||||
{ label: 'Über uns', url: `/${safeLocale}/ueber-uns` },
|
||||
{ label: 'Karriere & Messen', url: `/${safeLocale}/karriere` }
|
||||
];
|
||||
|
||||
|
||||
const navLinks = [
|
||||
{
|
||||
label: safeLocale === 'de' ? 'Kompetenzen' : 'Competencies',
|
||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('kompetenzen', safeLocale)}`
|
||||
},
|
||||
{
|
||||
label: safeLocale === 'de' ? 'Über uns' : 'About Us',
|
||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('ueber-uns', safeLocale)}`
|
||||
},
|
||||
{
|
||||
label: safeLocale === 'de' ? 'Karriere & Messen' : 'Careers & Events',
|
||||
url: `/${safeLocale}/${await mapFileSlugToTranslated('karriere', safeLocale)}`
|
||||
}
|
||||
];
|
||||
|
||||
const companyInfo = {
|
||||
contactEmail: settings?.contactEmail || 'info@e-tib.com',
|
||||
contactPhone: settings?.contactPhone || '+49 15207230518',
|
||||
address: settings?.address || 'Gewerbestraße 22\\n03172 Guben'
|
||||
contactEmail: 'info@e-tib.com',
|
||||
contactPhone: '+49 15207230518',
|
||||
address: 'Gewerbestraße 22\n03172 Guben'
|
||||
};
|
||||
|
||||
// Pick only the namespaces required by client components to reduce the hydration payload size
|
||||
@@ -185,7 +181,6 @@ export default async function Layout(props: {
|
||||
|
||||
<Footer companyInfo={companyInfo} />
|
||||
<JsonLd />
|
||||
<CMSConnectivityNotice />
|
||||
<AnalyticsShell />
|
||||
{feedbackEnabled && <FeedbackClientWrapper feedbackEnabled={feedbackEnabled} />}
|
||||
</NextIntlClientProvider>
|
||||
|
||||
@@ -1,87 +1,19 @@
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Container, Button, Heading } from '@/components/ui';
|
||||
import Scribble from '@/components/Scribble';
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import { headers } from 'next/headers';
|
||||
import ClientNotFoundTracker from '@/components/analytics/ClientNotFoundTracker';
|
||||
|
||||
export default async function NotFound() {
|
||||
console.log('--- NOT FOUND RENDERED ---');
|
||||
const t = await getTranslations('Error.notFound');
|
||||
|
||||
// Try to determine the requested path
|
||||
const headersList = await headers();
|
||||
const urlPath = headersList.get('x-invoke-path') || '';
|
||||
|
||||
// Suggested URL logic removed along with Payload CMS
|
||||
let suggestedUrl = null;
|
||||
let suggestedLang = null;
|
||||
|
||||
// If we have a path, try to see if the last segment (slug) exists in ANY locale
|
||||
if (urlPath) {
|
||||
const slug = urlPath.split('/').filter(Boolean).pop();
|
||||
if (slug) {
|
||||
try {
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
|
||||
// Check posts
|
||||
const postRes = await payload.find({
|
||||
collection: 'posts',
|
||||
where: { slug: { equals: slug } },
|
||||
locale: 'all',
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
// Check competences
|
||||
const competenceRes =
|
||||
postRes.docs.length === 0
|
||||
? await payload.find({
|
||||
collection: 'competences',
|
||||
where: { slug: { equals: slug } },
|
||||
locale: 'all',
|
||||
limit: 1,
|
||||
})
|
||||
: { docs: [] };
|
||||
|
||||
// Check pages
|
||||
const pageRes =
|
||||
postRes.docs.length === 0 && competenceRes.docs.length === 0
|
||||
? await payload.find({
|
||||
collection: 'pages',
|
||||
where: { slug: { equals: slug } },
|
||||
locale: 'all',
|
||||
limit: 1,
|
||||
})
|
||||
: { docs: [] };
|
||||
|
||||
const anyDoc = postRes.docs[0] || competenceRes.docs[0] || pageRes.docs[0];
|
||||
|
||||
if (anyDoc) {
|
||||
// If the doc exists, we can figure out its native locale or
|
||||
// offer the alternative locale (if we are in 'de', offer 'en')
|
||||
const currentLocale = urlPath.startsWith('/en') ? 'en' : 'de';
|
||||
const alternativeLocale = currentLocale === 'de' ? 'en' : 'de';
|
||||
|
||||
suggestedLang = alternativeLocale === 'de' ? 'Deutsch' : 'English';
|
||||
|
||||
// Reconstruct the URL for the alternative locale
|
||||
const pathParts = urlPath.split('/').filter(Boolean);
|
||||
if (pathParts.length > 0 && (pathParts[0] === 'en' || pathParts[0] === 'de')) {
|
||||
pathParts[0] = alternativeLocale;
|
||||
} else {
|
||||
pathParts.unshift(alternativeLocale);
|
||||
}
|
||||
suggestedUrl = '/' + pathParts.join('/');
|
||||
}
|
||||
} catch {
|
||||
// Ignore Payload errors in 404
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ClientNotFoundTracker path={urlPath} />
|
||||
<ClientNotFoundTracker path="" />
|
||||
<Container className="relative py-24 flex flex-col items-center justify-center text-center min-h-[70vh] overflow-hidden">
|
||||
{/* Industrial Background Element */}
|
||||
<div className="absolute inset-0 -z-10 opacity-[0.03] pointer-events-none flex items-center justify-center">
|
||||
|
||||
@@ -1,80 +1,52 @@
|
||||
import { Metadata } from 'next';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { getPayload } from 'payload';
|
||||
import configPromise from '@payload-config';
|
||||
import PayloadRichText from '@/components/PayloadRichText';
|
||||
import { getMdxContent } from '@/lib/mdx';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
|
||||
// Import components used in MDX
|
||||
import { HeroVideo as HomeHero } from '@/components/blocks/HeroVideo';
|
||||
import { SubCompanyTiles as HomeSubCompanyTiles } from '@/components/blocks/SubCompanyTiles';
|
||||
import { CompetenceBentoGrid as HomeCompetenceBentoGrid } from '@/components/blocks/CompetenceBentoGrid';
|
||||
import { ReferencesSlider as HomeReferencesSlider } from '@/components/blocks/ReferencesSlider';
|
||||
|
||||
const mdxComponents = {
|
||||
HomeHero,
|
||||
HomeSubCompanyTiles,
|
||||
HomeCompetenceBentoGrid,
|
||||
HomeReferencesSlider,
|
||||
};
|
||||
|
||||
export async function generateMetadata(props: any): Promise<Metadata> {
|
||||
const { locale } = await props.params;
|
||||
if (locale !== 'de' && locale !== 'en') return {};
|
||||
|
||||
const mdx = await getMdxContent(locale, 'home');
|
||||
|
||||
return {
|
||||
title: locale === 'de' ? 'Startseite' : 'Home',
|
||||
title: mdx?.frontmatter?.title || (locale === 'de' ? 'Startseite' : 'Home'),
|
||||
description: mdx?.frontmatter?.description,
|
||||
};
|
||||
}
|
||||
|
||||
export default async function Home(props: any) {
|
||||
const { locale } = await props.params;
|
||||
|
||||
// Guard against invalid locales (e.g. favicon.ico) hitting the DB
|
||||
if (locale !== 'de' && locale !== 'en') {
|
||||
notFound();
|
||||
}
|
||||
|
||||
setRequestLocale(locale);
|
||||
|
||||
const payload = await getPayload({ config: configPromise });
|
||||
|
||||
// 1. Fetch references for the ReferencesSlider (passed globally to PayloadRichText)
|
||||
const { docs: references } = await payload.find({
|
||||
collection: 'references',
|
||||
locale: locale as any,
|
||||
limit: 10,
|
||||
});
|
||||
const mdx = await getMdxContent(locale, 'home');
|
||||
|
||||
const mappedReferences = references.map((ref: any) => ({
|
||||
id: ref.id,
|
||||
title: ref.title,
|
||||
slug: ref.slug,
|
||||
category: ref.category,
|
||||
image: ref.image,
|
||||
}));
|
||||
|
||||
// 2. Fetch the Home page content
|
||||
const { docs: pages } = await payload.find({
|
||||
collection: 'pages',
|
||||
where: {
|
||||
slug: {
|
||||
equals: 'home',
|
||||
},
|
||||
},
|
||||
locale: locale as any,
|
||||
limit: 1,
|
||||
draft: true, // Allow finding drafts in development
|
||||
});
|
||||
|
||||
const page = pages[0];
|
||||
|
||||
// Fallback to static components if page doesn't exist in CMS yet
|
||||
// This prevents the site from breaking before the first seed/save
|
||||
if (!page) {
|
||||
console.warn(`[Home] Page slug "home" not found in CMS for locale "${locale}".`);
|
||||
const { totalDocs: allPages } = await payload.find({ collection: 'pages', locale: 'all' });
|
||||
console.log(`[Home] Total pages in CMS (all locales): ${allPages}`);
|
||||
return (
|
||||
<main className="min-h-screen flex items-center justify-center bg-neutral-dark text-white p-10 text-center">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold mb-4">Willkommen bei E-TIB</h1>
|
||||
<p className="text-xl text-white/60">Die CMS-Inhalte für diese Seite müssen noch angelegt werden.</p>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
if (!mdx) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<PayloadRichText data={page.content} references={mappedReferences} />
|
||||
<MDXRemote source={mdx.content} components={mdxComponents} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user