Files
demo/pymesbot/backend/templates/chat.html
Renato 47264049e6 Initial commit: PymesBot Demo with IA integration
- FastAPI backend with WebSocket chat
- SQLite database for products
- Z.AI (GLM-4.7) integration for AI responses
- Docker deployment ready
- Caddy proxy configuration
2026-02-15 17:07:39 +01:00

218 lines
7.9 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Demo Librería - PymesBot</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f5;
height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
height: 100%;
}
.app {
display: grid;
grid-template-columns: 1fr 300px;
gap: 20px;
height: calc(100vh - 40px);
}
.chat-box {
background: white;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
.chat-header {
padding: 16px 20px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
gap: 12px;
}
.chat-header h1 { font-size: 18px; color: #333; }
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.message { margin-bottom: 16px; max-width: 80%; }
.message.bot { margin-right: auto; }
.message.user { margin-left: auto; text-align: right; }
.message .bubble {
display: inline-block;
padding: 12px 16px;
border-radius: 18px;
white-space: pre-wrap;
}
.message.bot .bubble { background: #f0f0f0; color: #333; }
.message.user .bubble { background: #007bff; color: white; }
.typing { color: #888; font-style: italic; padding: 8px; }
.chat-input {
padding: 16px 20px;
border-top: 1px solid #eee;
display: flex;
gap: 12px;
}
.chat-input input {
flex: 1;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 24px;
outline: none;
font-size: 16px;
}
.chat-input input:focus { border-color: #007bff; }
.chat-input button {
padding: 12px 24px;
background: #007bff;
color: white;
border: none;
border-radius: 24px;
cursor: pointer;
font-size: 16px;
}
.chat-input button:hover { background: #0056b3; }
.sidebar { display: flex; flex-direction: column; gap: 20px; }
.sidebar-box {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.sidebar-box h2 { font-size: 16px; margin-bottom: 12px; color: #333; }
.stat { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #eee; }
.stat-value { font-weight: 600; color: #007bff; }
.product-list { max-height: 200px; overflow-y: auto; }
.product-item { padding: 8px 0; border-bottom: 1px solid #eee; font-size: 14px; }
.product-item .name { font-weight: 500; }
.product-item .price { color: #007bff; }
.product-item .stock { color: #28a745; font-size: 12px; }
.product-item .no-stock { color: #dc3545; font-size: 12px; }
@media (max-width: 768px) {
.app { grid-template-columns: 1fr; }
.sidebar { display: none; }
}
</style>
</head>
<body>
<div class="container">
<div class="app">
<div class="chat-box">
<div class="chat-header">
<span style="font-size: 24px;">🛒</span>
<h1>Demo Librería</h1>
</div>
<div class="chat-messages" id="messages">
<div class="message bot">
<div class="bubble">¡Hola! Soy el asistente de ventas de Demo Librería. ¿Qué estás buscando?</div>
</div>
</div>
<div id="typing-indicator" class="typing" style="display: none;">
Escribiendo...
</div>
<div class="chat-input">
<input type="text" id="msgInput" placeholder="Escribí tu consulta..." autocomplete="off">
<button onclick="sendMessage()">Enviar</button>
</div>
</div>
<div class="sidebar">
<div class="sidebar-box">
<h2>📊 Hoy</h2>
<div class="stat">
<span>Ventas</span>
<span class="stat-value" id="ventas-hoy">0</span>
</div>
<div class="stat">
<span>Total</span>
<span class="stat-value" id="total-hoy">$0</span>
</div>
</div>
<div class="sidebar-box">
<h2>📦 Productos</h2>
<div class="product-list" id="product-list">
<div style="color: #888; font-size: 14px;">Escribí para buscar...</div>
</div>
</div>
</div>
</div>
</div>
<script>
let ws = null;
const sessionId = Math.random().toString(36).substring(7);
function connect() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
ws = new WebSocket(`${protocol}//${window.location.host}/chat/ws/${sessionId}`);
ws.onopen = () => console.log('Conectado');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.typing !== undefined) {
document.getElementById('typing-indicator').style.display = data.typing ? 'block' : 'none';
}
if (data.msg) {
addMessage(data.msg, 'bot');
}
};
ws.onerror = (e) => console.error('WS Error:', e);
ws.onclose = () => {
console.log('Desconectado, reconectando...');
setTimeout(connect, 3000);
};
}
function addMessage(text, type) {
const div = document.createElement('div');
div.className = `message ${type}`;
div.innerHTML = `<div class="bubble">${escapeHtml(text)}</div>`;
document.getElementById('messages').appendChild(div);
document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function sendMessage() {
const input = document.getElementById('msgInput');
const text = input.value.trim();
if (!text || !ws || ws.readyState !== WebSocket.OPEN) return;
addMessage(text, 'user');
ws.send(JSON.stringify({ msg: text, session_id: sessionId }));
input.value = '';
}
document.getElementById('msgInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});
connect();
fetch('/stats/ventas?periodo=hoy')
.then(r => r.json())
.then(data => {
document.getElementById('ventas-hoy').textContent = data.total_ventas || 0;
document.getElementById('total-hoy').textContent = '$' + (data.total_pesos || 0).toLocaleString();
})
.catch(() => {});
</script>
</body>
</html>