feat: Page Editor Phase 1 - React DynamicPageRenderer

- Add DynamicPageRenderer component for structural pages and CPT content
- Add 6 section components:
  - HeroSection with multiple layout variants
  - ContentSection for rich text/HTML content
  - ImageTextSection with image-left/right layouts
  - FeatureGridSection with grid-2/3/4 layouts
  - CTABannerSection with color schemes
  - ContactFormSection with webhook POST and redirect
- Add dynamic routes to App.tsx for /:slug and /:pathBase/:slug
- Build customer-spa successfully
This commit is contained in:
Dwindi Ramadhana
2026-01-11 22:35:15 +07:00
parent 9331989102
commit 749cfb3f92
8 changed files with 748 additions and 2 deletions

View File

@@ -0,0 +1,75 @@
import { cn } from '@/lib/utils';
interface ImageTextSectionProps {
id: string;
layout?: string;
colorScheme?: string;
title?: string;
text?: string;
image?: string;
}
export function ImageTextSection({
id,
layout = 'image-left',
colorScheme = 'default',
title,
text,
image,
}: ImageTextSectionProps) {
const isImageLeft = layout === 'image-left' || layout === 'left';
const isImageRight = layout === 'image-right' || layout === 'right';
return (
<section
id={id}
className={cn(
'wn-section wn-image-text',
`wn-image-text--${layout}`,
`wn-scheme--${colorScheme}`,
'py-16 md:py-24',
{
'bg-white': colorScheme === 'default',
'bg-muted': colorScheme === 'muted',
'bg-primary/5': colorScheme === 'primary',
}
)}
>
<div className="container mx-auto px-4">
<div className={cn(
'flex flex-col md:flex-row items-center gap-8 md:gap-16',
{
'md:flex-row-reverse': isImageRight,
}
)}>
{/* Image */}
{image && (
<div className="w-full md:w-1/2">
<img
src={image}
alt={title || 'Section image'}
className="w-full h-auto rounded-xl shadow-lg"
/>
</div>
)}
{/* Content */}
<div className="w-full md:w-1/2">
{title && (
<h2 className="text-3xl md:text-4xl font-bold mb-6">
{title}
</h2>
)}
{text && (
<div
className="prose prose-lg text-gray-600"
dangerouslySetInnerHTML={{ __html: text }}
/>
)}
</div>
</div>
</div>
</section>
);
}