Update CORS settings, enhance API URL detection, and improve template rendering

- Added new CORS origins for local development and specific IP addresses in `settings.py`.
- Refactored API URL retrieval logic in `loadSiteDefinition.ts` and `fileAccess.ts` to auto-detect based on the current origin.
- Enhanced error handling in API calls and improved logging for better debugging.
- Updated `renderTemplate` function to support additional block types and improved rendering logic for various components in `templateEngine.tsx`.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-18 19:52:42 +00:00
parent d696d55309
commit 0eb039e1a7
12 changed files with 341 additions and 17 deletions

View File

@@ -7,7 +7,30 @@
*/
const SITES_DATA_PATH = import.meta.env.SITES_DATA_PATH || '/sites';
const API_URL = import.meta.env.VITE_API_URL || 'https://api.igny8.com/api';
/**
* Get API base URL - auto-detect based on current origin
*/
function getApiBaseUrl(): string {
const envUrl = import.meta.env.VITE_API_URL;
if (envUrl) {
return envUrl.endsWith('/api') ? envUrl : `${envUrl}/api`;
}
if (typeof window !== 'undefined') {
const origin = window.location.origin;
if (/^\d+\.\d+\.\d+\.\d+/.test(origin) || origin.includes('localhost') || origin.includes('127.0.0.1')) {
if (origin.includes(':8024')) {
return origin.replace(':8024', ':8011') + '/api';
}
return origin.split(':')[0] + ':8011/api';
}
}
return 'https://api.igny8.com/api';
}
const API_URL = getApiBaseUrl();
/**
* Get file URL for a site asset.

View File

@@ -189,16 +189,22 @@ function renderNavigation(navigation: SiteDefinition['navigation']): React.React
* Render pages.
*/
function renderPages(pages: SiteDefinition['pages']): React.ReactElement[] {
// Filter pages - include ready, generating, and deployed statuses
// Only exclude draft status
return pages
.filter((page) => page.status === 'ready')
.filter((page) => page.status !== 'draft')
.map((page) => (
<div key={page.id} className="page" data-page-slug={page.slug}>
<h2>{page.title}</h2>
{page.blocks.map((block, index) => (
<div key={index} className="block" data-block-type={block.type}>
{renderTemplate(block)}
</div>
))}
{page.blocks && page.blocks.length > 0 ? (
page.blocks.map((block, index) => (
<div key={index} className="block" data-block-type={block.type}>
{renderTemplate(block)}
</div>
))
) : (
<p>No content available for this page.</p>
)}
</div>
));
}

View File

