diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts
index 67a4e4c..184db7e 100644
--- a/src/app/api/chat/route.ts
+++ b/src/app/api/chat/route.ts
@@ -23,7 +23,14 @@ export async function POST(req: NextRequest) {
console.error('Error parsing chat body:', e);
}
- const query = userMessage.toLowerCase().trim();
+ // Normalizar consulta eliminando tildes, signos de interrogación y puntuación
+ const normQuery = userMessage
+ .toLowerCase()
+ .normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '')
+ .replace(/[¿?¡!.,;:_()]/g, ' ')
+ .replace(/\s+/g, ' ')
+ .trim();
// 2. Obtener tenant_id dinámico del usuario autenticado
let tenantId = '0b3c5fc0-6901-4caf-b26a-b6a0201d8541';
@@ -51,39 +58,64 @@ export async function POST(req: NextRequest) {
return NextResponse.json([{ text }]);
};
- // 3. Intención: Saludos y Presentación
- if (
- query === '' ||
- query === 'hola' ||
- query === 'buenas' ||
- query === 'buenos dias' ||
- query === 'buenas tardes' ||
- query === 'hey' ||
- query.includes('que puedes hacer') ||
- query.includes('quien eres') ||
- query.includes('ayuda')
- ) {
+ // 3. Intención: Saludos, Capacidades y "¿Qué puedo preguntar?"
+ const isHelpQuery =
+ normQuery === '' ||
+ normQuery === 'hola' ||
+ normQuery === 'buenas' ||
+ normQuery === 'buenos dias' ||
+ normQuery === 'buenas tardes' ||
+ normQuery === 'hey' ||
+ normQuery.includes('que puedo preguntar') ||
+ normQuery.includes('que te puedo preguntar') ||
+ normQuery.includes('que preguntar') ||
+ normQuery.includes('que sabes') ||
+ normQuery.includes('que puedes hacer') ||
+ normQuery.includes('quien eres') ||
+ normQuery.includes('ayuda') ||
+ normQuery.includes('ejemplos') ||
+ normQuery.includes('comandos') ||
+ normQuery.includes('opciones') ||
+ normQuery.includes('como funciona el asistente') ||
+ normQuery.includes('instrucciones');
+
+ if (isHelpQuery) {
return reply(
- `👋 **¡Hola! Soy el Asistente Financiero y de Crecimiento de 4Ventures** para **${companyName}**.\n\n` +
- `Estoy conectado directamente a tu base de datos analítica y ejecuto cálculos matemáticos deterministas (temperatura 0.0, sin alucinaciones):\n\n` +
- `💡 **¿Qué puedes consultarme?**\n` +
- `1. **Simulaciones presupuestarias:** *«¿Qué pasa si invierto 1.500 € en Meta?»* o *«Simular 2.000 € en Google»*.\n` +
- `2. **Cumplimiento de Objetivos:** *«¿Cómo vamos con los objetivos de este mes?»*.\n` +
- `3. **Embudo y Conversión:** *«Muéstrame el embudo comercial y tasa de cierre»*.\n` +
- `4. **Métricas de CAC y ROAS:** *«¿Cuál es el CAC y ROAS de los últimos 30 días?»*.\n` +
- `5. **Ranking de Anuncios:** *«¿Cuáles son las mejores campañas activas?»*.\n` +
- `6. **Biblioteca de Activos:** *«Busca copys de Meta Ads o informes de auditoría»*.\n\n` +
- `¿Qué métrica o escenario te gustaría analizar hoy?`
+ `👋 **¡Hola! Soy tu Asistente Financiero & de Crecimiento de 4Ventures** para **${companyName}**.\n\n` +
+ `Estoy conectado directamente a tu base de datos analítica y ejecuto cálculos matemáticos deterministas (temperatura 0.0, sin alucinaciones). Aquí tienes todo lo que puedes preguntarme:\n\n` +
+ `1. **🔮 Simulaciones de Inversión ("¿Qué pasa si...?"):**\n` +
+ ` - *«¿Qué pasa si invierto 1.000 € en Meta?»*\n` +
+ ` - *«Simular 2.500 € en Google Ads»*\n` +
+ ` - *«Proyección con 5.000 € de presupuesto»*\n` +
+ ` *(Calcula retornos p10 conservador, p50 esperado y p90 optimista según tu histórico real).* \n\n` +
+ `2. **🎯 Objetivos y Cumplimiento Mensual:**\n` +
+ ` - *«¿Cómo van los objetivos de este mes?»*\n` +
+ ` - *«¿Cuánto llevamos facturado vs la meta?»*\n` +
+ ` - *«Progreso de captación de leads»*\n\n` +
+ `3. **🔄 Embudo Comercial y Conversión:**\n` +
+ ` - *«Muéstrame el embudo comercial»*\n` +
+ ` - *«¿Cuál es nuestra tasa de cierre lead a venta?»*\n` +
+ ` - *«Ticket medio de los clientes ganados»*\n\n` +
+ `4. **📊 Métricas CAC y ROAS por Canal:**\n` +
+ ` - *«¿Cuál es el CAC y ROAS de los últimos 30 días?»*\n` +
+ ` - *«Rendimiento por canal publicitario»*\n\n` +
+ `5. **🏆 Ranking de Campañas:**\n` +
+ ` - *«¿Cuáles son las mejores campañas activas?»*\n` +
+ ` - *«¿Dónde estamos invirtiendo más?»*\n\n` +
+ `6. **📖 Metodología y Glosario 4Ventures:**\n` +
+ ` - *«¿Qué diferencia hay entre CAC de cohorte y CAC de caja?»*\n` +
+ ` - *«¿Cómo funciona la atribución universal a 90 días?»*\n\n` +
+ `💡 *Puedes hacer clic en cualquiera de los botones rápidos de abajo o escribir directamente tu consulta.*`
);
}
// 4. Intención: Simulación Determinista "¿Qué pasa si...?"
if (
- query.includes('simul') ||
- query.includes('invierto') ||
- query.includes('pasa si') ||
- query.includes('proyec') ||
- query.includes('presupuesto')
+ normQuery.includes('simul') ||
+ normQuery.includes('invierto') ||
+ normQuery.includes('pasa si') ||
+ normQuery.includes('proyec') ||
+ normQuery.includes('presupuesto')
) {
try {
const matchNumber = query.match(/(\d+[\d\.,]*)/);
@@ -93,10 +125,10 @@ export async function POST(req: NextRequest) {
}
let channel: string | null = null;
- if (query.includes('meta') || query.includes('facebook') || query.includes('instagram')) channel = 'meta';
- else if (query.includes('google')) channel = 'google';
- else if (query.includes('tiktok')) channel = 'tiktok';
- else if (query.includes('linkedin')) channel = 'linkedin';
+ if (normQuery.includes('meta') || normQuery.includes('facebook') || normQuery.includes('instagram')) channel = 'meta';
+ else if (normQuery.includes('google')) channel = 'google';
+ else if (normQuery.includes('tiktok')) channel = 'tiktok';
+ else if (normQuery.includes('linkedin')) channel = 'linkedin';
const res = await fetch(`${supabaseUrl}/rest/v1/rpc/simulate_budget`, {
method: 'POST',
@@ -137,11 +169,11 @@ export async function POST(req: NextRequest) {
// 5. Intención: Objetivos vs Real / Estrategia
if (
- query.includes('objetivo') ||
- query.includes('meta') ||
- query.includes('factura') ||
- query.includes('target') ||
- query.includes('cumplimiento')
+ normQuery.includes('objetivo') ||
+ normQuery.includes('meta') ||
+ normQuery.includes('factura') ||
+ normQuery.includes('target') ||
+ normQuery.includes('cumplimiento')
) {
try {
const now = new Date();
@@ -176,12 +208,12 @@ export async function POST(req: NextRequest) {
// 6. Intención: Embudo / Funnel / Conversión
if (
- query.includes('funnel') ||
- query.includes('embudo') ||
- query.includes('tasa') ||
- query.includes('ticket') ||
- query.includes('conversion') ||
- query.includes('cierre')
+ normQuery.includes('funnel') ||
+ normQuery.includes('embudo') ||
+ normQuery.includes('tasa') ||
+ normQuery.includes('ticket') ||
+ normQuery.includes('conversion') ||
+ normQuery.includes('cierre')
) {
try {
const res = await fetch(`${supabaseUrl}/rest/v1/rpc/lead_funnel`, {
@@ -213,11 +245,11 @@ export async function POST(req: NextRequest) {
// 7. Intención: Ranking de Campañas / Anuncios
if (
- query.includes('campa') ||
- query.includes('anuncio') ||
- query.includes('ranking') ||
- query.includes('mejor') ||
- query.includes('ads')
+ normQuery.includes('campa') ||
+ normQuery.includes('anuncio') ||
+ normQuery.includes('ranking') ||
+ normQuery.includes('mejor') ||
+ normQuery.includes('ads')
) {
try {
const res = await fetch(`${supabaseUrl}/rest/v1/rpc/rank_campaigns`, {
@@ -245,12 +277,12 @@ export async function POST(req: NextRequest) {
// 8. Intención: RAG Documental Híbrido (Biblioteca de Activos)
if (
- query.includes('document') ||
- query.includes('copy') ||
- query.includes('informe') ||
- query.includes('brief') ||
- query.includes('activo') ||
- query.includes('creativo')
+ normQuery.includes('document') ||
+ normQuery.includes('copy') ||
+ normQuery.includes('informe') ||
+ normQuery.includes('brief') ||
+ normQuery.includes('activo') ||
+ normQuery.includes('creativo')
) {
try {
const res = await fetch(`${supabaseUrl}/rest/v1/rpc/match_documents`, {
@@ -290,12 +322,12 @@ export async function POST(req: NextRequest) {
// 9. Intención: Metodología y Glosario 4Ventures
if (
- query.includes('que es cac') ||
- query.includes('cohorte') ||
- query.includes('caja') ||
- query.includes('atribucion') ||
- query.includes('90 dias') ||
- query.includes('metodologia')
+ normQuery.includes('que es cac') ||
+ normQuery.includes('cohorte') ||
+ normQuery.includes('caja') ||
+ normQuery.includes('atribucion') ||
+ normQuery.includes('90 dias') ||
+ normQuery.includes('metodologia')
) {
return reply(
`📖 **Metodología Oficial de Crecimiento — 4Ventures:**\n\n` +
diff --git a/src/app/dashboard/asistente/page.tsx b/src/app/dashboard/asistente/page.tsx
index 9ffe7d1..3ff9aca 100644
--- a/src/app/dashboard/asistente/page.tsx
+++ b/src/app/dashboard/asistente/page.tsx
@@ -1,7 +1,105 @@
"use client";
import React, { useState, useRef, useEffect } from 'react';
-import { Bot, Send, Sparkles, TrendingUp, Target, Filter, DollarSign, BookOpen } from 'lucide-react';
+import { Bot, Send, Sparkles, TrendingUp, Target, Filter, DollarSign, BookOpen, HelpCircle } from 'lucide-react';
+
+function FormattedMessage({ content }: { content: string }) {
+ const lines = content.split('\n');
+
+ const renderInline = (text: string) => {
+ const parts: React.ReactNode[] = [];
+ const regex = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g;
+ let lastIndex = 0;
+ let match: RegExpExecArray | null;
+
+ while ((match = regex.exec(text)) !== null) {
+ if (match.index > lastIndex) {
+ parts.push(text.substring(lastIndex, match.index));
+ }
+ const matchStr = match[0];
+ if (matchStr.startsWith('**') && matchStr.endsWith('**')) {
+ parts.push(
+
+ {matchStr.slice(2, -2)}
+
+ );
+ } else if (matchStr.startsWith('*') && matchStr.endsWith('*')) {
+ parts.push(
+
+ {matchStr.slice(1, -1)}
+
+ );
+ } else if (matchStr.startsWith('`') && matchStr.endsWith('`')) {
+ parts.push(
+
+ {matchStr.slice(1, -1)}
+
+ );
+ }
+ lastIndex = regex.lastIndex;
+ }
+
+ if (lastIndex < text.length) {
+ parts.push(text.substring(lastIndex));
+ }
+
+ return parts;
+ };
+
+ return (
+
+ {renderInline(displayText)} ++ ); + } + + return ( +
+ {renderInline(trimmed)} +
+ ); + })} +