Enhance public access and error handling in site-related views and loaders

- Updated `DebugScopedRateThrottle` to allow public access for blueprint list requests with site filters.
- Modified `SiteViewSet` and `SiteBlueprintViewSet` to permit public read access for list requests.
- Enhanced `loadSiteDefinition` to resolve site slugs to IDs, improving the loading process for site definitions.
- Improved error handling in `SiteDefinitionView` and `loadSiteDefinition` for better user feedback.
- Adjusted CSS styles for better layout and alignment in shared components.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-18 22:40:00 +00:00
parent 8ab15d1d79
commit 6c6133a683
14 changed files with 361 additions and 76 deletions

View File

@@ -1,23 +1,23 @@
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useParams, Link } from 'react-router-dom';
import { loadSiteDefinition } from '../loaders/loadSiteDefinition';
import { renderLayout } from '../utils/layoutRenderer';
import type { SiteDefinition } from '../types';
function SiteRenderer() {
const { siteId } = useParams<{ siteId: string }>();
const { siteSlug, pageSlug } = useParams<{ siteSlug: string; pageSlug?: string }>();
const [siteDefinition, setSiteDefinition] = useState<SiteDefinition | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!siteId) {
setError('Site ID is required');
if (!siteSlug) {
setError('Site slug is required');
setLoading(false);
return;
}
loadSiteDefinition(siteId)
loadSiteDefinition(siteSlug)
.then((definition) => {
setSiteDefinition(definition);
setLoading(false);
@@ -26,7 +26,7 @@ function SiteRenderer() {
setError(err.message || 'Failed to load site');
setLoading(false);
});
}, [siteId]);
}, [siteSlug, pageSlug]);
if (loading) {
return <div>Loading site...</div>;
@@ -40,9 +40,122 @@ function SiteRenderer() {
return <div>Site not found</div>;
}
// Build navigation from site definition
// Show pages that are published, ready, or in navigation (excluding home and draft/generating)
const navigation = siteDefinition.navigation || siteDefinition.pages
.filter(p =>
p.slug !== 'home' &&
(p.status === 'published' || p.status === 'ready' || siteDefinition.navigation?.some(n => n.slug === p.slug))
)
.sort((a, b) => {
// Try to get order from navigation or use page order
const navA = siteDefinition.navigation?.find(n => n.slug === a.slug);
const navB = siteDefinition.navigation?.find(n => n.slug === b.slug);
return (navA?.order ?? a.order ?? 0) - (navB?.order ?? b.order ?? 0);
})
.map(page => ({
label: page.title,
slug: page.slug,
order: page.order || 0
}));
// Filter pages based on current route
const currentPageSlug = pageSlug || 'home';
const currentPage = siteDefinition.pages.find(p => p.slug === currentPageSlug);
// If specific page requested, show only that page; otherwise show all published/ready pages
const pagesToRender = currentPageSlug && currentPageSlug !== 'home' && currentPage
? [currentPage]
: siteDefinition.pages.filter(p =>
p.status === 'published' ||
p.status === 'ready' ||
(p.slug === 'home' && p.status !== 'draft' && p.status !== 'generating')
);
return (
<div className="site-renderer">
{renderLayout(siteDefinition)}
<div className="site-renderer" style={{
maxWidth: '1200px',
margin: '0 auto',
padding: '0 1rem',
width: '100%'
}}>
{/* Navigation Menu */}
<nav style={{
padding: '1rem 0',
borderBottom: '1px solid #e5e7eb',
marginBottom: '2rem'
}}>
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'wrap',
gap: '1rem'
}}>
<Link
to={`/${siteSlug}`}
style={{
fontSize: '1.5rem',
fontWeight: 'bold',
textDecoration: 'none',
color: '#0f172a'
}}
>
{siteDefinition.name}
</Link>
<div style={{
display: 'flex',
gap: '1.5rem',
flexWrap: 'wrap'
}}>
<Link
to={`/${siteSlug}`}
style={{
textDecoration: 'none',
color: currentPageSlug === 'home' ? '#4c1d95' : '#64748b',
fontWeight: currentPageSlug === 'home' ? 600 : 400
}}
>
Home
</Link>
{navigation.map((item) => (
<Link
key={item.slug}
to={`/${siteSlug}/${item.slug}`}
style={{
textDecoration: 'none',
color: currentPageSlug === item.slug ? '#4c1d95' : '#64748b',
fontWeight: currentPageSlug === item.slug ? 600 : 400
}}
>
{item.label}
</Link>
))}
</div>
</div>
</nav>
{/* Main Content */}
{renderLayout({ ...siteDefinition, pages: pagesToRender })}
{/* Footer */}
<footer style={{
marginTop: '4rem',
padding: '2rem 0',
borderTop: '1px solid #e5e7eb',
textAlign: 'center',
color: '#64748b',
fontSize: '0.875rem'
}}>
<p style={{ margin: 0 }}>
© {new Date().getFullYear()} {siteDefinition.name}. All rights reserved.
</p>
{siteDefinition.description && (
<p style={{ margin: '0.5rem 0 0', fontSize: '0.75rem' }}>
{siteDefinition.description}
</p>
)}
</footer>
</div>
);
}