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>
81 lines
3.4 KiB
HTML
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>
|