feat: Add error display for sheet generation failures, improve SVG content detection, and validate generated data on the server.

This commit is contained in:
francy 2026-02-01 22:21:25 +01:00
parent 5b0b37df3e
commit 9f5a0e155a
3 changed files with 14 additions and 3 deletions

View File

@ -111,6 +111,10 @@ CRITICAL: content_svg MUST be a string with single quotes in SVG attributes. Pat
const jsonStr = text.replace(/```json/g, '').replace(/```/g, '').trim(); const jsonStr = text.replace(/```json/g, '').replace(/```/g, '').trim();
const data = JSON.parse(jsonStr); const data = JSON.parse(jsonStr);
if (!data.title || !data.sections || !Array.isArray(data.sections)) {
throw new Error('Invalid generation format');
}
res.json(data); res.json(data);
} catch (error) { } catch (error) {
console.error('Generation error:', error); console.error('Generation error:', error);

View File

@ -11,9 +11,11 @@ function App() {
const [isGenerating, setIsGenerating] = React.useState(false); const [isGenerating, setIsGenerating] = React.useState(false);
const [history, setHistory] = React.useState([]); const [history, setHistory] = React.useState([]);
const [error, setError] = React.useState(null);
const handleGenerate = async (data) => { const handleGenerate = async (data) => {
setIsGenerating(true); setIsGenerating(true);
setError(null);
try { try {
const response = await fetch('/api/generate', { const response = await fetch('/api/generate', {
method: 'POST', method: 'POST',
@ -40,7 +42,7 @@ function App() {
} catch (error) { } catch (error) {
console.error("Error generating sheet:", error); console.error("Error generating sheet:", error);
// Optional: Show error state setError(error.message || "Failed to generate sheet. Please try again.");
} finally { } finally {
setIsGenerating(false); setIsGenerating(false);
} }
@ -75,6 +77,11 @@ function App() {
{/* Main Content */} {/* Main Content */}
<main className="max-w-7xl mx-auto px-4"> <main className="max-w-7xl mx-auto px-4">
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-xl mb-6 flex items-center gap-2">
<span className="font-bold">Error:</span> {error}
</div>
)}
<Hero onGenerate={handleGenerate} generatedSheet={generatedSheet} isGenerating={isGenerating} /> <Hero onGenerate={handleGenerate} generatedSheet={generatedSheet} isGenerating={isGenerating} />
<Gallery items={galleryItems} title={galleryTitle} onRestore={handleRestore} /> <Gallery items={galleryItems} title={galleryTitle} onRestore={handleRestore} />
</main> </main>

View File

@ -68,7 +68,7 @@ export const Hero = ({ onGenerate, generatedSheet, isGenerating }) => {
// since we don't have a separate loading state, we can reuse isGenerating logic or add one. // since we don't have a separate loading state, we can reuse isGenerating logic or add one.
// For now, we'll just set the values when they arrive. // For now, we'll just set the values when they arrive.
const res = await fetch('http://localhost:3001/api/surprise', { method: 'POST' }); const res = await fetch('/api/surprise', { method: 'POST' });
if (!res.ok) throw new Error('Surprise failed'); if (!res.ok) throw new Error('Surprise failed');
const idea = await res.json(); const idea = await res.json();
@ -310,7 +310,7 @@ export const Hero = ({ onGenerate, generatedSheet, isGenerating }) => {
> >
<div className="text-[10px] font-bold text-slate-400 uppercase mb-1 w-full text-left">{sec.type}</div> <div className="text-[10px] font-bold text-slate-400 uppercase mb-1 w-full text-left">{sec.type}</div>
{/* Handle both new content_svg and legacy content fields */} {/* Handle both new content_svg and legacy content fields */}
{(sec.content_svg || sec.content || '').trim().startsWith('<svg') ? ( {(sec.content_svg || sec.content || '').toLowerCase().includes('<svg') ? (
<div <div
className={`w-full flex-1 flex items-center justify-center ${generatedSheet.sections.length === 1 ? 'max-h-full' : ''}`} className={`w-full flex-1 flex items-center justify-center ${generatedSheet.sections.length === 1 ? 'max-h-full' : ''}`}
dangerouslySetInnerHTML={{ __html: sec.content_svg || sec.content }} dangerouslySetInnerHTML={{ __html: sec.content_svg || sec.content }}