Spaces:
No application file
No application file
| "use client" | |
| import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" | |
| import { Button } from "@/components/ui/button" | |
| import { Badge } from "@/components/ui/badge" | |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" | |
| import { ScrollArea } from "@/components/ui/scroll-area" | |
| import { Code, Eye, Download, Copy, RefreshCw } from "lucide-react" | |
| import { useState } from "react" | |
| interface WebsitePreviewProps { | |
| generatedWebsite?: { | |
| html: string | |
| css: string | |
| js: string | |
| preview: string | |
| } | |
| isGenerating?: boolean | |
| } | |
| export function WebsitePreview({ generatedWebsite, isGenerating }: WebsitePreviewProps) { | |
| const [activeTab, setActiveTab] = useState("preview") | |
| const copyToClipboard = (content: string) => { | |
| navigator.clipboard.writeText(content) | |
| } | |
| const downloadFile = (content: string, filename: string) => { | |
| const blob = new Blob([content], { type: "text/plain" }) | |
| const url = URL.createObjectURL(blob) | |
| const a = document.createElement("a") | |
| a.href = url | |
| a.download = filename | |
| a.click() | |
| URL.revokeObjectURL(url) | |
| } | |
| return ( | |
| <Card className="w-full h-[700px] flex flex-col"> | |
| <CardHeader className="pb-3"> | |
| <div className="flex items-center justify-between"> | |
| <CardTitle className="flex items-center gap-2"> | |
| <Eye className="h-5 w-5" /> | |
| Website Preview | |
| </CardTitle> | |
| <div className="flex items-center gap-2"> | |
| {isGenerating && ( | |
| <Badge variant="secondary" className="gap-1"> | |
| <RefreshCw className="h-3 w-3 animate-spin" /> | |
| Generating... | |
| </Badge> | |
| )} | |
| {generatedWebsite && <Badge variant="default">Ready</Badge>} | |
| </div> | |
| </div> | |
| </CardHeader> | |
| <CardContent className="flex-1 flex flex-col overflow-hidden p-0"> | |
| {generatedWebsite ? ( | |
| <Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col"> | |
| <div className="px-6 pb-0"> | |
| <TabsList className="grid w-full grid-cols-4"> | |
| <TabsTrigger value="preview">Preview</TabsTrigger> | |
| <TabsTrigger value="html">HTML</TabsTrigger> | |
| <TabsTrigger value="css">CSS</TabsTrigger> | |
| <TabsTrigger value="js">JavaScript</TabsTrigger> | |
| </TabsList> | |
| </div> | |
| <div className="flex-1 overflow-hidden"> | |
| <TabsContent value="preview" className="h-full m-0 p-6"> | |
| <div className="h-full border rounded-lg overflow-hidden bg-white"> | |
| <iframe | |
| srcDoc={generatedWebsite.preview} | |
| className="w-full h-full border-0" | |
| title="Website Preview" | |
| /> | |
| </div> | |
| </TabsContent> | |
| <TabsContent value="html" className="h-full m-0 p-6"> | |
| <div className="h-full flex flex-col"> | |
| <div className="flex items-center justify-between mb-3"> | |
| <h3 className="text-sm font-medium">HTML Code</h3> | |
| <div className="flex gap-2"> | |
| <Button size="sm" variant="outline" onClick={() => copyToClipboard(generatedWebsite.html)}> | |
| <Copy className="h-4 w-4" /> | |
| </Button> | |
| <Button | |
| size="sm" | |
| variant="outline" | |
| onClick={() => downloadFile(generatedWebsite.html, "index.html")} | |
| > | |
| <Download className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| </div> | |
| <ScrollArea className="flex-1 border rounded-lg"> | |
| <pre className="p-4 text-sm"> | |
| <code>{generatedWebsite.html}</code> | |
| </pre> | |
| </ScrollArea> | |
| </div> | |
| </TabsContent> | |
| <TabsContent value="css" className="h-full m-0 p-6"> | |
| <div className="h-full flex flex-col"> | |
| <div className="flex items-center justify-between mb-3"> | |
| <h3 className="text-sm font-medium">CSS Code</h3> | |
| <div className="flex gap-2"> | |
| <Button size="sm" variant="outline" onClick={() => copyToClipboard(generatedWebsite.css)}> | |
| <Copy className="h-4 w-4" /> | |
| </Button> | |
| <Button | |
| size="sm" | |
| variant="outline" | |
| onClick={() => downloadFile(generatedWebsite.css, "styles.css")} | |
| > | |
| <Download className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| </div> | |
| <ScrollArea className="flex-1 border rounded-lg"> | |
| <pre className="p-4 text-sm"> | |
| <code>{generatedWebsite.css}</code> | |
| </pre> | |
| </ScrollArea> | |
| </div> | |
| </TabsContent> | |
| <TabsContent value="js" className="h-full m-0 p-6"> | |
| <div className="h-full flex flex-col"> | |
| <div className="flex items-center justify-between mb-3"> | |
| <h3 className="text-sm font-medium">JavaScript Code</h3> | |
| <div className="flex gap-2"> | |
| <Button size="sm" variant="outline" onClick={() => copyToClipboard(generatedWebsite.js)}> | |
| <Copy className="h-4 w-4" /> | |
| </Button> | |
| <Button | |
| size="sm" | |
| variant="outline" | |
| onClick={() => downloadFile(generatedWebsite.js, "script.js")} | |
| > | |
| <Download className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| </div> | |
| <ScrollArea className="flex-1 border rounded-lg"> | |
| <pre className="p-4 text-sm"> | |
| <code>{generatedWebsite.js}</code> | |
| </pre> | |
| </ScrollArea> | |
| </div> | |
| </TabsContent> | |
| </div> | |
| </Tabs> | |
| ) : ( | |
| <div className="flex-1 flex items-center justify-center p-6"> | |
| <div className="text-center"> | |
| <Code className="h-12 w-12 text-muted-foreground mx-auto mb-4" /> | |
| <h3 className="text-lg font-medium mb-2">No Website Generated</h3> | |
| <p className="text-muted-foreground text-sm"> | |
| Ask the AI agent to generate a website, landing page, or web application | |
| </p> | |
| </div> | |
| </div> | |
| )} | |
| </CardContent> | |
| </Card> | |
| ) | |
| } | |