Files
Consultoría AS e32885afc5 fix: Fix YAML syntax errors and validator prompt formatting
- Fix YAML files with unquoted strings containing quotes
- Export singleton instances in ai/__init__.py
- Fix validator scoring prompt to use replace() instead of format()
  to avoid conflicts with JSON curly braces

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 21:13:58 +00:00

344 lines
17 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Configuración{% endblock %}
{% block content %}
<div class="animate-fade-in">
<!-- Header -->
<div class="mb-8">
<h1 class="text-3xl font-bold">Configuración</h1>
<p class="text-gray-400 mt-1">Administra las conexiones y preferencias del sistema</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Platform Connections -->
<div class="card rounded-2xl p-6">
<h2 class="text-lg font-semibold mb-6 flex items-center gap-2">
<span>🔗</span>
<span>Conexiones de Plataformas</span>
</h2>
<div class="space-y-4" id="platforms-container">
<div class="text-center py-4 text-gray-500">
<div class="animate-spin w-6 h-6 border-2 border-primary border-t-transparent rounded-full mx-auto"></div>
<p class="mt-2">Verificando conexiones...</p>
</div>
</div>
</div>
<!-- Telegram Notifications -->
<div class="card rounded-2xl p-6">
<h2 class="text-lg font-semibold mb-6 flex items-center gap-2">
<span>📱</span>
<span>Notificaciones Telegram</span>
</h2>
<div id="telegram-status" class="mb-6">
<div class="text-center py-4 text-gray-500">
<div class="animate-spin w-6 h-6 border-2 border-primary border-t-transparent rounded-full mx-auto"></div>
<p class="mt-2">Verificando...</p>
</div>
</div>
<div class="flex gap-3">
<button onclick="testTelegram()" class="btn-primary px-4 py-2 rounded-lg flex items-center gap-2 transition-all">
<span>📤</span>
<span>Enviar Prueba</span>
</button>
<button onclick="toggleTelegramGuide()" class="btn-secondary px-4 py-2 rounded-lg flex items-center gap-2">
<span>📖</span>
<span>Ver Guía</span>
</button>
</div>
<div id="telegram-guide" class="hidden mt-6 bg-dark-800 rounded-xl p-4 text-sm space-y-3">
<h4 class="font-medium text-primary">Configurar Telegram Bot</h4>
<ol class="list-decimal list-inside space-y-2 text-gray-400">
<li>Busca <strong>@BotFather</strong> en Telegram</li>
<li>Envía <code class="bg-dark-700 px-1 rounded">/newbot</code> y sigue las instrucciones</li>
<li>Copia el <strong>Bot Token</strong> que te proporciona</li>
<li>Inicia chat con tu nuevo bot</li>
<li>Visita <code class="bg-dark-700 px-1 rounded">api.telegram.org/bot{TOKEN}/getUpdates</code></li>
<li>Copia tu <strong>Chat ID</strong> del resultado</li>
<li>Configura ambos valores en el archivo .env</li>
</ol>
</div>
</div>
<!-- Odoo Integration -->
<div class="card rounded-2xl p-6">
<h2 class="text-lg font-semibold mb-6 flex items-center gap-2">
<span>🏢</span>
<span>Integración Odoo</span>
</h2>
<div id="odoo-status" class="mb-6">
<div class="text-center py-4 text-gray-500">
<div class="animate-spin w-6 h-6 border-2 border-primary border-t-transparent rounded-full mx-auto"></div>
<p class="mt-2">Verificando...</p>
</div>
</div>
<div class="flex flex-wrap gap-3">
<button onclick="syncOdooProducts()" class="btn-secondary px-4 py-2 rounded-lg flex items-center gap-2">
<span>📦</span>
<span>Sync Productos</span>
</button>
<button onclick="syncOdooServices()" class="btn-secondary px-4 py-2 rounded-lg flex items-center gap-2">
<span>🛠️</span>
<span>Sync Servicios</span>
</button>
<button onclick="syncOdooLeads()" class="btn-secondary px-4 py-2 rounded-lg flex items-center gap-2">
<span>🎯</span>
<span>Exportar Leads</span>
</button>
</div>
</div>
<!-- AI Generation -->
<div class="card rounded-2xl p-6">
<h2 class="text-lg font-semibold mb-6 flex items-center gap-2">
<span>🤖</span>
<span>Generación con IA</span>
</h2>
<div id="ai-status" class="mb-6">
<div class="text-center py-4 text-gray-500">
<div class="animate-spin w-6 h-6 border-2 border-primary border-t-transparent rounded-full mx-auto"></div>
<p class="mt-2">Verificando...</p>
</div>
</div>
<div class="bg-dark-800/50 rounded-xl p-4">
<h4 class="font-medium mb-2">Información del Negocio</h4>
<div class="space-y-2 text-sm text-gray-400">
<p><strong>Nombre:</strong> <span id="business-name">-</span></p>
<p><strong>Ubicación:</strong> <span id="business-location">-</span></p>
<p><strong>Tono:</strong> <span id="content-tone">-</span></p>
</div>
</div>
</div>
<!-- System Info -->
<div class="card rounded-2xl p-6 lg:col-span-2">
<h2 class="text-lg font-semibold mb-6 flex items-center gap-2">
<span></span>
<span>Información del Sistema</span>
</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="bg-dark-800/50 rounded-xl p-4 text-center">
<p class="text-2xl font-bold text-primary">1.0.0</p>
<p class="text-sm text-gray-500">Versión</p>
</div>
<div class="bg-dark-800/50 rounded-xl p-4 text-center">
<p class="text-2xl font-bold text-green-400" id="system-status"></p>
<p class="text-sm text-gray-500">Estado</p>
</div>
<div class="bg-dark-800/50 rounded-xl p-4 text-center">
<p class="text-2xl font-bold" id="db-status">-</p>
<p class="text-sm text-gray-500">Base de Datos</p>
</div>
<div class="bg-dark-800/50 rounded-xl p-4 text-center">
<p class="text-2xl font-bold" id="worker-status">-</p>
<p class="text-sm text-gray-500">Workers</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
// Load all statuses on page load
document.addEventListener('DOMContentLoaded', function() {
loadPlatformStatus();
loadTelegramStatus();
loadOdooStatus();
loadAIStatus();
loadSystemStatus();
});
async function loadPlatformStatus() {
const container = document.getElementById('platforms-container');
try {
const response = await fetch('/api/publish/test');
const data = await response.json();
container.innerHTML = '';
for (const [platform, status] of Object.entries(data)) {
const isConnected = status.configured && status.connected;
container.innerHTML += `
<div class="flex items-center justify-between bg-dark-800/50 rounded-xl p-4">
<div class="flex items-center gap-3">
<span class="text-2xl">${getPlatformIcon(platform)}</span>
<div>
<p class="font-medium capitalize">${platform}</p>
${status.details?.username ? `<p class="text-xs text-gray-500">@${status.details.username}</p>` : ''}
</div>
</div>
<span class="px-3 py-1 rounded-full text-sm ${isConnected ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
${isConnected ? '✓ Conectado' : '✗ Desconectado'}
</span>
</div>
`;
}
} catch (error) {
container.innerHTML = '<div class="text-center text-red-400 py-4">Error cargando estado</div>';
}
}
async function loadTelegramStatus() {
const container = document.getElementById('telegram-status');
try {
const response = await fetch('/api/notifications/status');
const data = await response.json();
const isConfigured = data.telegram_configured;
container.innerHTML = `
<div class="flex items-center justify-between bg-dark-800/50 rounded-xl p-4">
<div class="flex items-center gap-3">
<span class="text-2xl">📱</span>
<div>
<p class="font-medium">Telegram Bot</p>
<p class="text-xs text-gray-500">${data.bot_token_set ? 'Token configurado' : 'Token no configurado'}</p>
</div>
</div>
<span class="px-3 py-1 rounded-full text-sm ${isConfigured ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
${isConfigured ? '✓ Activo' : '✗ Inactivo'}
</span>
</div>
`;
} catch (error) {
container.innerHTML = '<div class="text-center text-red-400 py-4">Error verificando Telegram</div>';
}
}
async function loadOdooStatus() {
const container = document.getElementById('odoo-status');
try {
const response = await fetch('/api/odoo/status');
const data = await response.json();
container.innerHTML = `
<div class="flex items-center justify-between bg-dark-800/50 rounded-xl p-4">
<div class="flex items-center gap-3">
<span class="text-2xl">🏢</span>
<div>
<p class="font-medium">Odoo ERP</p>
<p class="text-xs text-gray-500">${data.version ? 'v' + data.version : (data.error || 'No configurado')}</p>
</div>
</div>
<span class="px-3 py-1 rounded-full text-sm ${data.connected ? 'bg-green-500/20 text-green-400' : 'bg-yellow-500/20 text-yellow-400'}">
${data.connected ? '✓ Conectado' : '⚠ ' + (data.configured ? 'Error' : 'No configurado')}
</span>
</div>
`;
} catch (error) {
container.innerHTML = '<div class="text-center text-red-400 py-4">Error verificando Odoo</div>';
}
}
async function loadAIStatus() {
const container = document.getElementById('ai-status');
try {
const response = await fetch('/api/generate/status');
const data = await response.json();
document.getElementById('business-name').textContent = data.business_name || '-';
document.getElementById('business-location').textContent = data.business_location || '-';
document.getElementById('content-tone').textContent = data.content_tone || '-';
container.innerHTML = `
<div class="flex items-center justify-between bg-dark-800/50 rounded-xl p-4">
<div class="flex items-center gap-3">
<span class="text-2xl">🤖</span>
<div>
<p class="font-medium">DeepSeek API</p>
<p class="text-xs text-gray-500">${data.provider || 'DeepSeek'}</p>
</div>
</div>
<span class="px-3 py-1 rounded-full text-sm ${data.configured ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
${data.configured ? '✓ Configurado' : '✗ No configurado'}
</span>
</div>
`;
} catch (error) {
container.innerHTML = '<div class="text-center text-red-400 py-4">Error verificando IA</div>';
}
}
async function loadSystemStatus() {
try {
const response = await fetch('/api/health');
const data = await response.json();
document.getElementById('system-status').textContent = data.status === 'healthy' ? '● Online' : '● Offline';
document.getElementById('system-status').className = data.status === 'healthy' ? 'text-2xl font-bold text-green-400' : 'text-2xl font-bold text-red-400';
document.getElementById('db-status').textContent = '✓ OK';
document.getElementById('db-status').className = 'text-2xl font-bold text-green-400';
document.getElementById('worker-status').textContent = '✓ OK';
document.getElementById('worker-status').className = 'text-2xl font-bold text-green-400';
} catch (error) {
document.getElementById('system-status').textContent = '● Error';
document.getElementById('system-status').className = 'text-2xl font-bold text-red-400';
}
}
async function testTelegram() {
showModal('Enviando mensaje de prueba...', true);
try {
const response = await fetch('/api/notifications/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({})
});
const data = await response.json();
if (data.success) {
showModal('<div class="text-center"><span class="text-4xl mb-4 block">✅</span><p class="text-lg">¡Mensaje enviado!</p><p class="text-gray-400 mt-2">Revisa tu Telegram</p></div>');
} else {
showModal('<div class="text-center"><span class="text-4xl mb-4 block">❌</span><p class="text-lg">Error al enviar</p><p class="text-gray-400 mt-2">' + (data.detail || 'Verifica la configuración') + '</p></div>');
}
} catch (error) {
showModal('<div class="text-center"><span class="text-4xl mb-4 block">❌</span><p class="text-lg">Error de conexión</p></div>');
}
}
function toggleTelegramGuide() {
const guide = document.getElementById('telegram-guide');
guide.classList.toggle('hidden');
}
async function syncOdooProducts() {
showModal('Sincronizando productos...', true);
try {
const response = await fetch('/api/odoo/sync/products', { method: 'POST' });
const data = await response.json();
showModal(`<div class="text-center"><span class="text-4xl mb-4 block">📦</span><p class="text-lg">Sincronización completada</p><p class="text-gray-400 mt-2">Creados: ${data.created} | Actualizados: ${data.updated}</p></div>`);
} catch (error) {
showModal('<div class="text-center"><span class="text-4xl mb-4 block">❌</span><p>Error sincronizando</p></div>');
}
}
async function syncOdooServices() {
showModal('Sincronizando servicios...', true);
try {
const response = await fetch('/api/odoo/sync/services', { method: 'POST' });
const data = await response.json();
showModal(`<div class="text-center"><span class="text-4xl mb-4 block">🛠️</span><p class="text-lg">Sincronización completada</p><p class="text-gray-400 mt-2">Creados: ${data.created} | Actualizados: ${data.updated}</p></div>`);
} catch (error) {
showModal('<div class="text-center"><span class="text-4xl mb-4 block">❌</span><p>Error sincronizando</p></div>');
}
}
async function syncOdooLeads() {
showModal('Exportando leads...', true);
try {
const response = await fetch('/api/odoo/sync/leads', { method: 'POST' });
const data = await response.json();
showModal(`<div class="text-center"><span class="text-4xl mb-4 block">🎯</span><p class="text-lg">Exportación completada</p><p class="text-gray-400 mt-2">Exportados: ${data.created}</p></div>`);
} catch (error) {
showModal('<div class="text-center"><span class="text-4xl mb-4 block">❌</span><p>Error exportando</p></div>');
}
}
function getPlatformIcon(platform) {
const icons = { x: '𝕏', facebook: '📘', instagram: '📸', threads: '🧵' };
return icons[platform] || '📱';
}
</script>
{% endblock %}