diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule index 46d89ca2..20c33110 100644 Binary files a/backend/celerybeat-schedule and b/backend/celerybeat-schedule differ diff --git a/sites/src/utils/layoutRenderer.tsx b/sites/src/utils/layoutRenderer.tsx index a81b6c0f..2885c724 100644 --- a/sites/src/utils/layoutRenderer.tsx +++ b/sites/src/utils/layoutRenderer.tsx @@ -27,7 +27,7 @@ export type LayoutType = | 'corporate'; /** - * Render site layout based on site definition1. + * Render site layout based on site definition. */ export function renderLayout(siteDefinition: SiteDefinition): React.ReactElement { const layoutType = siteDefinition.layout as LayoutType; @@ -276,4 +276,3 @@ function renderCorporateLayout(siteDefinition: SiteDefinition): React.ReactEleme } // Note: Navigation and page rendering are now handled within each layout component - diff --git a/sites/src/utils/layoutRenderer.tsx.backup b/sites/src/utils/layoutRenderer.tsx.backup new file mode 100644 index 00000000..71a07135 --- /dev/null +++ b/sites/src/utils/layoutRenderer.tsx.backup @@ -0,0 +1,279 @@ +/** + * Layout Renderer + * Phase 5: Sites Renderer & Publishing + * + * Renders different layout types for sites using shared components. + */ +import React from 'react'; +import type { SiteDefinition } from '../types'; +import { renderTemplate } from './templateEngine'; +import { + DefaultLayout, + MinimalLayout, + MagazineLayout, + EcommerceLayout, + PortfolioLayout, + BlogLayout, + CorporateLayout, +} from '@shared/layouts'; + +export type LayoutType = + | 'default' + | 'minimal' + | 'magazine' + | 'ecommerce' + | 'portfolio' + | 'blog' + | 'corporate'; + +/** + * Render site layout based on site definition. + */ +export function renderLayout(siteDefinition: SiteDefinition): React.ReactElement { + const layoutType = siteDefinition.layout as LayoutType; + + switch (layoutType) { + case 'minimal': + return renderMinimalLayout(siteDefinition); + case 'magazine': + return renderMagazineLayout(siteDefinition); + case 'ecommerce': + return renderEcommerceLayout(siteDefinition); + case 'portfolio': + return renderPortfolioLayout(siteDefinition); + case 'blog': + return renderBlogLayout(siteDefinition); + case 'corporate': + return renderCorporateLayout(siteDefinition); + case 'default': + default: + return renderDefaultLayout(siteDefinition); + } +} + +/** + * Default layout: Standard header, content, footer. + * Uses shared DefaultLayout component with fully styled modern design. + */ +function renderDefaultLayout(siteDefinition: SiteDefinition): React.ReactElement { + // Find home page for hero + const homePage = siteDefinition.pages.find(p => p.slug === 'home'); + const heroBlock = homePage?.blocks?.find(b => b.type === 'hero'); + const hero: React.ReactNode = heroBlock ? (renderTemplate(heroBlock) as React.ReactNode) : undefined; + + // Render all pages as sections (excluding hero from home page if it exists) + const sections = siteDefinition.pages + .filter((page) => page.status !== 'draft') + .map((page) => { + // Filter out hero block if it's the home page (already rendered as hero) + const blocksToRender = page.slug === 'home' && heroBlock + ? page.blocks?.filter(b => b.type !== 'hero') || [] + : page.blocks || []; + + return ( +
+ {page.slug !== 'home' &&

{page.title}

} + {blocksToRender.length > 0 ? ( + blocksToRender.map((block, index) => ( +
+ {renderTemplate(block)} +
+ )) + ) : page.slug !== 'home' ? ( +

No content available for this page.

+ ) : null} +
+ ); + }); + + return ( + + ); +} + +/** + * Minimal layout: Clean, minimal design. + * Uses shared MinimalLayout component. + */ +function renderMinimalLayout(siteDefinition: SiteDefinition): React.ReactElement { + const mainContent = ( + <> + {siteDefinition.pages + .filter((page) => page.status !== 'draft') + .map((page) => ( +
+

{page.title}

+ {page.blocks && page.blocks.length > 0 ? ( + page.blocks.map((block, index) => ( +
+ {renderTemplate(block)} +
+ )) + ) : null} +
+ ))} + + ); + + return ( + + {mainContent} + + ); +} + +/** + * Magazine layout: Editorial, content-focused. + * Uses shared MagazineLayout component. + */ +function renderMagazineLayout(siteDefinition: SiteDefinition): React.ReactElement { + const mainContent = ( + <> + {siteDefinition.pages + .filter((page) => page.status !== 'draft') + .map((page) => ( +
+ {page.blocks && page.blocks.length > 0 ? ( + page.blocks.map((block, index) => ( +
+ {renderTemplate(block)} +
+ )) + ) : null} +
+ ))} + + ); + + return ( + + ); +} + +/** + * Ecommerce layout: Product-focused. + * Uses shared EcommerceLayout component. + */ +function renderEcommerceLayout(siteDefinition: SiteDefinition): React.ReactElement { + const mainContent = ( + <> + {siteDefinition.pages + .filter((page) => page.status !== 'draft') + .map((page) => ( +
+ {page.blocks && page.blocks.length > 0 ? ( + page.blocks.map((block, index) => ( +
+ {renderTemplate(block)} +
+ )) + ) : null} +
+ ))} + + ); + + return ( + + ); +} + +/** + * Portfolio layout: Showcase. + * Uses shared PortfolioLayout component. + */ +function renderPortfolioLayout(siteDefinition: SiteDefinition): React.ReactElement { + const mainContent = ( + <> + {siteDefinition.pages + .filter((page) => page.status !== 'draft') + .map((page) => ( +
+ {page.blocks && page.blocks.length > 0 ? ( + page.blocks.map((block, index) => ( +
+ {renderTemplate(block)} +
+ )) + ) : null} +
+ ))} + + ); + + return ( + + ); +} + +/** + * Blog layout: Content-first. + * Uses shared BlogLayout component. + */ +function renderBlogLayout(siteDefinition: SiteDefinition): React.ReactElement { + const mainContent = ( + <> + {siteDefinition.pages + .filter((page) => page.status !== 'draft') + .map((page) => ( +
+ {page.blocks && page.blocks.length > 0 ? ( + page.blocks.map((block, index) => ( +
+ {renderTemplate(block)} +
+ )) + ) : null} +
+ ))} + + ); + + return ( + + ); +} + +/** + * Corporate layout: Business. + * Uses shared CorporateLayout component. + */ +function renderCorporateLayout(siteDefinition: SiteDefinition): React.ReactElement { + const mainContent = ( + <> + {siteDefinition.pages + .filter((page) => page.status !== 'draft') + .map((page) => ( +
+ {page.blocks && page.blocks.length > 0 ? ( + page.blocks.map((block, index) => ( +
+ {renderTemplate(block)} +
+ )) + ) : null} +
+ ))} + + ); + + return ( + + ); +} + +// Note: Navigation and page rendering are now handled within each layout component +