Files
web/proteccion.html
2026-04-02 20:26:37 +00:00

400 lines
31 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Seguros SaaS - IntegraRepara</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<style>
.fade-in { animation: fadeIn 0.2s ease-in-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.tab-btn { transition: all 0.3s ease; border-bottom: 2px solid transparent; }
.tab-btn.active { color: #2563eb; border-color: #2563eb; background-color: #eff6ff; }
.modal-backdrop { background: rgba(15, 23, 42, 0.45); backdrop-filter: blur(4px); }
</style>
</head>
<body class="bg-gray-100 text-gray-800 font-sans h-screen overflow-hidden flex">
<div id="sidebar-container" class="h-full shrink-0"></div>
<div class="flex-1 flex flex-col h-full min-w-0">
<div id="header-container"></div>
<main class="flex-1 flex flex-col overflow-hidden relative p-6 space-y-6">
<div class="flex flex-col lg:flex-row lg:items-center justify-between gap-4 bg-white p-6 rounded-2xl shadow-sm border border-gray-200 shrink-0">
<div class="flex items-center gap-4">
<div class="w-12 h-12 bg-blue-600 rounded-xl flex items-center justify-center text-white shadow-lg">
<i data-lucide="shield-check" class="w-7 h-7"></i>
</div>
<div>
<h1 class="text-xl font-black text-gray-800 tracking-tight">Seguros / Planes de Protección</h1>
<p class="text-gray-400 text-xs font-bold uppercase tracking-widest">MÓDULO SAAS PARA ASEGURADORAS Y SUSCRIPCIONES</p>
</div>
</div>
<div class="flex flex-wrap gap-2">
<button onclick="openPlanModal()" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest shadow-md hover:bg-blue-700 transition-all flex items-center gap-2">
<i data-lucide="plus" class="w-3.5 h-3.5"></i> Nuevo Plan
</button>
<button onclick="openClientModal()" class="bg-white border border-gray-200 text-gray-700 px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest shadow-sm hover:border-blue-300 transition-all flex items-center gap-2">
<i data-lucide="user-plus" class="w-3.5 h-3.5"></i> Nuevo Suscriptor
</button>
</div>
</div>
<div class="flex bg-gray-100 p-1 rounded-xl shrink-0 w-fit">
<button onclick="switchTab('dashboard')" id="btn-tab-dashboard" class="tab-btn active px-5 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest flex items-center gap-2">
<i data-lucide="layout-dashboard" class="w-3 h-3"></i> Resumen
</button>
<button onclick="switchTab('clients')" id="btn-tab-clients" class="tab-btn px-5 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest flex items-center gap-2">
<i data-lucide="users" class="w-3 h-3"></i> Suscriptores
</button>
<button onclick="switchTab('plans')" id="btn-tab-plans" class="tab-btn px-5 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest flex items-center gap-2">
<i data-lucide="badge-euro" class="w-3 h-3"></i> Planes
</button>
<button onclick="switchTab('config')" id="btn-tab-config" class="tab-btn px-5 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest flex items-center gap-2">
<i data-lucide="settings" class="w-3 h-3"></i> Configuración
</button>
</div>
<div id="tab-dashboard" class="tab-content flex-1 flex flex-col min-h-0 fade-in overflow-hidden">
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4 mb-6 shrink-0" id="statsGrid"></div>
<div class="grid grid-cols-1 xl:grid-cols-3 gap-6 flex-1 min-h-0">
<div class="xl:col-span-2 bg-white rounded-2xl shadow-sm border border-gray-200 flex flex-col overflow-hidden">
<div class="p-4 border-b border-gray-100 flex justify-between items-center bg-gray-50/50 shrink-0">
<div>
<h3 class="text-sm font-black text-gray-800">Actividad reciente</h3>
<p class="text-[11px] text-gray-400 font-semibold">Altas, cobros, renovaciones y uso de coberturas</p>
</div>
</div>
<div class="flex-1 overflow-y-auto no-scrollbar p-4 space-y-3" id="activityList"></div>
</div>
<div class="bg-white rounded-2xl shadow-sm border border-gray-200 flex flex-col overflow-hidden">
<div class="p-4 border-b border-gray-100 bg-gray-50/50">
<h3 class="text-sm font-black text-gray-800">Planes más vendidos</h3>
<p class="text-[11px] text-gray-400 font-semibold">Resumen rápido del catálogo</p>
</div>
<div class="flex-1 overflow-y-auto no-scrollbar p-4 space-y-4" id="topPlansList"></div>
</div>
</div>
</div>
<div id="tab-clients" class="tab-content hidden flex-1 flex flex-col min-h-0 fade-in">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6 shrink-0">
<div class="relative w-full md:w-80">
<i data-lucide="search" class="w-4 h-4 absolute left-3 top-2.5 text-gray-400"></i>
<input id="searchClientInput" type="text" placeholder="Buscar suscriptor..." class="w-full pl-10 pr-4 py-2 bg-white border border-gray-200 rounded-lg text-xs font-bold outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div class="flex-1 bg-white rounded-2xl shadow-sm border border-gray-200 flex flex-col overflow-hidden">
<div class="flex-1 overflow-y-auto no-scrollbar">
<table class="w-full text-left">
<thead class="sticky top-0 bg-white shadow-sm text-[10px] font-black text-gray-400 uppercase tracking-widest">
<tr>
<th class="px-6 py-4">Socio / Datos</th>
<th class="px-6 py-4">Plan / Cuota</th>
<th class="px-6 py-4 text-center">Uso (Bricos/Urg)</th>
<th class="px-6 py-4">Estado Pago</th>
<th class="px-6 py-4">Renovación</th>
<th class="px-6 py-4 text-right">Acciones</th>
</tr>
</thead>
<tbody id="clientsTableBody" class="text-sm font-bold text-gray-600 divide-y divide-gray-50"></tbody>
</table>
</div>
</div>
</div>
<div id="tab-plans" class="tab-content hidden flex-1 min-h-0 fade-in overflow-y-auto no-scrollbar pb-10">
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6" id="plansGrid"></div>
</div>
<div id="tab-config" class="tab-content hidden flex-1 min-h-0 fade-in overflow-y-auto no-scrollbar pb-10">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-1 space-y-6">
<div class="bg-white p-6 rounded-2xl border border-gray-200 shadow-sm">
<h3 class="text-xs font-black text-gray-800 uppercase tracking-widest mb-4 flex items-center gap-2 border-b pb-3"><i data-lucide="building-2" class="w-4 h-4 text-blue-600"></i> Datos Empresa</h3>
<div class="space-y-4">
<div><label class="text-[10px] font-black text-gray-400 uppercase">Nombre</label><input id="cfg_name" type="text" class="w-full px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Email</label><input id="cfg_email" type="text" class="w-full px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Teléfono</label><input id="cfg_phone" type="text" class="w-full px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
</div>
</div>
<div class="bg-white p-6 rounded-2xl border border-gray-200 shadow-sm">
<h3 class="text-xs font-black text-gray-800 uppercase tracking-widest mb-4 flex items-center gap-2 border-b pb-3 text-amber-600"><i data-lucide="refresh-cw" class="w-4 h-4"></i> Renovaciones</h3>
<div class="space-y-4">
<div class="flex items-center justify-between bg-gray-50 rounded-xl px-4 py-3"><span class="text-xs font-black text-gray-600 uppercase">Automática</span><input id="cfg_auto_renew" type="checkbox" class="w-5 h-5"></div>
<div class="flex items-center justify-between bg-gray-50 rounded-xl px-4 py-3"><span class="text-xs font-black text-gray-600 uppercase">Aviso previo</span><input id="cfg_pre_notice" type="checkbox" class="w-5 h-5"></div>
</div>
</div>
<div class="bg-white p-6 rounded-2xl border border-gray-200 shadow-sm">
<h3 class="text-xs font-black text-gray-800 uppercase tracking-widest mb-4 flex items-center gap-2 border-b pb-3 text-blue-600"><i data-lucide="credit-card" class="w-4 h-4"></i> Cobro</h3>
<div>
<select id="cfg_billing_method" class="w-full px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm">
<option value="stripe">Stripe</option>
<option value="recibo">Recibo</option>
<option value="transferencia">Transferencia</option>
</select>
</div>
</div>
</div>
<div class="lg:col-span-2">
<div class="bg-white p-6 rounded-2xl border border-gray-200 shadow-sm h-full flex flex-col">
<div class="flex justify-between items-center mb-4 border-b pb-3">
<h3 class="text-xs font-black text-gray-800 uppercase tracking-widest flex items-center gap-2"><i data-lucide="file-signature" class="w-4 h-4 text-blue-600"></i> Contrato Global</h3>
<button onclick="saveConfig()" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-[10px] font-black uppercase tracking-widest shadow-md hover:bg-blue-700 transition-all flex items-center gap-2"><i data-lucide="save" class="w-3.5 h-3.5"></i> Guardar Cambios</button>
</div>
<textarea id="cfg_contract" class="flex-1 w-full p-6 bg-gray-50 border rounded-2xl text-xs font-medium leading-relaxed text-gray-500 focus:ring-2 focus:ring-blue-500 outline-none min-h-[400px]"></textarea>
</div>
</div>
</div>
</div>
</main>
</div>
<div id="modalBackdrop" class="hidden fixed inset-0 z-40 modal-backdrop" onclick="closeModals()"></div>
<div id="planModal" class="hidden fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-none">
<div class="bg-white w-full max-w-2xl rounded-2xl shadow-2xl border border-gray-200 overflow-hidden pointer-events-auto">
<div class="p-5 border-b border-gray-100 flex items-center justify-between">
<h3 class="text-sm font-black text-gray-800 uppercase tracking-widest">Nuevo Plan</h3>
<button onclick="closeModals()" class="text-gray-400 hover:text-gray-700"><i data-lucide="x" class="w-5 h-5"></i></button>
</div>
<div class="p-5 grid grid-cols-1 md:grid-cols-2 gap-4">
<div><label class="text-[10px] font-black text-gray-400 uppercase">Nombre</label><input id="plan_name" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Tipo</label><select id="plan_type" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"><option value="mensual">Mensual</option><option value="anual">Anual</option></select></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Precio Oferta (€)</label><input id="plan_price" type="number" step="0.01" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Precio Renovación (€)</label><input id="plan_renewal" type="number" step="0.01" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Urgencias / año</label><input id="plan_urgencies" type="number" value="0" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Bricos / año</label><input id="plan_bricos" type="number" value="0" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div class="md:col-span-2"><label class="text-[10px] font-black text-gray-400 uppercase">Coberturas (separadas por coma)</label><input id="plan_coverages" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm" placeholder="Electricidad, Fontanería"></div>
</div>
<div class="p-5 border-t border-gray-100 flex justify-end gap-2 bg-gray-50/50">
<button onclick="closeModals()" class="px-4 py-2 rounded-lg border border-gray-200 text-[10px] font-black uppercase tracking-widest">Cancelar</button>
<button onclick="savePlan()" class="px-4 py-2 rounded-lg bg-blue-600 text-white text-[10px] font-black uppercase tracking-widest shadow-md">Guardar</button>
</div>
</div>
</div>
<div id="clientModal" class="hidden fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-none">
<div class="bg-white w-full max-w-3xl rounded-2xl shadow-2xl border border-gray-200 overflow-hidden pointer-events-auto">
<div class="p-5 border-b border-gray-100 flex items-center justify-between">
<h3 class="text-sm font-black text-gray-800 uppercase tracking-widest">Nuevo Suscriptor</h3>
<button onclick="closeModals()" class="text-gray-400 hover:text-gray-700"><i data-lucide="x" class="w-5 h-5"></i></button>
</div>
<div class="p-5 grid grid-cols-1 md:grid-cols-2 gap-4">
<div><label class="text-[10px] font-black text-gray-400 uppercase">Nombre completo</label><input id="client_name" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">DNI</label><input id="client_dni" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Teléfono</label><input id="client_phone" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Plan</label><select id="client_plan" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></select></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Estado pago</label><select id="client_payment_status" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"><option value="pagado">Pagado</option><option value="impagado">Impagado</option></select></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Estado</label><select id="client_status" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"><option value="activo">Activo</option><option value="suspendido">Suspendido</option></select></div>
<div><label class="text-[10px] font-black text-gray-400 uppercase">Renovación</label><input id="client_renewal" type="date" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm"></div>
<div class="flex gap-2">
<div class="flex-1"><label class="text-[10px] font-black text-gray-400 uppercase">Bricos consumidos</label><input id="client_bricos" type="number" value="0" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm" title="Solo cambiar si el cliente ya ha gastado bricos de su plan este año"></div>
<div class="flex-1"><label class="text-[10px] font-black text-gray-400 uppercase">Urg. gastadas</label><input id="client_urg" type="number" value="0" class="w-full mt-1 px-4 py-2.5 bg-gray-50 border rounded-xl font-bold text-sm" title="Solo cambiar si el cliente ya ha gastado urgencias de su plan este año"></div> </div>
</div>
<div class="p-5 border-t border-gray-100 flex justify-end gap-2 bg-gray-50/50">
<button onclick="closeModals()" class="px-4 py-2 rounded-lg border border-gray-200 text-[10px] font-black uppercase tracking-widest">Cancelar</button>
<button onclick="saveClient()" class="px-4 py-2 rounded-lg bg-blue-600 text-white text-[10px] font-black uppercase tracking-widest shadow-md">Guardar</button>
</div>
</div>
</div>
<script src="js/layout.js"></script>
<script>
let allClients = [];
document.addEventListener('DOMContentLoaded', () => {
loadAllData();
document.getElementById('searchClientInput').addEventListener('input', renderClients);
if(window.lucide) lucide.createIcons();
});
async function fetchAPI(endpoint, method = 'GET', body = null) {
const options = { method, headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json' }};
if (body) options.body = JSON.stringify(body);
const res = await fetch(`${API_URL}${endpoint}`, options);
return res.json();
}
async function loadAllData() {
const [dashRes, plansRes, clientsRes, confRes] = await Promise.all([
fetchAPI('/protection/dashboard'),
fetchAPI('/protection/plans'),
fetchAPI('/protection/subscribers'),
fetchAPI('/protection/config')
]);
if(dashRes.ok) {
renderStats(dashRes.stats);
renderActivity(dashRes.activity);
renderTopPlans(dashRes.topPlans);
}
if(plansRes.ok) {
renderPlans(plansRes.plans);
const sel = document.getElementById('client_plan');
sel.innerHTML = plansRes.plans.map(p => `<option value="${p.id}">${p.name} (${p.price}€)</option>`).join('');
}
if(clientsRes.ok) {
allClients = clientsRes.subscribers;
renderClients();
}
if(confRes.ok && confRes.config) {
document.getElementById('cfg_name').value = confRes.config.name || '';
document.getElementById('cfg_email').value = confRes.config.email || '';
document.getElementById('cfg_phone').value = confRes.config.phone || '';
document.getElementById('cfg_auto_renew').checked = !!confRes.config.auto_renew;
document.getElementById('cfg_pre_notice').checked = !!confRes.config.pre_notice;
document.getElementById('cfg_billing_method').value = confRes.config.billing_method || 'stripe';
document.getElementById('cfg_contract').value = confRes.config.contract_text || '';
}
if(window.lucide) lucide.createIcons();
}
async function savePlan() {
const data = {
name: document.getElementById('plan_name').value,
type: document.getElementById('plan_type').value,
price: parseFloat(document.getElementById('plan_price').value || 0),
renewal: parseFloat(document.getElementById('plan_renewal').value || 0),
urgencies: parseInt(document.getElementById('plan_urgencies').value || 0),
bricos: parseInt(document.getElementById('plan_bricos').value || 0),
coverages: document.getElementById('plan_coverages').value
};
if(!data.name) return toast('Falta el nombre');
await fetchAPI('/protection/plans', 'POST', data);
closeModals(); loadAllData(); toast('Plan creado');
}
async function saveClient() {
const data = {
plan_id: document.getElementById('client_plan').value,
name: document.getElementById('client_name').value,
dni: document.getElementById('client_dni').value,
phone: document.getElementById('client_phone').value,
payment_status: document.getElementById('client_payment_status').value,
status: document.getElementById('client_status').value,
renewal_date: document.getElementById('client_renewal').value,
bricos_used: document.getElementById('client_bricos').value,
urgencies_used: document.getElementById('client_urg').value
};
if(!data.name || !data.plan_id) return toast('Faltan datos clave');
await fetchAPI('/protection/subscribers', 'POST', data);
closeModals(); loadAllData(); toast('Suscriptor creado');
}
async function saveConfig() {
const data = {
name: document.getElementById('cfg_name').value,
email: document.getElementById('cfg_email').value,
phone: document.getElementById('cfg_phone').value,
auto_renew: document.getElementById('cfg_auto_renew').checked,
pre_notice: document.getElementById('cfg_pre_notice').checked,
billing_method: document.getElementById('cfg_billing_method').value,
contract_text: document.getElementById('cfg_contract').value
};
await fetchAPI('/protection/config', 'POST', data);
toast('Configuración guardada');
}
async function toggleStatus(id, field, currentValue) {
const newValue = (field === 'payment_status')
? (currentValue === 'pagado' ? 'impagado' : 'pagado')
: (currentValue === 'activo' ? 'suspendido' : 'activo');
await fetchAPI(`/protection/subscribers/${id}/toggle`, 'PUT', { field, value: newValue });
loadAllData();
}
function renderStats(s) {
document.getElementById('statsGrid').innerHTML = `
<div class="bg-white p-4 rounded-2xl border border-gray-200 shadow-sm"><p class="text-[9px] font-black text-gray-400 uppercase mb-1">Total Asegurados</p><p class="text-xl font-black text-gray-800">${s.total || 0}</p></div>
<div class="bg-white p-4 rounded-2xl border border-gray-200 shadow-sm"><p class="text-[9px] font-black text-gray-400 uppercase mb-1">Ingresos MRR</p><p class="text-xl font-black text-blue-600">${Number(s.mrr || 0).toFixed(2)}€</p></div>
<div class="bg-white p-4 rounded-2xl border border-gray-200 shadow-sm"><p class="text-[9px] font-black text-gray-400 uppercase mb-1">Urgencias/Bricos Mes</p><p class="text-xl font-black text-amber-500">${s.urgUsed || 0} / ${s.briUsed || 0}</p></div>
<div class="bg-white p-4 rounded-2xl border border-gray-200 shadow-sm"><p class="text-[9px] font-black text-gray-400 uppercase mb-1">Pagos Fallidos</p><p class="text-xl font-black text-rose-500">${s.unpaid || 0}</p></div>
`;
}
function renderActivity(acts) {
document.getElementById('activityList').innerHTML = acts.map(a => `
<div class="p-4 rounded-xl border border-gray-100 bg-white flex items-start gap-3">
<div class="w-9 h-9 rounded-xl bg-blue-50 text-blue-600 flex items-center justify-center shrink-0"><i data-lucide="${a.type==='alta'?'user-plus':(a.type==='cobro'?'credit-card':'alert-circle')}" class="w-4 h-4"></i></div>
<div><p class="text-sm font-black text-gray-800">${a.description}</p><p class="text-[11px] font-semibold text-gray-400">${new Date(a.created_at).toLocaleDateString()}</p></div>
</div>
`).join('') || '<p class="text-xs text-gray-400">Sin actividad</p>';
}
function renderTopPlans(plans) {
document.getElementById('topPlansList').innerHTML = plans.map(p => `
<div class="p-4 rounded-2xl border border-gray-100 bg-gray-50 flex items-center justify-between">
<div><p class="text-sm font-black text-gray-800">${p.name}</p><p class="text-[11px] text-gray-400 font-semibold">${p.type.toUpperCase()} · ${p.price}€</p></div>
<span class="px-2 py-1 rounded-lg bg-white text-blue-700 text-[10px] font-black uppercase tracking-widest">${p.users} socios</span>
</div>
`).join('') || '<p class="text-xs text-gray-400">Sin planes</p>';
}
function renderPlans(plans) {
document.getElementById('plansGrid').innerHTML = plans.map(p => `
<div class="bg-white rounded-2xl border border-gray-200 shadow-sm p-6">
<h3 class="text-lg font-black text-gray-900">${p.name}</h3><p class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-4">${p.type}</p>
<div class="space-y-3 text-sm font-bold text-gray-600">
<div class="flex justify-between"><span>Cuota</span><span class="text-blue-600">${p.price}€</span></div>
<div class="flex justify-between"><span>Renovación</span><span class="text-amber-600">${p.renewal_price}€</span></div>
<div class="flex justify-between"><span>Urg / Bricos</span><span>${p.urgencies_limit} / ${p.bricos_limit}</span></div>
</div>
</div>
`).join('');
}
function renderClients() {
const search = document.getElementById('searchClientInput').value.toLowerCase();
const filtered = allClients.filter(c => c.client_name.toLowerCase().includes(search) || (c.client_dni||'').toLowerCase().includes(search));
document.getElementById('clientsTableBody').innerHTML = filtered.map(c => {
const prog = c.urgencies_limit ? Math.min(100, (c.urgencies_used / c.urgencies_limit) * 100) : 0;
return `
<tr class="hover:bg-blue-50/50 transition-colors">
<td class="px-6 py-4"><div class="flex items-center gap-3"><div class="w-8 h-8 bg-blue-100 text-blue-600 rounded-full flex items-center justify-center text-xs font-black uppercase">${c.client_name.substring(0,2)}</div><div class="flex flex-col"><span class="text-sm font-black">${c.client_name}</span><span class="text-[9px] font-medium text-gray-400">DNI: ${c.client_dni||'-'}${c.client_phone||'-'}</span></div></div></td>
<td class="px-6 py-4 text-xs font-black text-gray-500"><span class="bg-blue-50 text-blue-700 px-2 py-1 rounded-md uppercase">${c.plan_name||'Sin Plan'}</span></td>
<td class="px-6 py-4"><div class="flex flex-col items-center gap-1"><span class="text-[10px] text-gray-400">Bricos: ${c.bricos_used}/${c.bricos_limit||0} | Urg: ${c.urgencies_used}/${c.urgencies_limit||0}</span><div class="w-24 h-1.5 bg-gray-100 rounded-full overflow-hidden"><div class="h-full bg-blue-500" style="width:${prog}%"></div></div></div></td>
<td class="px-6 py-4"><span class="${c.payment_status==='pagado'?'text-emerald-500':'text-rose-500'} font-black text-[10px] uppercase tracking-widest flex items-center gap-1.5"><div class="w-1.5 h-1.5 rounded-full ${c.payment_status==='pagado'?'bg-emerald-500':'bg-rose-500'} animate-pulse"></div>${c.payment_status}</span></td>
<td class="px-6 py-4 text-xs font-black text-gray-500">${c.renewal_date ? new Date(c.renewal_date).toLocaleDateString() : '-'}</td>
<td class="px-6 py-4 text-right">
<button onclick="toggleStatus(${c.id}, 'payment_status', '${c.payment_status}')" class="p-2 text-gray-400 hover:text-blue-600" title="Cambiar Pago"><i data-lucide="credit-card" class="w-4 h-4"></i></button>
<button onclick="toggleStatus(${c.id}, 'status', '${c.status}')" class="p-2 text-gray-400 hover:text-amber-600" title="Cambiar Estado"><i data-lucide="power" class="w-4 h-4"></i></button>
</td>
</tr>`;
}).join('');
if(window.lucide) lucide.createIcons();
}
// Utilidades UI
function switchTab(tab) {
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
document.getElementById('tab-' + tab).classList.remove('hidden');
document.getElementById('btn-tab-' + tab).classList.add('active');
if (window.lucide) lucide.createIcons();
}
function openPlanModal() { document.getElementById('modalBackdrop').classList.remove('hidden'); document.getElementById('planModal').classList.remove('hidden'); }
function openClientModal() { document.getElementById('modalBackdrop').classList.remove('hidden'); document.getElementById('clientModal').classList.remove('hidden'); }
function closeModals() { document.getElementById('modalBackdrop').classList.add('hidden'); document.getElementById('planModal').classList.add('hidden'); document.getElementById('clientModal').classList.add('hidden'); }
function toast(msg) {
const t = document.createElement('div');
t.className = 'fixed bottom-5 right-5 bg-slate-900 text-white px-4 py-3 rounded-xl shadow-2xl text-xs font-black uppercase tracking-widest z-[999] fade-in';
t.textContent = msg; document.body.appendChild(t); setTimeout(() => t.remove(), 2500);
}
</script>
</body>
</html>