Spaces:
Build error
Build error
| "use client"; | |
| import { useState, useEffect, useCallback } from "react"; | |
| import { motion, AnimatePresence } from "framer-motion"; | |
| import { | |
| Menu, X, Bot, Wand2, ImageIcon, Video, Users, FolderGit2, Shield, | |
| RefreshCw, DollarSign, Clock, Film, Zap, TrendingUp, Calendar, | |
| Play, Pause, Trash2, Plus, Send, Copy, Eye, Settings, ChevronRight, | |
| CheckCircle2, AlertCircle, ExternalLink, CreditCard, Target, Sparkles, | |
| Heart, PawPrint, Star, Users2, Lightbulb, Flame, Rocket | |
| } from "lucide-react"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Input } from "@/components/ui/input"; | |
| 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 { ScrollArea } from "@/components/ui/scroll-area"; | |
| import { Label } from "@/components/ui/label"; | |
| import { Switch } from "@/components/ui/switch"; | |
| import { toast } from "sonner"; | |
| // Types | |
| interface Content { id: string; type: string; title: string; status: string; platform: string; createdAt: string; } | |
| interface Character { id: string; name: string; description: string | null; referenceImage: string | null; } | |
| interface Platform { id: string; name: string; type: string; isActive: boolean; isVerified: boolean; } | |
| interface Post { id: string; title: string | null; type: string; status: string; scheduledAt: string | null; } | |
| interface Story { id: string; title: string; genre: string; status: string; totalEpisodes: number; } | |
| interface Automation { id: string; name: string; type: string; isActive: boolean; runCount: number; } | |
| interface Pet { id: string; name: string; type: string; breed: string | null; personality: string | null; referenceImage: string | null; } | |
| interface Influencer { name: string; handle: string; platform: string; followers: number; engagement: number; niche: string; petCompanion: boolean; petType?: string; keyLessons: string[]; } | |
| // API Helper | |
| async function apiFetch(endpoint: string, options: RequestInit = {}) { | |
| const response = await fetch(`/api${endpoint}`, { | |
| headers: { "Content-Type": "application/json", ...options.headers }, | |
| ...options, | |
| }); | |
| return response.json(); | |
| } | |
| export default function Dashboard() { | |
| const [sidebarOpen, setSidebarOpen] = useState(true); | |
| const [activeTab, setActiveTab] = useState("prompt-engineer"); | |
| const [loading, setLoading] = useState(false); | |
| // Prompt Engineer | |
| const [userPrompt, setUserPrompt] = useState(""); | |
| const [promptType, setPromptType] = useState("image"); | |
| const [targetPlatform, setTargetPlatform] = useState("general"); | |
| const [optimizedPrompt, setOptimizedPrompt] = useState<Record<string, unknown> | null>(null); | |
| const [promptLoading, setPromptLoading] = useState(false); | |
| // Image Generation | |
| const [imagePrompt, setImagePrompt] = useState(""); | |
| const [imageStyle, setImageStyle] = useState("realistic"); | |
| const [imagePlatform, setImagePlatform] = useState("general"); | |
| const [imageLoading, setImageLoading] = useState(false); | |
| // Monetization | |
| const [platforms, setPlatforms] = useState<Platform[]>([]); | |
| const [selectedMonetizationPlatform, setSelectedMonetizationPlatform] = useState(""); | |
| // Posts | |
| const [posts, setPosts] = useState<Post[]>([]); | |
| const [postTitle, setPostTitle] = useState(""); | |
| const [postType, setPostType] = useState("reel"); | |
| const [postCaption, setPostCaption] = useState(""); | |
| const [scheduledTime, setScheduledTime] = useState(""); | |
| // Stories | |
| const [stories, setStories] = useState<Story[]>([]); | |
| const [storyPrompt, setStoryPrompt] = useState(""); | |
| const [storyGenre, setStoryGenre] = useState("lifestyle"); | |
| const [storyEpisodes, setStoryEpisodes] = useState(7); | |
| const [storyLoading, setStoryLoading] = useState(false); | |
| // Automation | |
| const [automations, setAutomations] = useState<Automation[]>([]); | |
| const [automationName, setAutomationName] = useState(""); | |
| const [automationType, setAutomationType] = useState("content_generation"); | |
| // Content & Characters | |
| const [contents, setContents] = useState<Content[]>([]); | |
| const [characters, setCharacters] = useState<Character[]>([]); | |
| // Pets | |
| const [pets, setPets] = useState<Pet[]>([]); | |
| const [petName, setPetName] = useState(""); | |
| const [petType, setPetType] = useState("dog"); | |
| const [petBreed, setPetBreed] = useState(""); | |
| const [petPersonality, setPetPersonality] = useState(""); | |
| const [includePetInContent, setIncludePetInContent] = useState(false); | |
| const [petLoading, setPetLoading] = useState(false); | |
| // Influencers | |
| const [influencers, setInfluencers] = useState<Influencer[]>([]); | |
| const [influencerNiche, setInfluencerNiche] = useState(""); | |
| const [influencerPlatform, setInfluencerPlatform] = useState(""); | |
| const [influencerWithPets, setInfluencerWithPets] = useState(false); | |
| const [influencerLoading, setInfluencerLoading] = useState(false); | |
| const [influencerAnalysis, setInfluencerAnalysis] = useState<Record<string, unknown> | null>(null); | |
| const [characterConcept, setCharacterConcept] = useState<Record<string, unknown> | null>(null); | |
| // Trends | |
| const [trends, setTrends] = useState<Record<string, unknown>[]>([]); | |
| const [viralStrategies, setViralStrategies] = useState<Record<string, unknown>[]>([]); | |
| const [contentIdeas, setContentIdeas] = useState<Record<string, unknown>[]>([]); | |
| const [trendAnalysis, setTrendAnalysis] = useState<Record<string, unknown> | null>(null); | |
| const [trendLoading, setTrendLoading] = useState(false); | |
| // Stats | |
| const [stats, setStats] = useState({ images: 0, videos: 0, stories: 0, automations: 0, pets: 0 }); | |
| // Load data | |
| const loadData = useCallback(async () => { | |
| setLoading(true); | |
| try { | |
| const [contentRes, platformsRes, postsRes, storiesRes, automationRes, petsRes] = await Promise.all([ | |
| apiFetch("/content"), | |
| apiFetch("/monetization"), | |
| apiFetch("/posts"), | |
| apiFetch("/storytelling"), | |
| apiFetch("/automation"), | |
| apiFetch("/pets"), | |
| ]); | |
| if (contentRes.success) { | |
| setContents(contentRes.contents); | |
| setStats({ | |
| images: contentRes.stats?.images || 0, | |
| videos: contentRes.stats?.videos || 0, | |
| stories: storiesRes?.total || 0, | |
| automations: automationRes?.stats?.total || 0, | |
| pets: petsRes?.total || 0, | |
| }); | |
| } | |
| if (platformsRes.success) setPlatforms(platformsRes.userPlatforms); | |
| if (postsRes.success) setPosts(postsRes.posts); | |
| if (storiesRes.success) setStories(storiesRes.stories); | |
| if (automationRes.success) setAutomations(automationRes.automations); | |
| if (petsRes.success) setPets(petsRes.pets); | |
| } catch { | |
| toast.error("Error al cargar datos"); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }, []); | |
| useEffect(() => { loadData(); }, [loadData]); | |
| // Actions | |
| 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: imagePlatform, | |
| style: imageStyle, | |
| includePet: includePetInContent, | |
| petId: pets[0]?.id | |
| }), | |
| }); | |
| if (result.success) { | |
| toast.success("Imagen generada"); | |
| loadData(); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| finally { setImageLoading(false); } | |
| }; | |
| const handleCreateStory = async () => { | |
| if (!storyPrompt.trim()) { toast.error("Describe tu historia"); return; } | |
| setStoryLoading(true); | |
| try { | |
| const result = await apiFetch("/storytelling", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| prompt: storyPrompt, | |
| genre: storyGenre, | |
| totalEpisodes: storyEpisodes, | |
| }), | |
| }); | |
| if (result.success) { | |
| toast.success(`Historia "${result.story?.title}" creada`); | |
| setStoryPrompt(""); | |
| loadData(); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| finally { setStoryLoading(false); } | |
| }; | |
| const handleCreatePost = async () => { | |
| if (!postTitle.trim()) { toast.error("Añade un título"); return; } | |
| try { | |
| const result = await apiFetch("/posts", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| title: postTitle, | |
| type: postType, | |
| caption: postCaption, | |
| scheduledAt: scheduledTime || null, | |
| autoGenerateCaption: true, | |
| }), | |
| }); | |
| if (result.success) { | |
| toast.success(scheduledTime ? "Post programado" : "Post creado"); | |
| setPostTitle(""); setPostCaption(""); setScheduledTime(""); | |
| loadData(); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| }; | |
| const handleCreateAutomation = async () => { | |
| if (!automationName.trim()) { toast.error("Dale un nombre"); return; } | |
| try { | |
| const result = await apiFetch("/automation", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| name: automationName, | |
| type: automationType, | |
| trigger: "schedule", | |
| triggerConfig: { schedule: "daily:09:00" }, | |
| actions: [{ type: "generate_content", prompt: "Genera contenido de lifestyle" }], | |
| }), | |
| }); | |
| if (result.success) { | |
| toast.success("Automatización creada"); | |
| setAutomationName(""); | |
| loadData(); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| }; | |
| const handleToggleAutomation = async (id: string, currentStatus: boolean) => { | |
| try { | |
| await apiFetch("/automation", { | |
| method: "PUT", | |
| body: JSON.stringify({ id, isActive: !currentStatus }), | |
| }); | |
| loadData(); | |
| } catch { toast.error("Error"); } | |
| }; | |
| const handleCreatePet = async () => { | |
| if (!petName.trim()) { toast.error("Dale un nombre a tu mascota"); return; } | |
| setPetLoading(true); | |
| try { | |
| const result = await apiFetch("/pets", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| name: petName, | |
| type: petType, | |
| breed: petBreed || undefined, | |
| personality: petPersonality || undefined, | |
| generateReference: true | |
| }), | |
| }); | |
| if (result.success) { | |
| toast.success(`Mascota "${petName}" creada (+${result.engagementBoost}% engagement)`); | |
| setPetName(""); setPetBreed(""); setPetPersonality(""); | |
| loadData(); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| finally { setPetLoading(false); } | |
| }; | |
| const handleAnalyzeInfluencers = async () => { | |
| setInfluencerLoading(true); | |
| try { | |
| const result = await apiFetch("/influencers", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| targetNiche: influencerNiche || "lifestyle", | |
| targetPlatform: influencerPlatform || undefined, | |
| includePets: influencerWithPets | |
| }), | |
| }); | |
| if (result.success) { | |
| setInfluencers(result.referenceInfluencers || []); | |
| setInfluencerAnalysis(result.analysis); | |
| setCharacterConcept(result.characterConcept); | |
| toast.success("Análisis completado"); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| finally { setInfluencerLoading(false); } | |
| }; | |
| const handleAnalyzeTrends = async () => { | |
| setTrendLoading(true); | |
| try { | |
| const result = await apiFetch("/trends", { | |
| method: "POST", | |
| body: JSON.stringify({ | |
| niche: influencerNiche || "lifestyle", | |
| platform: influencerPlatform || undefined, | |
| includePets: includePetInContent, | |
| daysToViral: 14 | |
| }), | |
| }); | |
| if (result.success) { | |
| setTrendAnalysis(result.analysis); | |
| toast.success("Análisis de tendencias completado"); | |
| } else toast.error(result.error); | |
| } catch { toast.error("Error"); } | |
| finally { setTrendLoading(false); } | |
| }; | |
| const loadTrends = async () => { | |
| try { | |
| const result = await apiFetch(`/trends?includePets=${includePetInContent}`); | |
| if (result.success) { | |
| setTrends(result.trends); | |
| setViralStrategies(result.viralStrategies); | |
| setContentIdeas(result.contentIdeas); | |
| } | |
| } catch { toast.error("Error cargando tendencias"); } | |
| }; | |
| useEffect(() => { | |
| if (activeTab === "trends") loadTrends(); | |
| }, [activeTab, includePetInContent]); | |
| const copyToClipboard = (text: string) => { | |
| navigator.clipboard.writeText(text); | |
| toast.success("Copiado"); | |
| }; | |
| const getStatusColor = (status: string) => { | |
| const colors: Record<string, string> = { | |
| completed: "bg-green-500/10 text-green-500", | |
| published: "bg-green-500/10 text-green-500", | |
| scheduled: "bg-blue-500/10 text-blue-500", | |
| draft: "bg-slate-500/10 text-slate-400", | |
| pending: "bg-yellow-500/10 text-yellow-500", | |
| active: "bg-green-500/10 text-green-500", | |
| }; | |
| return colors[status] || "bg-slate-500/10 text-slate-400"; | |
| }; | |
| const petTypes = [ | |
| { value: "dog", label: "🐕 Perro" }, | |
| { value: "cat", label: "🐱 Gato" }, | |
| { value: "bird", label: "🐦 Pájaro" }, | |
| { value: "rabbit", label: "🐰 Conejo" }, | |
| { value: "hamster", label: "🐹 Hámster" } | |
| ]; | |
| return ( | |
| <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 text-white"> | |
| {/* Sidebar */} | |
| <AnimatePresence mode="wait"> | |
| {sidebarOpen && ( | |
| <motion.aside | |
| initial={{ x: -280 }} animate={{ x: 0 }} exit={{ x: -280 }} | |
| className="fixed left-0 top-0 h-full w-64 bg-slate-900/90 backdrop-blur-xl border-r border-slate-800 z-50" | |
| > | |
| <div className="p-5"> | |
| <div className="flex items-center gap-3 mb-6"> | |
| <div className="w-10 h-10 rounded-xl bg-gradient-to-br from-violet-500 to-purple-600 flex items-center justify-center"> | |
| <Bot className="h-5 w-5" /> | |
| </div> | |
| <div> | |
| <h1 className="text-lg font-bold bg-gradient-to-r from-violet-400 to-purple-400 bg-clip-text text-transparent">Sofía Cloud</h1> | |
| <p className="text-xs text-slate-400">Monetización Pro</p> | |
| </div> | |
| </div> | |
| <nav className="space-y-1"> | |
| {[ | |
| { id: "prompt-engineer", icon: Wand2, label: "Ingeniero IA" }, | |
| { id: "images", icon: ImageIcon, label: "Imágenes" }, | |
| { id: "videos", icon: Video, label: "Videos" }, | |
| { id: "monetization", icon: DollarSign, label: "Monetización" }, | |
| { id: "posts", icon: Calendar, label: "Publicaciones" }, | |
| { id: "storytelling", icon: Film, label: "Storytelling" }, | |
| { id: "automation", icon: Zap, label: "Automatización" }, | |
| { id: "influencers", icon: Users2, label: "Influencers IA" }, | |
| { id: "pets", icon: PawPrint, label: "Mascotas" }, | |
| { id: "trends", icon: TrendingUp, label: "Tendencias" }, | |
| { id: "content", icon: FolderGit2, label: "Contenido" }, | |
| ].map((item) => ( | |
| <button key={item.id} onClick={() => setActiveTab(item.id)} | |
| className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg transition-all text-sm ${ | |
| activeTab === item.id ? "bg-violet-500/20 text-violet-400 border border-violet-500/30" : "text-slate-400 hover:bg-slate-800"}`}> | |
| <item.icon className="h-4 w-4" /><span>{item.label}</span> | |
| </button> | |
| ))} | |
| </nav> | |
| </div> | |
| <div className="absolute bottom-0 left-0 right-0 p-4 border-t border-slate-800"> | |
| <div className="grid grid-cols-2 gap-2 text-center text-xs"> | |
| <div><p className="text-xl font-bold text-violet-400">{stats.images}</p><p className="text-slate-500">Imágenes</p></div> | |
| <div><p className="text-xl font-bold text-blue-400">{stats.videos}</p><p className="text-slate-500">Videos</p></div> | |
| <div><p className="text-xl font-bold text-green-400">{stats.stories}</p><p className="text-slate-500">Historias</p></div> | |
| <div><p className="text-xl font-bold text-amber-400">{stats.pets}</p><p className="text-slate-500">Mascotas</p></div> | |
| </div> | |
| </div> | |
| </motion.aside> | |
| )} | |
| </AnimatePresence> | |
| {/* Main */} | |
| <div className={`transition-all duration-300 ${sidebarOpen ? "ml-64" : "ml-0"}`}> | |
| <header className="sticky top-0 z-40 bg-slate-950/80 backdrop-blur-xl border-b border-slate-800"> | |
| <div className="flex items-center justify-between px-6 py-3"> | |
| <div className="flex items-center gap-3"> | |
| <Button variant="ghost" size="icon" onClick={() => setSidebarOpen(!sidebarOpen)} className="text-slate-400"> | |
| {sidebarOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />} | |
| </Button> | |
| <h2 className="text-lg font-semibold capitalize">{activeTab.replace("-", " ")}</h2> | |
| </div> | |
| <Button variant="outline" size="sm" onClick={loadData} disabled={loading} className="border-slate-700"> | |
| <RefreshCw className={`h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}`} />Actualizar | |
| </Button> | |
| </div> | |
| </header> | |
| <main className="p-6"> | |
| {/* Prompt Engineer */} | |
| {activeTab === "prompt-engineer" && ( | |
| <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={() => setActiveTab("posts")} 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> | |
| )} | |
| {/* Images */} | |
| {activeTab === "images" && ( | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle className="flex items-center gap-2"><ImageIcon className="h-5 w-5 text-green-400" />Generar Imágenes</CardTitle></CardHeader> | |
| <CardContent className="space-y-4"> | |
| <Textarea placeholder="Describe la imagen..." value={userPrompt} onChange={(e) => setUserPrompt(e.target.value)} className="bg-slate-800 border-slate-700" /> | |
| <div className="grid grid-cols-3 gap-4"> | |
| <div> | |
| <Label className="text-xs text-slate-400">Estilo</Label> | |
| <Select value={imageStyle} onValueChange={setImageStyle}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="realistic">Realista</SelectItem> | |
| <SelectItem value="anime">Anime</SelectItem> | |
| <SelectItem value="artistic">Artístico</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div> | |
| <Label className="text-xs text-slate-400">Plataforma</Label> | |
| <Select value={imagePlatform} onValueChange={setImagePlatform}> | |
| <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="instagram">Instagram</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 ({pets[0]?.name})</Label> | |
| </div> | |
| )} | |
| <Button onClick={handleGenerateImage} disabled={imageLoading} className="w-full bg-green-600 hover:bg-green-700"> | |
| {imageLoading ? <RefreshCw className="h-4 w-4 mr-2 animate-spin" /> : <ImageIcon className="h-4 w-4 mr-2" />}Generar | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| )} | |
| {/* Monetization */} | |
| {activeTab === "monetization" && ( | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | |
| {[ | |
| { name: "OnlyFans", type: "subscription", fee: "20%", color: "from-blue-500 to-cyan-500", legal: "Adult content permitido" }, | |
| { name: "Patreon", type: "subscription", fee: "12%", color: "from-orange-500 to-red-500", legal: "Sin adult content real" }, | |
| { name: "Fansly", type: "mixed", fee: "20%", color: "from-purple-500 to-pink-500", legal: "Adult content permitido" }, | |
| { name: "Fanvue", type: "subscription", fee: "15%", color: "from-green-500 to-teal-500", legal: "Adult content permitido" }, | |
| { name: "Ko-fi", type: "tips", fee: "0%", color: "from-sky-500 to-blue-500", legal: "Sin adult content" }, | |
| { name: "Instagram", type: "free", fee: "0%", color: "from-pink-500 to-purple-500", legal: "Sin desnudez" }, | |
| ].map((p) => ( | |
| <Card key={p.name} className="bg-slate-900/50 border-slate-800"> | |
| <CardContent className="p-4"> | |
| <div className={`w-10 h-10 rounded-lg bg-gradient-to-br ${p.color} flex items-center justify-center mb-3`}> | |
| <DollarSign className="h-5 w-5 text-white" /> | |
| </div> | |
| <h3 className="font-semibold">{p.name}</h3> | |
| <p className="text-xs text-slate-400 mt-1">Fee: {p.fee}</p> | |
| <Badge variant="outline" className="mt-2 text-xs">{p.type}</Badge> | |
| <p className="text-xs text-amber-400 mt-2">⚖️ {p.legal}</p> | |
| <Button size="sm" className="w-full mt-3 bg-violet-600 hover:bg-violet-700"> | |
| <Plus className="h-3 w-3 mr-1" />Conectar | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| ))} | |
| </div> | |
| )} | |
| {/* Posts */} | |
| {activeTab === "posts" && ( | |
| <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"><Calendar className="h-5 w-5 text-blue-400" />Crear Publicación</CardTitle></CardHeader> | |
| <CardContent className="space-y-4"> | |
| <Input placeholder="Título" value={postTitle} onChange={(e) => setPostTitle(e.target.value)} className="bg-slate-800 border-slate-700" /> | |
| <Textarea placeholder="Caption..." value={postCaption} onChange={(e) => setPostCaption(e.target.value)} className="bg-slate-800 border-slate-700 min-h-20" /> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div> | |
| <Label className="text-xs text-slate-400">Tipo</Label> | |
| <Select value={postType} onValueChange={setPostType}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="reel">Reel</SelectItem> | |
| <SelectItem value="photo">Foto</SelectItem> | |
| <SelectItem value="carousel">Carrusel</SelectItem> | |
| <SelectItem value="story">Story</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div> | |
| <Label className="text-xs text-slate-400">Programar</Label> | |
| <Input type="datetime-local" value={scheduledTime} onChange={(e) => setScheduledTime(e.target.value)} className="bg-slate-800 border-slate-700 mt-1" /> | |
| </div> | |
| </div> | |
| <Button onClick={handleCreatePost} className="w-full bg-blue-600 hover:bg-blue-700"> | |
| <Calendar className="h-4 w-4 mr-2" />{scheduledTime ? "Programar" : "Crear"} | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle>Publicaciones</CardTitle></CardHeader> | |
| <CardContent> | |
| <ScrollArea className="h-64"> | |
| {posts.length === 0 ? ( | |
| <p className="text-slate-400 text-center py-8">No hay publicaciones</p> | |
| ) : ( | |
| <div className="space-y-2"> | |
| {posts.map((p) => ( | |
| <div key={p.id} className="p-3 rounded-lg bg-slate-800/50 flex items-center justify-between"> | |
| <div> | |
| <p className="font-medium text-sm">{p.title || "Sin título"}</p> | |
| <p className="text-xs text-slate-400">{p.type} • {p.scheduledAt ? new Date(p.scheduledAt).toLocaleString() : "Borrador"}</p> | |
| </div> | |
| <Badge className={getStatusColor(p.status)}>{p.status}</Badge> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </ScrollArea> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| )} | |
| {/* Storytelling */} | |
| {activeTab === "storytelling" && ( | |
| <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"><Film className="h-5 w-5 text-purple-400" />Crear Historia</CardTitle></CardHeader> | |
| <CardContent className="space-y-4"> | |
| <Textarea placeholder="Describe tu historia... Ej: Una historia de transformación fitness de 30 días..." value={storyPrompt} onChange={(e) => setStoryPrompt(e.target.value)} className="bg-slate-800 border-slate-700 min-h-24" /> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div> | |
| <Label className="text-xs text-slate-400">Género</Label> | |
| <Select value={storyGenre} onValueChange={setStoryGenre}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="lifestyle">Lifestyle</SelectItem> | |
| <SelectItem value="fitness">Fitness</SelectItem> | |
| <SelectItem value="romance">Romance</SelectItem> | |
| <SelectItem value="drama">Drama</SelectItem> | |
| <SelectItem value="comedy">Comedia</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div> | |
| <Label className="text-xs text-slate-400">Episodios</Label> | |
| <Input type="number" value={storyEpisodes} onChange={(e) => setStoryEpisodes(parseInt(e.target.value) || 7)} className="bg-slate-800 border-slate-700 mt-1" /> | |
| </div> | |
| </div> | |
| <Button onClick={handleCreateStory} disabled={storyLoading} className="w-full bg-purple-600 hover:bg-purple-700"> | |
| {storyLoading ? <RefreshCw className="h-4 w-4 mr-2 animate-spin" /> : <Sparkles className="h-4 w-4 mr-2" />}Generar Historia | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle>Historias Creadas</CardTitle></CardHeader> | |
| <CardContent> | |
| <ScrollArea className="h-64"> | |
| {stories.length === 0 ? ( | |
| <p className="text-slate-400 text-center py-8">No hay historias</p> | |
| ) : ( | |
| <div className="space-y-2"> | |
| {stories.map((s) => ( | |
| <div key={s.id} className="p-3 rounded-lg bg-slate-800/50"> | |
| <div className="flex justify-between items-start"> | |
| <div> | |
| <p className="font-medium">{s.title}</p> | |
| <p className="text-xs text-slate-400">{s.totalEpisodes} episodios • {s.genre}</p> | |
| </div> | |
| <Badge className={getStatusColor(s.status)}>{s.status}</Badge> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </ScrollArea> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| )} | |
| {/* Automation */} | |
| {activeTab === "automation" && ( | |
| <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"><Zap className="h-5 w-5 text-amber-400" />Crear Automatización</CardTitle></CardHeader> | |
| <CardContent className="space-y-4"> | |
| <Input placeholder="Nombre de la automatización" value={automationName} onChange={(e) => setAutomationName(e.target.value)} className="bg-slate-800 border-slate-700" /> | |
| <div> | |
| <Label className="text-xs text-slate-400">Tipo</Label> | |
| <Select value={automationType} onValueChange={setAutomationType}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="content_generation">Generación de Contenido</SelectItem> | |
| <SelectItem value="posting">Publicación Automática</SelectItem> | |
| <SelectItem value="cross_posting">Cross-Posting</SelectItem> | |
| <SelectItem value="trend_tracking">Seguimiento de Tendencias</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <Button onClick={handleCreateAutomation} className="w-full bg-amber-600 hover:bg-amber-700"> | |
| <Zap className="h-4 w-4 mr-2" />Crear | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle>Automatizaciones Activas</CardTitle></CardHeader> | |
| <CardContent> | |
| <ScrollArea className="h-64"> | |
| {automations.length === 0 ? ( | |
| <p className="text-slate-400 text-center py-8">No hay automatizaciones</p> | |
| ) : ( | |
| <div className="space-y-2"> | |
| {automations.map((a) => ( | |
| <div key={a.id} className="p-3 rounded-lg bg-slate-800/50 flex items-center justify-between"> | |
| <div> | |
| <p className="font-medium text-sm">{a.name}</p> | |
| <p className="text-xs text-slate-400">{a.type} • {a.runCount} ejecuciones</p> | |
| </div> | |
| <Button size="sm" variant="ghost" onClick={() => handleToggleAutomation(a.id, a.isActive)}> | |
| {a.isActive ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />} | |
| </Button> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </ScrollArea> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| )} | |
| {/* Influencers IA */} | |
| {activeTab === "influencers" && ( | |
| <div className="space-y-6"> | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2"><Users2 className="h-5 w-5 text-pink-400" />Análisis de Influencers IA Famosos</CardTitle> | |
| <CardDescription>Estudia los patrones de influencers IA exitosos para aplicarlos a tu contenido</CardDescription> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| <div className="grid grid-cols-1 md:grid-cols-4 gap-4"> | |
| <div> | |
| <Label className="text-xs text-slate-400">Tu Nicho</Label> | |
| <Input placeholder="lifestyle, fashion..." value={influencerNiche} onChange={(e) => setInfluencerNiche(e.target.value)} className="bg-slate-800 border-slate-700 mt-1" /> | |
| </div> | |
| <div> | |
| <Label className="text-xs text-slate-400">Plataforma Objetivo</Label> | |
| <Select value={influencerPlatform} onValueChange={setInfluencerPlatform}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue placeholder="Todas" /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="">Todas</SelectItem> | |
| <SelectItem value="instagram">Instagram</SelectItem> | |
| <SelectItem value="tiktok">TikTok</SelectItem> | |
| <SelectItem value="youtube">YouTube</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div className="flex items-end"> | |
| <div className="flex items-center gap-2 pb-2"> | |
| <Switch checked={influencerWithPets} onCheckedChange={setInfluencerWithPets} /> | |
| <Label className="text-sm text-slate-400">Con mascotas</Label> | |
| </div> | |
| </div> | |
| <div className="flex items-end"> | |
| <Button onClick={handleAnalyzeInfluencers} disabled={influencerLoading} className="w-full bg-pink-600 hover:bg-pink-700"> | |
| {influencerLoading ? <RefreshCw className="h-4 w-4 mr-2 animate-spin" /> : <Lightbulb className="h-4 w-4 mr-2" />}Analizar | |
| </Button> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| {influencers.length > 0 && ( | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> | |
| {influencers.map((inf, i) => ( | |
| <Card key={i} className="bg-slate-900/50 border-slate-800"> | |
| <CardContent className="p-4"> | |
| <div className="flex items-center gap-2 mb-2"> | |
| <div className="w-8 h-8 rounded-full bg-gradient-to-br from-pink-500 to-purple-500 flex items-center justify-center"> | |
| <Star className="h-4 w-4 text-white" /> | |
| </div> | |
| <div> | |
| <p className="font-medium text-sm">{inf.name}</p> | |
| <p className="text-xs text-slate-400">{inf.handle}</p> | |
| </div> | |
| </div> | |
| <div className="space-y-1 text-xs"> | |
| <p><span className="text-slate-400">Seguidores:</span> <span className="text-green-400">{(inf.followers / 1000000).toFixed(1)}M</span></p> | |
| <p><span className="text-slate-400">Engagement:</span> <span className="text-blue-400">{inf.engagement}%</span></p> | |
| <p><span className="text-slate-400">Nicho:</span> {inf.niche}</p> | |
| {inf.petCompanion && <Badge className="bg-amber-500/20 text-amber-400 mt-1"><PawPrint className="h-3 w-3 mr-1" />{inf.petType}</Badge>} | |
| </div> | |
| <div className="mt-2 pt-2 border-t border-slate-700"> | |
| <p className="text-xs text-slate-400">Lecciones:</p> | |
| <ul className="text-xs mt-1 space-y-1"> | |
| {inf.keyLessons.slice(0, 2).map((l, j) => ( | |
| <li key={j} className="text-slate-300">• {l}</li> | |
| ))} | |
| </ul> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ))} | |
| </div> | |
| )} | |
| {characterConcept && ( | |
| <Card className="bg-slate-900/50 border-slate-800 border-violet-500/30"> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2 text-violet-400"><Sparkles className="h-5 w-5" />Concepto de Personaje Sugerido</CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <h4 className="font-medium mb-2">Personaje</h4> | |
| <div className="p-3 rounded-lg bg-slate-800/50 text-sm"> | |
| {characterConcept.character && ( | |
| <> | |
| <p><strong>Nombre:</strong> {String(characterConcept.character.name || "")}</p> | |
| <p className="mt-1"><strong>Personalidad:</strong> {String(characterConcept.character.personality || "")}</p> | |
| <p className="mt-1"><strong>Historia:</strong> {String(characterConcept.character.backstory || "")}</p> | |
| </> | |
| )} | |
| </div> | |
| </div> | |
| <div> | |
| <h4 className="font-medium mb-2">Estilo Visual</h4> | |
| <div className="p-3 rounded-lg bg-slate-800/50 text-sm"> | |
| {characterConcept.visualStyle && ( | |
| <> | |
| <p><strong>Estética:</strong> {String(characterConcept.visualStyle.aesthetic || "")}</p> | |
| <p className="mt-1"><strong>Colores:</strong> {Array.isArray(characterConcept.visualStyle.colors) ? characterConcept.visualStyle.colors.join(", ") : ""}</p> | |
| </> | |
| )} | |
| </div> | |
| {Array.isArray(characterConcept.hashtags) && characterConcept.hashtags.length > 0 && ( | |
| <div className="mt-3 flex flex-wrap gap-1"> | |
| {characterConcept.hashtags.slice(0, 10).map((tag: string, i: number) => ( | |
| <Badge key={i} variant="outline" className="text-xs">{tag}</Badge> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| )} | |
| </div> | |
| )} | |
| {/* Pets */} | |
| {activeTab === "pets" && ( | |
| <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"><PawPrint className="h-5 w-5 text-amber-400" />Crear Mascota</CardTitle> | |
| <CardDescription>Las mascotas aumentan el engagement hasta un 35%</CardDescription> | |
| </CardHeader> | |
| <CardContent className="space-y-4"> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div> | |
| <Label className="text-xs text-slate-400">Nombre</Label> | |
| <Input placeholder="Max, Luna..." value={petName} onChange={(e) => setPetName(e.target.value)} className="bg-slate-800 border-slate-700 mt-1" /> | |
| </div> | |
| <div> | |
| <Label className="text-xs text-slate-400">Tipo</Label> | |
| <Select value={petType} onValueChange={setPetType}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| {petTypes.map((pt) => ( | |
| <SelectItem key={pt.value} value={pt.value}>{pt.label}</SelectItem> | |
| ))} | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| </div> | |
| <div className="grid grid-cols-2 gap-4"> | |
| <div> | |
| <Label className="text-xs text-slate-400">Raza (opcional)</Label> | |
| <Input placeholder="Golden Retriever..." value={petBreed} onChange={(e) => setPetBreed(e.target.value)} className="bg-slate-800 border-slate-700 mt-1" /> | |
| </div> | |
| <div> | |
| <Label className="text-xs text-slate-400">Personalidad</Label> | |
| <Select value={petPersonality} onValueChange={setPetPersonality}> | |
| <SelectTrigger className="bg-slate-800 border-slate-700 mt-1"><SelectValue placeholder="Seleccionar" /></SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="playful">Juguetón</SelectItem> | |
| <SelectItem value="calm">Tranquilo</SelectItem> | |
| <SelectItem value="energetic">Energético</SelectItem> | |
| <SelectItem value="curious">Curioso</SelectItem> | |
| <SelectItem value="lazy">Perezoso</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| </div> | |
| <Button onClick={handleCreatePet} disabled={petLoading} className="w-full bg-amber-600 hover:bg-amber-700"> | |
| {petLoading ? <RefreshCw className="h-4 w-4 mr-2 animate-spin" /> : <PawPrint className="h-4 w-4 mr-2" />}Crear Mascota | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle>Mis Mascotas</CardTitle></CardHeader> | |
| <CardContent> | |
| <ScrollArea className="h-64"> | |
| {pets.length === 0 ? ( | |
| <div className="text-center py-8 text-slate-400"> | |
| <PawPrint className="h-12 w-12 mx-auto mb-3 opacity-50" /> | |
| <p>No tienes mascotas</p> | |
| <p className="text-xs mt-1">Las mascotas aumentan el engagement</p> | |
| </div> | |
| ) : ( | |
| <div className="space-y-2"> | |
| {pets.map((p) => ( | |
| <div key={p.id} className="p-3 rounded-lg bg-slate-800/50 flex items-center justify-between"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-10 h-10 rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 flex items-center justify-center"> | |
| <PawPrint className="h-5 w-5 text-white" /> | |
| </div> | |
| <div> | |
| <p className="font-medium">{p.name}</p> | |
| <p className="text-xs text-slate-400">{p.breed || p.type} • {p.personality || "Sin personalidad"}</p> | |
| </div> | |
| </div> | |
| <Badge className="bg-green-500/20 text-green-400">+35%</Badge> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </ScrollArea> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| )} | |
| {/* Trends */} | |
| {activeTab === "trends" && ( | |
| <div className="space-y-6"> | |
| <div className="flex items-center gap-4"> | |
| <div className="flex items-center gap-2"> | |
| <Switch checked={includePetInContent} onCheckedChange={setIncludePetInContent} /> | |
| <Label className="text-sm text-slate-400">Incluir tendencias de mascotas</Label> | |
| </div> | |
| <Button onClick={handleAnalyzeTrends} disabled={trendLoading} className="bg-violet-600 hover:bg-violet-700"> | |
| {trendLoading ? <RefreshCw className="h-4 w-4 mr-2 animate-spin" /> : <Rocket className="h-4 w-4 mr-2" />}Analizar para Viralizar | |
| </Button> | |
| </div> | |
| {/* Tendencias Actuales */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> | |
| {trends.slice(0, 8).map((t, i) => ( | |
| <Card key={i} className="bg-slate-900/50 border-slate-800"> | |
| <CardContent className="p-4"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <Badge variant="outline" className="text-xs">{String(t.platform || "")}</Badge> | |
| <Flame className={`h-4 w-4 ${Number(t.growth) > 30 ? "text-red-400" : "text-orange-400"}`} /> | |
| </div> | |
| <p className="font-semibold">{String(t.name)}</p> | |
| <p className="text-green-400 text-sm">+{Number(t.growth).toFixed(1)}%</p> | |
| <p className="text-xs text-slate-400 mt-1">{String(t.category || "")}</p> | |
| </CardContent> | |
| </Card> | |
| ))} | |
| </div> | |
| {/* Estrategias Virales */} | |
| {viralStrategies.length > 0 && ( | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle className="flex items-center gap-2"><Rocket className="h-5 w-5 text-violet-400" />Estrategias Virales</CardTitle></CardHeader> | |
| <CardContent> | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> | |
| {viralStrategies.slice(0, 4).map((s, i) => ( | |
| <div key={i} className="p-3 rounded-lg bg-slate-800/50 border border-slate-700"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <p className="font-medium text-sm">{String(s.name)}</p> | |
| <Badge className="bg-green-500/20 text-green-400 text-xs">{String(s.successRate)}%</Badge> | |
| </div> | |
| <p className="text-xs text-slate-400 line-clamp-2">{String(s.description)}</p> | |
| <div className="flex flex-wrap gap-1 mt-2"> | |
| {Array.isArray(s.platforms) && s.platforms.slice(0, 3).map((p: string, j: number) => ( | |
| <Badge key={j} variant="outline" className="text-xs">{p}</Badge> | |
| ))} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| )} | |
| {/* Ideas de Contenido */} | |
| {contentIdeas.length > 0 && ( | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle className="flex items-center gap-2"><Lightbulb className="h-5 w-5 text-amber-400" />Ideas de Contenido Viral</CardTitle></CardHeader> | |
| <CardContent> | |
| <div className="space-y-3"> | |
| {contentIdeas.slice(0, 5).map((idea, i) => ( | |
| <div key={i} className="p-3 rounded-lg bg-slate-800/50 border border-slate-700"> | |
| <div className="flex items-center justify-between"> | |
| <div> | |
| <p className="font-medium">{String(idea.title)}</p> | |
| <p className="text-xs text-slate-400 mt-1">{String(idea.description)}</p> | |
| {idea.hook && <p className="text-xs text-violet-400 mt-1">Hook: "{String(idea.hook)}"</p>} | |
| </div> | |
| <div className="text-right"> | |
| <Badge className="bg-violet-500/20 text-violet-400">{String(idea.format)}</Badge> | |
| {idea.viralScore && <p className="text-xs text-green-400 mt-1">Score: {String(idea.viralScore)}</p>} | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| )} | |
| {/* Análisis de Tendencias */} | |
| {trendAnalysis && ( | |
| <Card className="bg-slate-900/50 border-slate-800 border-violet-500/30"> | |
| <CardHeader> | |
| <CardTitle className="flex items-center gap-2 text-violet-400"><Target className="h-5 w-5" />Plan para Viralizar</CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <h4 className="font-medium mb-2">Tendencias Emergentes</h4> | |
| <div className="space-y-2"> | |
| {Array.isArray(trendAnalysis.emergingTrends) && trendAnalysis.emergingTrends.slice(0, 3).map((t: Record<string, unknown>, i: number) => ( | |
| <div key={i} className="p-2 rounded bg-slate-800/50 text-sm"> | |
| <p className="font-medium">{String(t.name)}</p> | |
| <p className="text-xs text-slate-400">Potencial: {String(t.potential)}</p> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div> | |
| <h4 className="font-medium mb-2">Recomendaciones</h4> | |
| <div className="space-y-1"> | |
| {Array.isArray(trendAnalysis.recommendations) && trendAnalysis.recommendations.slice(0, 4).map((r: string, i: number) => ( | |
| <p key={i} className="text-sm text-slate-300">• {r}</p> | |
| ))} | |
| </div> | |
| {trendAnalysis.predictedViralPotential && ( | |
| <div className="mt-4 p-3 rounded-lg bg-violet-500/10 border border-violet-500/30"> | |
| <p className="text-sm">Potencial Viral Estimado:</p> | |
| <p className="text-2xl font-bold text-violet-400">{String(trendAnalysis.predictedViralPotential)}%</p> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| )} | |
| </div> | |
| )} | |
| {/* Content */} | |
| {activeTab === "content" && ( | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle>Contenido Generado</CardTitle></CardHeader> | |
| <CardContent> | |
| <ScrollArea className="h-96"> | |
| {contents.length === 0 ? ( | |
| <p className="text-slate-400 text-center py-8">No hay contenido generado</p> | |
| ) : ( | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | |
| {contents.map((c) => ( | |
| <div key={c.id} className="p-4 rounded-lg bg-slate-800/50 border border-slate-700"> | |
| <div className="flex justify-between mb-2"> | |
| <Badge className={getStatusColor(c.status)}>{c.status}</Badge> | |
| <Badge variant="outline">{c.type}</Badge> | |
| </div> | |
| <p className="font-medium line-clamp-2">{c.title}</p> | |
| <p className="text-xs text-slate-400 mt-1">{c.platform}</p> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </ScrollArea> | |
| </CardContent> | |
| </Card> | |
| )} | |
| {/* Videos */} | |
| {activeTab === "videos" && ( | |
| <Card className="bg-slate-900/50 border-slate-800"> | |
| <CardHeader><CardTitle className="flex items-center gap-2"><Video className="h-5 w-5 text-blue-400" />Generar Videos</CardTitle></CardHeader> | |
| <CardContent className="space-y-4"> | |
| <Textarea placeholder="Describe el video..." value={userPrompt} onChange={(e) => setUserPrompt(e.target.value)} className="bg-slate-800 border-slate-700" /> | |
| <Button className="w-full bg-blue-600 hover:bg-blue-700"> | |
| <Video className="h-4 w-4 mr-2" />Generar Video | |
| </Button> | |
| </CardContent> | |
| </Card> | |
| )} | |
| </main> | |
| </div> | |
| </div> | |
| ); | |
| } | |