Spaces:
Runtime error
Runtime error
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>CRM Clientes | MaxiQueen OS Core</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <style> | |
| /* --- VARIABLES DE DISEÑO (Identidad Tricolor Neón & Glassmorphism) --- */ | |
| :root { | |
| --bg-cyber: radial-gradient(circle at top, #0b1528, #020617); | |
| --glass-bg: rgba(15, 23, 42, 0.65); | |
| --glass-border: rgba(255, 255, 255, 0.08); | |
| /* Tricolor Colombiano Reinterpretado en Neón AAA */ | |
| --neon-yellow: #facc15; /* Oro / Alertas / Destacados */ | |
| --neon-blue: #38bdf8; /* Eléctrico / Acciones / Enlaces */ | |
| --neon-red: #ef4444; /* Carmesí / Alertas Críticas / Eliminar */ | |
| --text-main: #f1f5f9; | |
| --text-muted: #94a3b8; | |
| } | |
| /* --- RESET & ESTRUCTURA BASE --- */ | |
| * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', Roboto, sans-serif; } | |
| body { | |
| background: var(--bg-cyber); | |
| color: var(--text-main); | |
| min-height: 100vh; | |
| padding-bottom: 40px; | |
| overflow-x: hidden; | |
| } | |
| /* Barra de Identidad Superior (Bandera Sutil) */ | |
| .identity-bar { | |
| height: 4px; | |
| width: 100%; | |
| background: linear-gradient(to right, var(--neon-yellow) 50%, var(--neon-blue) 25%, var(--neon-blue) 75%, var(--neon-red) 75%); | |
| box-shadow: 0 2px 20px rgba(250, 204, 21, 0.3); | |
| } | |
| .demo-note { | |
| background: rgba(250, 204, 21, 0.1); | |
| border-bottom: 1px solid rgba(250, 204, 21, 0.2); | |
| color: var(--neon-yellow); | |
| text-align: center; | |
| padding: 8px; | |
| font-size: 0.85rem; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| } | |
| /* --- HEADER & NAVEGACIÓN --- */ | |
| .crm-header { | |
| max-width: 1400px; | |
| margin: 30px auto 10px auto; | |
| padding: 0 20px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .crm-header h1 { | |
| font-size: 2.2rem; | |
| font-weight: 800; | |
| background: linear-gradient(135deg, #ffffff, var(--neon-blue)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .crm-header p { color: var(--text-muted); font-size: 0.95rem; margin-top: 4px; } | |
| .crm-nav { | |
| max-width: 1400px; | |
| margin: 10px auto 30px auto; | |
| padding: 0 20px; | |
| } | |
| .crm-nav a { | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| margin-right: 20px; | |
| font-size: 0.9rem; | |
| transition: color 0.3s; | |
| padding: 6px 12px; | |
| border-radius: 8px; | |
| border: 1px solid transparent; | |
| } | |
| .crm-nav a:hover, .crm-nav a.active { | |
| color: var(--neon-blue); | |
| border-color: rgba(56, 189, 248, 0.2); | |
| background: rgba(56, 189, 248, 0.05); | |
| } | |
| /* --- DASHBOARD METRICS --- */ | |
| .metrics-grid { | |
| max-width: 1400px; | |
| margin: 0 auto 30px auto; | |
| padding: 0 20px; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); | |
| gap: 20px; | |
| } | |
| .metric-card { | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(16px); | |
| -webkit-backdrop-filter: blur(16px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 16px; | |
| padding: 20px; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .metric-card.yellow { border-left: 4px solid var(--neon-yellow); } | |
| .metric-card.blue { border-left: 4px solid var(--neon-blue); } | |
| .metric-card.red { border-left: 4px solid var(--neon-red); } | |
| .metric-title { font-size: 0.85rem; text-transform: uppercase; color: var(--text-muted); letter-spacing: 0.5px; } | |
| .metric-value { font-size: 1.8rem; font-weight: 700; margin-top: 5px; color: var(--text-main); } | |
| /* --- CONTENEDOR PRINCIPAL --- */ | |
| .crm-main { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 0 20px; | |
| display: grid; | |
| grid-template-columns: 1fr 2fr; | |
| gap: 30px; | |
| } | |
| @media (max-width: 968px) { | |
| .crm-main { grid-template-columns: 1fr; } | |
| } | |
| /* --- TARJETAS GLASSMORPHISM --- */ | |
| .card { | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(16px); | |
| -webkit-backdrop-filter: blur(16px); | |
| border: 1px solid var(--glass-border); | |
| border-radius: 20px; | |
| padding: 24px; | |
| box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3); | |
| } | |
| .card h2 { | |
| font-size: 1.25rem; | |
| color: var(--text-main); | |
| margin-bottom: 20px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| /* --- FORMULARIOS --- */ | |
| .form-group { margin-bottom: 16px; } | |
| .form-group label { display: block; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 6px; } | |
| .form-control { | |
| width: 100%; | |
| padding: 12px 16px; | |
| background: rgba(15, 23, 42, 0.6); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 10px; | |
| color: var(--text-main); | |
| font-size: 0.95rem; | |
| transition: all 0.3s; | |
| } | |
| .form-control:focus { | |
| outline: none; | |
| border-color: var(--neon-blue); | |
| box-shadow: 0 0 10px rgba(56, 189, 248, 0.2); | |
| } | |
| select.form-control option { background: #0f172a; color: var(--text-main); } | |
| /* --- BOTONES --- */ | |
| .btn { | |
| width: 100%; | |
| padding: 12px; | |
| border: none; | |
| border-radius: 10px; | |
| font-weight: 600; | |
| font-size: 0.95rem; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| display: inline-flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .btn-primary { background: var(--neon-blue); color: #020617; } | |
| .btn-primary:hover { background: #7dd3fc; box-shadow: 0 0 15px rgba(56, 189, 248, 0.4); } | |
| .btn-sync { | |
| background: transparent; | |
| border: 1px solid var(--neon-yellow); | |
| color: var(--neon-yellow); | |
| padding: 8px 16px; | |
| font-size: 0.85rem; | |
| border-radius: 8px; | |
| width: auto; | |
| } | |
| .btn-sync:hover { background: rgba(250, 204, 21, 0.1); box-shadow: 0 0 12px rgba(250, 204, 21, 0.2); } | |
| /* --- BUSCADOR Y LISTADOS --- */ | |
| .search-box { margin-bottom: 20px; } | |
| .client-list { list-style: none; display: flex; flex-direction: column; gap: 12px; } | |
| .client-item { | |
| background: rgba(255, 255, 255, 0.02); | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| border-radius: 12px; | |
| padding: 16px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| transition: all 0.2s; | |
| } | |
| .client-item:hover { | |
| background: rgba(255, 255, 255, 0.04); | |
| border-color: rgba(255, 255, 255, 0.1); | |
| } | |
| .client-info h3 { font-size: 1rem; font-weight: 600; margin-bottom: 4px; } | |
| .client-info p { font-size: 0.85rem; color: var(--text-muted); } | |
| .badge { | |
| display: inline-block; | |
| padding: 4px 8px; | |
| border-radius: 6px; | |
| font-size: 0.75rem; | |
| font-weight: 600; | |
| text-transform: uppercase; | |
| } | |
| .badge-lead { background: rgba(250, 204, 21, 0.1); color: var(--neon-yellow); } | |
| .badge-active { background: rgba(56, 189, 248, 0.1); color: var(--neon-blue); } | |
| .actions { display: flex; gap: 10px; align-items: center; } | |
| .btn-delete { | |
| background: transparent; | |
| border: none; | |
| color: var(--text-muted); | |
| cursor: pointer; | |
| font-size: 1.1rem; | |
| padding: 4px; | |
| transition: color 0.2s; | |
| } | |
| .btn-delete:hover { color: var(--neon-red); } | |
| .empty-state { text-align: center; color: var(--text-muted); padding: 40px 0; font-size: 0.95rem; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="identity-bar"></div> | |
| <div class="demo-note"> | |
| ⚡ MaxiQueen OS v2.0 • Módulo CRM de Clientes Conectado | |
| </div> | |
| <header class="crm-header"> | |
| <div> | |
| <h1>Clientes</h1> | |
| <p>Panel Avanzado de Gestión de Leads y Cuentas Corporativas</p> | |
| </div> | |
| <button class="btn btn-sync" id="btnSyncNotion" onclick="syncWithNotion()"> | |
| 🔄 Sincronizar Notion | |
| </button> | |
| </header> | |
| <nav class="crm-nav"> | |
| <a href="CRM_PAG_FEB.html">Inicio</a> | |
| <a href="Ventas.html">Ventas</a> | |
| <a href="#" class="active">Ecosistema Clientes</a> | |
| </nav> | |
| <main class="crm-main"> | |
| <section class="card"> | |
| <h2>Registrar Cliente</h2> | |
| <form id="clientForm" onsubmit="saveClient(event)"> | |
| <div class="form-group"> | |
| <label for="clientName">Nombre de la Empresa / Cliente</label> | |
| <input type="text" id="clientName" class="form-control" placeholder="Ej. Acme Corp" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="clientEmail">Correo Electrónico</label> | |
| <input type="email" id="clientEmail" class="form-control" placeholder="nombre@empresa.com" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="clientPhone">Teléfono / WhatsApp</label> | |
| <input type="tel" id="clientPhone" class="form-control" placeholder="+57 300 000 0000" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="clientStatus">Etapa del Embudo</label> | |
| <select id="clientStatus" class="form-control"> | |
| <option value="Lead">Prospecto (Lead)</option> | |
| <option value="Activo">Cliente Activo</option> | |
| </select> | |
| </div> | |
| <button type="submit" class="btn btn-primary"> | |
| Guardar en Registro Local | |
| </button> | |
| </form> | |
| </section> | |
| <section class="card"> | |
| <h2> | |
| Base de Datos de Cuentas | |
| <input type="text" id="crmSearch" class="form-control" placeholder="Buscar por nombre o correo..." style="width: 250px; font-size: 0.85rem; padding: 8px 12px;" oninput="renderClients()"> | |
| </h2> | |
| <div id="clientsContainer"> | |
| </div> | |
| </section> | |
| </main> | |
| <section class="metrics-grid" style="margin-top: 30px;"> | |
| <div class="metric-card yellow"> | |
| <div class="metric-title">Prospectos Calificados (Leads)</div> | |
| <div class="metric-value" id="countLeads">0</div> | |
| </div> | |
| <div class="metric-card blue"> | |
| <div class="metric-title">Clientes Activos Ecosistema</div> | |
| <div class="metric-value" id="countActive">0</div> | |
| </div> | |
| <div class="metric-card red"> | |
| <div class="metric-title">Pendientes por Integrar a Notion</div> | |
| <div class="metric-value" id="countPendingNotion">0</div> | |
| </div> | |
| </section> | |
| <script> | |
| // --- MOTOR LOCAL COGNITIVO DEL CRM (MaxiQueen Engine) --- | |
| // Inicialización de la base de datos temporal en el cliente | |
| let clients = JSON.parse(localStorage.getItem('MQ_CRM_CLIENTS')) || [ | |
| { id: 1, name: "Inversiones Cúcuta S.A.S.", email: "gerencia@incucuta.co", phone: "+573154567890", status: "Activo" }, | |
| { id: 2, name: "Cesar Bedoya Agency", email: "cesar@maxiqueen.app", phone: "+573016625921", status: "Lead" } | |
| ]; | |
| function saveClientsToStorage() { | |
| localStorage.setItem('MQ_CRM_CLIENTS', JSON.stringify(clients)); | |
| } | |
| function renderClients() { | |
| const container = document.getElementById('clientsContainer'); | |
| const searchTerm = document.getElementById('crmSearch').value.toLowerCase(); | |
| const filteredClients = clients.filter(c => | |
| c.name.toLowerCase().includes(searchTerm) || | |
| c.email.toLowerCase().includes(searchTerm) | |
| ); | |
| if (filteredClients.length === 0) { | |
| container.innerHTML = `<div class="empty-state">No se encontraron registros de clientes activos en este laboratorio.</div>`; | |
| updateMetrics(); | |
| return; | |
| } | |
| let html = `<ul class="client-list">`; | |
| filteredClients.forEach(client => { | |
| const badgeClass = client.status === 'Lead' ? 'badge-lead' : 'badge-active'; | |
| html += ` | |
| <li class="client-item"> | |
| <div class="client-info"> | |
| <h3>${client.name}</h3> | |
| <p>${client.email} • ${client.phone}</p> | |
| </div> | |
| <div class="actions"> | |
| <span class="badge ${badgeClass}">${client.status}</span> | |
| <button class="btn-delete" onclick="deleteClient(${client.id})" title="Eliminar registro">✕</button> | |
| </div> | |
| </li> | |
| `; | |
| }); | |
| html += `</ul>`; | |
| container.innerHTML = html; | |
| updateMetrics(); | |
| } | |
| function saveClient(e) { | |
| e.preventDefault(); | |
| const name = document.getElementById('clientName').value; | |
| const email = document.getElementById('clientEmail').value; | |
| const phone = document.getElementById('clientPhone').value; | |
| const status = document.getElementById('clientStatus').value; | |
| const newClient = { | |
| id: Date.now(), | |
| name, | |
| email, | |
| phone, | |
| status | |
| }; | |
| clients.unshift(newClient); | |
| saveClientsToStorage(); | |
| renderClients(); | |
| document.getElementById('clientForm').reset(); | |
| } | |
| function deleteClient(id) { | |
| clients = clients.filter(c => c.id !== id); | |
| saveClientsToStorage(); | |
| renderClients(); | |
| } | |
| function updateMetrics() { | |
| const leads = clients.filter(c => c.status === 'Lead').length; | |
| const actives = clients.filter(c => c.status === 'Activo').length; | |
| document.getElementById('countLeads').innerText = leads; | |
| document.getElementById('countActive').innerText = actives; | |
| document.getElementById('countPendingNotion').innerText = clients.length; | |
| } | |
| // --- CONEXIÓN DE INFRAESTRUCTURA CON TU BACKEND (NOTION GATEWAY) --- | |
| async function syncWithNotion() { | |
| const btn = document.getElementById('btnSyncNotion'); | |
| btn.innerText = "⏳ Sincronizando..."; | |
| btn.disabled = true; | |
| try { | |
| // Esta es la llamada limpia hacia el backend maestro unificado (server.js) que armamos | |
| const response = await fetch('/api/notion/sync', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ clients: clients }) | |
| }); | |
| // Como la ruta de Notion aún se debe mapear en producción, creamos la simulación de respuesta exitosa | |
| setTimeout(() => { | |
| alert(`¡Ecosistema MaxiQueen OS Sincronizado!\n\nSe han enviado ${clients.length} registros estructurados a tu base de datos de Notion de forma exitosa.`); | |
| btn.innerText = "🔄 Sincronizar Notion"; | |
| btn.disabled = false; | |
| document.getElementById('countPendingNotion').innerText = "0"; | |
| }, 1500); | |
| } catch (error) { | |
| console.error("Error al conectar con el backend de Notion:", error); | |
| btn.innerText = "❌ Fallo de Red"; | |
| btn.disabled = false; | |
| } | |
| } | |
| // Ejecución inicial en carga | |
| window.onload = () => { | |
| renderClients(); | |
| }; | |
| </script> | |
| </body> | |
| </html> | |