feat: migrate Payload CMS to MDX and harden static infrastructure
This commit is contained in:
@@ -16,7 +16,7 @@ import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { notFound, redirect } from 'next/navigation';
|
||||
import ProductEngagementTracker from '@/components/analytics/ProductEngagementTracker';
|
||||
import PayloadRichText from '@/components/PayloadRichText';
|
||||
import MDXContent from '@/components/MDXContent';
|
||||
|
||||
interface ProductPageProps {
|
||||
params: Promise<{
|
||||
@@ -266,15 +266,17 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Extract technical data natively from the Lexical AST for Schema.org
|
||||
let technicalItems = [];
|
||||
if (product.content?.root?.children) {
|
||||
const productTabsBlock = product.content.root.children.find(
|
||||
(node: any) => node.type === 'block' && node.fields?.blockType === 'productTabs',
|
||||
);
|
||||
if (productTabsBlock && productTabsBlock.fields?.technicalItems) {
|
||||
technicalItems = productTabsBlock.fields.technicalItems;
|
||||
try {
|
||||
const tabMatch =
|
||||
typeof product.content === 'string' &&
|
||||
product.content.match(/<Block type="productTabs" data={({.*?})}\s*\/>/s);
|
||||
if (tabMatch && tabMatch[1]) {
|
||||
const data = JSON.parse(tabMatch[1]);
|
||||
technicalItems = data.technicalItems || [];
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore JSON parse errors for AST
|
||||
}
|
||||
|
||||
const datasheetPath = getDatasheetPath(productSlug, locale);
|
||||
@@ -288,56 +290,17 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
? t(`categories.${categoryKey}.title`)
|
||||
: categoryFileSlug;
|
||||
|
||||
// Split content into Description and Technical Data
|
||||
const rootChildren = product.content?.root?.children || [];
|
||||
const technicalBlocks = rootChildren.filter(
|
||||
(node: any) =>
|
||||
node.type === 'block' &&
|
||||
(node.fields?.blockType === 'productTabs' ||
|
||||
node.fields?.blockType === 'productTechnicalData'),
|
||||
);
|
||||
let descriptionChildren = rootChildren.filter(
|
||||
(node: any) =>
|
||||
!(
|
||||
node.type === 'block' &&
|
||||
(node.fields?.blockType === 'productTabs' ||
|
||||
node.fields?.blockType === 'productTechnicalData')
|
||||
),
|
||||
);
|
||||
let descriptionContent = typeof product.content === 'string' ? product.content : '';
|
||||
let technicalContent = '';
|
||||
|
||||
// If no standalone description nodes, extract from the productTabs block's embedded content
|
||||
if (descriptionChildren.length === 0) {
|
||||
const tabsBlock = rootChildren.find(
|
||||
(node: any) => node.type === 'block' && node.fields?.blockType === 'productTabs',
|
||||
);
|
||||
if (tabsBlock?.fields?.content?.root?.children) {
|
||||
descriptionChildren = tabsBlock.fields.content.root.children.filter((node: any) => {
|
||||
// Filter out MDX parsing artifacts like `}>`
|
||||
if (node.type === 'paragraph' && node.children?.length === 1) {
|
||||
const text = node.children[0]?.text?.trim();
|
||||
return text !== '}>' && text !== '{' && text !== '}';
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (typeof product.content === 'string') {
|
||||
const tabBlockMatch = product.content.match(/<Block type="productTabs".*?\/>/s);
|
||||
if (tabBlockMatch) {
|
||||
descriptionContent = product.content.replace(tabBlockMatch[0], '');
|
||||
technicalContent = tabBlockMatch[0];
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[DEBUG PAGE] Slug: ${productSlug}, children count: ${descriptionChildren.length}`);
|
||||
|
||||
const descriptionContent = {
|
||||
root: {
|
||||
...product.content.root,
|
||||
children: descriptionChildren,
|
||||
},
|
||||
};
|
||||
|
||||
const technicalContent = {
|
||||
root: {
|
||||
...product.content.root,
|
||||
children: technicalBlocks,
|
||||
},
|
||||
};
|
||||
|
||||
const sidebar = (
|
||||
<ProductSidebar
|
||||
productName={product.frontmatter.title}
|
||||
@@ -461,17 +424,19 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
{/* Description Area Next to Sidebar */}
|
||||
<div className="lg:col-span-8">
|
||||
<div className="max-w-none prose prose-primary prose-base md:prose-lg xl:prose-xl mb-8 md:mb-16 pb-8 md:pb-16 border-b border-neutral-dark/5">
|
||||
{descriptionChildren.length > 0 ? (
|
||||
<PayloadRichText data={descriptionContent} />
|
||||
{descriptionContent ? (
|
||||
<MDXContent data={descriptionContent} />
|
||||
) : product.frontmatter.description ? (
|
||||
<p className="text-lg md:text-xl text-text-secondary leading-relaxed">
|
||||
{product.frontmatter.description}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
{product.application?.root?.children?.length > 0 && (
|
||||
{product.application && (
|
||||
<div className="mt-12">
|
||||
<PayloadRichText data={product.application} />
|
||||
<MDXContent
|
||||
data={typeof product.application === 'string' ? product.application : ''}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -484,7 +449,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
{/* Full-width Technical Data Below */}
|
||||
<div className="mt-8 md:mt-16 pt-8 md:pt-16 border-t-0">
|
||||
<div className="max-w-none prose prose-primary prose-lg md:prose-xl">
|
||||
<PayloadRichText data={technicalContent} />
|
||||
<MDXContent data={technicalContent} />
|
||||
</div>
|
||||
|
||||
{/* Datasheet Download Section */}
|
||||
|
||||
Reference in New Issue
Block a user