1443 lines
63 KiB
HTML
1443 lines
63 KiB
HTML
<!DOCTYPE html>
|
|
<!--
|
|
============================================================================
|
|
PROPRIEDAD INTELECTUAL - GKACHELE
|
|
============================================================================
|
|
|
|
Copyright (c) 2025 GKACHELE
|
|
Desarrollado desde: 3 de noviembre de 2025
|
|
|
|
Este código y su estructura son propiedad exclusiva de GKACHELE.
|
|
Todos los derechos reservados.
|
|
|
|
PROPIEDAD:
|
|
- Código HTML/CSS/JavaScript: Propiedad de GKACHELE
|
|
- Sistema de automatización: Propiedad de GKACHELE
|
|
- Estructura y diseño: Propiedad de GKACHELE
|
|
- Lógica de negocio: Propiedad de GKACHELE
|
|
|
|
PROHIBICIONES:
|
|
- Prohibida la copia, reproducción o distribución sin autorización
|
|
- Prohibida la modificación no autorizada
|
|
- Prohibido el uso comercial sin licencia
|
|
|
|
CONTACTO: gkachele.duckdns.org
|
|
|
|
============================================================================
|
|
-->
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes" />
|
|
<!-- Security Headers -->
|
|
<meta http-equiv="X-Content-Type-Options" content="nosniff" />
|
|
<meta http-equiv="X-Frame-Options" content="DENY" />
|
|
<meta http-equiv="X-XSS-Protection" content="1; mode=block" />
|
|
<meta http-equiv="Referrer-Policy" content="strict-origin-when-cross-origin" />
|
|
<meta http-equiv="Permissions-Policy" content="geolocation=(), microphone=(), camera=()" />
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" />
|
|
|
|
<!-- Cache Control -->
|
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
<meta http-equiv="Expires" content="0" />
|
|
|
|
<!-- SEO Meta Tags -->
|
|
<meta name="author" content="GKACHELE" />
|
|
<meta name="generator" content="GKACHELE Sistema Automatizado - Copyright 2025" />
|
|
<meta name="copyright" content="Copyright (c) 2025 GKACHELE - Todos los derechos reservados" />
|
|
<title>GKACHELE™ | Desarrollo Web Premium</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
|
</head>
|
|
<body>
|
|
<!-- Navigation Sticky -->
|
|
<nav class="nav-sticky">
|
|
<div class="nav-container">
|
|
<div class="nav-logo">
|
|
<a href="index.html">GKACHELE™</a>
|
|
</div>
|
|
<div class="nav-menu">
|
|
<a href="#planes" class="nav-link">Planes</a>
|
|
<a href="#demos" class="nav-link">Demos</a>
|
|
<a href="#contacto" class="nav-link">Contacto</a>
|
|
</div>
|
|
<div class="nav-social">
|
|
<a href="https://www.facebook.com/gkachele" target="_blank" title="Facebook"><i class="fab fa-facebook-f"></i></a>
|
|
<a href="https://www.instagram.com/gkachele" target="_blank" title="Instagram"><i class="fab fa-instagram"></i></a>
|
|
<a href="https://www.youtube.com/@gkachele" target="_blank" title="YouTube"><i class="fab fa-youtube"></i></a>
|
|
<a href="https://www.tiktok.com/@gkachele" target="_blank" title="TikTok"><i class="fab fa-tiktok"></i></a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Hero Section -->
|
|
<section class="hero-section">
|
|
<div class="hero-bg-overlay"></div>
|
|
<div class="hero-content">
|
|
<h1 class="hero-title">GKACHELE™</h1>
|
|
<p class="hero-subtitle">Desarrollo Web Premium</p>
|
|
<p class="hero-description">Estamos listos para ayudarte a crear tu sitio moderno, rápido y personalizado.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Plans Section - Acordeón Desplegable -->
|
|
<section id="planes" class="plans-section">
|
|
<div class="plans-bg-overlay"></div>
|
|
<div class="container-large">
|
|
<h2 class="section-title">Nuestros Planes</h2>
|
|
|
|
<!-- Plans Accordion -->
|
|
<div class="plans-accordion">
|
|
<!-- Base Plan -->
|
|
<div class="plan-accordion-item" data-plan="base">
|
|
<button class="plan-accordion-header">
|
|
<div class="plan-header-content">
|
|
<h3>Base</h3>
|
|
<p class="plan-tagline">Simple, efectivo y profesional</p>
|
|
</div>
|
|
<i class="fas fa-chevron-down plan-chevron"></i>
|
|
</button>
|
|
<div class="plan-accordion-body">
|
|
<div class="plan-card-body">
|
|
<h4>¿Qué incluye?</h4>
|
|
<ul>
|
|
<li>✅ Sitio web de una página</li>
|
|
<li>✅ Diseño limpio y profesional</li>
|
|
<li>✅ Información de contacto</li>
|
|
<li>✅ Enlaces a redes sociales</li>
|
|
<li>✅ Responsive (móvil y desktop)</li>
|
|
<li>✅ Hosting gratuito</li>
|
|
<li>✅ Dominio personalizado</li>
|
|
</ul>
|
|
<h4>Personalización</h4>
|
|
<ul>
|
|
<li>🎨 Color principal personalizado</li>
|
|
<li>✍️ Tipografía a elección</li>
|
|
<li>📝 Textos personalizados</li>
|
|
</ul>
|
|
<p class="plan-ideal"><strong>Ideal para:</strong> Profesionales independientes, pequeños comercios, páginas institucionales básicas.</p>
|
|
<div class="qr-mini-card">
|
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://gkachele.duckdns.org" alt="QR Base">
|
|
<p>Comparte este plan</p>
|
|
</div>
|
|
<div class="plan-buttons-group">
|
|
<button onclick="openForm('base'); return false;" class="plan-btn" style="width: 100%; display: block; text-align: center; text-decoration: none; border: none; cursor: pointer;">
|
|
Solicitar Plan Base
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pro Plan -->
|
|
<div class="plan-accordion-item" data-plan="pro">
|
|
<button class="plan-accordion-header">
|
|
<div class="plan-header-content">
|
|
<h3>Pro</h3>
|
|
<p class="plan-tagline">Visual, dinámico y moderno</p>
|
|
</div>
|
|
<i class="fas fa-chevron-down plan-chevron"></i>
|
|
</button>
|
|
<div class="plan-accordion-body">
|
|
<div class="plan-card-body">
|
|
<h4>¿Qué incluye?</h4>
|
|
<ul>
|
|
<li>✅ Sitio web multipágina</li>
|
|
<li>✅ Animaciones suaves y efectos visuales</li>
|
|
<li>✅ Galería de imágenes o portafolio</li>
|
|
<li>✅ Formulario de contacto funcional</li>
|
|
<li>✅ Responsive avanzado</li>
|
|
<li>✅ Hosting gratuito</li>
|
|
<li>✅ Dominio personalizado</li>
|
|
</ul>
|
|
<h4>Personalización</h4>
|
|
<ul>
|
|
<li>🎨 Paleta de colores personalizada</li>
|
|
<li>✍️ Tipografía profesional</li>
|
|
<li>🧩 Secciones a medida</li>
|
|
</ul>
|
|
<p class="plan-ideal"><strong>Ideal para:</strong> Marcas personales, portfolios creativos, negocios que quieren destacar visualmente.</p>
|
|
<div class="qr-mini-card">
|
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://gkachele.duckdns.org#planes" alt="QR Pro">
|
|
<p>Comparte este plan</p>
|
|
</div>
|
|
<div class="plan-buttons-group">
|
|
<button onclick="openForm('pro'); return false;" class="plan-btn" style="width: 100%; display: block; text-align: center; text-decoration: none; border: none; cursor: pointer;">
|
|
Solicitar Plan Pro
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Premium Plan -->
|
|
<div class="plan-accordion-item" data-plan="premium">
|
|
<button class="plan-accordion-header">
|
|
<div class="plan-header-content">
|
|
<h3>Premium</h3>
|
|
<p class="plan-tagline">Experiencia completa y profesional</p>
|
|
</div>
|
|
<i class="fas fa-chevron-down plan-chevron"></i>
|
|
</button>
|
|
<div class="plan-accordion-body">
|
|
<div class="plan-card-body">
|
|
<h4>¿Qué incluye?</h4>
|
|
<ul>
|
|
<li>✅ Sitio web completo y escalable</li>
|
|
<li>✅ Integración con redes sociales y herramientas externas</li>
|
|
<li>✅ SEO optimizado</li>
|
|
<li>✅ Panel de administración (opcional)</li>
|
|
<li>✅ Hosting y dominio incluidos</li>
|
|
<li>✅ Soporte técnico personalizado</li>
|
|
</ul>
|
|
<h4>Personalización</h4>
|
|
<ul>
|
|
<li>🎨 Diseño a medida</li>
|
|
<li>📊 Integración con analítica</li>
|
|
<li>🔒 Seguridad avanzada</li>
|
|
</ul>
|
|
<p class="plan-ideal"><strong>Ideal para:</strong> Empresas, proyectos grandes, tiendas online, instituciones con necesidades específicas.</p>
|
|
<div class="qr-mini-card">
|
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://gkachele.duckdns.org#planes" alt="QR Premium">
|
|
<p>Comparte este plan</p>
|
|
</div>
|
|
<div class="plan-buttons-group">
|
|
<button onclick="openForm('premium'); return false;" class="plan-btn premium-btn" style="width: 100%; display: block; text-align: center; text-decoration: none; border: none; cursor: pointer;">
|
|
Solicitar Plan Premium
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Formulario de Pedido de Plan -->
|
|
<div id="planFormModal" class="modal-form" style="display: none;">
|
|
<div class="modal-form-content">
|
|
<div class="modal-form-header">
|
|
<h2>Solicitar Plan <span id="selectedPlanName"></span></h2>
|
|
<button type="button" class="modal-close" onclick="closeForm()">×</button>
|
|
</div>
|
|
<form id="planRequestForm" onsubmit="event.preventDefault(); generatePreview(); return false;">
|
|
<input type="hidden" id="selectedPlan" name="plan" required>
|
|
<input type="hidden" id="templateVariation" name="templateVariation" value="">
|
|
|
|
<div class="form-group">
|
|
<label for="nombre">Nombre Completo *</label>
|
|
<input type="text" id="nombre" name="nombre" required placeholder="Tu nombre completo">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="rubro">Rubro/Negocio *</label>
|
|
<select id="rubro" name="rubro" required onchange="actualizarCamposRubro()">
|
|
<option value="">Seleccionar rubro...</option>
|
|
<option value="restaurante">Restaurante</option>
|
|
<option value="danza">Danza</option>
|
|
<option value="cosmeticos">Cosméticos</option>
|
|
<option value="despachos">Despachos</option>
|
|
<option value="gimnasios">Gimnasios</option>
|
|
<option value="educacion">Educación</option>
|
|
<option value="base">Base (Otro)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Campos ocultos para mantener compatibilidad con generatePreview() -->
|
|
<input type="hidden" id="email" name="email" value="">
|
|
<input type="hidden" id="telefono" name="telefono" value="">
|
|
|
|
<!-- Campos específicos por rubro (ocultos - se completan en admin.html del preview) -->
|
|
<div id="campos-rubro-especificos" style="display: none !important;">
|
|
<hr style="margin: 20px 0; border: 1px solid #e0e0e0;">
|
|
<h3 style="margin-bottom: 15px; color: #2c3e50;" id="titulo-campos-rubro">Información Específica del Rubro</h3>
|
|
|
|
<!-- Campos para Restaurante -->
|
|
<div id="campos-restaurante" class="campos-rubro" style="display: none;">
|
|
<div class="form-group">
|
|
<label for="menu_url">URL del Menú (opcional)</label>
|
|
<input type="url" id="menu_url" name="menu_url" placeholder="https://ejemplo.com/menu.pdf">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="horarios">Horarios de Atención</label>
|
|
<input type="text" id="horarios" name="horarios" placeholder="Ej: Lunes a Viernes 12:00-15:00 y 19:00-23:00">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="reservas">Sistema de Reservas</label>
|
|
<input type="text" id="reservas" name="reservas" placeholder="Ej: WhatsApp, teléfono, online">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="capacidad">Capacidad (opcional)</label>
|
|
<input type="text" id="capacidad" name="capacidad" placeholder="Ej: 50 personas">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="especialidad_culinaria">Especialidad Culinaria</label>
|
|
<input type="text" id="especialidad_culinaria" name="especialidad_culinaria" placeholder="Ej: Cocina italiana, Parrilla, Sushi">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Campos para Danza -->
|
|
<div id="campos-danza" class="campos-rubro" style="display: none;">
|
|
<div class="form-group">
|
|
<label for="clases">Tipos de Clases</label>
|
|
<input type="text" id="clases" name="clases" placeholder="Ej: Ballet, Salsa, Hip Hop, Contemporáneo">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="horarios_clases">Horarios de Clases</label>
|
|
<input type="text" id="horarios_clases" name="horarios_clases" placeholder="Ej: Lunes y Miércoles 18:00-20:00">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="niveles">Niveles</label>
|
|
<input type="text" id="niveles" name="niveles" placeholder="Ej: Principiante, Intermedio, Avanzado">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="estilos">Estilos de Danza</label>
|
|
<input type="text" id="estilos" name="estilos" placeholder="Ej: Clásico, Moderno, Urbano">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="profesores">Profesores (opcional)</label>
|
|
<input type="text" id="profesores" name="profesores" placeholder="Ej: María García, Juan Pérez">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Campos para Cosméticos -->
|
|
<div id="campos-cosmeticos" class="campos-rubro" style="display: none;">
|
|
<div class="form-group">
|
|
<label for="productos_url">URL del Catálogo (opcional)</label>
|
|
<input type="url" id="productos_url" name="productos_url" placeholder="https://ejemplo.com/catalogo">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="catalogo">Tipo de Catálogo</label>
|
|
<input type="text" id="catalogo" name="catalogo" placeholder="Ej: Maquillaje, Cuidado facial, Perfumes">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="categorias">Categorías de Productos</label>
|
|
<input type="text" id="categorias" name="categorias" placeholder="Ej: Labiales, Bases, Sombras">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ofertas">Ofertas Especiales (opcional)</label>
|
|
<input type="text" id="ofertas" name="ofertas" placeholder="Ej: 2x1 en labiales, 20% descuento">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="envios">Información de Envíos</label>
|
|
<input type="text" id="envios" name="envios" placeholder="Ej: Envío gratis desde $5000, Retiro en local">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Campos para Despachos -->
|
|
<div id="campos-despachos" class="campos-rubro" style="display: none;">
|
|
<div class="form-group">
|
|
<label for="propuestas">Servicios Principales</label>
|
|
<textarea id="propuestas" name="propuestas" rows="3" placeholder="Describe los servicios principales del despacho (asesoría legal, contable, etc.)"></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="agenda">Agenda de Actividades (opcional)</label>
|
|
<input type="text" id="agenda" name="agenda" placeholder="Ej: Consultas semanales, reuniones con clientes">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="eventos">Próximos Eventos (opcional)</label>
|
|
<input type="text" id="eventos" name="eventos" placeholder="Ej: Charla el 15/03, Seminario el 20/03">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="noticias">Noticias/Comunicados (opcional)</label>
|
|
<input type="text" id="noticias" name="noticias" placeholder="URL o fuente de noticias">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Campos para Gimnasios -->
|
|
<div id="campos-gimnasios" class="campos-rubro" style="display: none;">
|
|
<div class="form-group">
|
|
<label for="planes_fitness">Planes de Entrenamiento</label>
|
|
<input type="text" id="planes_fitness" name="planes_fitness" placeholder="Ej: Básico, Premium, Personalizado">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="horarios_fitness">Horarios</label>
|
|
<input type="text" id="horarios_fitness" name="horarios_fitness" placeholder="Ej: Lunes a Sábado 6:00-22:00">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="entrenadores">Entrenadores (opcional)</label>
|
|
<input type="text" id="entrenadores" name="entrenadores" placeholder="Ej: Personal trainers certificados">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="instalaciones">Instalaciones (opcional)</label>
|
|
<input type="text" id="instalaciones" name="instalaciones" placeholder="Ej: Gimnasio completo, piscina, sauna">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="precios">Precios (opcional)</label>
|
|
<input type="text" id="precios" name="precios" placeholder="Ej: Desde $5000/mes">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Campos para Educación -->
|
|
<div id="campos-educacion" class="campos-rubro" style="display: none;">
|
|
<div class="form-group">
|
|
<label for="cursos">Cursos Ofrecidos</label>
|
|
<input type="text" id="cursos" name="cursos" placeholder="Ej: Inglés, Programación, Diseño">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="horarios_educacion">Horarios de Clases</label>
|
|
<input type="text" id="horarios_educacion" name="horarios_educacion" placeholder="Ej: Mañana, tarde, noche">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="niveles_educacion">Niveles</label>
|
|
<input type="text" id="niveles_educacion" name="niveles_educacion" placeholder="Ej: Inicial, Intermedio, Avanzado">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="profesores_educacion">Profesores (opcional)</label>
|
|
<input type="text" id="profesores_educacion" name="profesores_educacion" placeholder="Ej: Docentes certificados">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="inscripcion">Forma de Inscripción</label>
|
|
<input type="text" id="inscripcion" name="inscripcion" placeholder="Ej: Online, presencial, WhatsApp">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Campos ocultos para mantener compatibilidad con generatePreview() -->
|
|
<input type="hidden" id="colores" name="colores" value="">
|
|
<input type="hidden" id="tipografia" name="tipografia" value="">
|
|
<input type="hidden" id="logo_url" name="logo_url" value="">
|
|
<input type="hidden" id="descripcion" name="descripcion" value="">
|
|
<input type="hidden" id="facebook" name="facebook" value="">
|
|
<input type="hidden" id="instagram" name="instagram" value="">
|
|
<input type="hidden" id="twitter" name="twitter" value="">
|
|
<input type="hidden" id="whatsapp" name="whatsapp" value="">
|
|
<input type="hidden" id="youtube" name="youtube" value="">
|
|
<input type="hidden" id="tiktok" name="tiktok" value="">
|
|
<input type="hidden" id="direccion_calle" name="direccion_calle" value="">
|
|
<input type="hidden" id="direccion_ciudad" name="direccion_ciudad" value="">
|
|
<input type="hidden" id="direccion_provincia" name="direccion_provincia" value="">
|
|
<input type="hidden" id="direccion_cp" name="direccion_cp" value="">
|
|
<input type="hidden" id="dominio" name="dominio" value="">
|
|
|
|
<div class="form-actions">
|
|
<button type="button" class="btn-cancel" onclick="closeForm()">Cancelar</button>
|
|
<button type="button" class="btn-submit" onclick="generatePreview()" id="btnPreview" style="width: 100%;">
|
|
<i class="fas fa-rocket"></i> Generar Preview
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal de Previews -->
|
|
<div id="previewsModal" class="previews-modal" style="display: none;">
|
|
<div class="previews-modal-content">
|
|
<div class="previews-modal-header">
|
|
<h2>Previews - Plan <span id="previewPlanName"></span></h2>
|
|
<button type="button" class="modal-close" onclick="closePreviews()">×</button>
|
|
</div>
|
|
<div class="previews-filters">
|
|
<div class="filter-group">
|
|
<label for="filterRubro">Filtrar por Rubro:</label>
|
|
<select id="filterRubro" onchange="filterPreviews()">
|
|
<!-- Las opciones se generan dinámicamente según los previews disponibles -->
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="previews-grid" id="previewsGrid">
|
|
<!-- Las cards se generarán dinámicamente con JavaScript -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal de Screenshot Grande (Lightbox) -->
|
|
<div id="screenshotLightbox" class="screenshot-lightbox" style="display: none;">
|
|
<div class="lightbox-content">
|
|
<span class="lightbox-close" onclick="closeLightbox()">×</span>
|
|
<img id="lightboxImage" src="" alt="Preview" />
|
|
<div class="lightbox-info">
|
|
<h3 id="lightboxTitle"></h3>
|
|
<p id="lightboxDescription"></p>
|
|
<button class="btn-request-design" onclick="requestDesign()">
|
|
<i class="fas fa-check"></i> Solicitar este diseño
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Demos Section -->
|
|
<section id="demos" class="demos-section">
|
|
<div class="demos-bg-overlay"></div>
|
|
<div class="container-large">
|
|
<h2 class="section-title">Proyectos Demo</h2>
|
|
<p class="section-subtitle">Ejemplos de sitios web que hemos creado</p>
|
|
|
|
<div class="demos-grid">
|
|
<!-- Demo 1: Blow Dance -->
|
|
<div class="demo-card">
|
|
<div class="demo-image">
|
|
<div style="width:100%;height:300px;background:linear-gradient(135deg,#e91e63 0%,#c2185b 100%);display:flex;align-items:center;justify-content:center;color:white;font-size:2rem;font-weight:bold;">💃 Blow Dance</div>
|
|
<div class="demo-overlay">
|
|
<a href="https://blowdance.duckdns.org" target="_blank" class="demo-btn">
|
|
<i class="fas fa-external-link-alt"></i> Ver Sitio
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="demo-info">
|
|
<h3>Blow Dance</h3>
|
|
<p class="demo-category">💃 Escuela de Danza</p>
|
|
<p class="demo-plan">Plan: <span class="badge-pro">Pro</span></p>
|
|
<p class="demo-description">Sitio web profesional para escuela de danza con galerías, videos y sistema de inscripción.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Demo 2: Dalmau Intendente -->
|
|
<div class="demo-card">
|
|
<div class="demo-image">
|
|
<div style="width:100%;height:300px;background:linear-gradient(135deg,#1e40af 0%,#1e3a8a 100%);display:flex;align-items:center;justify-content:center;color:white;font-size:2rem;font-weight:bold;">🗳️ Dalmau</div>
|
|
<div class="demo-overlay">
|
|
<a href="https://dalmau-intendente.duckdns.org" target="_blank" class="demo-btn">
|
|
<i class="fas fa-external-link-alt"></i> Ver Sitio
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="demo-info">
|
|
<h3>Dalmau Intendente</h3>
|
|
<p class="demo-category">⚖️ Despacho Profesional</p>
|
|
<p class="demo-plan">Plan: <span class="badge-base">Base</span></p>
|
|
<p class="demo-description">Landing page profesional para despachos de abogados y contadores con información clara y llamados a la acción.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Demo 3: GKACHELE Portfolio -->
|
|
<div class="demo-card">
|
|
<div class="demo-image">
|
|
<div style="width:100%;height:300px;background:linear-gradient(135deg,#ff6b35 0%,#e55a28 100%);display:flex;align-items:center;justify-content:center;color:white;font-size:2rem;font-weight:bold;">💼 GKACHELE</div>
|
|
<div class="demo-overlay">
|
|
<a href="https://gkachele.duckdns.org" target="_blank" class="demo-btn">
|
|
<i class="fas fa-external-link-alt"></i> Ver Sitio
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="demo-info">
|
|
<h3>GKACHELE™</h3>
|
|
<p class="demo-category">💼 Portfolio Profesional</p>
|
|
<p class="demo-plan">Plan: <span class="badge-premium">Premium</span></p>
|
|
<p class="demo-description">Sitio web premium con diseño moderno, animaciones y sistema de gestión avanzado.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Video Demo - DESACTIVADO hasta tener YouTube -->
|
|
<!--
|
|
<div class="video-demo-section">
|
|
<h3>Video Demostrativo</h3>
|
|
<div class="video-container">
|
|
<div class="video-placeholder">
|
|
<i class="fas fa-play-circle"></i>
|
|
<p>Próximamente: Video demo de nuestros proyectos</p>
|
|
<small>Sube tu video a YouTube y pega el link aquí</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
-->
|
|
</div>
|
|
</section>
|
|
|
|
<!-- QR Section -->
|
|
<section class="qr-section">
|
|
<div class="qr-bg-overlay"></div>
|
|
<div class="container-large">
|
|
<div class="qr-card">
|
|
<h3>Comparte tu sitio</h3>
|
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=https://gkachele.duckdns.org" alt="QR Code GKACHELE™" class="qr-image">
|
|
<p>Escanea el código QR para compartir</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Contact Section -->
|
|
<section id="contacto" class="contact-section">
|
|
<div class="contact-bg-overlay"></div>
|
|
<div class="container-large">
|
|
<h2 class="section-title">Contacto</h2>
|
|
<div class="contact-grid">
|
|
<div class="contact-cards">
|
|
<a href="tel:+5491123456789" class="contact-card">
|
|
<i class="fas fa-phone"></i>
|
|
<span>+54 9 11 2345 6789</span>
|
|
</a>
|
|
<a href="mailto:contacto@gkachele.com" class="contact-card">
|
|
<i class="fas fa-envelope"></i>
|
|
<span>contacto@gkachele.com</span>
|
|
</a>
|
|
<a href="https://wa.me/5491123456789?text=Hola!%20Me%20interesa%20conocer%20más%20sobre%20GKACHELE" target="_blank" class="contact-card whatsapp-card">
|
|
<i class="fab fa-whatsapp"></i>
|
|
<span>WhatsApp</span>
|
|
</a>
|
|
</div>
|
|
<div class="social-card">
|
|
<h3>Redes Sociales</h3>
|
|
<div class="social-links-grid">
|
|
<a href="https://www.facebook.com/gkachele" target="_blank" class="social-link" title="Facebook">
|
|
<i class="fab fa-facebook-f"></i>
|
|
</a>
|
|
<a href="https://www.instagram.com/gkachele" target="_blank" class="social-link" title="Instagram">
|
|
<i class="fab fa-instagram"></i>
|
|
</a>
|
|
<a href="https://www.youtube.com/@gkachele" target="_blank" class="social-link" title="YouTube">
|
|
<i class="fab fa-youtube"></i>
|
|
</a>
|
|
<a href="https://www.tiktok.com/@gkachele" target="_blank" class="social-link" title="TikTok">
|
|
<i class="fab fa-tiktok"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- WhatsApp Float Button -->
|
|
<a href="https://wa.me/5491123456789?text=Hola!%20Me%20interesa%20conocer%20más%20sobre%20GKACHELE" target="_blank" class="whatsapp-float">
|
|
<i class="fab fa-whatsapp"></i>
|
|
</a>
|
|
|
|
<!-- Scroll to Top Button -->
|
|
<button class="scroll-to-top" id="scrollToTop" aria-label="Ir arriba">
|
|
<i class="fas fa-arrow-up"></i>
|
|
</button>
|
|
|
|
<!-- Footer -->
|
|
<footer class="footer-main">
|
|
<div class="footer-bg-overlay"></div>
|
|
<div class="container-large">
|
|
<p>© 2025 GKACHELE™. Todos los derechos reservados.</p>
|
|
<span class="gkachele-watermark" aria-hidden="true">
|
|
Desarrollado desde noviembre 2025 por GKACHELE
|
|
</span>
|
|
<p style="font-size: 0.7rem; color: rgba(255, 255, 255, 0.5); margin-top: 0.25rem; opacity: 0.7;">
|
|
Código propiedad de GKACHELE © 2025 - Prohibida su reproducción sin autorización
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
// Endpoints locales - sin dependencias externas
|
|
|
|
// Plan Accordion - Desplegable Premium
|
|
const accordionItems = document.querySelectorAll('.plan-accordion-item');
|
|
|
|
accordionItems.forEach(item => {
|
|
const header = item.querySelector('.plan-accordion-header');
|
|
const body = item.querySelector('.plan-accordion-body');
|
|
const chevron = item.querySelector('.plan-chevron');
|
|
|
|
header.addEventListener('click', () => {
|
|
const isActive = item.classList.contains('active');
|
|
|
|
// Cerrar todos los items con transición suave
|
|
accordionItems.forEach(otherItem => {
|
|
if (otherItem !== item) {
|
|
const otherBody = otherItem.querySelector('.plan-accordion-body');
|
|
const otherChevron = otherItem.querySelector('.plan-chevron');
|
|
const wasOtherActive = otherItem.classList.contains('active');
|
|
|
|
if (wasOtherActive) {
|
|
// Cerrar con transición
|
|
otherItem.classList.remove('active');
|
|
setTimeout(() => {
|
|
otherBody.style.maxHeight = null;
|
|
otherChevron.style.transform = 'rotate(0deg)';
|
|
}, 50);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Toggle del item actual con transición
|
|
if (isActive) {
|
|
// Cerrar el item actual
|
|
item.classList.remove('active');
|
|
setTimeout(() => {
|
|
body.style.maxHeight = null;
|
|
chevron.style.transform = 'rotate(0deg)';
|
|
}, 50);
|
|
} else {
|
|
// Abrir el item actual
|
|
item.classList.add('active');
|
|
body.style.maxHeight = body.scrollHeight + 'px';
|
|
chevron.style.transform = 'rotate(180deg)';
|
|
|
|
// Smooth scroll al item expandido con delay para la transición
|
|
setTimeout(() => {
|
|
item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
}, 300);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Asegurar que todos los acordeones empiecen cerrados
|
|
// Forzar que ningún item tenga 'active' al cargar la página
|
|
accordionItems.forEach(item => {
|
|
item.classList.remove('active');
|
|
const body = item.querySelector('.plan-accordion-body');
|
|
const chevron = item.querySelector('.plan-chevron');
|
|
body.style.maxHeight = null;
|
|
chevron.style.transform = 'rotate(0deg)';
|
|
});
|
|
|
|
// Formulario de Pedido de Plan
|
|
function openForm(planType) {
|
|
const modal = document.getElementById('planFormModal');
|
|
const selectedPlan = document.getElementById('selectedPlan');
|
|
const selectedPlanName = document.getElementById('selectedPlanName');
|
|
|
|
const planNames = {
|
|
'base': 'Base',
|
|
'pro': 'Pro',
|
|
'premium': 'Premium'
|
|
};
|
|
|
|
selectedPlan.value = planType;
|
|
selectedPlanName.textContent = planNames[planType];
|
|
modal.style.display = 'flex';
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// Desactivar temporalmente el cierre por clic fuera
|
|
modal.dataset.preventClose = 'true';
|
|
}
|
|
|
|
function closeForm() {
|
|
const modal = document.getElementById('planFormModal');
|
|
modal.style.display = 'none';
|
|
document.body.style.overflow = 'auto';
|
|
document.getElementById('planRequestForm').reset();
|
|
// Ocultar campos de rubro al cerrar
|
|
document.getElementById('campos-rubro-especificos').style.display = 'none';
|
|
document.querySelectorAll('.campos-rubro').forEach(el => el.style.display = 'none');
|
|
// Reactivar el cierre por clic fuera
|
|
delete modal.dataset.preventClose;
|
|
}
|
|
|
|
// Función para actualizar campos según rubro seleccionado
|
|
function actualizarCamposRubro() {
|
|
// NO mostrar campos específicos del rubro - se completan en admin.html del preview
|
|
const contenedor = document.getElementById('campos-rubro-especificos');
|
|
if (contenedor) {
|
|
contenedor.style.display = 'none !important';
|
|
}
|
|
// Ocultar todos los campos de rubro
|
|
document.querySelectorAll('.campos-rubro').forEach(el => el.style.display = 'none');
|
|
|
|
// Prellenar colores y tipografía sugeridos según rubro
|
|
const rubrosData = {
|
|
restaurante: { colores: 'Rojo y naranja', tipografia: 'Roboto' },
|
|
danza: { colores: 'Rosa y magenta', tipografia: 'Poppins' },
|
|
cosmeticos: { colores: 'Morado y violeta', tipografia: 'Montserrat' },
|
|
despachos: { colores: 'Azul y azul oscuro', tipografia: 'Georgia' },
|
|
gimnasios: { colores: 'Rojo y rojo oscuro', tipografia: 'Roboto' },
|
|
educacion: { colores: 'Azul y azul claro', tipografia: 'Open Sans' }
|
|
};
|
|
|
|
if (rubrosData[rubro]) {
|
|
if (!document.getElementById('colores').value) {
|
|
document.getElementById('colores').value = rubrosData[rubro].colores;
|
|
}
|
|
if (!document.getElementById('tipografia').value) {
|
|
document.getElementById('tipografia').value = rubrosData[rubro].tipografia;
|
|
}
|
|
}
|
|
}
|
|
|
|
// NOTA: handleFormSubmit ahora se llama desde admin.html cuando el usuario hace clic en "Completar Solicitud Final"
|
|
// Esta función se mantiene por compatibilidad pero no se usa desde el formulario inicial
|
|
async function handleFormSubmit(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
const formData = new FormData(event.target);
|
|
const data = Object.fromEntries(formData);
|
|
|
|
// Generar nombre de dominio si no se proporcionó
|
|
const dominio = data.dominio || data.nombre.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
|
|
// Recopilar campos específicos del rubro
|
|
const camposRubro = {};
|
|
const rubro = data.rubro;
|
|
|
|
// Campos específicos por rubro
|
|
if (rubro === 'restaurante') {
|
|
if (data.menu_url) camposRubro.menu_url = data.menu_url;
|
|
if (data.horarios) camposRubro.horarios = data.horarios;
|
|
if (data.reservas) camposRubro.reservas = data.reservas;
|
|
if (data.capacidad) camposRubro.capacidad = data.capacidad;
|
|
if (data.especialidad_culinaria) camposRubro.especialidad_culinaria = data.especialidad_culinaria;
|
|
} else if (rubro === 'danza') {
|
|
if (data.clases) camposRubro.clases = data.clases;
|
|
if (data.horarios_clases) camposRubro.horarios_clases = data.horarios_clases;
|
|
if (data.niveles) camposRubro.niveles = data.niveles;
|
|
if (data.estilos) camposRubro.estilos = data.estilos;
|
|
if (data.profesores) camposRubro.profesores = data.profesores;
|
|
} else if (rubro === 'cosmeticos') {
|
|
if (data.productos_url) camposRubro.productos_url = data.productos_url;
|
|
if (data.catalogo) camposRubro.catalogo = data.catalogo;
|
|
if (data.categorias) camposRubro.categorias = data.categorias;
|
|
if (data.ofertas) camposRubro.ofertas = data.ofertas;
|
|
if (data.envios) camposRubro.envios = data.envios;
|
|
} else if (rubro === 'despachos') {
|
|
if (data.propuestas) camposRubro.propuestas = data.propuestas;
|
|
if (data.agenda) camposRubro.agenda = data.agenda;
|
|
if (data.eventos) camposRubro.eventos = data.eventos;
|
|
if (data.noticias) camposRubro.noticias = data.noticias;
|
|
} else if (rubro === 'gimnasios') {
|
|
if (data.planes_fitness) camposRubro.planes = data.planes_fitness;
|
|
if (data.horarios_fitness) camposRubro.horarios = data.horarios_fitness;
|
|
if (data.entrenadores) camposRubro.entrenadores = data.entrenadores;
|
|
if (data.instalaciones) camposRubro.instalaciones = data.instalaciones;
|
|
if (data.precios) camposRubro.precios = data.precios;
|
|
} else if (rubro === 'educacion') {
|
|
if (data.cursos) camposRubro.cursos = data.cursos;
|
|
if (data.horarios_educacion) camposRubro.horarios = data.horarios_educacion;
|
|
if (data.niveles_educacion) camposRubro.niveles = data.niveles_educacion;
|
|
if (data.profesores_educacion) camposRubro.profesores = data.profesores_educacion;
|
|
if (data.inscripcion) camposRubro.inscripcion = data.inscripcion;
|
|
}
|
|
|
|
// Crear JSON con formato estructurado
|
|
const jsonData = {
|
|
plan: data.plan,
|
|
cliente: {
|
|
nombre_completo: data.nombre,
|
|
email: data.email,
|
|
telefono: data.telefono,
|
|
rubro: data.rubro
|
|
},
|
|
personalizacion: {
|
|
colores_preferidos: data.colores || '',
|
|
tipografia: data.tipografia || null,
|
|
logo_url: data.logo_url || null,
|
|
logo_upload: null
|
|
},
|
|
redes_sociales: {
|
|
facebook: data.facebook || null,
|
|
instagram: data.instagram || null,
|
|
twitter: data.twitter || null,
|
|
youtube: data.youtube || null,
|
|
tiktok: data.tiktok || null,
|
|
linkedin: null,
|
|
whatsapp: data.whatsapp || null
|
|
},
|
|
direccion: {
|
|
calle: data.direccion_calle || '',
|
|
ciudad: data.direccion_ciudad || '',
|
|
provincia: data.direccion_provincia || '',
|
|
codigo_postal: data.direccion_cp || '',
|
|
pais: 'Argentina'
|
|
},
|
|
rubro_especifico: Object.keys(camposRubro).length > 0 ? camposRubro : null,
|
|
dominio: {
|
|
preferido: dominio,
|
|
generado: `${dominio}.gkachele.duckdns.org`
|
|
},
|
|
descripcion: data.descripcion || '',
|
|
estado: 'pendiente_pago',
|
|
fecha_creacion: new Date().toISOString()
|
|
};
|
|
|
|
// Mostrar loading
|
|
const submitBtn = event.target.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.textContent;
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = 'Enviando...';
|
|
|
|
try {
|
|
const payload = new URLSearchParams();
|
|
const datosEnvio = {
|
|
plan: jsonData.plan,
|
|
nombre: jsonData.cliente.nombre_completo,
|
|
email: jsonData.cliente.email,
|
|
telefono: jsonData.cliente.telefono,
|
|
rubro: jsonData.cliente.rubro,
|
|
dominio: jsonData.dominio.generado,
|
|
descripcion: jsonData.descripcion,
|
|
colores: jsonData.personalizacion.colores_preferidos,
|
|
tipografia: jsonData.personalizacion.tipografia || '',
|
|
logo_url: jsonData.personalizacion.logo_url || '',
|
|
redes: JSON.stringify(jsonData.redes_sociales),
|
|
whatsapp: jsonData.redes_sociales.whatsapp || '',
|
|
rubro_especifico: JSON.stringify(jsonData.rubro_especifico || {}),
|
|
templateVariation: data.templateVariation || '',
|
|
estado: jsonData.estado
|
|
};
|
|
|
|
Object.entries(datosEnvio).forEach(([clave, valor]) => {
|
|
payload.append(clave, valor ?? '');
|
|
});
|
|
|
|
// Solicitud se enviará desde el preview - función deshabilitada por ahora
|
|
// TODO: Implementar endpoint /api/request_create en Flask
|
|
alert('✅ Redirigiendo al formulario de preview...\n\n' +
|
|
'📋 Completa el formulario para generar tu preview.');
|
|
|
|
// Redirigir al formulario de preview
|
|
const plan = document.getElementById('selectedPlan')?.value || 'base';
|
|
const rubro = document.getElementById('rubro')?.value || 'gimnasio';
|
|
window.location.href = `/register?plan=${plan}&rubro=${rubro}`;
|
|
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = originalText;
|
|
|
|
closeForm();
|
|
event.target.reset();
|
|
|
|
} catch (error) {
|
|
console.error('Error al enviar solicitud:', error);
|
|
alert('❌ Error al enviar la solicitud. Por favor, intenta nuevamente.');
|
|
submitBtn.disabled = false;
|
|
submitBtn.textContent = originalText;
|
|
}
|
|
}
|
|
|
|
// Generar preview dinámico antes de enviar
|
|
async function generatePreview() {
|
|
const form = document.getElementById('planRequestForm');
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData(form);
|
|
const data = Object.fromEntries(formData);
|
|
|
|
// Generar nombre de dominio si no se proporcionó
|
|
const dominio = data.dominio || data.nombre.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
|
|
// Recopilar campos específicos del rubro (mismo código que handleFormSubmit)
|
|
const camposRubro = {};
|
|
const rubro = data.rubro;
|
|
|
|
if (rubro === 'restaurante') {
|
|
if (data.menu_url) camposRubro.menu_url = data.menu_url;
|
|
if (data.horarios) camposRubro.horarios = data.horarios;
|
|
if (data.reservas) camposRubro.reservas = data.reservas;
|
|
if (data.capacidad) camposRubro.capacidad = data.capacidad;
|
|
if (data.especialidad_culinaria) camposRubro.especialidad_culinaria = data.especialidad_culinaria;
|
|
} else if (rubro === 'danza') {
|
|
if (data.clases) camposRubro.clases = data.clases;
|
|
if (data.horarios_clases) camposRubro.horarios_clases = data.horarios_clases;
|
|
if (data.niveles) camposRubro.niveles = data.niveles;
|
|
if (data.estilos) camposRubro.estilos = data.estilos;
|
|
if (data.profesores) camposRubro.profesores = data.profesores;
|
|
} else if (rubro === 'cosmeticos') {
|
|
if (data.productos_url) camposRubro.productos_url = data.productos_url;
|
|
if (data.catalogo) camposRubro.catalogo = data.catalogo;
|
|
if (data.categorias) camposRubro.categorias = data.categorias;
|
|
if (data.ofertas) camposRubro.ofertas = data.ofertas;
|
|
if (data.envios) camposRubro.envios = data.envios;
|
|
} else if (rubro === 'despachos') {
|
|
if (data.propuestas) camposRubro.propuestas = data.propuestas;
|
|
if (data.agenda) camposRubro.agenda = data.agenda;
|
|
if (data.eventos) camposRubro.eventos = data.eventos;
|
|
if (data.noticias) camposRubro.noticias = data.noticias;
|
|
} else if (rubro === 'gimnasios') {
|
|
if (data.planes_fitness) camposRubro.planes = data.planes_fitness;
|
|
if (data.horarios_fitness) camposRubro.horarios = data.horarios_fitness;
|
|
if (data.entrenadores) camposRubro.entrenadores = data.entrenadores;
|
|
if (data.instalaciones) camposRubro.instalaciones = data.instalaciones;
|
|
if (data.precios) camposRubro.precios = data.precios;
|
|
} else if (rubro === 'educacion') {
|
|
if (data.cursos) camposRubro.cursos = data.cursos;
|
|
if (data.horarios_educacion) camposRubro.horarios = data.horarios_educacion;
|
|
if (data.niveles_educacion) camposRubro.niveles = data.niveles_educacion;
|
|
if (data.profesores_educacion) camposRubro.profesores = data.profesores_educacion;
|
|
if (data.inscripcion) camposRubro.inscripcion = data.inscripcion;
|
|
}
|
|
|
|
// Crear JSON con formato estructurado
|
|
const jsonData = {
|
|
plan: data.plan,
|
|
cliente: {
|
|
nombre_completo: data.nombre,
|
|
email: data.email,
|
|
telefono: data.telefono,
|
|
rubro: data.rubro
|
|
},
|
|
personalizacion: {
|
|
colores_preferidos: data.colores || '',
|
|
tipografia: data.tipografia || null,
|
|
logo_url: data.logo_url || null,
|
|
logo_upload: null
|
|
},
|
|
redes_sociales: {
|
|
facebook: data.facebook || null,
|
|
instagram: data.instagram || null,
|
|
twitter: data.twitter || null,
|
|
youtube: data.youtube || null,
|
|
tiktok: data.tiktok || null,
|
|
linkedin: null,
|
|
whatsapp: data.whatsapp || null
|
|
},
|
|
direccion: {
|
|
calle: data.direccion_calle || '',
|
|
ciudad: data.direccion_ciudad || '',
|
|
provincia: data.direccion_provincia || '',
|
|
codigo_postal: data.direccion_cp || '',
|
|
pais: 'Argentina'
|
|
},
|
|
rubro_especifico: Object.keys(camposRubro).length > 0 ? camposRubro : null,
|
|
dominio: {
|
|
preferido: dominio,
|
|
generado: `${dominio}.gkachele.duckdns.org`
|
|
},
|
|
descripcion: data.descripcion || '',
|
|
estado: 'preview',
|
|
fecha_creacion: new Date().toISOString()
|
|
};
|
|
|
|
const btnPreview = document.getElementById('btnPreview');
|
|
const originalText = btnPreview.innerHTML;
|
|
btnPreview.disabled = true;
|
|
btnPreview.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Generando Preview...';
|
|
|
|
try {
|
|
// COPIAR EXACTAMENTE EL FLUJO DE v1/admin/form.html QUE FUNCIONA
|
|
const REPO_OWNER = 'komkida91';
|
|
const BACKEND_REPO = 'gkachele-requests';
|
|
const BACKEND_WORKFLOW = 'submit-request-through-app.yml';
|
|
const TARGET_WORKFLOW = 'generate-preview.yml';
|
|
|
|
// Token de sesión local (no se usa GitHub)
|
|
const DEFAULT_SESSION_TOKEN = 'GK@ch3l3-M1s10n3s-2025!#$';
|
|
|
|
// Construir payload EXACTAMENTE igual que v1/admin/form.html
|
|
const slug = data.nombre ? data.nombre.toLowerCase().replace(/[^a-z0-9]+/g, '-') : 'sitio-' + Date.now();
|
|
const dominioPreferido = data.dominio || slug;
|
|
|
|
const jsonData = {
|
|
plan: data.plan,
|
|
cliente: {
|
|
nombre_completo: data.nombre,
|
|
email: data.email,
|
|
telefono: data.telefono,
|
|
rubro: data.rubro
|
|
},
|
|
personalizacion: {
|
|
colores_preferidos: data.colores || '',
|
|
tipografia: data.tipografia || null,
|
|
logo_url: data.logo_url || null
|
|
},
|
|
redes_sociales: {
|
|
facebook: data.facebook || null,
|
|
instagram: data.instagram || null,
|
|
twitter: data.twitter || null,
|
|
whatsapp: data.whatsapp || null,
|
|
youtube: data.youtube || null,
|
|
tiktok: data.tiktok || null
|
|
},
|
|
direccion: {
|
|
calle: data.direccion_calle || '',
|
|
ciudad: data.direccion_ciudad || '',
|
|
provincia: data.direccion_provincia || '',
|
|
codigo_postal: data.direccion_cp || '',
|
|
pais: 'Argentina'
|
|
},
|
|
dominio: {
|
|
preferido: dominioPreferido,
|
|
generado: `${dominioPreferido}.gkachele.duckdns.org`
|
|
},
|
|
descripcion: data.descripcion || '',
|
|
estado: 'preview',
|
|
fecha_creacion: new Date().toISOString()
|
|
};
|
|
|
|
const workflowInputs = {
|
|
form_data: JSON.stringify(jsonData),
|
|
rubro: data.rubro,
|
|
plan: data.plan,
|
|
target_branch: 'main' // SIEMPRE usar main después del rollback
|
|
};
|
|
|
|
const inputsBase64 = btoa(JSON.stringify(workflowInputs));
|
|
|
|
// ✅ SOLUCIÓN COMPLETAMENTE DIFERENTE: Generar token en cliente, guardar en localStorage
|
|
console.log('🔄 Generando preview LOCALMENTE (sin llamadas externas)...');
|
|
|
|
// Generar token dinámico en el cliente (igual formato que el workflow)
|
|
function generateToken() {
|
|
const randomHash = Array.from(crypto.getRandomValues(new Uint8Array(4)))
|
|
.map(b => b.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
const day = String(now.getDate()).padStart(2, '0');
|
|
const hours = String(now.getHours()).padStart(2, '0');
|
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
|
|
const dateStr = `${year}${month}${day}`;
|
|
const timeStr = `${hours}${minutes}${seconds}`;
|
|
|
|
return `${randomHash}-${dateStr}-${timeStr}`;
|
|
}
|
|
|
|
const token = generateToken();
|
|
console.log('✅ Token generado:', token);
|
|
|
|
// Guardar datos del preview en localStorage
|
|
// Calcular expiración: 15 minutos exactos
|
|
const expiresAt = new Date(Date.now() + 15 * 60 * 1000).toISOString();
|
|
|
|
const previewData = {
|
|
token: token,
|
|
form_data: jsonData,
|
|
rubro: data.rubro,
|
|
plan: data.plan,
|
|
created_at: new Date().toISOString(),
|
|
expires_at: expiresAt, // 15 minutos exactos
|
|
status: 'local_preview'
|
|
};
|
|
|
|
// Guardar en localStorage con clave única
|
|
localStorage.setItem('preview_' + token, JSON.stringify(previewData));
|
|
sessionStorage.setItem('current_preview_token', token);
|
|
|
|
// Guardar también en formato que el admin.html espera (con expiración de 15 min)
|
|
const adminData = {
|
|
form_data: jsonData,
|
|
rubro: data.rubro,
|
|
plan: data.plan,
|
|
created_at: new Date().toISOString(),
|
|
expires_at: expiresAt, // 15 minutos exactos
|
|
session_token_expires_at: expiresAt // Para que el admin muestre correctamente
|
|
};
|
|
localStorage.setItem('preview_data_' + token, JSON.stringify(adminData));
|
|
|
|
console.log('✅ Datos del preview guardados en localStorage');
|
|
|
|
// Redirigir al formulario de creación con plan y rubro
|
|
const createUrl = `/register?plan=${data.plan}&rubro=${data.rubro}`;
|
|
|
|
console.log('✅ Redirigiendo a:', createUrl);
|
|
|
|
btnPreview.innerHTML = '<i class="fas fa-check"></i> Preview Generado';
|
|
|
|
// Cerrar el modal del formulario
|
|
closeForm();
|
|
|
|
// Redirigir directamente al registro
|
|
window.location.href = createUrl;
|
|
return;
|
|
|
|
} catch (error) {
|
|
console.error('Error generando preview:', error);
|
|
alert('❌ No se pudo generar el preview en este momento.\n\n' +
|
|
'Por favor, intenta nuevamente en unos instantes.');
|
|
} finally {
|
|
btnPreview.disabled = false;
|
|
btnPreview.innerHTML = originalText;
|
|
}
|
|
}
|
|
|
|
// DESACTIVAR COMPLETAMENTE el cierre por clic fuera del modal
|
|
// Solo se puede cerrar con los botones explícitos (X, Cancelar, o después de enviar)
|
|
const modal = document.getElementById('planFormModal');
|
|
const modalContent = modal.querySelector('.modal-form-content');
|
|
|
|
// Prevenir TODOS los clics dentro del contenido
|
|
modalContent.addEventListener('click', function(event) {
|
|
event.stopPropagation();
|
|
});
|
|
|
|
// Prevenir TODOS los clics en el fondo del modal también
|
|
modal.addEventListener('click', function(event) {
|
|
// NO cerrar automáticamente - solo con botones explícitos
|
|
event.stopPropagation();
|
|
});
|
|
|
|
// Scroll to Top Button
|
|
const scrollToTopBtn = document.getElementById('scrollToTop');
|
|
|
|
window.addEventListener('scroll', () => {
|
|
if (window.pageYOffset > 300) {
|
|
scrollToTopBtn.classList.add('show');
|
|
} else {
|
|
scrollToTopBtn.classList.remove('show');
|
|
}
|
|
});
|
|
|
|
scrollToTopBtn.addEventListener('click', () => {
|
|
window.scrollTo({
|
|
top: 0,
|
|
behavior: 'smooth'
|
|
});
|
|
});
|
|
|
|
// Smooth scroll for anchor links
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
if (target) {
|
|
target.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
// ============================================
|
|
// SISTEMA DE PREVIEWS
|
|
// ============================================
|
|
|
|
// Datos de previews (mock - luego vendrán de screenshots reales)
|
|
const previewsData = {
|
|
base: [
|
|
{ rubro: 'gimnasios', variacion: 'fuerte', nombre: 'Gimnasios - Diseño Fuerte', descripcion: 'Diseño oscuro y atlético, perfecto para gimnasios y centros de fitness', imagen: 'https://via.placeholder.com/800x450/1a1a1a/ffffff?text=Gimnasios+Fuerte' },
|
|
{ rubro: 'motos', variacion: 'clasico', nombre: 'Motos - Diseño Clásico', descripcion: 'Estilo robusto e industrial, ideal para concesionarias y talleres', imagen: 'https://via.placeholder.com/800x450/e74c3c/ffffff?text=Motos+Clasico' },
|
|
{ rubro: 'restaurante', variacion: 'acogedor', nombre: 'Restaurante - Diseño Acogedor', descripcion: 'Cálido y amigable, perfecto para restaurantes y cafeterías', imagen: 'https://via.placeholder.com/800x450/f39c12/ffffff?text=Restaurante+Acogedor' },
|
|
{ rubro: 'danza', variacion: 'vibrante', nombre: 'Danza - Diseño Vibrante', descripcion: 'Colorido y dinámico, ideal para escuelas de danza y academias', imagen: 'https://via.placeholder.com/800x450/e91e63/ffffff?text=Danza+Vibrante' },
|
|
{ rubro: 'cosmeticos', variacion: 'elegante', nombre: 'Cosméticos - Diseño Elegante', descripcion: 'Sofisticado y refinado, perfecto para salones de belleza y cosmética', imagen: 'https://via.placeholder.com/800x450/9b59b6/ffffff?text=Cosmeticos+Elegante' },
|
|
{ rubro: 'despachos', variacion: 'moderno', nombre: 'Despachos - Diseño Moderno', descripcion: 'Minimalista y profesional, ideal para despachos de abogados y contadores', imagen: 'https://via.placeholder.com/800x450/3498db/ffffff?text=Despachos+Moderno' }
|
|
],
|
|
pro: [
|
|
{ rubro: 'gimnasios', variacion: 'fuerte', nombre: 'Gimnasios PRO - Fuerte', descripcion: 'Diseño oscuro y atlético con animaciones, perfecto para gimnasios premium', imagen: 'https://via.placeholder.com/800x450/1a1a1a/ffffff?text=Gimnasios+PRO+Fuerte' },
|
|
{ rubro: 'gimnasios', variacion: 'moderno', nombre: 'Gimnasios PRO - Moderno', descripcion: 'Minimalista y limpio, ideal para centros de fitness modernos', imagen: 'https://via.placeholder.com/800x450/3498db/ffffff?text=Gimnasios+PRO+Moderno' },
|
|
{ rubro: 'gimnasios', variacion: 'elegante', nombre: 'Gimnasios PRO - Elegante', descripcion: 'Sofisticado y refinado, perfecto para estudios de yoga y pilates', imagen: 'https://via.placeholder.com/800x450/9b59b6/ffffff?text=Gimnasios+PRO+Elegante' }
|
|
],
|
|
premium: [
|
|
{ rubro: 'gimnasios', variacion: 'fuerte', nombre: 'Gimnasios PREMIUM - Fuerte', descripcion: 'Diseño premium con todas las características, ideal para cadenas de gimnasios', imagen: 'https://via.placeholder.com/800x450/1a1a1a/ffffff?text=Gimnasios+PREMIUM+Fuerte' }
|
|
]
|
|
};
|
|
|
|
// Variables globales para el sistema de previews
|
|
let currentPlan = '';
|
|
let selectedPreview = null;
|
|
|
|
// Abrir modal de previews
|
|
function openPreviews(planType) {
|
|
currentPlan = planType;
|
|
const modal = document.getElementById('previewsModal');
|
|
const planName = document.getElementById('previewPlanName');
|
|
|
|
const planNames = {
|
|
'base': 'Base',
|
|
'pro': 'Pro',
|
|
'premium': 'Premium',
|
|
'vip': 'VIP'
|
|
};
|
|
|
|
planName.textContent = planNames[planType] || planType;
|
|
modal.style.display = 'flex';
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// Cargar previews del plan
|
|
loadPreviews(planType);
|
|
}
|
|
|
|
// Cerrar modal de previews
|
|
function closePreviews() {
|
|
const modal = document.getElementById('previewsModal');
|
|
modal.style.display = 'none';
|
|
document.body.style.overflow = 'auto';
|
|
// Resetear filtro
|
|
const filterSelect = document.getElementById('filterRubro');
|
|
if (filterSelect) {
|
|
filterSelect.value = '';
|
|
}
|
|
}
|
|
|
|
// Cargar previews en el grid
|
|
function loadPreviews(planType) {
|
|
const grid = document.getElementById('previewsGrid');
|
|
const previews = previewsData[planType] || [];
|
|
|
|
grid.innerHTML = '';
|
|
|
|
// Crear las cards de preview
|
|
previews.forEach(preview => {
|
|
const card = createPreviewCard(preview, planType);
|
|
grid.appendChild(card);
|
|
});
|
|
|
|
// Actualizar filtro con TODOS los rubros disponibles
|
|
updateFilterOptions(previews);
|
|
|
|
// Filtrar según el rubro seleccionado
|
|
filterPreviews();
|
|
}
|
|
|
|
// Actualizar opciones del filtro - TODOS los rubros disponibles (sin screenshots)
|
|
function updateFilterOptions(previews) {
|
|
const filterSelect = document.getElementById('filterRubro');
|
|
const rubroNames = {
|
|
'gimnasios': 'Gimnasios',
|
|
'motos': 'Motos',
|
|
'restaurante': 'Restaurante',
|
|
'danza': 'Danza',
|
|
'cosmeticos': 'Cosméticos',
|
|
'despachos': 'Despachos'
|
|
};
|
|
|
|
// Obtener TODOS los rubros disponibles (no solo los que tienen previews)
|
|
const todosLosRubros = Object.keys(rubroNames);
|
|
|
|
// Mostrar filtro siempre
|
|
document.querySelector('.previews-filters').style.display = 'block';
|
|
filterSelect.innerHTML = '';
|
|
|
|
// Agregar TODOS los rubros al filtro
|
|
todosLosRubros.forEach(rubro => {
|
|
const option = document.createElement('option');
|
|
option.value = rubro;
|
|
option.textContent = rubroNames[rubro] || rubro;
|
|
filterSelect.appendChild(option);
|
|
});
|
|
|
|
// Seleccionar el primer rubro por defecto
|
|
if (todosLosRubros.length > 0) {
|
|
filterSelect.value = todosLosRubros[0];
|
|
}
|
|
}
|
|
|
|
// Crear card de preview
|
|
function createPreviewCard(preview, planType) {
|
|
const card = document.createElement('div');
|
|
card.className = 'template-card';
|
|
card.dataset.rubro = preview.rubro;
|
|
card.dataset.variacion = preview.variacion;
|
|
|
|
const rubroNames = {
|
|
'gimnasios': 'Gimnasios',
|
|
'motos': 'Motos',
|
|
'restaurante': 'Restaurante',
|
|
'danza': 'Danza',
|
|
'cosmeticos': 'Cosméticos',
|
|
'despachos': 'Despachos (Abogados/Contadores)'
|
|
};
|
|
|
|
const planBadgeClass = `badge-${planType}`;
|
|
|
|
card.innerHTML = `
|
|
<div class="template-preview">
|
|
<img src="${preview.imagen}" alt="${preview.nombre}" loading="lazy" />
|
|
<div class="template-overlay">
|
|
<div class="overlay-info">
|
|
<h4>${preview.nombre}</h4>
|
|
<p>${preview.descripcion}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="template-info">
|
|
<h3>${preview.nombre}</h3>
|
|
<div class="template-meta">
|
|
<span class="badge-plan ${planBadgeClass}">${planType.toUpperCase()}</span>
|
|
<span class="badge-rubro">${rubroNames[preview.rubro] || preview.rubro}</span>
|
|
</div>
|
|
<p class="template-description">${preview.descripcion}</p>
|
|
</div>
|
|
`;
|
|
|
|
// Click en card → abrir lightbox
|
|
card.addEventListener('click', () => {
|
|
openLightbox(preview, planType);
|
|
});
|
|
|
|
return card;
|
|
}
|
|
|
|
// Filtrar previews por rubro
|
|
function filterPreviews() {
|
|
const filterSelect = document.getElementById('filterRubro');
|
|
if (!filterSelect) return;
|
|
|
|
const filterValue = filterSelect.value.toLowerCase();
|
|
const cards = document.querySelectorAll('.template-card');
|
|
|
|
cards.forEach(card => {
|
|
const rubro = card.dataset.rubro;
|
|
if (!filterValue || rubro === filterValue) {
|
|
card.style.display = 'block';
|
|
} else {
|
|
card.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Abrir lightbox con screenshot grande
|
|
function openLightbox(preview, planType) {
|
|
selectedPreview = { ...preview, plan: planType };
|
|
const lightbox = document.getElementById('screenshotLightbox');
|
|
const image = document.getElementById('lightboxImage');
|
|
const title = document.getElementById('lightboxTitle');
|
|
const description = document.getElementById('lightboxDescription');
|
|
|
|
image.src = preview.imagen;
|
|
image.alt = preview.nombre;
|
|
title.textContent = preview.nombre;
|
|
description.textContent = preview.descripcion;
|
|
|
|
lightbox.style.display = 'flex';
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
// Cerrar lightbox
|
|
function closeLightbox() {
|
|
const lightbox = document.getElementById('screenshotLightbox');
|
|
lightbox.style.display = 'none';
|
|
document.body.style.overflow = 'auto';
|
|
selectedPreview = null;
|
|
}
|
|
|
|
// Solicitar diseño (abre formulario prellenado)
|
|
function requestDesign() {
|
|
if (!selectedPreview) return;
|
|
|
|
// Cerrar lightbox y modal de previews
|
|
closeLightbox();
|
|
closePreviews();
|
|
|
|
// Abrir formulario con datos prellenados
|
|
const planType = selectedPreview.plan;
|
|
openForm(planType);
|
|
|
|
// Prellenar campos después de un pequeño delay para que el formulario se abra
|
|
setTimeout(() => {
|
|
const rubroSelect = document.getElementById('rubro');
|
|
const templateVariation = document.getElementById('templateVariation');
|
|
|
|
if (rubroSelect) {
|
|
rubroSelect.value = selectedPreview.rubro;
|
|
// Disparar evento para actualizar campos de rubro
|
|
rubroSelect.dispatchEvent(new Event('change'));
|
|
}
|
|
|
|
if (templateVariation) {
|
|
templateVariation.value = selectedPreview.variacion;
|
|
}
|
|
|
|
// Actualizar campos de rubro si existe la función
|
|
if (typeof actualizarCamposRubro === 'function') {
|
|
actualizarCamposRubro();
|
|
}
|
|
}, 300);
|
|
}
|
|
|
|
// Cerrar modales al hacer clic fuera
|
|
document.getElementById('previewsModal').addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
closePreviews();
|
|
}
|
|
});
|
|
|
|
document.getElementById('screenshotLightbox').addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
closeLightbox();
|
|
}
|
|
});
|
|
|
|
// Cerrar con ESC
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
closePreviews();
|
|
closeLightbox();
|
|
}
|
|
});
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|