Files
igny8/sites/src/utils/layoutRenderer.tsx
IGNY8 VPS (Salman) e4e7ddfdf3 Add generate_page_content functionality for structured page content generation
- Introduced a new AI function `generate_page_content` to create structured content for website pages using JSON blocks.
- Updated `AIEngine` to handle the new function and return appropriate messages for content generation.
- Enhanced `PageGenerationService` to utilize the new AI function for generating page content based on blueprints.
- Modified `prompts.py` to include detailed content generation requirements for the new function.
- Updated site rendering logic to accommodate structured content blocks in various layouts.
2025-11-18 23:30:20 +00:00

246 lines
6.8 KiB
TypeScript

/**
* 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 { renderPageByType } from './pageTypeRenderer';
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 (only show hero on home page or when showing all pages)
const homePage = siteDefinition.pages.find(p => p.slug === 'home');
const heroBlock = homePage?.blocks?.find(b => b.type === 'hero');
// Only show hero if we're on home page or showing all pages
const isHomePage = siteDefinition.pages.length === 1 && siteDefinition.pages[0]?.slug === 'home';
const showHero = isHomePage || (homePage && siteDefinition.pages.length > 1);
const hero: React.ReactNode = (showHero && heroBlock) ? (renderTemplate(heroBlock) as React.ReactNode) : undefined;
// Render all pages using page-type-specific templates
const sections = siteDefinition.pages
.filter((page) => page.status !== 'draft' && page.status !== 'generating')
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((page) => {
// Use page-type-specific renderer if available
const blocksToRender = page.slug === 'home' && heroBlock && showHero
? page.blocks?.filter(b => b.type !== 'hero') || []
: page.blocks || [];
// Render using page-type template
return (
<div key={page.id} className="page-wrapper" data-page-slug={page.slug}>
{renderPageByType(page, blocksToRender)}
</div>
);
});
return (
<DefaultLayout
hero={hero as any}
sections={sections}
/>
);
}
/**
* Minimal layout: Clean, minimal design.
* Uses shared MinimalLayout component.
*/
function renderMinimalLayout(siteDefinition: SiteDefinition): React.ReactElement {
const mainContent = (
<>
{siteDefinition.pages
.filter((page) => page.status !== 'draft' && page.status !== 'generating')
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((page) => (
<div key={page.id} className="page-wrapper" data-page-slug={page.slug}>
{renderPageByType(page, page.blocks || [])}
</div>
))}
</>
);
return (
<MinimalLayout>
{mainContent}
</MinimalLayout>
);
}
/**
* Magazine layout: Editorial, content-focused.
* Uses shared MagazineLayout component.
*/
function renderMagazineLayout(siteDefinition: SiteDefinition): React.ReactElement {
const mainContent = (
<>
{siteDefinition.pages
.filter((page) => page.status !== 'draft' && page.status !== 'generating')
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((page) => (
<div key={page.id} className="page-wrapper" data-page-slug={page.slug}>
{renderPageByType(page, page.blocks || [])}
</div>
))}
</>
);
return (
<MagazineLayout
mainContent={mainContent}
/>
);
}
/**
* Ecommerce layout: Product-focused.
* Uses shared EcommerceLayout component.
*/
function renderEcommerceLayout(siteDefinition: SiteDefinition): React.ReactElement {
const mainContent = (
<>
{siteDefinition.pages
.filter((page) => page.status !== 'draft' && page.status !== 'generating')
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((page) => (
<div key={page.id} className="page-wrapper" data-page-slug={page.slug}>
{renderPageByType(page, page.blocks || [])}
</div>
))}
</>
);
return (
<EcommerceLayout
mainContent={mainContent}
/>
);
}
/**
* Portfolio layout: Showcase.
* Uses shared PortfolioLayout component.
*/
function renderPortfolioLayout(siteDefinition: SiteDefinition): React.ReactElement {
const mainContent = (
<>
{siteDefinition.pages
.filter((page) => page.status !== 'draft' && page.status !== 'generating')
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((page) => (
<div key={page.id} className="page-wrapper" data-page-slug={page.slug}>
{renderPageByType(page, page.blocks || [])}
</div>
))}
</>
);
return (
<PortfolioLayout
mainContent={mainContent}
/>
);
}
/**
* Blog layout: Content-first.
* Uses shared BlogLayout component.
*/
function renderBlogLayout(siteDefinition: SiteDefinition): React.ReactElement {
const mainContent = (
<>
{siteDefinition.pages
.filter((page) => page.status !== 'draft' && page.status !== 'generating')
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((page) => (
<div key={page.id} className="page-wrapper" data-page-slug={page.slug}>
{renderPageByType(page, page.blocks || [])}
</div>
))}
</>
);
return (
<BlogLayout
mainContent={mainContent}
/>
);
}
/**
* Corporate layout: Business.
* Uses shared CorporateLayout component.
*/
function renderCorporateLayout(siteDefinition: SiteDefinition): React.ReactElement {
const mainContent = (
<>
{siteDefinition.pages
.filter((page) => page.status !== 'draft' && page.status !== 'generating')
.sort((a, b) => (a.order || 0) - (b.order || 0))
.map((page) => (
<div key={page.id} className="page-wrapper" data-page-slug={page.slug}>
{renderPageByType(page, page.blocks || [])}
</div>
))}
</>
);
return (
<CorporateLayout
mainContent={mainContent}
/>
);
}
// Note: Navigation and page rendering are now handled within each layout component