Spaces:
Build error
Build error
| "use client"; | |
| import { useState } from "react"; | |
| import { Wand2, ImageIcon, Calendar, RefreshCw, Copy } from "lucide-react"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Textarea } from "@/components/ui/textarea"; | |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Badge } from "@/components/ui/badge"; | |
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; | |
| import { Label } from "@/components/ui/label"; | |
| import { Switch } from "@/components/ui/switch"; | |
| import { toast } from "sonner"; | |
| import { apiFetch, copyToClipboard, type Pet } from "./types"; | |
| interface Props { | |
| pets: Pet[]; | |
| includePetInContent: boolean; | |
| setIncludePetInContent: (v: boolean) => void; | |
| onImageGenerated: () => void; | |
| } | |
| export default function PromptEngineerTab({ pets, includePetInContent, setIncludePetInContent, onImageGenerated }: Props) { | |
| const [userPrompt, setUserPrompt] = useState(""); | |
| const [promptType, setPromptType] = useState("image"); | |
| const [targetPlatform, setTargetPlatform] = useState("general"); | |
| const [optimizedPrompt, setOptimizedPrompt] = useState<any | null>(null); | |
| const [promptLoading, setPromptLoading] = useState(false); | |
| const [imageLoading, setImageLoading] = useState(false); | |
| const handleOptimizePrompt = async () => { | |
| if (!userPrompt.trim()) { toast.error("Escribe un prompt"); return; } | |
| setPromptLoading(true); | |
| try { | |
| const result = await apiFetch("/prompt-engineer", { | |
| method: "POST", | |
| body: JSON.stringify({ prompt: userPrompt, type: promptType, platform: targetPlatform }), | |
| }); | |
| if (result.success) { | |
| setOptimizedPrompt(result); | |
| toast.success("Prompt optimizado"); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| finally { setPromptLoading(false); } | |
| }; | |
| const handleGenerateImage = async () => { | |
| if (!userPrompt.trim()) { toast.error("Escribe un prompt"); return; } | |
| setImageLoading(true); | |
| try { | |
| const result = await apiFetch("/generate/image", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| prompt: userPrompt, | |
| optimizedPrompt: optimizedPrompt?.optimizedPrompt, | |
| platform: targetPlatform, | |
| style: "realistic", | |
| includePet: includePetInContent, | |
| petId: pets[0]?.id | |
| }), | |
| }); | |
| if (result.success) { | |
| toast.success("Imagen generada"); | |
| onImageGenerated(); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| finally { setImageLoading(false); } | |
| }; | |
| return ( | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2"><Wand2 className="h-5 w-5 text-violet-400" />Ingeniero de Prompts</CardTitle> | |
| <CardDescription>Describe en lenguaje natural lo que quieres crear</CardDescription> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| <Textarea placeholder="Ej: Quiero fotos de una mujer rubia en la playa para OnlyFans..." value={userPrompt} onChange={(e) => setUserPrompt(e.target.value)} className="bg-slate-800 border-slate-700 min-h-28" /> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div> | |
| <Label className="text-xs text-slate-400">Tipo</Label> | |
| <Select value={promptType} onValueChange={setPromptType}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="image">Imagen</SelectItem> | |
| <SelectItem value="video">Video</SelectItem> | |
| <SelectItem value="reel">Reel</SelectItem> | |
| <SelectItem value="carousel">Carrusel</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div> | |
| <Label className="text-xs text-slate-400">Plataforma</Label> | |
| <Select value={targetPlatform} onValueChange={setTargetPlatform}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="general">General</SelectItem> | |
| <SelectItem value="onlyfans">OnlyFans</SelectItem> | |
| <SelectItem value="patreon">Patreon</SelectItem> | |
| <SelectItem value="instagram">Instagram</SelectItem> | |
| <SelectItem value="tiktok">TikTok</SelectItem> | |
| <SelectItem value="youtube">YouTube</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| </div> | |
| {pets.length > 0 && ( | |
| <div className="flex items-center gap-2"> | |
| <Switch checked={includePetInContent} onCheckedChange={setIncludePetInContent} /> | |
| <Label className="text-sm text-slate-400">Incluir mascota en el contenido (+35% engagement)</Label> | |
| </div> | |
| )} | |
| <Button onClick={handleOptimizePrompt} disabled={promptLoading} className="w-full bg-violet-600 hover:bg-violet-700"> | |
| {promptLoading ? <RefreshCw className="h-4 w-4 mr-2 animate-spin" /> : <Wand2 className="h-4 w-4 mr-2" />}Optimizar | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle>Prompt Optimizado</CardTitle></CardHeader> | |
| <CardContent> | |
| {optimizedPrompt ? ( | |
| <div className="space-y-3"> | |
| <div className="p-3 rounded-lg bg-slate-800/50 border border-slate-700"> | |
| <div className="flex justify-between mb-2"> | |
| <Badge className="bg-violet-500/20 text-violet-400">{String(optimizedPrompt.type)}</Badge> | |
| <Button variant="ghost" size="sm" onClick={() => copyToClipboard(String(optimizedPrompt.optimizedPrompt))}><Copy className="h-4 w-4" /></Button> | |
| </div> | |
| <p className="text-sm whitespace-pre-wrap">{String(optimizedPrompt.optimizedPrompt)}</p> | |
| </div> | |
| <div className="flex gap-2"> | |
| <Button onClick={handleGenerateImage} disabled={imageLoading} className="flex-1 bg-green-600 hover:bg-green-700"> | |
| <ImageIcon className="h-4 w-4 mr-2" />Generar Imagen | |
| </Button> | |
| <Button onClick={() => { }} className="flex-1 bg-blue-600 hover:bg-blue-700"> | |
| <Calendar className="h-4 w-4 mr-2" />Crear Post | |
| </Button> | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className="text-center py-12 text-slate-400"> | |
| <Wand2 className="h-12 w-12 mx-auto mb-3 opacity-50" /><p>El prompt optimizado aparecerá aquí</p> | |
| </div> | |
| )} | |
| </CardContent> | |
| </Card> | |
| </div> | |
| ); | |
| } | |