maxiqueen-os-v2 / Clientes.html
MaxiQueenOS's picture
MaxiQueen OS: Despliegue 100% compatible con LFS
8e15eed
Raw
History Blame Contribute Delete
14.4 kB
<!DOCTYPE html>
<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>