fix(assistant): fix message extraction, dynamic tenant resolution, markdown line breaks and conversational intents
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Bot, Send, Sparkles } from 'lucide-react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Bot, Send, Sparkles, TrendingUp, Target, Filter, DollarSign, BookOpen } from 'lucide-react';
|
||||
|
||||
export default function AsistentePage() {
|
||||
const [input, setInput] = useState('');
|
||||
@@ -12,6 +12,15 @@ export default function AsistentePage() {
|
||||
}
|
||||
]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages, loading]);
|
||||
|
||||
const sendMessage = async (textToSend?: string) => {
|
||||
const text = textToSend || input;
|
||||
@@ -26,12 +35,25 @@ export default function AsistentePage() {
|
||||
const res = await fetch('/api/chat', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ messages: newMessages })
|
||||
body: JSON.stringify({
|
||||
message: text,
|
||||
messages: newMessages
|
||||
})
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const assistantText = typeof data === 'string' ? data : (data[0]?.text || JSON.stringify(data));
|
||||
let assistantText = '';
|
||||
if (Array.isArray(data) && data[0]?.text) {
|
||||
assistantText = data[0].text;
|
||||
} else if (data && typeof data.text === 'string') {
|
||||
assistantText = data.text;
|
||||
} else if (typeof data === 'string') {
|
||||
assistantText = data;
|
||||
} else {
|
||||
assistantText = JSON.stringify(data);
|
||||
}
|
||||
|
||||
setMessages([...newMessages, { role: 'assistant', content: assistantText }]);
|
||||
} else {
|
||||
setMessages([...newMessages, { role: 'assistant', content: 'Lo siento, hubo un problema al procesar la consulta con las herramientas de analítica.' }]);
|
||||
@@ -45,6 +67,7 @@ export default function AsistentePage() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-[calc(100vh-10rem)] bg-[#111827] border border-gray-800 rounded-xl overflow-hidden shadow-sm">
|
||||
{/* Header */}
|
||||
<div className="p-4 border-b border-gray-800 flex items-center justify-between bg-[#0D121F]">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-brand-500/20 text-brand-500 flex items-center justify-center">
|
||||
@@ -52,11 +75,12 @@ export default function AsistentePage() {
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold text-sm text-white">Asistente IA Financiero & KPIs</h3>
|
||||
<p className="text-[11px] text-gray-400">Temperatura 0.0 · Verificación Numérica Estricta · Límite: 200 msg/mes</p>
|
||||
<p className="text-[11px] text-gray-400">Temperatura 0.0 · Verificación Numérica Estricta · Supabase RPC</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Messages Feed */}
|
||||
<div className="flex-1 p-6 overflow-y-auto space-y-4">
|
||||
{messages.map((m, idx) => (
|
||||
<div
|
||||
@@ -64,41 +88,68 @@ export default function AsistentePage() {
|
||||
className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
<div
|
||||
className={`max-w-2xl px-4 py-3 rounded-xl text-sm leading-relaxed ${
|
||||
className={`max-w-2xl px-4 py-3 rounded-xl text-sm leading-relaxed whitespace-pre-wrap ${
|
||||
m.role === 'user'
|
||||
? 'bg-brand-500 text-white font-medium'
|
||||
: 'bg-[#1F2937] text-gray-200 border border-gray-700/60'
|
||||
? 'bg-brand-500 text-white font-medium shadow-md shadow-brand-500/10'
|
||||
: 'bg-[#1F2937] text-gray-200 border border-gray-700/60 shadow-md shadow-black/20'
|
||||
}`}
|
||||
>
|
||||
{m.content}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{loading && (
|
||||
<div className="flex justify-start">
|
||||
<div className="bg-[#1F2937] text-gray-400 px-4 py-3 rounded-xl text-sm border border-gray-700/60 flex items-center gap-2">
|
||||
<div className="bg-[#1F2937] text-gray-300 px-4 py-3 rounded-xl text-sm border border-gray-700/60 flex items-center gap-2">
|
||||
<Sparkles className="w-4 h-4 animate-spin text-brand-500" />
|
||||
Consultando base de datos y calculando métricas...
|
||||
Calculando proyecciones y consultando base de datos...
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-2 bg-[#0D121F]/60 border-t border-gray-800/80 flex gap-2 overflow-x-auto text-xs text-gray-300">
|
||||
{/* Quick Prompts Chips */}
|
||||
<div className="px-6 py-2.5 bg-[#0D121F]/80 border-t border-gray-800/80 flex gap-2 overflow-x-auto text-xs text-gray-300">
|
||||
<button
|
||||
onClick={() => sendMessage('¿Cuál es el CAC y ROAS por canal de los últimos 30 días?')}
|
||||
className="px-3 py-1 rounded-full bg-gray-800 hover:bg-gray-700 whitespace-nowrap transition-colors"
|
||||
onClick={() => sendMessage('¿Qué pasa si invierto 1.000 € en Meta?')}
|
||||
className="px-3 py-1.5 rounded-full bg-gray-800 hover:bg-gray-700 hover:text-white whitespace-nowrap transition-colors flex items-center gap-1.5 cursor-pointer border border-gray-700/50"
|
||||
>
|
||||
📊 CAC & ROAS últimos 30 días
|
||||
<DollarSign className="w-3.5 h-3.5 text-brand-400" />
|
||||
🔮 Simular 1.000 € en Meta
|
||||
</button>
|
||||
<button
|
||||
onClick={() => sendMessage('¿Cómo va el cumplimiento de objetivos de este mes?')}
|
||||
className="px-3 py-1 rounded-full bg-gray-800 hover:bg-gray-700 whitespace-nowrap transition-colors"
|
||||
className="px-3 py-1.5 rounded-full bg-gray-800 hover:bg-gray-700 hover:text-white whitespace-nowrap transition-colors flex items-center gap-1.5 cursor-pointer border border-gray-700/50"
|
||||
>
|
||||
<Target className="w-3.5 h-3.5 text-emerald-400" />
|
||||
🎯 Objetivos de este mes
|
||||
</button>
|
||||
<button
|
||||
onClick={() => sendMessage('Muéstrame el embudo de conversión y tasa de cierre de los últimos 30 días')}
|
||||
className="px-3 py-1.5 rounded-full bg-gray-800 hover:bg-gray-700 hover:text-white whitespace-nowrap transition-colors flex items-center gap-1.5 cursor-pointer border border-gray-700/50"
|
||||
>
|
||||
<Filter className="w-3.5 h-3.5 text-blue-400" />
|
||||
🔄 Embudo comercial
|
||||
</button>
|
||||
<button
|
||||
onClick={() => sendMessage('¿Cuál es el CAC y ROAS por canal de los últimos 30 días?')}
|
||||
className="px-3 py-1.5 rounded-full bg-gray-800 hover:bg-gray-700 hover:text-white whitespace-nowrap transition-colors flex items-center gap-1.5 cursor-pointer border border-gray-700/50"
|
||||
>
|
||||
<TrendingUp className="w-3.5 h-3.5 text-purple-400" />
|
||||
📊 CAC & ROAS por canal
|
||||
</button>
|
||||
<button
|
||||
onClick={() => sendMessage('Explícame la metodología 4Ventures y cómo calculáis el CAC de cohorte')}
|
||||
className="px-3 py-1.5 rounded-full bg-gray-800 hover:bg-gray-700 hover:text-white whitespace-nowrap transition-colors flex items-center gap-1.5 cursor-pointer border border-gray-700/50"
|
||||
>
|
||||
<BookOpen className="w-3.5 h-3.5 text-amber-400" />
|
||||
📖 Metodología y atribución
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Input Box */}
|
||||
<div className="p-4 border-t border-gray-800 bg-[#0D121F]">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
@@ -111,13 +162,13 @@ export default function AsistentePage() {
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
placeholder="Pregunta sobre métricas, CAC, ROAS o cumplimiento de objetivos..."
|
||||
className="flex-1 bg-[#111827] border border-gray-700 rounded-lg px-4 py-2.5 text-sm text-white focus:outline-none focus:border-brand-500"
|
||||
placeholder="Escribe una pregunta sobre inversión, metas, CAC, o escribe «Hola»..."
|
||||
className="flex-1 bg-[#111827] border border-gray-700 rounded-xl px-4 py-2.5 text-sm text-white placeholder-gray-500 focus:outline-none focus:border-brand-500 transition-all"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="bg-brand-500 hover:bg-brand-600 disabled:opacity-50 text-white font-medium px-4 py-2.5 rounded-lg flex items-center gap-2 text-sm transition-colors"
|
||||
disabled={loading || !input.trim()}
|
||||
className="bg-gradient-to-r from-brand-500 to-amber-600 hover:from-brand-600 hover:to-amber-700 disabled:opacity-50 text-white font-medium px-5 py-2.5 rounded-xl flex items-center gap-2 text-sm transition-all shadow-lg shadow-brand-500/20 cursor-pointer"
|
||||
>
|
||||
<Send className="w-4 h-4" /> Enviar
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user