migration wip
This commit is contained in:
2
.next/cache/.tsbuildinfo
vendored
2
.next/cache/.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
BIN
.next/cache/webpack/client-production/0.pack
vendored
BIN
.next/cache/webpack/client-production/0.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/1.pack
vendored
BIN
.next/cache/webpack/client-production/1.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/2.pack
vendored
BIN
.next/cache/webpack/client-production/2.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/3.pack
vendored
BIN
.next/cache/webpack/client-production/3.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/4.pack
vendored
BIN
.next/cache/webpack/client-production/4.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/5.pack
vendored
BIN
.next/cache/webpack/client-production/5.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/6.pack
vendored
BIN
.next/cache/webpack/client-production/6.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/index.pack
vendored
BIN
.next/cache/webpack/client-production/index.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-production/index.pack.old
vendored
BIN
.next/cache/webpack/client-production/index.pack.old
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/1.pack.gz
vendored
BIN
.next/cache/webpack/server-development/1.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
Binary file not shown.
Binary file not shown.
BIN
.next/cache/webpack/server-production/0.pack
vendored
BIN
.next/cache/webpack/server-production/0.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-production/1.pack
vendored
BIN
.next/cache/webpack/server-production/1.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-production/2.pack
vendored
BIN
.next/cache/webpack/server-production/2.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-production/3.pack
vendored
BIN
.next/cache/webpack/server-production/3.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-production/4.pack
vendored
BIN
.next/cache/webpack/server-production/4.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-production/index.pack
vendored
BIN
.next/cache/webpack/server-production/index.pack
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-production/index.pack.old
vendored
BIN
.next/cache/webpack/server-production/index.pack.old
vendored
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -18,7 +18,7 @@
|
||||
"assets": [],
|
||||
"env": {
|
||||
"__NEXT_BUILD_ID": "development",
|
||||
"NEXT_SERVER_ACTIONS_ENCRYPTION_KEY": "FV9R3duv3e6hZD53ocfPgBrQwGX7rHfUyZRgkqwqFtk="
|
||||
"NEXT_SERVER_ACTIONS_ENCRYPTION_KEY": "0R3AeKRiCnSumpEO3wgP/k8sKq7OCUs1SBp+q3dmhMw="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"node": {},
|
||||
"edge": {},
|
||||
"encryptionKey": "FV9R3duv3e6hZD53ocfPgBrQwGX7rHfUyZRgkqwqFtk="
|
||||
"encryptionKey": "0R3AeKRiCnSumpEO3wgP/k8sKq7OCUs1SBp+q3dmhMw="
|
||||
}
|
||||
@@ -125,7 +125,7 @@
|
||||
/******/
|
||||
/******/ /* webpack/runtime/getFullHash */
|
||||
/******/ (() => {
|
||||
/******/ __webpack_require__.h = () => ("f11be5ba913d1b5f")
|
||||
/******/ __webpack_require__.h = () => ("c80591bbb933a4a4")
|
||||
/******/ })();
|
||||
/******/
|
||||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||
|
||||
18
.next/trace
18
.next/trace
File diff suppressed because one or more lines are too long
@@ -7,6 +7,7 @@ import { getLocalizedPath } from '@/lib/i18n';
|
||||
import { t } from '@/lib/i18n';
|
||||
import { SEO } from '@/components/SEO';
|
||||
import { LocaleSwitcher } from '@/components/LocaleSwitcher';
|
||||
import { ContentRenderer } from '@/components/content/ContentRenderer';
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
@@ -211,10 +212,12 @@ export default async function BlogDetailPage({ params }: PageProps) {
|
||||
})()}
|
||||
|
||||
{/* Article Content */}
|
||||
<div
|
||||
className="prose prose-lg prose-blue max-w-none mb-12"
|
||||
dangerouslySetInnerHTML={{ __html: processedContent }}
|
||||
/>
|
||||
<div className="mb-12">
|
||||
<ContentRenderer
|
||||
content={post.contentHtml}
|
||||
className="prose prose-lg prose-blue"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Article Footer */}
|
||||
<footer className="border-t border-gray-200 pt-8 mt-12">
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getPostsByLocale, getCategoriesByLocale, getMediaById } from '@/lib/dat
|
||||
import { getSiteInfo, t, getLocalizedPath } from '@/lib/i18n';
|
||||
import { SEO } from '@/components/SEO';
|
||||
import { LocaleSwitcher } from '@/components/LocaleSwitcher';
|
||||
import { processHTML } from '@/lib/html-compat';
|
||||
import { ContentRenderer } from '@/components/content/ContentRenderer';
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
@@ -140,10 +140,12 @@ export default async function BlogPage({ params }: PageProps) {
|
||||
{post.title}
|
||||
</Link>
|
||||
</h3>
|
||||
<div
|
||||
className="text-gray-600 line-clamp-3 text-sm mb-4"
|
||||
dangerouslySetInnerHTML={{ __html: processHTML(post.excerptHtml) }}
|
||||
/>
|
||||
<div className="text-gray-600 line-clamp-3 text-sm mb-4">
|
||||
<ContentRenderer
|
||||
content={post.excerptHtml}
|
||||
className="text-gray-600 line-clamp-3 text-sm"
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
href={getLocalizedPath(`/blog/${post.slug}`, locale as 'en' | 'de')}
|
||||
className="inline-flex items-center text-sm font-medium text-blue-600 hover:text-blue-700"
|
||||
@@ -205,10 +207,12 @@ export default async function BlogPage({ params }: PageProps) {
|
||||
{post.title}
|
||||
</Link>
|
||||
</h3>
|
||||
<div
|
||||
className="text-gray-600 mb-3"
|
||||
dangerouslySetInnerHTML={{ __html: processHTML(post.excerptHtml) }}
|
||||
/>
|
||||
<div className="text-gray-600 mb-3">
|
||||
<ContentRenderer
|
||||
content={post.excerptHtml}
|
||||
className="text-gray-600 mb-3"
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
href={getLocalizedPath(`/blog/${post.slug}`, locale as 'en' | 'de')}
|
||||
className="inline-flex items-center text-sm font-medium text-blue-600 hover:text-blue-700"
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ResponsiveSection, ResponsiveWrapper, ResponsiveGrid } from '@/componen
|
||||
import { FeaturedImage } from '@/components/content/FeaturedImage';
|
||||
import { Container } from '@/components/ui/Container';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { ContentRenderer } from '@/components/content/ContentRenderer';
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
@@ -117,9 +118,9 @@ export default async function Page({ params }: PageProps) {
|
||||
{page.title}
|
||||
</h1>
|
||||
{page.excerptHtml && (
|
||||
<div
|
||||
<ContentRenderer
|
||||
content={page.excerptHtml}
|
||||
className="text-lg sm:text-xl text-gray-600 leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: processHTML(page.excerptHtml) }}
|
||||
/>
|
||||
)}
|
||||
</ResponsiveWrapper>
|
||||
@@ -127,9 +128,9 @@ export default async function Page({ params }: PageProps) {
|
||||
|
||||
{processedContent && (
|
||||
<ResponsiveWrapper className="bg-white rounded-lg shadow-sm p-6 sm:p-8" container={true} maxWidth="full">
|
||||
<div
|
||||
<ContentRenderer
|
||||
content={contentToDisplay || ''}
|
||||
className="prose prose-lg max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: processedContent }}
|
||||
/>
|
||||
</ResponsiveWrapper>
|
||||
)}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'
|
||||
import { getAllCategories, getProductsByCategory } from '@/lib/data'
|
||||
import { ProductList } from '@/components/ProductList'
|
||||
import { Metadata } from 'next'
|
||||
import { ContentRenderer } from '@/components/content/ContentRenderer'
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
@@ -48,9 +49,9 @@ export default async function ProductCategoryPage({ params }: PageProps) {
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<h1 className="text-4xl font-bold mb-6">{category.name}</h1>
|
||||
{category.description && (
|
||||
<div
|
||||
<ContentRenderer
|
||||
content={category.description}
|
||||
className="mb-8 prose max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: category.description }}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getSiteInfo, t, getLocaleFromPath, getLocalizedPath } from '@/lib/i18n'
|
||||
import { processHTML } from '@/lib/html-compat';
|
||||
import { SEO } from '@/components/SEO';
|
||||
import { LocaleSwitcher } from '@/components/LocaleSwitcher';
|
||||
import { ContentRenderer } from '@/components/content/ContentRenderer';
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
@@ -187,9 +188,9 @@ export default async function ProductDetailPage({ params }: PageProps) {
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-3">
|
||||
{t('products.description', locale as 'en' | 'de')}
|
||||
</h3>
|
||||
<div
|
||||
<ContentRenderer
|
||||
content={product.descriptionHtml || ''}
|
||||
className="prose prose-sm max-w-none text-gray-600"
|
||||
dangerouslySetInnerHTML={{ __html: processedDescription }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -89,8 +89,16 @@ export const ContentRenderer: React.FC<ContentRendererProps> = ({
|
||||
/**
|
||||
* Parse HTML string to React elements
|
||||
* This is a safe parser that only allows specific tags and attributes
|
||||
* Works in both server and client environments
|
||||
*/
|
||||
function parseHTMLToReact(html: string): React.ReactNode {
|
||||
// For server-side rendering, use a simple approach with dangerouslySetInnerHTML
|
||||
// The HTML has already been sanitized by processHTML, so it's safe
|
||||
if (typeof window === 'undefined') {
|
||||
return <div dangerouslySetInnerHTML={{ __html: html }} />;
|
||||
}
|
||||
|
||||
// Client-side: use DOMParser for proper parsing
|
||||
// Define allowed tags and their properties
|
||||
const allowedTags = {
|
||||
div: ['className', 'id', 'style'],
|
||||
|
||||
@@ -113,8 +113,8 @@ function sanitizeHTML(html: string): string {
|
||||
// Remove dangerous attributes
|
||||
processed = processed.replace(/\s+(href|src)\s*=\s*["']\s*javascript:/gi, '');
|
||||
|
||||
// Remove any remaining WordPress shortcode-like content (e.g., [vc_row...])
|
||||
processed = processed.replace(/\[[^\]]*\]/g, '');
|
||||
// Note: Shortcode removal is handled in processShortcodes function
|
||||
// Don't remove shortcodes here as they need to be processed first
|
||||
|
||||
// Allow safe HTML tags
|
||||
const allowedTags = [
|
||||
@@ -170,23 +170,72 @@ function processVcRowShortcodes(html: string): string {
|
||||
const bgImage = extractAttribute(attrs, 'bg_image');
|
||||
const bgColor = extractAttribute(attrs, 'bg_color');
|
||||
const colorOverlay = extractAttribute(attrs, 'color_overlay');
|
||||
const colorOverlay2 = extractAttribute(attrs, 'color_overlay_2');
|
||||
const overlayStrength = extractAttribute(attrs, 'overlay_strength');
|
||||
const enableGradient = extractAttribute(attrs, 'enable_gradient');
|
||||
const gradientDirection = extractAttribute(attrs, 'gradient_direction');
|
||||
const topPadding = extractAttribute(attrs, 'top_padding');
|
||||
const bottomPadding = extractAttribute(attrs, 'bottom_padding');
|
||||
const fullScreen = extractAttribute(attrs, 'full_screen_row_position');
|
||||
const videoBg = extractAttribute(attrs, 'video_bg');
|
||||
const videoMp4 = extractAttribute(attrs, 'video_mp4');
|
||||
const videoWebm = extractAttribute(attrs, 'video_webm');
|
||||
const textAlign = extractAttribute(attrs, 'text_align');
|
||||
const textColor = extractAttribute(attrs, 'text_color');
|
||||
const overflow = extractAttribute(attrs, 'overflow');
|
||||
const equalHeight = extractAttribute(attrs, 'equal_height');
|
||||
const contentPlacement = extractAttribute(attrs, 'content_placement');
|
||||
const columnDirection = extractAttribute(attrs, 'column_direction');
|
||||
const rowBorderRadius = extractAttribute(attrs, 'row_border_radius');
|
||||
const rowBorderRadiusApplies = extractAttribute(attrs, 'row_border_radius_applies');
|
||||
|
||||
// Build style string
|
||||
let style = '';
|
||||
let wrapperClasses = [...classes];
|
||||
|
||||
// Handle text alignment
|
||||
if (textAlign === 'center') wrapperClasses.push('text-center');
|
||||
if (textAlign === 'right') wrapperClasses.push('text-right');
|
||||
if (textAlign === 'left') wrapperClasses.push('text-left');
|
||||
|
||||
// Handle text color
|
||||
if (textColor === 'light') wrapperClasses.push('text-white');
|
||||
|
||||
// Handle overflow
|
||||
if (overflow === 'visible') wrapperClasses.push('overflow-visible');
|
||||
|
||||
// Handle equal height
|
||||
if (equalHeight === 'yes') {
|
||||
wrapperClasses.push('items-stretch');
|
||||
wrapperClasses.push('flex');
|
||||
}
|
||||
|
||||
// Handle content placement
|
||||
if (contentPlacement === 'bottom') wrapperClasses.push('justify-end');
|
||||
if (contentPlacement === 'middle') wrapperClasses.push('justify-center');
|
||||
|
||||
// Handle column direction
|
||||
if (columnDirection === 'column') wrapperClasses.push('flex-col');
|
||||
|
||||
// Handle border radius
|
||||
if (rowBorderRadius === 'none' && rowBorderRadiusApplies === 'bg') {
|
||||
wrapperClasses.push('rounded-none');
|
||||
}
|
||||
|
||||
// Handle background image
|
||||
if (bgImage) {
|
||||
style += `background-image: url(/media/${bgImage}.webp); `;
|
||||
// Try to get media by ID first
|
||||
const mediaId = parseInt(bgImage);
|
||||
if (!isNaN(mediaId)) {
|
||||
// This will be handled by ContentRenderer with data attributes
|
||||
wrapperClasses.push('bg-cover', 'bg-center');
|
||||
style += `background-image: url(/media/${bgImage}.webp); `;
|
||||
} else {
|
||||
// Assume it's a direct URL
|
||||
style += `background-image: url(${bgImage}); `;
|
||||
}
|
||||
style += `background-size: cover; `;
|
||||
style += `background-position: center; `;
|
||||
wrapperClasses.push('bg-cover', 'bg-center');
|
||||
}
|
||||
|
||||
// Handle background color
|
||||
@@ -194,21 +243,63 @@ function processVcRowShortcodes(html: string): string {
|
||||
style += `background-color: ${bgColor}; `;
|
||||
}
|
||||
|
||||
// Handle color overlay
|
||||
if (colorOverlay) {
|
||||
const opacity = overlayStrength ? parseFloat(overlayStrength) : 0.5;
|
||||
// Handle video background
|
||||
if (videoBg === 'use_video' && (videoMp4 || videoWebm)) {
|
||||
// Mark for ContentRenderer to handle
|
||||
wrapperClasses.push('relative', 'overflow-hidden');
|
||||
style += `position: relative; `;
|
||||
|
||||
// Create video background structure
|
||||
const videoAttrs = [];
|
||||
if (videoMp4) videoAttrs.push(`data-video-mp4="${videoMp4}"`);
|
||||
if (videoWebm) videoAttrs.push(`data-video-webm="${videoWebm}"`);
|
||||
videoAttrs.push('data-video-bg="true"');
|
||||
|
||||
return `<div class="${wrapperClasses.join(' ')}" style="${style}" ${videoAttrs.join(' ')}>
|
||||
<div class="relative flex flex-wrap -mx-4 w-full h-full">${content}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Handle color overlay (single or gradient)
|
||||
if (colorOverlay || colorOverlay2 || enableGradient === 'true' || enableGradient === '1') {
|
||||
style += `position: relative; `;
|
||||
wrapperClasses.push('relative');
|
||||
|
||||
// Create overlay div
|
||||
const overlayStyle = `background-color: ${colorOverlay}; opacity: ${opacity};`;
|
||||
let overlayStyle = '';
|
||||
if (colorOverlay2 && enableGradient === 'true') {
|
||||
// Gradient overlay
|
||||
const gradientDir = gradientDirection || 'left_to_right';
|
||||
let gradientCSS = '';
|
||||
switch(gradientDir) {
|
||||
case 'left_to_right':
|
||||
gradientCSS = `linear-gradient(to right, ${colorOverlay}, ${colorOverlay2})`;
|
||||
break;
|
||||
case 'right_to_left':
|
||||
gradientCSS = `linear-gradient(to left, ${colorOverlay}, ${colorOverlay2})`;
|
||||
break;
|
||||
case 'top_to_bottom':
|
||||
gradientCSS = `linear-gradient(to bottom, ${colorOverlay}, ${colorOverlay2})`;
|
||||
break;
|
||||
case 'bottom_to_top':
|
||||
gradientCSS = `linear-gradient(to top, ${colorOverlay}, ${colorOverlay2})`;
|
||||
break;
|
||||
default:
|
||||
gradientCSS = `linear-gradient(to right, ${colorOverlay}, ${colorOverlay2})`;
|
||||
}
|
||||
overlayStyle = `background: ${gradientCSS}; opacity: 0.32;`;
|
||||
} else if (colorOverlay) {
|
||||
// Solid color overlay
|
||||
const opacity = overlayStrength ? parseFloat(overlayStrength) : 0.5;
|
||||
overlayStyle = `background-color: ${colorOverlay}; opacity: ${opacity};`;
|
||||
}
|
||||
|
||||
return `<div class="${wrapperClasses.join(' ')}" style="${style}">
|
||||
<div class="absolute inset-0" style="${overlayStyle}"></div>
|
||||
<div class="relative flex flex-wrap -mx-4 w-full">${content}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Handle gradient
|
||||
// Handle gradient (without overlay)
|
||||
if (enableGradient === 'true' || enableGradient === '1') {
|
||||
const gradientClass = getGradientClass(gradientDirection);
|
||||
wrapperClasses.push(gradientClass);
|
||||
@@ -216,9 +307,11 @@ function processVcRowShortcodes(html: string): string {
|
||||
|
||||
// Handle padding
|
||||
if (topPadding || bottomPadding) {
|
||||
// Convert percentage values to Tailwind arbitrary values
|
||||
const pt = topPadding ? `pt-[${topPadding}]` : '';
|
||||
const pb = bottomPadding ? `pb-[${bottomPadding}]` : '';
|
||||
wrapperClasses.push(pt, pb);
|
||||
if (pt) wrapperClasses.push(pt);
|
||||
if (pb) wrapperClasses.push(pb);
|
||||
}
|
||||
|
||||
// Handle full screen
|
||||
|
||||
Reference in New Issue
Block a user