import React, { useState, useEffect } from 'react'; import { Home, Wand2, Copy, CheckCircle2, AlertCircle, Loader2, LayoutTemplate, MessageSquare, MonitorPlay, Sparkles, Lightbulb, Download } from 'lucide-react'; const ESTRUCTURAS = [ { id: 'recorrido', nombre: 'Recorrido de Propiedad (Gancho, Tour, Llamado a la acción)' }, { id: 'aida', nombre: 'AIDA (Atención, Interés, Deseo, Acción) - Ideal para ventas' }, { id: 'pas', nombre: 'PAS (Problema, Agitación, Solución) - Captación de propietarios' }, { id: 'storytelling', nombre: 'Storytelling (La historia detrás de la casa o la zona)' }, { id: 'educativo', nombre: 'Educativo / Tips (Mito vs Realidad, 3 Consejos de compra)' }, { id: 'venta_directa', nombre: 'Venta Directa (Características clave, Beneficios, Precio)' } ]; const INTENCIONES = [ { id: 'profesional', nombre: 'Profesional y Confiable' }, { id: 'cercano', nombre: 'Cercano y Amigable' }, { id: 'lujo', nombre: 'Lujoso y Exclusivo' }, { id: 'urgencia', nombre: 'Sentido de Urgencia / Oportunidad de Inversión' }, { id: 'emocional', nombre: 'Emocional / Hogareño' } ]; const PLATAFORMAS = [ { id: 'reels', nombre: 'Instagram Reels / TikTok (Tour rápido, < 1 min)' }, { id: 'youtube', nombre: 'Video de YouTube (Recorrido detallado, > 3 min)' }, { id: 'historias', nombre: 'Historias de Instagram (Secuencia de interacción)' }, { id: 'llamada', nombre: 'Guion para Llamada en Frío (Captación de clientes)' }, { id: 'portal', nombre: 'Descripción para Portal Inmobiliario / Copy' } ]; export default function App() { const [idea, setIdea] = useState(''); const [estructura, setEstructura] = useState(ESTRUCTURAS[0].nombre); const [intencion, setIntencion] = useState(INTENCIONES[0].nombre); const [plataforma, setPlataforma] = useState(PLATAFORMAS[0].nombre); const [loading, setLoading] = useState(false); const [resultado, setResultado] = useState(''); const [error, setError] = useState(''); const [copiado, setCopiado] = useState(false); const [isEnhancing, setIsEnhancing] = useState(false); const [titulos, setTitulos] = useState([]); const [isGeneratingTitles, setIsGeneratingTitles] = useState(false); // Estados para PWA const [isInstallable, setIsInstallable] = useState(false); const [deferredPrompt, setDeferredPrompt] = useState(null); // 1. SEGURIDAD PARA CLOUDFLARE: const apiKey = ""; // --- LÓGICA DE PWA (Progressive Web App) --- useEffect(() => { // 1. Inyectar Web App Manifest dinámicamente const manifest = { name: "Real Estate Script Studio", short_name: "ScriptStudio", description: "Generador de contenido con IA para asesores inmobiliarios", start_url: ".", display: "standalone", background_color: "#f8fafc", theme_color: "#4f46e5", icons: [ { src: "https://api.iconify.design/lucide/home.svg?color=%234f46e5&width=192&height=192", sizes: "192x192", type: "image/svg+xml", purpose: "any maskable" }, { src: "https://api.iconify.design/lucide/home.svg?color=%234f46e5&width=512&height=512", sizes: "512x512", type: "image/svg+xml", purpose: "any maskable" } ] }; const manifestBlob = new Blob([JSON.stringify(manifest)], { type: 'application/json' }); const manifestUrl = URL.createObjectURL(manifestBlob); let manifestLink = document.querySelector('link[rel="manifest"]'); if (!manifestLink) { manifestLink = document.createElement('link'); manifestLink.rel = 'manifest'; document.head.appendChild(manifestLink); } manifestLink.href = manifestUrl; // 2. Registrar Service Worker Básico Dinámico const swCode = ` self.addEventListener('install', (e) => self.skipWaiting()); self.addEventListener('activate', (e) => e.waitUntil(clients.claim())); self.addEventListener('fetch', (e) => { // En un entorno de producción, aquí va la lógica de caché offline }); `; const swBlob = new Blob([swCode], { type: 'application/javascript' }); const swUrl = URL.createObjectURL(swBlob); if ('serviceWorker' in navigator) { navigator.serviceWorker.register(swUrl).catch(err => { console.log('SW registration failed (esperado en algunos iframes):', err); }); } // 3. Capturar el evento de instalación const handleBeforeInstallPrompt = (e) => { e.preventDefault(); // Evitar que aparezca el prompt por defecto setDeferredPrompt(e); setIsInstallable(true); }; window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); return () => { window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); URL.revokeObjectURL(manifestUrl); URL.revokeObjectURL(swUrl); }; }, []); const handleInstallClick = async () => { if (!deferredPrompt) return; // Mostrar el prompt de instalación nativo deferredPrompt.prompt(); // Esperar a que el usuario responda const { outcome } = await deferredPrompt.userChoice; if (outcome === 'accepted') { console.log('El usuario aceptó instalar la PWA'); setIsInstallable(false); // Ocultar el botón después de instalar } // El prompt solo se puede usar una vez setDeferredPrompt(null); }; // --- FIN LÓGICA DE PWA --- const fetchWithRetry = async (url, options, retries = 5) => { const delays = [1000, 2000, 4000, 8000, 16000]; for (let i = 0; i < retries; i++) { try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`Error HTTP: ${response.status}`); } return await response.json(); } catch (e) { if (i === retries - 1) throw e; await new Promise(res => setTimeout(res, delays[i])); } } }; const generarGuion = async () => { if (!idea.trim()) { setError('Por favor, ingresa una idea o frase para el guion.'); return; } setLoading(true); setError(''); setResultado(''); setCopiado(false); setTitulos([]); const promptText = ` Actúa basándote en tus instrucciones principales y escribe un guion o texto persuasivo con estos parámetros: - Propiedad o Tema a tratar: "${idea}" - Estructura a utilizar: ${estructura} - Tono o Intención: ${intencion} - Formato / Plataforma de destino: ${plataforma} Instrucciones adicionales de formato: 1. Incluye un Título atractivo o un "Gancho" (Hook). 2. Si es formato de video, divide el texto indicando [VISUAL] y [AUDIO]. 3. Termina siempre con un Llamado a la Acción (CTA) claro. `; const systemPrompt = ` Eres un asistente de marketing inmobiliario altamente capacitado con experiencia en ventas de lujo. Tu objetivo es generar textos magnéticos, usar un lenguaje sofisticado y nunca inventar datos que no se te hayan proporcionado. Ayudas a asesores y agentes inmobiliarios a generar guiones para videos, descripciones de propiedades y textos de captación que sean atractivos, profesionales y diseñados para generar leads y ventas. `; try { if (!apiKey) { throw new Error("Falta la API Key. Configura la clave en tu entorno de desarrollo."); } const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: promptText }] }], systemInstruction: { parts: [{ text: systemPrompt }] } }; const data = await fetchWithRetry(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const textResult = data.candidates?.[0]?.content?.parts?.[0]?.text; if (textResult) { setResultado(textResult); } else { throw new Error("No se recibió contenido de la API."); } } catch (err) { console.error(err); setError('Ocurrió un error al generar el guion. Por favor, intenta de nuevo más tarde.'); } finally { setLoading(false); } }; const mejorarIdea = async () => { if (!idea.trim()) return; setIsEnhancing(true); setError(''); const promptText = `Toma estas notas breves o descripción básica de una propiedad y conviértelas en un párrafo detallado, atractivo y profesional, ideal para marketing inmobiliario. Resalta los posibles beneficios ocultos. Notas originales: "${idea}"`; const systemPrompt = "Eres un asistente de redacción inmobiliaria. Tu objetivo es embellecer y expandir descripciones de propiedades a partir de notas cortas. Devuelve SOLO el texto mejorado, sin introducciones ni comillas."; try { const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`; const data = await fetchWithRetry(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: promptText }] }], systemInstruction: { parts: [{ text: systemPrompt }] } }) }); const textResult = data.candidates?.[0]?.content?.parts?.[0]?.text; if (textResult) setIdea(textResult.trim()); } catch (err) { setError('Error al mejorar la descripción con IA.'); } finally { setIsEnhancing(false); } }; const generarTitulosVirales = async () => { if (!resultado) return; setIsGeneratingTitles(true); const promptText = `Basándote en el siguiente guion o texto inmobiliario, genera exactamente 5 opciones de títulos o "ganchos" (hooks) virales y llamativos para redes sociales (TikTok/Reels/YouTube). Hazlos irresistibles para que la audiencia se detenga a ver el video.\n\nTexto original:\n${resultado}`; const systemPrompt = "Eres un experto en viralidad en redes sociales. Genera exactamente 5 ganchos cortos y contundentes basados en el texto proporcionado. Responde en formato de lista."; try { const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`; const data = await fetchWithRetry(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: promptText }] }], systemInstruction: { parts: [{ text: systemPrompt }] } }) }); const textResult = data.candidates?.[0]?.content?.parts?.[0]?.text; if (textResult) { const lineas = textResult.split('\n').filter(l => l.trim().length > 5); setTitulos(lineas); } } catch (err) { console.error(err); } finally { setIsGeneratingTitles(false); } }; const copiarAlPortapapeles = () => { try { document.execCommand('copy'); navigator.clipboard.writeText(resultado).then(() => { setCopiado(true); setTimeout(() => setCopiado(false), 2000); }); } catch (err) { const textArea = document.createElement("textarea"); textArea.value = resultado; document.body.appendChild(textArea); textArea.select(); document.execCommand("copy"); document.body.removeChild(textArea); setCopiado(true); setTimeout(() => setCopiado(false), 2000); } }; return (
{/* Header con Botón de Instalar */}

Real Estate Script Studio

Generador de contenido para asesores inmobiliarios

{/* Botón de Instalar PWA (Sólo visible si es instalable) */} {isInstallable && ( )}
{/* Columna Izquierda: Formulario (5 columnas) */}

Configura tu Guion

{/* Input Idea */}