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,94 @@
import { cn } from '@/lib/utils';
interface FeatureItem {
title?: string;
description?: string;
icon?: string;
}
interface FeatureGridSectionProps {
id: string;
layout?: string;
colorScheme?: string;
heading?: string;
items?: FeatureItem[];
}
export function FeatureGridSection({
id,
layout = 'grid-3',
colorScheme = 'default',
heading,
items = [],
}: FeatureGridSectionProps) {
const gridCols = {
'grid-2': 'md:grid-cols-2',
'grid-3': 'md:grid-cols-3',
'grid-4': 'md:grid-cols-2 lg:grid-cols-4',
}[layout] || 'md:grid-cols-3';
return (
<section
id={id}
className={cn(
'wn-section wn-feature-grid',
`wn-feature-grid--${layout}`,
`wn-scheme--${colorScheme}`,
'py-16 md:py-24',
{
'bg-white': colorScheme === 'default',
'bg-muted': colorScheme === 'muted',
'bg-primary text-primary-foreground': colorScheme === 'primary',
}
)}
>
<div className="container mx-auto px-4">
{heading && (
<h2 className="text-3xl md:text-4xl font-bold text-center mb-12">
{heading}
</h2>
)}
<div className={cn('grid gap-8', gridCols)}>
{items.map((item, index) => (
<div
key={index}
className={cn(
'wn-feature-grid__item',
'p-6 rounded-xl',
{
'bg-white shadow-lg': colorScheme !== 'primary',
'bg-white/10': colorScheme === 'primary',
}
)}
>
{item.icon && (
<span className="wn-feature-grid__icon text-4xl mb-4 block">
{item.icon}
</span>
)}
{item.title && (
<h3 className="wn-feature-grid__item-title text-xl font-semibold mb-3">
{item.title}
</h3>
)}
{item.description && (
<p className={cn(
'wn-feature-grid__item-desc',
{
'text-gray-600': colorScheme !== 'primary',
'text-white/80': colorScheme === 'primary',
}
)}>
{item.description}
</p>
)}
</div>
))}
</div>
</div>
</section>
);
}