Skip to content

Commit bb74b69

Browse files
Juan Castañojuancastano
authored andcommitted
Adding error screen
1 parent aff62b6 commit bb74b69

File tree

8 files changed

+368
-33
lines changed

8 files changed

+368
-33
lines changed

app/api/generate/route.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,16 @@ export async function POST(request: NextRequest) {
5252
// }
5353
// }
5454

55-
const { sbxId, template, url, allFiles } = await createSandbox({ files: repairedFiles });
55+
const sandboxResult = await createSandbox({ files: repairedFiles });
5656

5757
// Return the results to the client
5858
return NextResponse.json({
5959
originalFiles: generatedFiles,
60-
repairedFiles: allFiles, // Use the allFiles from the sandbox
61-
buildOutput: `Sandbox created with template: ${template}, ID: ${sbxId}`,
62-
previewUrl: url,
60+
repairedFiles: sandboxResult.allFiles, // Use the allFiles from the sandbox
61+
buildOutput: `Sandbox created with template: ${sandboxResult.template}, ID: ${sandboxResult.sbxId}`,
62+
previewUrl: sandboxResult.url,
63+
buildErrors: sandboxResult.buildErrors,
64+
hasErrors: sandboxResult.hasErrors,
6365
});
6466
} catch (error) {
6567
console.error('Error generating app:', error);

app/chat/page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ type GenerationResult = {
1212
originalFiles?: z.infer<typeof benchifyFileSchema>;
1313
buildOutput: string;
1414
previewUrl: string;
15+
buildErrors?: Array<{
16+
type: 'typescript' | 'build' | 'runtime';
17+
message: string;
18+
file?: string;
19+
line?: number;
20+
column?: number;
21+
}>;
22+
hasErrors?: boolean;
1523
};
1624

1725
export default function ChatPage() {
@@ -103,6 +111,8 @@ export default function ChatPage() {
103111
code={result?.repairedFiles || result?.originalFiles || []}
104112
isGenerating={isGenerating}
105113
prompt={initialPrompt}
114+
buildErrors={result?.buildErrors}
115+
hasErrors={result?.hasErrors}
106116
/>
107117
</div>
108118
</div>
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
'use client';
2+
3+
import { AlertCircle, Code, FileX, Terminal } from 'lucide-react';
4+
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
5+
import { Badge } from '@/components/ui/badge';
6+
import { ScrollArea } from '@/components/ui/scroll-area';
7+
8+
interface BuildError {
9+
type: 'typescript' | 'build' | 'runtime';
10+
message: string;
11+
file?: string;
12+
line?: number;
13+
column?: number;
14+
}
15+
16+
interface ErrorDisplayProps {
17+
errors: BuildError[];
18+
}
19+
20+
export function ErrorDisplay({ errors }: ErrorDisplayProps) {
21+
const getErrorIcon = (type: BuildError['type']) => {
22+
switch (type) {
23+
case 'typescript':
24+
return <Code className="h-4 w-4" />;
25+
case 'build':
26+
return <Terminal className="h-4 w-4" />;
27+
case 'runtime':
28+
return <AlertCircle className="h-4 w-4" />;
29+
default:
30+
return <FileX className="h-4 w-4" />;
31+
}
32+
};
33+
34+
const getErrorColor = (type: BuildError['type']) => {
35+
switch (type) {
36+
case 'typescript':
37+
return 'bg-blue-500/10 text-blue-700 dark:text-blue-400';
38+
case 'build':
39+
return 'bg-orange-500/10 text-orange-700 dark:text-orange-400';
40+
case 'runtime':
41+
return 'bg-red-500/10 text-red-700 dark:text-red-400';
42+
default:
43+
return 'bg-gray-500/10 text-gray-700 dark:text-gray-400';
44+
}
45+
};
46+
47+
const groupedErrors = errors.reduce((acc, error) => {
48+
if (!acc[error.type]) {
49+
acc[error.type] = [];
50+
}
51+
acc[error.type].push(error);
52+
return acc;
53+
}, {} as Record<string, BuildError[]>);
54+
55+
return (
56+
<div className="w-full h-full flex items-center justify-center rounded-md border bg-background p-6">
57+
<div className="w-full max-w-2xl">
58+
<div className="text-center mb-6">
59+
<AlertCircle className="h-12 w-12 text-destructive mx-auto mb-4" />
60+
<h3 className="text-lg font-semibold mb-2">Build Errors Detected</h3>
61+
<p className="text-muted-foreground text-sm">
62+
Your project has some issues that need to be fixed before it can run properly.
63+
</p>
64+
</div>
65+
66+
<ScrollArea className="max-h-96">
67+
<div className="space-y-4">
68+
{Object.entries(groupedErrors).map(([type, typeErrors]) => (
69+
<div key={type} className="space-y-2">
70+
<div className="flex items-center gap-2 mb-3">
71+
{getErrorIcon(type as BuildError['type'])}
72+
<h4 className="font-medium capitalize">{type} Errors</h4>
73+
<Badge variant="secondary" className="text-xs">
74+
{typeErrors.length}
75+
</Badge>
76+
</div>
77+
78+
{typeErrors.map((error, index) => (
79+
<Alert key={index} className="border-l-4 border-destructive">
80+
<AlertTitle className="flex items-center gap-2 text-sm">
81+
<Badge
82+
variant="outline"
83+
className={`text-xs ${getErrorColor(error.type)}`}
84+
>
85+
{error.type}
86+
</Badge>
87+
{error.file && (
88+
<span className="text-muted-foreground">
89+
{error.file}
90+
{error.line && `:${error.line}`}
91+
{error.column && `:${error.column}`}
92+
</span>
93+
)}
94+
</AlertTitle>
95+
<AlertDescription className="mt-2 text-sm font-mono bg-muted/50 p-2 rounded text-wrap break-words">
96+
{error.message}
97+
</AlertDescription>
98+
</Alert>
99+
))}
100+
</div>
101+
))}
102+
</div>
103+
</ScrollArea>
104+
105+
<div className="mt-6 p-4 bg-muted/50 rounded-lg">
106+
<h4 className="text-sm font-medium mb-2">💡 Common Solutions:</h4>
107+
<ul className="text-sm text-muted-foreground space-y-1">
108+
<li>• Check for missing imports or typos in component names</li>
109+
<li>• Verify that all props are properly typed</li>
110+
<li>• Make sure all dependencies are correctly installed</li>
111+
<li>• Try regenerating the component with more specific requirements</li>
112+
</ul>
113+
</div>
114+
</div>
115+
</div>
116+
);
117+
}

components/ui-builder/preview-card.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { benchifyFileSchema } from "@/lib/schemas";
66
import { z } from "zod";
77
import { CodeEditor } from "./code-editor";
88
import { DownloadButton } from "./download-button";
9+
import { ErrorDisplay } from "./error-display";
910

1011
interface Step {
1112
id: string;
@@ -36,14 +37,31 @@ const GENERATION_STEPS: Step[] = [
3637
},
3738
];
3839

40+
interface BuildError {
41+
type: 'typescript' | 'build' | 'runtime';
42+
message: string;
43+
file?: string;
44+
line?: number;
45+
column?: number;
46+
}
47+
3948
interface PreviewCardProps {
4049
previewUrl?: string;
4150
code: z.infer<typeof benchifyFileSchema>;
4251
isGenerating?: boolean;
4352
prompt?: string;
53+
buildErrors?: BuildError[];
54+
hasErrors?: boolean;
4455
}
4556

46-
export function PreviewCard({ previewUrl, code, isGenerating = false, prompt }: PreviewCardProps) {
57+
export function PreviewCard({
58+
previewUrl,
59+
code,
60+
isGenerating = false,
61+
prompt,
62+
buildErrors = [],
63+
hasErrors = false
64+
}: PreviewCardProps) {
4765
const files = code || [];
4866
const [currentStep, setCurrentStep] = useState(0);
4967

@@ -111,8 +129,8 @@ export function PreviewCard({ previewUrl, code, isGenerating = false, prompt }:
111129
</div>
112130
<div className="flex-1 min-w-0">
113131
<p className={`text-sm font-medium ${isCompleted ? 'text-green-700 dark:text-green-400' :
114-
isCurrent ? 'text-primary' :
115-
'text-muted-foreground'
132+
isCurrent ? 'text-primary' :
133+
'text-muted-foreground'
116134
}`}>
117135
{step.label}
118136
</p>
@@ -127,6 +145,9 @@ export function PreviewCard({ previewUrl, code, isGenerating = false, prompt }:
127145
</CardContent>
128146
</Card>
129147
</div>
148+
) : hasErrors && buildErrors.length > 0 ? (
149+
// Show build errors if there are any
150+
<ErrorDisplay errors={buildErrors} />
130151
) : previewUrl ? (
131152
// Show the actual preview iframe when ready
132153
<div className="w-full h-full overflow-hidden rounded-md border bg-background">

components/ui/badge.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as React from "react"
2+
import { Slot } from "@radix-ui/react-slot"
3+
import { cva, type VariantProps } from "class-variance-authority"
4+
5+
import { cn } from "@/lib/utils"
6+
7+
const badgeVariants = cva(
8+
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9+
{
10+
variants: {
11+
variant: {
12+
default:
13+
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14+
secondary:
15+
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16+
destructive:
17+
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18+
outline:
19+
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20+
},
21+
},
22+
defaultVariants: {
23+
variant: "default",
24+
},
25+
}
26+
)
27+
28+
function Badge({
29+
className,
30+
variant,
31+
asChild = false,
32+
...props
33+
}: React.ComponentProps<"span"> &
34+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
35+
const Comp = asChild ? Slot : "span"
36+
37+
return (
38+
<Comp
39+
data-slot="badge"
40+
className={cn(badgeVariants({ variant }), className)}
41+
{...props}
42+
/>
43+
)
44+
}
45+
46+
export { Badge, badgeVariants }

0 commit comments

Comments
 (0)