mi-proyecto/app/templates/admin_ai_chatbot.html

115 lines
11 KiB
HTML

{% extends 'base.html' %}
{% block content %}
<div class="page-toolbar">
<div>
<h1 class="h3 mb-1">Chatbot IA del frontend</h1>
<p class="text-muted mb-0">Asistente propio embebido en la web pública. No depende de WhatsApp, Meta ni Telegram.</p>
</div>
<div class="d-flex gap-2 flex-wrap">
<a class="btn btn-outline-secondary" href="{{ url_for('index') }}" target="_blank"><i class="bi bi-box-arrow-up-right"></i> Ver frontend</a>
<form method="post" class="d-inline"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="action" value="sync_system"><button class="btn btn-primary"><i class="bi bi-arrow-repeat"></i> Sincronizar conocimiento</button></form>
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-md-3"><div class="stat-card"><div class="stat-label">Estado</div><div class="stat-value">{{ 'Activo' if cfg.enabled else 'Inactivo' }}</div></div></div>
<div class="col-md-3"><div class="stat-card"><div class="stat-label">IA generativa</div><div class="stat-value">{{ 'ON' if cfg.openai_enabled and cfg.api_key_configured else 'Fallback' }}</div></div></div>
<div class="col-md-3"><div class="stat-card"><div class="stat-label">Conocimiento</div><div class="stat-value">{{ knowledge|length }}</div></div></div>
<div class="col-md-3"><div class="stat-card"><div class="stat-label">Última sync</div><div class="stat-value small">{{ cfg.last_sync_at or '—' }}</div></div></div>
</div>
<ul class="nav nav-tabs mb-4" role="tablist">
<li class="nav-item"><button class="nav-link active" data-bs-toggle="tab" data-bs-target="#ai-config" type="button">Configuración</button></li>
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#ai-knowledge" type="button">Conocimiento</button></li>
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#ai-test" type="button">Prueba rápida</button></li>
<li class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#ai-logs" type="button">Conversaciones</button></li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="ai-config">
<div class="card table-panel"><div class="card-body">
<form method="post" class="row g-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="action" value="settings">
<div class="col-md-4"><div class="form-check form-switch"><input class="form-check-input" type="checkbox" name="ai_chatbot_enabled" id="ai_chatbot_enabled" {% if cfg.enabled %}checked{% endif %}><label class="form-check-label" for="ai_chatbot_enabled">Activar Chatbot IA</label></div></div>
<div class="col-md-4"><div class="form-check form-switch"><input class="form-check-input" type="checkbox" name="ai_chatbot_floating_enabled" id="ai_chatbot_floating_enabled" {% if cfg.floating_enabled %}checked{% endif %}><label class="form-check-label" for="ai_chatbot_floating_enabled">Mostrar botón flotante</label></div></div>
<div class="col-md-4"><div class="form-check form-switch"><input class="form-check-input" type="checkbox" name="ai_chatbot_booking_enabled" id="ai_chatbot_booking_enabled" {% if cfg.booking_enabled %}checked{% endif %}><label class="form-check-label" for="ai_chatbot_booking_enabled">Permitir reservar turnos</label></div></div>
<div class="col-md-6"><label class="form-label">Nombre del asistente</label><input class="form-control" name="ai_chatbot_assistant_name" value="{{ cfg.assistant_name }}"></div>
<div class="col-md-6"><label class="form-label">Mensaje de bienvenida</label><input class="form-control" name="ai_chatbot_welcome_message" value="{{ cfg.welcome_message }}"></div>
<div class="col-12"><hr><h3 class="h6">Motor IA</h3><p class="text-muted small mb-2">Si no cargás API key, el bot funciona igual con respuestas inteligentes por reglas y datos reales del sistema.</p></div>
<div class="col-md-4"><div class="form-check form-switch"><input class="form-check-input" type="checkbox" name="ai_chatbot_openai_enabled" id="ai_chatbot_openai_enabled" {% if cfg.openai_enabled %}checked{% endif %}><label class="form-check-label" for="ai_chatbot_openai_enabled">Usar IA generativa OpenAI</label></div></div>
<div class="col-md-4"><label class="form-label">Modelo</label><input class="form-control" name="ai_chatbot_openai_model" value="{{ cfg.openai_model }}"></div>
<div class="col-md-4"><label class="form-label">API Key</label><input class="form-control" type="password" name="ai_chatbot_api_key" placeholder="Dejar vacío para conservar la actual"><div class="form-text">También podés usar OPENAI_API_KEY en .env.</div></div>
<div class="col-12"><label class="form-label">Instrucciones del asistente</label><textarea class="form-control" rows="5" name="ai_chatbot_system_instructions">{{ cfg.system_instructions }}</textarea></div>
<div class="col-12"><label class="form-label">Respuesta fallback</label><textarea class="form-control" rows="2" name="ai_chatbot_fallback_message">{{ cfg.fallback_message }}</textarea></div>
<div class="col-12 d-grid"><button class="btn btn-primary btn-lg">Guardar configuración</button></div>
</form>
</div></div>
</div>
<div class="tab-pane fade" id="ai-knowledge">
<div class="row g-4">
<div class="col-lg-5">
<div class="card table-panel mb-4"><div class="card-header"><strong>Cargar conocimiento manual</strong></div><div class="card-body">
<form method="post" class="row g-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="action" value="save_manual">
<div class="col-12"><label class="form-label">Título</label><input class="form-control" name="title" placeholder="Ej.: Cobertura de primera consulta"></div>
<div class="col-12"><label class="form-label">Contenido</label><textarea class="form-control" name="content" rows="6" placeholder="Texto que el chatbot puede usar para responder."></textarea></div>
<div class="col-12"><label class="form-label">Palabras clave</label><input class="form-control" name="keywords" placeholder="consulta, precio, cobertura"></div>
<div class="col-12"><div class="form-check"><input class="form-check-input" type="checkbox" name="is_active" checked id="manual_active"><label for="manual_active" class="form-check-label">Activo</label></div></div>
<div class="col-12 d-grid"><button class="btn btn-outline-primary">Guardar conocimiento</button></div>
</form>
</div></div>
<div class="card table-panel"><div class="card-header"><strong>Leer una URL pública</strong></div><div class="card-body">
<form method="post" class="row g-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="action" value="train_url">
<div class="col-12"><label class="form-label">URL</label><input class="form-control" name="source_url" placeholder="https://tusitio.com/pagina"></div>
<div class="col-12"><label class="form-label">Palabras clave</label><input class="form-control" name="keywords"></div>
<div class="col-12 d-grid"><button class="btn btn-outline-secondary">Leer y agregar</button></div>
</form>
</div></div>
</div>
<div class="col-lg-7">
<div class="card table-panel"><div class="card-header"><strong>Base de conocimiento</strong></div><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Fuente</th><th>Título</th><th>Contenido</th><th></th></tr></thead><tbody>
{% for item in knowledge %}
<tr><td><span class="badge text-bg-light">{{ item.source_type }}</span></td><td>{{ item.title }}<br><span class="small text-muted">{{ item.source_ref or item.keywords or '' }}</span></td><td class="small text-muted">{{ item.content[:180] }}{% if item.content|length > 180 %}...{% endif %}</td><td class="text-end"><form method="post" onsubmit="return confirm('¿Eliminar ítem?')"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="action" value="delete_knowledge"><input type="hidden" name="knowledge_id" value="{{ item.id }}"><button class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button></form></td></tr>
{% else %}<tr><td colspan="4" class="text-center text-muted py-4">Sin conocimiento cargado.</td></tr>{% endfor %}
</tbody></table></div></div>
</div>
</div>
</div>
<div class="tab-pane fade" id="ai-test">
<div class="card table-panel"><div class="card-body">
<p class="text-muted">Probá consultas como: “¿Qué especialidades tienen?”, “¿Cuánto cuesta la consulta?”, “Quiero un turno”.</p>
<div class="input-group"><input class="form-control" id="aiAdminTestInput" placeholder="Escribí una consulta"><button class="btn btn-primary" id="aiAdminTestBtn" type="button">Probar</button></div>
<pre class="bg-light border rounded-4 p-3 mt-3 mb-0" id="aiAdminTestOutput" style="white-space:pre-wrap; min-height:120px;"></pre>
</div></div>
</div>
<div class="tab-pane fade" id="ai-logs">
<div class="card table-panel"><div class="table-responsive"><table class="table align-middle mb-0"><thead><tr><th>Fecha</th><th>Sesión</th><th>Intención</th><th>Consulta</th><th>Respuesta</th></tr></thead><tbody>
{% for log in logs %}<tr><td>{{ log.created_at.strftime('%d/%m/%Y %H:%M') if log.created_at else '' }}</td><td class="small">{{ log.session_id }}</td><td><span class="badge text-bg-light">{{ log.intent }}</span></td><td>{{ log.incoming_text[:160] if log.incoming_text else '' }}</td><td class="small text-muted">{{ log.outgoing_text[:220] if log.outgoing_text else '' }}</td></tr>{% else %}<tr><td colspan="5" class="text-center text-muted py-4">Todavía no hay conversaciones.</td></tr>{% endfor %}
</tbody></table></div></div>
</div>
</div>
{% endblock %}
{% block scripts %}{{ super() }}
<script>
document.addEventListener('DOMContentLoaded', function(){
const btn = document.getElementById('aiAdminTestBtn');
const input = document.getElementById('aiAdminTestInput');
const out = document.getElementById('aiAdminTestOutput');
const csrf = document.body.dataset.csrfToken || '{{ csrf_token }}';
async function send(){
const message = (input.value || '').trim(); if(!message) return;
out.textContent = 'Consultando...';
const resp = await fetch('{{ url_for('api_ai_chatbot_message') }}', {method:'POST', headers:{'Content-Type':'application/json','X-CSRF-Token':csrf}, body:JSON.stringify({message})});
const data = await resp.json();
out.textContent = data.ok ? data.reply : (data.error || 'Error');
}
btn?.addEventListener('click', send);
input?.addEventListener('keydown', e => { if(e.key === 'Enter') send(); });
});
</script>
{% endblock %}