Files
Autoparts-DB/manager/templates/index.html
consultoria-as 718fa06888 feat: module toggles in POS config and Instance Manager
- Add GET/PUT /pos/api/config/modules endpoints in POS config_bp.py
- Update sidebar.js to filter nav items based on enabled modules
- Add Modules section to POS config.html with toggles for WhatsApp, Marketplace, MercadoLibre
- Add module load/save logic to POS config.js
- Preload modules in app-init.js for sidebar caching

- Add tenant module management to Instance Manager
  - get_tenant_modules / update_tenant_modules in tenant_service.py
  - GET/PUT /api/tenants/<id>/modules endpoints in tenants_bp.py
  - Add modules modal to manager index.html
  - Add module editing UI and logic to manager.js
  - Add toggle-switch CSS to manager.css
2026-05-28 00:21:52 +00:00

372 lines
19 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nexus Instance Manager</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link rel="stylesheet" href="/static/css/manager.css">
</head>
<body>
<!-- Login Screen -->
<div id="login-screen" class="login-screen">
<div class="login-card">
<div class="login-logo">
<i class="fas fa-cube"></i>
<h1>Nexus Manager</h1>
<p>Control Central de Instancias</p>
</div>
<form id="login-form">
<div class="form-group">
<label>Email</label>
<input type="email" id="login-email" required placeholder="admin@nexus.local">
</div>
<div class="form-group">
<label>Contraseña</label>
<input type="password" id="login-password" required placeholder="••••••••">
</div>
<button type="submit" class="btn btn-primary btn-block">
<i class="fas fa-sign-in-alt"></i> Ingresar
</button>
<div id="login-error" class="alert alert-error" style="display:none;"></div>
</form>
</div>
</div>
<!-- Main App -->
<div id="app" class="app" style="display:none;">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-brand">
<i class="fas fa-cube"></i>
<span>Nexus Manager</span>
</div>
<nav class="sidebar-nav">
<a href="#/dashboard" class="nav-item active" data-page="dashboard">
<i class="fas fa-chart-line"></i>
<span>Dashboard</span>
</a>
<a href="#/demos" class="nav-item" data-page="demos">
<i class="fas fa-rocket"></i>
<span>Crear Demos</span>
</a>
<a href="#/tenants" class="nav-item" data-page="tenants">
<i class="fas fa-building"></i>
<span>Tenants</span>
<span class="badge" id="tenant-count">0</span>
</a>
<a href="#/health" class="nav-item" data-page="health">
<i class="fas fa-heartbeat"></i>
<span>Salud</span>
</a>
<a href="#/migrations" class="nav-item" data-page="migrations">
<i class="fas fa-database"></i>
<span>Migraciones</span>
</a>
</nav>
<div class="sidebar-footer">
<div class="user-info">
<span id="user-email">admin</span>
<button onclick="logout()" class="btn-icon" title="Cerrar sesión">
<i class="fas fa-sign-out-alt"></i>
</button>
</div>
</div>
</aside>
<!-- Content -->
<main class="main">
<header class="topbar">
<h2 id="page-title">Dashboard</h2>
<div class="topbar-actions">
<span class="status-indicator" id="system-status">
<i class="fas fa-circle"></i> Online
</span>
</div>
</header>
<div class="content">
<!-- Dashboard Page -->
<section id="page-dashboard" class="page">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon bg-blue"><i class="fas fa-building"></i></div>
<div class="stat-info">
<h3 id="stat-total">0</h3>
<p>Total Tenants</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-green"><i class="fas fa-check-circle"></i></div>
<div class="stat-info">
<h3 id="stat-active">0</h3>
<p>Activos</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-purple"><i class="fas fa-rocket"></i></div>
<div class="stat-info">
<h3 id="stat-demos">0</h3>
<p>Demos</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-orange"><i class="fas fa-clock"></i></div>
<div class="stat-info">
<h3 id="stat-expiring">0</h3>
<p>Expiran pronto</p>
</div>
</div>
</div>
<div class="grid-2">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-server"></i> Estado del Sistema</h3>
</div>
<div class="card-body" id="system-health-summary">
<div class="loading">Cargando...</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3><i class="fas fa-building"></i> Demos Recientes</h3>
</div>
<div class="card-body">
<table class="table compact">
<thead>
<tr><th>Nombre</th><th>Subdominio</th><th>Expira</th><th>Estado</th></tr>
</thead>
<tbody id="recent-demos-table">
<tr><td colspan="4" class="text-muted">Cargando...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Demos Page -->
<section id="page-demos" class="page" style="display:none;">
<div class="grid-2">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-plus-circle"></i> Nueva Demo</h3>
</div>
<div class="card-body">
<form id="demo-form">
<div class="form-group">
<label>Nombre del negocio *</label>
<input type="text" id="demo-name" required placeholder="Refaccionaria López">
</div>
<div class="form-group">
<label>Email de contacto</label>
<input type="email" id="demo-email" placeholder="cliente@email.com">
</div>
<div class="form-row">
<div class="form-group">
<label>Días de vigencia</label>
<input type="number" id="demo-days" value="14" min="1" max="90">
</div>
<div class="form-group">
<label>PIN del owner</label>
<input type="text" id="demo-pin" value="0000" maxlength="10">
</div>
</div>
<div class="form-group">
<label>Subdominio (opcional)</label>
<div class="input-group">
<input type="text" id="demo-subdomain" placeholder="refaccionaria-lopez">
<span class="input-suffix">.nexusautoparts.com.mx</span>
</div>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-rocket"></i> Crear Demo
</button>
</form>
<div id="demo-result" class="result-box" style="display:none;"></div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3><i class="fas fa-list"></i> Demos Activas</h3>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr><th>Negocio</th><th>URL</th><th>Días rest.</th><th>Acciones</th></tr>
</thead>
<tbody id="demos-table">
<tr><td colspan="4" class="text-muted">Cargando...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
<!-- Tenants Page -->
<section id="page-tenants" class="page" style="display:none;">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-building"></i> Todos los Tenants</h3>
<div class="card-actions">
<input type="text" id="tenant-search" placeholder="Buscar..." class="input-sm">
<button class="btn btn-sm btn-secondary" onclick="loadTenants(true)">
<i class="fas fa-sync"></i> Refrescar
</button>
</div>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Subdominio</th>
<th>Plan</th>
<th>Versión</th>
<th>Estado</th>
<th>Creado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="tenants-table">
<tr><td colspan="8" class="text-muted">Cargando...</td></tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Health Page -->
<section id="page-health" class="page" style="display:none;">
<div class="grid-3">
<div class="card">
<div class="card-header"><h3><i class="fas fa-database"></i> PostgreSQL</h3></div>
<div class="card-body" id="health-postgresql"><div class="loading">...</div></div>
</div>
<div class="card">
<div class="card-header"><h3><i class="fas fa-bolt"></i> Redis</h3></div>
<div class="card-body" id="health-redis"><div class="loading">...</div></div>
</div>
<div class="card">
<div class="card-header"><h3><i class="fas fa-hdd"></i> Disco</h3></div>
<div class="card-body" id="health-disk"><div class="loading">...</div></div>
</div>
</div>
<div class="grid-2">
<div class="card">
<div class="card-header"><h3><i class="fas fa-memory"></i> Memoria</h3></div>
<div class="card-body" id="health-memory"><div class="loading">...</div></div>
</div>
<div class="card">
<div class="card-header"><h3><i class="fas fa-cogs"></i> Servicios Systemd</h3></div>
<div class="card-body" id="health-services"><div class="loading">...</div></div>
</div>
</div>
<div class="card">
<div class="card-header"><h3><i class="fas fa-network-wired"></i> Servicios HTTP</h3></div>
<div class="card-body" id="health-http"><div class="loading">...</div></div>
</div>
</section>
<!-- Migrations Page -->
<section id="page-migrations" class="page" style="display:none;">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-database"></i> Migraciones de Schema</h3>
<div class="card-actions">
<button class="btn btn-primary" onclick="runAllMigrations()">
<i class="fas fa-play"></i> Ejecutar todas pendientes
</button>
</div>
</div>
<div class="card-body">
<div id="migration-log" class="log-box" style="display:none;"></div>
<table class="table">
<thead>
<tr><th>Tenant</th><th>DB</th><th>Versión actual</th><th>Estado</th></tr>
</thead>
<tbody id="migrations-table">
<tr><td colspan="4" class="text-muted">Cargando...</td></tr>
</tbody>
</table>
</div>
</div>
</section>
</div>
</main>
</div>
<!-- Modal -->
<div id="modal" class="modal" style="display:none;">
<div class="modal-overlay" onclick="closeModal()"></div>
<div class="modal-content">
<div class="modal-header">
<h3 id="modal-title">Confirmar</h3>
<button class="btn-icon" onclick="closeModal()"><i class="fas fa-times"></i></button>
</div>
<div class="modal-body" id="modal-body"></div>
<div class="modal-footer" id="modal-footer">
<button class="btn btn-secondary" onclick="closeModal()">Cancelar</button>
<button class="btn btn-danger" id="modal-confirm-btn">Confirmar</button>
</div>
</div>
</div>
<!-- Modules Modal -->
<div id="modules-modal" class="modal" style="display:none;">
<div class="modal-overlay" onclick="closeModulesModal()"></div>
<div class="modal-content" style="max-width:480px;">
<div class="modal-header">
<h3 id="modules-modal-title">Módulos del Tenant</h3>
<button class="btn-icon" onclick="closeModulesModal()"><i class="fas fa-times"></i></button>
</div>
<div class="modal-body">
<div class="form-group" style="display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-bottom:1px solid var(--border);">
<div>
<div style="font-weight:600;color:var(--text);">WhatsApp</div>
<div style="font-size:12px;color:var(--text-muted);">Mostrar menú de WhatsApp Bridge</div>
</div>
<label class="toggle-switch">
<input type="checkbox" id="mod-whatsapp">
<span class="toggle-slider"></span>
</label>
</div>
<div class="form-group" style="display:flex;align-items:center;justify-content:space-between;padding:12px 0;border-bottom:1px solid var(--border);">
<div>
<div style="font-weight:600;color:var(--text);">Marketplace</div>
<div style="font-size:12px;color:var(--text-muted);">Mostrar menú de Marketplace interno</div>
</div>
<label class="toggle-switch">
<input type="checkbox" id="mod-marketplace">
<span class="toggle-slider"></span>
</label>
</div>
<div class="form-group" style="display:flex;align-items:center;justify-content:space-between;padding:12px 0;">
<div>
<div style="font-weight:600;color:var(--text);">MercadoLibre</div>
<div style="font-size:12px;color:var(--text-muted);">Mostrar menú de MercadoLibre</div>
</div>
<label class="toggle-switch">
<input type="checkbox" id="mod-meli">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModulesModal()">Cancelar</button>
<button class="btn btn-primary" id="modules-save-btn" onclick="saveModules()">Guardar</button>
</div>
</div>
</div>
<!-- Toast -->
<div id="toast-container"></div>
<script src="/static/js/manager.js"></script>
</body>
</html>