Files
soft_usite/resources/views/automatizacion/asistente.html
T
Lizandro GDandClaude Sonnet 5 516220a8a1 feat: automatización de cotizaciones, contratos, actas y cuentas de cobro con IA
Implementa las 4 fases de la especificación de automatización: módulo de
plantillas/tarifas editable por el equipo, generación de PDF (HTML+JS vía
Chrome headless) para cotizaciones/contratos/arquitecturas/cuentas de cobro,
chat propio en el dashboard reutilizando el mismo motor y tools del bot de
Telegram, y nuevas tools del agente para crear estos documentos end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 00:45:58 +00:00

81 lines
3.4 KiB
HTML

<div x-data="app" x-init="init" class="bg-white rounded-lg shadow flex flex-col" style="height: calc(100vh - 140px)">
<div class="border-b border-gray-200 px-6 py-4 flex justify-between items-center">
<div>
<h1 class="text-2xl font-bold mb-1">Asistente U-Site</h1>
<p class="text-sm text-gray-500">Mismo motor y mismas herramientas que el bot de Telegram: cotizaciones, contratos, contabilidad, proyectos, Coolify…</p>
</div>
<button @click="reset()" class="text-sm text-gray-400 hover:text-red-500 border rounded px-3 py-1.5">Borrar historial</button>
</div>
<div class="flex-1 overflow-y-auto px-6 py-4 space-y-4" x-ref="scrollArea">
<template x-if="mensajes.length === 0">
<div class="text-center text-gray-400 text-sm mt-10">
Escríbeme en lenguaje natural, por ejemplo:<br>
<em>"cotización para ZABDI, migración a M365 Business Premium para 25 usuarios"</em>
</div>
</template>
<template x-for="(m, i) in mensajes" :key="i">
<div class="flex" :class="m.rol === 'user' ? 'justify-end' : 'justify-start'">
<div class="max-w-[75%] rounded-lg px-4 py-2 text-sm whitespace-pre-wrap"
:class="m.rol === 'user' ? 'bg-[#8eb02f] text-white' : 'bg-gray-100 text-gray-800'"
x-text="m.texto"></div>
</div>
</template>
<div x-show="pensando" class="flex justify-start">
<div class="bg-gray-100 text-gray-400 rounded-lg px-4 py-2 text-sm">Pensando…</div>
</div>
</div>
<form @submit.prevent="enviar()" class="border-t border-gray-200 p-4 flex gap-2">
<input x-model="input" :disabled="pensando" placeholder="Escribe tu mensaje…"
class="flex-1 border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[#8eb02f]/40" />
<button type="submit" :disabled="pensando || !input.trim()" class="bg-[#8eb02f] text-white px-5 py-2 rounded-lg text-sm disabled:opacity-40">
Enviar
</button>
</form>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('app', () => ({
mensajes: [],
input: '',
pensando: false,
init() {},
scrollDown() {
this.$nextTick(() => {
const el = this.$refs.scrollArea;
if (el) el.scrollTop = el.scrollHeight;
});
},
async enviar() {
const texto = this.input.trim();
if (!texto || this.pensando) return;
this.mensajes.push({ rol: 'user', texto });
this.input = '';
this.pensando = true;
this.scrollDown();
try {
const { data } = await axios.post('/app/api/asistente/chat', { mensaje: texto });
this.mensajes.push({ rol: 'assistant', texto: data.respuesta || '' });
} catch (e) {
this.mensajes.push({ rol: 'assistant', texto: e.response?.data?.error || 'Error al contactar el asistente.' });
}
this.pensando = false;
this.scrollDown();
},
async reset() {
if (!confirm('¿Borrar el historial de esta conversación?')) return;
try {
await axios.delete('/app/api/asistente/historial');
this.mensajes = [];
} catch (e) { /* noop */ }
}
}));
});
</script>