@@ -11,8 +11,20 @@ import type { Block } from '../types';
* Render a block using the template engine.
* Imports shared components dynamically.
*/
export function renderTemplate(block: Block): React.ReactElement {
const { type, data } = block;
export function renderTemplate(block: Block | any): React.ReactElement {
// Handle both formats: { type, data } or { type, ...properties }
let type: string;
let data: Record<string, any>;
if (block.type && block.data) {
// Standard format: { type, data }
type = block.type;
data = block.data;
} else {
// API format: { type, heading, subheading, content, ... }
type = block.type;
data = block;
}
try {
// Try to import shared component
@@ -20,6 +32,10 @@ export function renderTemplate(block: Block): React.ReactElement {
switch (type) {
case 'hero':
return renderHeroBlock(data);
case 'features':
return renderFeaturesBlock(data);
case 'testimonials':
return renderTestimonialsBlock(data);
case 'text':
return renderTextBlock(data);
case 'image':
@@ -42,6 +58,14 @@ export function renderTemplate(block: Block): React.ReactElement {
return renderFormBlock(data);
case 'accordion':
return renderAccordionBlock(data);
case 'faq':
return renderFAQBlock(data);
case 'cta':
return renderCTABlock(data);
case 'services':
return renderServicesBlock(data);
case 'stats':
return renderStatsBlock(data);
default:
return <div className="block-unknown">Unknown block type: {type}</div>;
}
@@ -55,12 +79,18 @@ export function renderTemplate(block: Block): React.ReactElement {
* Render hero block.
*/
function renderHeroBlock(data: Record<string, any>): React.ReactElement {
// Handle both API format (heading/subheading) and template format (title/subtitle)
const title = data.heading || data.title || 'Hero Title';
const subtitle = data.subheading || data.subtitle;
const content = Array.isArray(data.content) ? data.content.join(' ') : data.content;
return (
<section className="block-hero" style={{ padding: '4rem 2rem', textAlign: 'center', background: data.background || '#f0f0f0' }}>
<h1>{data.title || 'Hero Title'}</h1>
{data.subtitle && <p>{data.subtitle}</p>}
<h1>{title}</h1>
{subtitle && <p style={{ fontSize: '1.2rem', color: '#666', marginTop: '1rem' }}>{subtitle}</p>}
{content && <p style={{ marginTop: '1rem' }}>{content}</p>}
{data.buttonText && (
<a href={data.buttonLink || '#'} className="button">
<a href={data.buttonLink || '#'} className="button" style={{ display: 'inline-block', marginTop: '2rem', padding: '0.75rem 1.5rem', background: '#007bff', color: 'white', textDecoration: 'none', borderRadius: '4px' }}>
{data.buttonText}
</a>
)}
@@ -243,3 +273,154 @@ function renderAccordionBlock(data: Record<string, any>): React.ReactElement {
);
}
/**
* Render features block.
*/
function renderFeaturesBlock(data: Record<string, any>): React.ReactElement {
const heading = data.heading || data.title || 'Features';
const subheading = data.subheading || data.subtitle;
const content = Array.isArray(data.content) ? data.content : [];
const layout = data.layout || 'two-column';
return (
<section className="block-features" style={{ padding: '3rem 2rem', background: data.background || 'transparent' }}>
{heading && <h2 style={{ textAlign: 'center', marginBottom: '1rem' }}>{heading}</h2>}
{subheading && <p style={{ textAlign: 'center', color: '#666', marginBottom: '2rem' }}>{subheading}</p>}
<div style={{
display: 'grid',
gridTemplateColumns: layout === 'two-column' ? 'repeat(2, 1fr)' : 'repeat(3, 1fr)',
gap: '2rem',
maxWidth: '1200px',
margin: '0 auto'
}}>
{content.map((item: string, index: number) => (
<div key={index} style={{ padding: '1.5rem', border: '1px solid #eee', borderRadius: '8px' }}>
<p>{item}</p>
</div>
))}
</div>
</section>
);
}
/**
* Render testimonials block.
*/
function renderTestimonialsBlock(data: Record<string, any>): React.ReactElement {
const heading = data.heading || data.title || 'Testimonials';
const subheading = data.subheading || data.subtitle;
const content = Array.isArray(data.content) ? data.content : [];
const layout = data.layout || 'cards';
return (
<section className="block-testimonials" style={{ padding: '3rem 2rem', background: data.background || '#f9f9f9' }}>
{heading && <h2 style={{ textAlign: 'center', marginBottom: '1rem' }}>{heading}</h2>}
{subheading && <p style={{ textAlign: 'center', color: '#666', marginBottom: '2rem' }}>{subheading}</p>}
<div style={{
display: 'grid',
gridTemplateColumns: layout === 'cards' ? 'repeat(auto-fit, minmax(300px, 1fr))' : '1fr',
gap: '2rem',
maxWidth: '1200px',
margin: '0 auto'
}}>
{content.map((item: string, index: number) => (
<div key={index} style={{ padding: '2rem', background: 'white', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0,0,0,0.1)' }}>
<p style={{ fontStyle: 'italic', marginBottom: '1rem' }}>"{item}"</p>
</div>
))}
</div>
</section>
);
}
/**
* Render FAQ block.
*/
function renderFAQBlock(data: Record<string, any>): React.ReactElement {
const heading = data.heading || data.title || 'FAQ';
const subheading = data.subheading || data.subtitle;
const content = Array.isArray(data.content) ? data.content : [];
return (
<section className="block-faq" style={{ padding: '3rem 2rem' }}>
{heading && <h2 style={{ marginBottom: '1rem' }}>{heading}</h2>}
{subheading && <p style={{ color: '#666', marginBottom: '2rem' }}>{subheading}</p>}
<div>
{content.map((item: string, index: number) => (
<div key={index} style={{ marginBottom: '1rem', padding: '1rem', borderBottom: '1px solid #eee' }}>
<p><strong>Q:</strong> {item}</p>
</div>
))}
</div>
</section>
);
}
/**
* Render CTA (Call to Action) block.
*/
function renderCTABlock(data: Record<string, any>): React.ReactElement {
const heading = data.heading || data.title || 'Call to Action';
const subheading = data.subheading || data.subtitle;
const content = Array.isArray(data.content) ? data.content[0] : data.content;
return (
<section className="block-cta" style={{ padding: '4rem 2rem', textAlign: 'center', background: data.background || '#007bff', color: 'white' }}>
{heading && <h2 style={{ marginBottom: '1rem' }}>{heading}</h2>}
{subheading && <p style={{ marginBottom: '2rem', fontSize: '1.1rem' }}>{subheading}</p>}
{content && <p style={{ marginBottom: '2rem' }}>{content}</p>}
{data.buttonText && (
<a href={data.buttonLink || '#'} style={{ display: 'inline-block', padding: '1rem 2rem', background: 'white', color: '#007bff', textDecoration: 'none', borderRadius: '4px', fontWeight: 'bold' }}>
{data.buttonText}
</a>
)}
</section>
);
}
/**
* Render services block.
*/
function renderServicesBlock(data: Record<string, any>): React.ReactElement {
const heading = data.heading || data.title || 'Services';
const subheading = data.subheading || data.subtitle;
const content = Array.isArray(data.content) ? data.content : [];
return (
<section className="block-services" style={{ padding: '3rem 2rem' }}>
{heading && <h2 style={{ marginBottom: '1rem' }}>{heading}</h2>}
{subheading && <p style={{ color: '#666', marginBottom: '2rem' }}>{subheading}</p>}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))', gap: '2rem' }}>
{content.map((item: string, index: number) => (
<div key={index} style={{ padding: '1.5rem', border: '1px solid #ddd', borderRadius: '8px' }}>
<p>{item}</p>
</div>
))}
</div>
</section>
);
}
/**
* Render stats block.
*/
function renderStatsBlock(data: Record<string, any>): React.ReactElement {
const heading = data.heading || data.title || 'Statistics';
const subheading = data.subheading || data.subtitle;
const content = Array.isArray(data.content) ? data.content : [];
return (
<section className="block-stats" style={{ padding: '3rem 2rem', background: data.background || '#f9f9f9' }}>
{heading && <h2 style={{ textAlign: 'center', marginBottom: '1rem' }}>{heading}</h2>}
{subheading && <p style={{ textAlign: 'center', color: '#666', marginBottom: '2rem' }}>{subheading}</p>}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '2rem', maxWidth: '1200px', margin: '0 auto' }}>
{content.map((item: string, index: number) => (
<div key={index} style={{ textAlign: 'center', padding: '1.5rem' }}>
<p style={{ fontSize: '1.1rem' }}>{item}</p>
</div>
))}
</div>
</section>
);
}