Explorar el Código

implement help chat why not

Matthew Trejo hace 2 meses
padre
commit
d322b30296

+ 62 - 0
docs/PSYCHOLOGICAL_CHAT_IMPLEMENTATION.md

@@ -0,0 +1,62 @@
+# Implementación Chat Psicológico
+
+## Objetivo
+Agregar chat de apoyo psicológico separado del chat médico, sin límite de mensajes y con opción de terminar conversación.
+
+## Diferencias con Chat Médico
+- ❌ Sin límite de 10 mensajes
+- ✅ Usuario termina conversación cuando quiera
+- 💭 Prompt enfocado en apoyo emocional
+- 🔒 Disclaimer sobre no ser terapia profesional
+
+---
+
+## Tareas
+
+### 1. Base de Datos
+- [x] Agregar enum `ChatType` (MEDICAL, PSYCHOLOGICAL)
+- [x] Agregar campo `chatType` al modelo Conversation
+- [x] Crear y ejecutar migración
+
+### 2. Página de Selección
+- [x] Crear `/chat/select` con AuthenticatedLayout
+- [x] Diseño con componentes UI reutilizables (Card)
+- [x] Dos opciones claras con descripción
+- [x] Redireccionamiento según selección
+- [x] Integración con sidebar y estilo consistente
+
+### 3. Backend/API
+- [x] Modificar `/api/chat` para aceptar `chatType`
+- [x] Prompt del sistema para chat psicológico
+- [x] Remover límite de mensajes para tipo PSYCHOLOGICAL
+- [ ] Endpoint para terminar conversación manualmente
+
+### 4. Frontend
+- [x] Adaptar `ImprovedChatInterface.tsx` para ambos tipos
+- [x] Modificar `useChat.ts` para manejar tipos
+- [x] Historial separado por tipo de chat (localStorage)
+- [x] Botón "Terminar conversación" en chat psicológico
+- [x] Modificar header para mostrar tipo de chat
+- [x] Iconos y colores diferenciados según tipo
+
+### 5. Seguridad y Ética
+- [x] Disclaimer claro al inicio (en página de selección)
+- [x] Disclaimer en chat psicológico con info de limitaciones
+- [x] Detección de crisis en prompt (keywords: suicidio, autolesión)
+- [x] Números de emergencia en disclaimer
+- [x] Banner dinámico con recursos de ayuda inmediata si se detecta crisis
+- [x] Múltiples líneas de emergencia (Cruz Roja, Policía, Línea de Vida)
+
+### 6. Testing
+- [ ] Crear conversación psicológica
+- [ ] Verificar sin límite de mensajes
+- [ ] Probar terminación manual
+- [ ] Validar separación de historiales
+- [ ] Test de crisis detection (respuesta del LLM con crisisDetected: true)
+
+---
+
+## Notas Técnicas
+- Reutilizar componentes existentes de chat médico
+- Mismo modelo de BD, solo agregar discriminante `chatType`
+- Prompts almacenados en código o variables de entorno

+ 5 - 0
prisma/migrations/20251016112204_add_chat_type/migration.sql

@@ -0,0 +1,5 @@
+-- CreateEnum
+CREATE TYPE "ChatType" AS ENUM ('MEDICAL', 'PSYCHOLOGICAL');
+
+-- AlterTable
+ALTER TABLE "Record" ADD COLUMN     "chatType" "ChatType" NOT NULL DEFAULT 'MEDICAL';

+ 6 - 0
prisma/schema.prisma

@@ -57,6 +57,7 @@ model Record {
   userId    String
   content   String
   messages  Json?
+  chatType  ChatType @default(MEDICAL)
   createdAt DateTime @default(now())
   user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
   appointment Appointment?
@@ -140,4 +141,9 @@ enum Gender {
   FEMALE
   OTHER
   PREFER_NOT_TO_SAY
+}
+
+enum ChatType {
+  MEDICAL
+  PSYCHOLOGICAL
 } 

+ 3 - 2
src/app/api/chat/report/route.ts

@@ -15,7 +15,7 @@ export async function POST(request: NextRequest) {
       )
     }
 
-    const { content, messages } = await request.json()
+    const { content, messages, chatType = "MEDICAL" } = await request.json()
 
     if (!content) {
       return NextResponse.json(
@@ -29,7 +29,8 @@ export async function POST(request: NextRequest) {
       data: {
         userId: session.user.id,
         content,
-        messages: messages || null
+        messages: messages || null,
+        chatType: chatType.toUpperCase()
       },
       include: {
         user: {

+ 7 - 81
src/app/api/chat/route.ts

@@ -3,6 +3,7 @@ import { getServerSession } from "next-auth";
 import { authOptions } from "@/lib/auth";
 import OpenAI from "openai";
 import { config } from "@/lib/config";
+import { getSystemPrompt } from "@/lib/chat-prompts";
 
 export async function POST(request: NextRequest) {
   const requestStartTime = Date.now();
@@ -24,10 +25,11 @@ export async function POST(request: NextRequest) {
       );
     }
 
-    const { message, messages } = await request.json();
+    const { message, messages, chatType = "medical" } = await request.json();
     console.log("📨 [API] Mensaje recibido:", {
       messageLength: message?.length || 0,
-      messagesCount: messages?.length || 0
+      messagesCount: messages?.length || 0,
+      chatType
     });
 
     if (!message) {
@@ -103,90 +105,14 @@ export async function POST(request: NextRequest) {
       const openRouterStartTime = Date.now();
       console.log("🤖 [API] Iniciando llamada a OpenRouter con streaming...");
 
+      const systemPrompt = getSystemPrompt(chatType as "medical" | "psychological");
+
       const stream = await openai.chat.completions.create({
         model: OPENROUTER_MODEL,
         messages: [
           {
             role: "system",
-            content: `Eres un asistente médico virtual llamado Ani Assistant especializado EXCLUSIVAMENTE en temas de salud y medicina. Tu función es:
-
-**TEMAS QUE SÍ PUEDES RESPONDER:**
-1. Información general sobre salud y bienestar
-2. Preguntas sobre síntomas comunes y su posible significado
-3. Consejos de estilo de vida saludable (ejercicio, nutrición, sueño)
-4. Información sobre medicamentos comunes y sus efectos
-5. Prevención de enfermedades
-6. Primeros auxilios básicos
-7. Salud mental y manejo del estrés
-8. Cuidados durante el embarazo
-9. Salud infantil básica
-10. Información sobre vacunas
-
-**TEMAS QUE NO PUEDES RESPONDER:**
-- Matemáticas, física, química (excepto si está directamente relacionado con medicina)
-- Historia, geografía, literatura
-- Tecnología, programación, ingeniería
-- Deportes (excepto ejercicio para la salud)
-- Entretenimiento, música, arte
-- Política, economía, finanzas
-- Cualquier tema que NO esté relacionado con salud o medicina
-
-**INSTRUCCIONES IMPORTANTES:**
-1. Si te preguntan algo que NO está relacionado con salud o medicina, responde: "Lo siento, soy un asistente médico virtual especializado únicamente en temas de salud. Solo puedo ayudarte con consultas médicas, síntomas, bienestar y cuidados de la salud. ¿Hay algún tema de salud en el que pueda asistirte?"
-2. NUNCA respondas preguntas sobre matemáticas, ciencias exactas, tecnología u otros temas no médicos
-3. Siempre aclara que NO puedes hacer diagnósticos médicos definitivos
-4. Recomienda consultar con un profesional médico para casos serios
-5. Sé empático y profesional en tus respuestas
-6. Responde siempre en español
-7. Mantén el enfoque estrictamente en salud y medicina
-
-**FORMATO DE RESPUESTA REQUERIDO:**
-Debes responder SIEMPRE en formato JSON válido. Tu respuesta DEBE ser ÚNICAMENTE el objeto JSON, sin texto adicional antes o después. Ejemplo:
-
-{
-  "response": "Tu respuesta médica aquí en formato markdown",
-  "medicalAlert": "NO_AGENDAR",
-  "suggestions": [
-    {
-      "title": "Título corto",
-      "emoji": "🩺",
-      "prompt": "Pregunta sugerida relacionada"
-    }
-  ]
-}
-
-**IMPORTANTE:**
-- NO agregues texto explicativo antes o después del JSON
-- NO uses comillas triples o bloques de código markdown
-- NO envuelvas el JSON en bloques de código
-- SOLO devuelve el objeto JSON válido sin formato adicional
-- Asegúrate de que todas las comillas estén correctamente escapadas
-- Tu respuesta debe comenzar directamente con { y terminar con }
-
-**SISTEMA DE ALERTAS MÉDICAS:**
-- NO_AGENDAR: Síntomas leves, información general, prevención
-- RECOMENDADO: Síntomas que ameritan consulta médica pero no urgente
-- URGENTE: Síntomas graves que requieren atención médica inmediata
-
-**SUGERENCIAS:**
-Incluye EXACTAMENTE 3 sugerencias específicas y útiles basadas en tu respuesta. Las sugerencias deben:
-1. Ser preguntas específicas y detalladas (no vagas como "Más información")
-2. Estar directamente relacionadas con el tema de tu respuesta
-3. Ayudar al usuario a profundizar en aspectos importantes del tema
-4. Usar emojis relevantes y descriptivos
-5. Ser preguntas que un paciente realmente haría
-
-Ejemplos de BUENAS sugerencias:
-- "¿Qué medicamentos de venta libre puedo tomar para el dolor de cabeza?"
-- "¿Cuándo debo preocuparme si la fiebre no baja?"
-- "¿Qué ejercicios son seguros durante el embarazo?"
-
-Ejemplos de MALAS sugerencias (evitar):
-- "Más información"
-- "¿Puedes darme más detalles?"
-- "Prevención"
-
-RECUERDA: Eres un asistente médico virtual, NO un asistente general. Tu especialidad es la salud y medicina únicamente.`,
+            content: systemPrompt,
           },
           ...conversationHistory,
         ],

+ 29 - 4
src/app/chat/page.tsx

@@ -1,14 +1,23 @@
 "use client"
 
 import { useSession } from "next-auth/react"
-import { useRouter } from "next/navigation"
-import { useEffect } from "react"
+import { useRouter, useSearchParams } from "next/navigation"
+import { useEffect, Suspense } from "react"
 import AuthenticatedLayout from "@/components/AuthenticatedLayout"
 import ImprovedChatInterface from "@/components/ImprovedChatInterface"
 
-export default function ChatPage() {
+function ChatPageContent() {
   const { data: session, status } = useSession()
   const router = useRouter()
+  const searchParams = useSearchParams()
+  const chatType = searchParams.get("type")
+
+  // Redirigir a selección si no hay tipo especificado
+  useEffect(() => {
+    if (status === "authenticated" && !chatType) {
+      router.push("/chat/select")
+    }
+  }, [status, chatType, router])
 
   useEffect(() => {
     if (status === "unauthenticated") {
@@ -45,9 +54,25 @@ export default function ChatPage() {
     )
   }
 
+  if (!chatType) {
+    return null
+  }
+
   return (
     <AuthenticatedLayout>
-      <ImprovedChatInterface />
+      <ImprovedChatInterface chatType={chatType as "medical" | "psychological"} />
     </AuthenticatedLayout>
   )
+}
+
+export default function ChatPage() {
+  return (
+    <Suspense fallback={
+      <div className="min-h-screen flex items-center justify-center">
+        <div className="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    }>
+      <ChatPageContent />
+    </Suspense>
+  )
 } 

+ 132 - 0
src/app/chat/select/page.tsx

@@ -0,0 +1,132 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useSession } from "next-auth/react";
+import { useEffect } from "react";
+import { MessageCircle, Heart } from "lucide-react";
+import AuthenticatedLayout from "@/components/AuthenticatedLayout";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { ChatSelectHeader } from "@/components/chatbot/ChatSelectHeader";
+
+export default function ChatSelectPage() {
+  const router = useRouter();
+  const { data: session, status } = useSession();
+
+  useEffect(() => {
+    if (status === "unauthenticated") {
+      router.push("/auth/login");
+    }
+  }, [status, router]);
+
+  useEffect(() => {
+    if (session && session.user.role !== "PATIENT") {
+      router.push("/dashboard");
+    }
+  }, [session, router]);
+
+  if (status === "loading") {
+    return (
+      <div className="min-h-screen flex items-center justify-center">
+        <div className="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
+      </div>
+    );
+  }
+
+  if (!session || session.user.role !== "PATIENT") {
+    return null;
+  }
+
+  const handleMedicalChat = () => {
+    router.push("/chat?type=medical");
+  };
+
+  const handlePsychologicalChat = () => {
+    router.push("/chat?type=psychological");
+  };
+
+  return (
+    <AuthenticatedLayout>
+      <div className="flex items-center justify-center min-h-[calc(100vh-4rem)] p-6">
+        <div className="w-full max-w-5xl space-y-8">
+          {/* Header */}
+          <ChatSelectHeader />
+
+          {/* Chat Options */}
+          <div className="grid gap-6 md:grid-cols-2">
+            {/* Chat Médico */}
+            <Card 
+              className="cursor-pointer transition-all hover:shadow-lg hover:border-blue-400 hover:scale-105"
+              onClick={handleMedicalChat}
+            >
+              <CardHeader>
+                <div className="flex items-center gap-4">
+                  <div className="w-12 h-12 bg-blue-100 dark:bg-blue-500 rounded-lg flex items-center justify-center">
+                    <MessageCircle className="w-6 h-6 text-blue-600 dark:text-blue-100" />
+                  </div>
+                  <div>
+                    <CardTitle className="text-xl">Consulta Médica</CardTitle>
+                  </div>
+                </div>
+              </CardHeader>
+              <CardContent>
+                <CardDescription className="text-base text-gray-700 dark:text-gray-700">
+                  Obtén información sobre síntomas, condiciones médicas y recomendaciones de salud
+                </CardDescription>
+                <ul className="mt-4 space-y-2 text-sm text-gray-600 dark:text-gray-400">
+                  <li className="flex items-center gap-2">
+                    <span className="text-blue-500">•</span>
+                    <span>Límite de 10 mensajes</span>
+                  </li>
+                  <li className="flex items-center gap-2">
+                    <span className="text-blue-500">•</span>
+                    <span>Reporte médico al finalizar</span>
+                  </li>
+                  <li className="flex items-center gap-2">
+                    <span className="text-blue-500">•</span>
+                    <span>Sistema de alertas</span>
+                  </li>
+                </ul>
+              </CardContent>
+            </Card>
+
+            {/* Chat Psicológico */}
+            <Card 
+              className="cursor-pointer transition-all hover:shadow-lg hover:border-purple-400 hover:scale-105"
+              onClick={handlePsychologicalChat}
+            >
+              <CardHeader>
+                <div className="flex items-center gap-4">
+                  <div className="w-12 h-12 bg-purple-100 dark:bg-purple-500 rounded-lg flex items-center justify-center">
+                    <Heart className="w-6 h-6 text-purple-600 dark:text-purple-100" />
+                  </div>
+                  <div>
+                    <CardTitle className="text-xl">Apoyo Psicológico</CardTitle>
+                  </div>
+                </div>
+              </CardHeader>
+              <CardContent>
+                <CardDescription className="text-base text-gray-700 dark:text-gray-700">
+                  Un espacio seguro para hablar sobre tus emociones, pensamientos y bienestar mental
+                </CardDescription>
+                <ul className="mt-4 space-y-2 text-sm text-gray-600 dark:text-gray-400">
+                  <li className="flex items-center gap-2">
+                    <span className="text-purple-500">•</span>
+                    <span>Sin límite de mensajes</span>
+                  </li>
+                  <li className="flex items-center gap-2">
+                    <span className="text-purple-500">•</span>
+                    <span>Conversación confidencial</span>
+                  </li>
+                  <li className="flex items-center gap-2">
+                    <span className="text-purple-500">•</span>
+                    <span>Termina cuando quieras</span>
+                  </li>
+                </ul>
+              </CardContent>
+            </Card>
+          </div>
+        </div>
+      </div>
+    </AuthenticatedLayout>
+  );
+}

+ 30 - 19
src/app/daily-log/page.tsx

@@ -150,30 +150,41 @@ export default function DailyLogPage() {
     }
   }
 
-  const handleExport = async (preset: DateRangePreset) => {
+  const handleExport = async (preset: DateRangePreset | { startDate: Date; endDate: Date }) => {
     const now = new Date()
     let startDate: Date
-
-    switch (preset) {
-      case "week":
-        startDate = new Date(now)
-        startDate.setDate(now.getDate() - 7)
-        break
-      case "month":
-        startDate = new Date(now)
-        startDate.setMonth(now.getMonth() - 1)
-        break
-      case "3months":
-        startDate = new Date(now)
-        startDate.setMonth(now.getMonth() - 3)
-        break
-      case "all":
-        startDate = new Date(0)
-        break
+    let endDate: Date = now
+
+    // Si preset es un objeto con fechas personalizadas
+    if (typeof preset === 'object' && 'startDate' in preset) {
+      startDate = preset.startDate
+      endDate = preset.endDate
+    } else {
+      // Si preset es un string con valores predefinidos
+      switch (preset) {
+        case "week":
+          startDate = new Date(now)
+          startDate.setDate(now.getDate() - 7)
+          break
+        case "month":
+          startDate = new Date(now)
+          startDate.setMonth(now.getMonth() - 1)
+          break
+        case "3months":
+          startDate = new Date(now)
+          startDate.setMonth(now.getMonth() - 3)
+          break
+        case "all":
+          startDate = new Date(0)
+          break
+        default:
+          startDate = new Date(now)
+          startDate.setMonth(now.getMonth() - 1)
+      }
     }
 
     const start = startDate.toISOString().split("T")[0]
-    const end = now.toISOString().split("T")[0]
+    const end = endDate.toISOString().split("T")[0]
 
     const logsToExport = await fetchLogs(start, end)
     if (logsToExport && logsToExport.length > 0) {

+ 6 - 2
src/components/ImprovedChatInterface.tsx

@@ -2,6 +2,10 @@
 
 import { ChatInterface } from "./chatbot";
 
-export default function ImprovedChatInterface() {
-  return <ChatInterface />;
+interface ImprovedChatInterfaceProps {
+  chatType: "medical" | "psychological";
+}
+
+export default function ImprovedChatInterface({ chatType }: ImprovedChatInterfaceProps) {
+  return <ChatInterface chatType={chatType} />;
 }

+ 1 - 1
src/components/Navigation.tsx

@@ -61,7 +61,7 @@ export default function Navigation() {
             <div className="flex items-center space-x-2">
               {session.user.role === "PATIENT" && (
                 <>
-                  <Link href="/chat">
+                  <Link href="/chat/select">
                     <Button variant="ghost" size="sm">
                       <MessageSquare className="w-4 h-4 mr-2" />
                       Chat

+ 76 - 45
src/components/chatbot/ChatHeader.tsx

@@ -1,5 +1,5 @@
 import { Button } from "@/components/ui/button";
-import { RotateCcw } from "lucide-react";
+import { RotateCcw, MessageCircle, Heart } from "lucide-react";
 import { Message } from "./types";
 
 interface ChatHeaderProps {
@@ -10,6 +10,7 @@ interface ChatHeaderProps {
   isLimitReached: boolean;
   isLoading: boolean;
   onResetClick: () => void;
+  chatType: "medical" | "psychological";
 }
 
 export const ChatHeader = ({
@@ -20,67 +21,97 @@ export const ChatHeader = ({
   isLimitReached,
   isLoading,
   onResetClick,
+  chatType,
 }: ChatHeaderProps) => {
+  const isMedical = chatType === "medical";
+  const isPsychological = chatType === "psychological";
+
   return (
-    <div className="p-6 border-b border-border">
-      <div className="flex items-center justify-between">
-        <div>
-          <h2 className="text-2xl font-bold text-foreground">
-            Asistente Médico Virtual
-          </h2>
-          <p className="text-muted-foreground mt-1">
-            Consulta médica inteligente con IA
-          </p>
-        </div>
-        <div className="flex items-center space-x-4">
-          <div className="flex flex-col">
-            <div className="flex items-center space-x-2">
-              <div
-                className={`w-3 h-3 rounded-full ${
-                  remainingMessages > 1
-                    ? "bg-success"
-                    : remainingMessages === 1
-                    ? "bg-warning"
-                    : "bg-destructive"
-                }`}
-              ></div>
-              <span
-                className={`text-sm ${
-                  remainingMessages > 1
-                    ? "text-muted-foreground"
-                    : remainingMessages === 1
-                    ? "text-warning"
-                    : "text-destructive"
-                }`}
-              >
-                {remainingMessages} restante
-                {remainingMessages !== 1 ? "s" : ""}
-              </span>
-            </div>
-            {isLastMessage && messages.length > 0 && (
-              <span className="text-xs text-warning mt-1">
-                ⚠️ Última consulta - Se generará reporte automáticamente
-              </span>
+    <div className="p-4 sm:p-6 border-b border-border">
+      {/* Layout responsive: columna en móvil, fila en desktop */}
+      <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
+        {/* Sección izquierda: Icono y título */}
+        <div className="flex items-center gap-3 min-w-0 flex-1">
+          {/* Icono según tipo */}
+          <div className={`w-10 h-10 sm:w-12 sm:h-12 flex-shrink-0 rounded-lg flex items-center justify-center ${
+            isMedical 
+              ? "bg-blue-100 dark:bg-blue-500" 
+              : "bg-purple-100 dark:bg-purple-500"
+          }`}>
+            {isMedical ? (
+              <MessageCircle className="w-5 h-5 sm:w-6 sm:h-6 text-blue-600 dark:text-blue-100" />
+            ) : (
+              <Heart className="w-5 h-5 sm:w-6 sm:h-6 text-purple-600 dark:text-purple-100" />
             )}
           </div>
+          
+          <div className="min-w-0 flex-1">
+            <h2 className="text-lg sm:text-2xl font-bold text-foreground truncate">
+              {isMedical ? "Asistente Médico Virtual" : "Apoyo Psicológico"}
+            </h2>
+            <p className="text-xs sm:text-sm text-muted-foreground mt-0.5 sm:mt-1 truncate">
+              {isMedical 
+                ? "Consulta médica inteligente con IA" 
+                : "Un espacio seguro para hablar"}
+            </p>
+          </div>
+        </div>
+        
+        {/* Sección derecha: Contador, estado y botón */}
+        <div className="flex items-center justify-between sm:justify-end gap-3 sm:gap-4 flex-shrink-0">
+          {/* Contador de mensajes (solo para médico) */}
+          {isMedical && (
+            <div className="flex flex-col min-w-0">
+              <div className="flex items-center space-x-2">
+                <div
+                  className={`w-2.5 h-2.5 sm:w-3 sm:h-3 rounded-full flex-shrink-0 ${
+                    remainingMessages > 1
+                      ? "bg-success"
+                      : remainingMessages === 1
+                      ? "bg-warning"
+                      : "bg-destructive"
+                  }`}
+                ></div>
+                <span
+                  className={`text-xs sm:text-sm whitespace-nowrap ${
+                    remainingMessages > 1
+                      ? "text-muted-foreground"
+                      : remainingMessages === 1
+                      ? "text-warning"
+                      : "text-destructive"
+                  }`}
+                >
+                  {remainingMessages} restante{remainingMessages !== 1 ? "s" : ""}
+                </span>
+              </div>
+              {isLastMessage && messages.length > 0 && (
+                <span className="text-[10px] sm:text-xs text-warning mt-1 whitespace-nowrap">
+                  ⚠️ Última consulta
+                </span>
+              )}
+            </div>
+          )}
+          
           {isGeneratingReport && (
             <div className="flex items-center space-x-2">
-              <div className="w-3 h-3 bg-primary rounded-full animate-pulse"></div>
-              <span className="text-sm text-primary">
+              <div className="w-2.5 h-2.5 sm:w-3 sm:h-3 bg-primary rounded-full animate-pulse flex-shrink-0"></div>
+              <span className="text-xs sm:text-sm text-primary whitespace-nowrap">
                 Generando reporte...
               </span>
             </div>
           )}
+          
           {messages.length > 0 && !isLimitReached && (
             <Button
               variant="outline"
               size="sm"
               onClick={onResetClick}
               disabled={isLoading}
-              className="flex items-center space-x-2"
+              className="flex items-center space-x-1.5 sm:space-x-2 flex-shrink-0"
             >
-              <RotateCcw className="w-4 h-4" />
-              <span>Nueva Consulta</span>
+              <RotateCcw className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
+              <span className="hidden sm:inline">Nueva Consulta</span>
+              <span className="sm:hidden">Nueva</span>
             </Button>
           )}
         </div>

+ 45 - 18
src/components/chatbot/ChatInput.tsx

@@ -1,6 +1,6 @@
 import { Button } from "@/components/ui/button";
 import { Input } from "@/components/ui/input";
-import { Send } from "lucide-react";
+import { Send, StopCircle } from "lucide-react";
 
 interface ChatInputProps {
   inputMessage: string;
@@ -8,6 +8,9 @@ interface ChatInputProps {
   onSendMessage: () => void;
   isLimitReached: boolean;
   isLoading: boolean;
+  chatType: "medical" | "psychological";
+  onEndConversation?: () => void;
+  hasMessages?: boolean;
 }
 
 export const ChatInput = ({
@@ -16,6 +19,9 @@ export const ChatInput = ({
   onSendMessage,
   isLimitReached,
   isLoading,
+  chatType,
+  onEndConversation,
+  hasMessages = false,
 }: ChatInputProps) => {
   const handleKeyPress = (e: React.KeyboardEvent) => {
     if (e.key === "Enter" && !e.shiftKey) {
@@ -24,25 +30,46 @@ export const ChatInput = ({
     }
   };
 
+  const isPsychological = chatType === "psychological";
+  const placeholder = isPsychological 
+    ? "Comparte lo que tengas en mente..." 
+    : "Describe tu consulta médica aquí...";
+
   return (
     <div className="p-6 border-t border-border">
-      <div className="flex space-x-3">
-        <Input
-          value={inputMessage}
-          onChange={(e) => setInputMessage(e.target.value)}
-          onKeyPress={handleKeyPress}
-          placeholder="Describe tu consulta médica aquí..."
-          disabled={isLimitReached || isLoading}
-          className="flex-1 rounded-xl"
-        />
-        <Button
-          onClick={onSendMessage}
-          disabled={!inputMessage.trim() || isLimitReached || isLoading}
-          size="icon"
-          className="rounded-xl"
-        >
-          <Send className="w-4 h-4" />
-        </Button>
+      <div className="flex flex-col space-y-3">
+        <div className="flex space-x-3">
+          <Input
+            value={inputMessage}
+            onChange={(e) => setInputMessage(e.target.value)}
+            onKeyPress={handleKeyPress}
+            placeholder={placeholder}
+            disabled={isLimitReached || isLoading}
+            className="flex-1 rounded-xl"
+          />
+          <Button
+            onClick={onSendMessage}
+            disabled={!inputMessage.trim() || isLimitReached || isLoading}
+            size="icon"
+            className="rounded-xl"
+          >
+            <Send className="w-4 h-4" />
+          </Button>
+        </div>
+        
+        {/* Botón terminar conversación para chat psicológico */}
+        {isPsychological && hasMessages && onEndConversation && (
+          <Button
+            variant="outline"
+            size="sm"
+            onClick={onEndConversation}
+            disabled={isLoading}
+            className="self-end text-purple-600 hover:text-purple-700 hover:bg-purple-50 dark:text-purple-400 dark:hover:bg-purple-900/20"
+          >
+            <StopCircle className="w-4 h-4 mr-2" />
+            Terminar conversación
+          </Button>
+        )}
       </div>
     </div>
   );

+ 45 - 9
src/components/chatbot/ChatInterface.tsx

@@ -16,14 +16,28 @@ import { ReportModal } from "./ReportModal";
 import { ResetConfirmationModal } from "./ResetConfirmationModal";
 import { AppointmentModalFromChat } from "./AppointmentModalFromChat";
 import { MedicalAlertBanner } from "./MedicalAlertBanner";
+import { PsychologicalDisclaimer } from "./PsychologicalDisclaimer";
+import { CrisisAlertBanner } from "./CrisisAlertBanner";
 
-export const ChatInterface = () => {
+interface ChatInterfaceProps {
+  chatType: "medical" | "psychological";
+}
+
+export const ChatInterface = ({ chatType }: ChatInterfaceProps) => {
   const { data: session } = useSession();
   const [showReportModal, setShowReportModal] = useState(false);
   const [showResetModal, setShowResetModal] = useState(false);
   const [showLastMessageToast, setShowLastMessageToast] = useState(false);
   const [isResetting, setIsResetting] = useState(false);
   const [showAppointmentModal, setShowAppointmentModal] = useState(false);
+  const [showPsychologicalDisclaimer, setShowPsychologicalDisclaimer] = useState(() => {
+    // Mostrar disclaimer solo si es chat psicológico y no se ha visto antes
+    if (chatType === "psychological") {
+      const seen = localStorage.getItem(`psychDisclaimer_seen`);
+      return !seen;
+    }
+    return false;
+  });
   const [appointmentReportId, setAppointmentReportId] = useState<string | undefined>();
 
   const {
@@ -55,7 +69,10 @@ export const ChatInterface = () => {
     isSchedulingFromAlert,
     handleScheduleFromAlert,
     dismissMedicalAlertBanner,
-  } = useChat();
+    crisisDetected,
+    showCrisisBanner,
+    dismissCrisisBanner,
+  } = useChat({ chatType });
 
   // Usar efectos del chat
   useChatEffects({
@@ -97,6 +114,11 @@ export const ChatInterface = () => {
     setShowResetModal(true);
   };
 
+  const handleDismissPsychologicalDisclaimer = () => {
+    setShowPsychologicalDisclaimer(false);
+    localStorage.setItem(`psychDisclaimer_seen`, "true");
+  };
+
   const handleResetWithReportAndModal = async () => {
     setIsResetting(true);
     try {
@@ -144,8 +166,19 @@ export const ChatInterface = () => {
             isLimitReached={isLimitReached}
             isLoading={isLoading}
             onResetClick={handleResetClick}
+            chatType={chatType}
           />
 
+          {/* Psychological Disclaimer */}
+          {chatType === "psychological" && showPsychologicalDisclaimer && (
+            <PsychologicalDisclaimer onDismiss={handleDismissPsychologicalDisclaimer} />
+          )}
+
+          {/* Crisis Alert Banner - para chat psicológico */}
+          {chatType === "psychological" && showCrisisBanner && crisisDetected && (
+            <CrisisAlertBanner onDismiss={dismissCrisisBanner} />
+          )}
+
           {/* Medical Alert Banner */}
           {showMedicalAlertBanner && medicalAlertDetected && (
             <MedicalAlertBanner
@@ -170,11 +203,11 @@ export const ChatInterface = () => {
 
                 {/* Welcome Message */}
                 {messages.length === 0 && (
-                  <WelcomeMessage maxMessages={MAX_MESSAGES} />
+                  <WelcomeMessage maxMessages={MAX_MESSAGES} chatType={chatType} />
                 )}
 
-                {/* Suggested Prompts */}
-                {showSuggestions && messages.length === 0 && (
+                {/* Suggested Prompts - solo para chat médico */}
+                {chatType === "medical" && showSuggestions && messages.length === 0 && (
                   <SuggestedPrompts
                     onSuggestionClick={handleSuggestionClick}
                     isLoading={isLoading}
@@ -201,18 +234,21 @@ export const ChatInterface = () => {
             </div>
 
             {/* Input Area */}
-            {messageCount < MAX_MESSAGES && (
+            {(chatType === "psychological" || messageCount < MAX_MESSAGES) && (
               <ChatInput
                 inputMessage={inputMessage}
                 setInputMessage={setInputMessage}
                 onSendMessage={handleSendMessage}
-                isLimitReached={messageCount >= MAX_MESSAGES}
+                isLimitReached={chatType === "medical" && messageCount >= MAX_MESSAGES}
                 isLoading={isLoading || inputDisabledForSuggestions}
+                chatType={chatType}
+                onEndConversation={chatType === "psychological" ? handleResetClick : undefined}
+                hasMessages={messages.length > 0}
               />
             )}
 
-            {/* Reset Button */}
-            {messageCount >= MAX_MESSAGES && (
+            {/* Reset Button - solo para médico */}
+            {chatType === "medical" && messageCount >= MAX_MESSAGES && (
               <ResetButton onReset={handleResetWithReportAndModal} />
             )}
           </div>

+ 37 - 0
src/components/chatbot/ChatSelectHeader.tsx

@@ -0,0 +1,37 @@
+"use client"
+
+import { MessageCircle, AlertCircle } from "lucide-react"
+
+export function ChatSelectHeader() {
+  return (
+    <div className="mb-6">
+      <div className="bg-card rounded-xl p-6 border shadow-sm">
+        <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
+          <div className="flex items-center space-x-3 flex-1 min-w-0">
+            <div className="w-10 h-10 flex-shrink-0 bg-primary rounded-lg flex items-center justify-center shadow-sm">
+              <MessageCircle className="w-5 h-5 text-primary-foreground" />
+            </div>
+            <div className="min-w-0">
+              <h1 className="text-xl font-bold text-foreground">
+                ¿En qué podemos ayudarte hoy?
+              </h1>
+              <p className="text-sm text-muted-foreground">
+                Elige el tipo de asistencia que necesitas
+              </p>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Disclaimer */}
+      <div className="mt-4 flex gap-3 rounded-lg border border-amber-300 bg-amber-50 p-4 dark:border-yellow-400 dark:bg-yellow-200/20">
+        <AlertCircle className="h-5 w-5 text-amber-700 dark:text-yellow-400 flex-shrink-0 mt-0.5" />
+        <div className="text-sm text-amber-900 dark:text-yellow-800">
+          <strong>Importante:</strong> Estos servicios son para orientación inicial y no reemplazan
+          la atención de profesionales de la salud. En caso de emergencia, contacta servicios médicos
+          de inmediato.
+        </div>
+      </div>
+    </div>
+  )
+}

+ 80 - 0
src/components/chatbot/CrisisAlertBanner.tsx

@@ -0,0 +1,80 @@
+import { AlertTriangle, Phone, X } from "lucide-react";
+import { Button } from "@/components/ui/button";
+
+interface CrisisAlertBannerProps {
+  onDismiss: () => void;
+}
+
+export const CrisisAlertBanner = ({ onDismiss }: CrisisAlertBannerProps) => {
+  return (
+    <div className="mx-6 mt-4 mb-2 p-5 bg-red-50 dark:bg-red-900/20 border-2 border-red-300 dark:border-red-800 rounded-lg shadow-lg">
+      <div className="flex gap-4">
+        <div className="flex-shrink-0">
+          <div className="w-12 h-12 bg-red-100 dark:bg-red-900 rounded-full flex items-center justify-center">
+            <AlertTriangle className="h-7 w-7 text-red-600 dark:text-red-400 animate-pulse" />
+          </div>
+        </div>
+        
+        <div className="flex-1">
+          <h3 className="font-bold text-red-900 dark:text-red-100 text-lg mb-3 flex items-center gap-2">
+            <Phone className="h-5 w-5" />
+            Necesitas Ayuda Inmediata
+          </h3>
+          
+          <div className="text-sm text-red-800 dark:text-red-200 space-y-3">
+            <p className="font-semibold">
+              Por favor, contacta de inmediato con los siguientes servicios de emergencia:
+            </p>
+            
+            <div className="bg-white dark:bg-red-950/50 rounded-lg p-4 space-y-2">
+              <div className="flex items-center gap-3">
+                <Phone className="h-4 w-4 text-red-600 dark:text-red-400" />
+                <div>
+                  <span className="font-bold">Cruz Roja:</span> 
+                  <a href="tel:132" className="ml-2 text-red-700 dark:text-red-300 underline font-semibold text-lg">132</a>
+                </div>
+              </div>
+              
+              <div className="flex items-center gap-3">
+                <Phone className="h-4 w-4 text-red-600 dark:text-red-400" />
+                <div>
+                  <span className="font-bold">Policía Nacional:</span> 
+                  <a href="tel:911" className="ml-2 text-red-700 dark:text-red-300 underline font-semibold text-lg">911</a>
+                </div>
+              </div>
+              
+              <div className="flex items-center gap-3">
+                <Phone className="h-4 w-4 text-red-600 dark:text-red-400" />
+                <div>
+                  <span className="font-bold">Línea de Vida (prevención):</span> 
+                  <a href="tel:01-800-273-8255" className="ml-2 text-red-700 dark:text-red-300 underline font-semibold">01-800-273-8255</a>
+                </div>
+              </div>
+            </div>
+
+            <div className="bg-red-100 dark:bg-red-900/40 rounded-lg p-3 mt-3">
+              <p className="font-semibold mb-2">También puedes:</p>
+              <ul className="list-disc list-inside space-y-1 ml-2">
+                <li>Acudir al servicio de salud de tu universidad</li>
+                <li>Ir al hospital o centro de salud más cercano</li>
+                <li>Contactar a un familiar o amigo de confianza</li>
+              </ul>
+            </div>
+
+            <p className="font-bold text-base mt-4 text-red-900 dark:text-red-100">
+              🫂 No estás solo. Hay personas capacitadas esperando para ayudarte.
+            </p>
+          </div>
+        </div>
+        
+        <button
+          onClick={onDismiss}
+          className="flex-shrink-0 text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200"
+          aria-label="Cerrar alerta"
+        >
+          <X className="h-5 w-5" />
+        </button>
+      </div>
+    </div>
+  );
+};

+ 41 - 0
src/components/chatbot/PsychologicalDisclaimer.tsx

@@ -0,0 +1,41 @@
+import { AlertCircle, X } from "lucide-react";
+import { Button } from "@/components/ui/button";
+
+interface PsychologicalDisclaimerProps {
+  onDismiss: () => void;
+}
+
+export const PsychologicalDisclaimer = ({ onDismiss }: PsychologicalDisclaimerProps) => {
+  return (
+    <div className="mx-6 mt-4 mb-2 p-4 bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800 rounded-lg">
+      <div className="flex gap-3">
+        <AlertCircle className="h-5 w-5 text-purple-600 dark:text-purple-400 flex-shrink-0 mt-0.5" />
+        <div className="flex-1">
+          <h3 className="font-semibold text-purple-900 dark:text-purple-900 mb-2">
+            Bienvenido al Espacio de Apoyo Psicológico
+          </h3>
+          <div className="text-sm text-purple-800 dark:text-purple-800 space-y-2">
+            <p>
+              Este es un espacio seguro para hablar sobre tus emociones y pensamientos. Sin embargo, es importante que sepas:
+            </p>
+            <ul className="list-disc list-inside space-y-1 ml-2">
+              <li>Esta conversación no reemplaza ayuda psicológica profesional</li>
+            </ul>
+            <p className="font-medium mt-3">
+              Si tienes pensamientos de hacerte daño o necesitas ayuda urgente, 
+              contacta de inmediato: Cruz Roja (132), Policía (911), o acude al servicio 
+              de salud de tu universidad.
+            </p>
+          </div>
+        </div>
+        <button
+          onClick={onDismiss}
+          className="text-purple-600 dark:text-purple-400 hover:text-purple-800 dark:hover:text-purple-200 flex-shrink-0"
+          aria-label="Cerrar"
+        >
+          <X className="h-5 w-5" />
+        </button>
+      </div>
+    </div>
+  );
+};

+ 3 - 3
src/components/chatbot/ResetConfirmationModal.tsx

@@ -42,9 +42,9 @@ export const ResetConfirmationModal = ({
             ¿Estás seguro de que quieres iniciar una nueva consulta?
           </p>
           <div className="space-y-2 text-sm text-muted-foreground">
-            <p>Se guardará un reporte con la conversación actual</p>
-            <p>⚠️ Se perderán las recomendaciones pendientes</p>
-            <p>🔄 Se iniciará una consulta completamente nueva</p>
+            <p>Se guardará un reporte con la conversación actual</p>
+            <p>Se perderán las recomendaciones pendientes</p>
+            <p>Se iniciará una consulta completamente nueva</p>
           </div>
         </div>
         <div className="mt-6 flex space-x-2">

+ 23 - 7
src/components/chatbot/WelcomeMessage.tsx

@@ -1,21 +1,37 @@
-import { Bot } from "lucide-react";
+import { Bot, Heart } from "lucide-react";
 
 interface WelcomeMessageProps {
   maxMessages: number;
+  chatType: "medical" | "psychological";
 }
 
-export const WelcomeMessage = ({ maxMessages }: WelcomeMessageProps) => {
+export const WelcomeMessage = ({ maxMessages, chatType }: WelcomeMessageProps) => {
+  const isMedical = chatType === "medical";
+  
   return (
     <div className="text-center py-8">
-      <div className="w-16 h-16 bg-primary/10 rounded-full flex items-center justify-center mx-auto mb-4">
-        <Bot className="w-8 h-8 text-primary" />
+      <div className={`w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4 ${
+        isMedical 
+          ? "bg-primary/10" 
+          : "bg-purple-100 dark:bg-purple-700/30"
+      }`}>
+        {isMedical ? (
+          <Bot className="w-8 h-8 text-primary" />
+        ) : (
+          <Heart className="w-8 h-8 text-purple-600 dark:text-purple-600" />
+        )}
       </div>
       <h3 className="text-lg font-semibold text-foreground mb-2">
-        ¡Hola! Soy tu asistente médico virtual
+        {isMedical 
+          ? "¡Hola! Soy tu asistente médico virtual"
+          : "¡Bienvenido/a! Estoy aquí para escucharte"
+        }
       </h3>
       <p className="text-muted-foreground max-w-md mx-auto">
-        Puedes hacer hasta {maxMessages} consultas. Después se generará
-        automáticamente un reporte médico que se guardará en tu historial.
+        {isMedical 
+          ? `Puedes hacer hasta ${maxMessages} consultas. Después se generará automáticamente un reporte médico que se guardará en tu historial.`
+          : "Este es un espacio seguro para hablar sobre tus emociones y pensamientos. Tómate el tiempo que necesites, no hay prisa."
+        }
       </p>
     </div>
   );

+ 2 - 1
src/components/chatbot/types.ts

@@ -17,7 +17,8 @@ export interface SuggestedPrompt {
 
 export interface ChatResponse {
   response: string;
-  medicalAlert: MedicalAlert;
+  medicalAlert?: MedicalAlert;
+  crisisDetected?: boolean;
   suggestions: SuggestedPrompt[];
 }
 

+ 1 - 1
src/components/daily-log/DailyLogCalendar.tsx

@@ -72,7 +72,7 @@ export function DailyLogCalendar({ logs, onDateClick }: DailyLogCalendarProps) {
     const dateString = date.toISOString().split("T")[0]
     const foundLog = logs.find((log) => {
       // Convertir la fecha del log a string YYYY-MM-DD
-      const logDate = log.date as any // Prisma puede devolver Date o string
+      const logDate = log.date as Date | string // Prisma puede devolver Date o string
       let logDateString: string
       if (logDate instanceof Date) {
         logDateString = logDate.toISOString().split("T")[0]

+ 2 - 1
src/components/daily-log/DailyLogHeader.tsx

@@ -3,11 +3,12 @@
 import { Calendar, Filter } from "lucide-react"
 import { Button } from "@/components/ui/button"
 import { ExportButton } from "./ExportButton"
+import { DateRangePreset } from "./DateRangeSelector"
 
 interface DailyLogHeaderProps {
   logsCount: number
   onToggleFilters: () => void
-  onExport: (preset: any) => void
+  onExport: (preset: DateRangePreset | { startDate: Date; endDate: Date }) => void
   showFilters: boolean
 }
 

+ 1 - 1
src/components/dashboard/QuickActions.tsx

@@ -79,7 +79,7 @@ export default function QuickActions({ isAdmin, isDoctor, isPatient }: QuickActi
               icon={MessageSquare}
               title="Chat Médico"
               description="Consulta con el asistente virtual"
-              href="/chat"
+              href="/chat/select"
               iconBg="bg-blue-100 dark:bg-blue-400/20"
               iconColor="text-blue-600 dark:text-blue-400"
             />

+ 3 - 2
src/components/landing/HeroSection.tsx

@@ -37,8 +37,9 @@ export default function HeroSection() {
             </h1>
 
             <p className="text-xl text-muted-foreground mb-8 leading-relaxed">
-              Ani Assistant utiliza inteligencia artificial avanzada para
-              brindarte evaluaciones médicas preliminares, agendar citas con
+              Ani Assistant es tu mejor compañero en todo momento.
+              Utiliza inteligencia artificial avanzada para brindarte
+              evaluaciones médicas preliminares, agendar citas con
               especialistas y realizar seguimiento personalizado de tu salud.
             </p>
 

+ 49 - 15
src/hooks/useChat.ts

@@ -5,7 +5,11 @@ import { Message, ChatState, ChatResponse, SuggestedPrompt, MedicalAlert } from
 
 export const MAX_MESSAGES = 5;
 
-export const useChat = () => {
+interface UseChatProps {
+  chatType: "medical" | "psychological";
+}
+
+export const useChat = ({ chatType }: UseChatProps) => {
   const [messages, setMessages] = useState<Message[]>([]);
   const [messageCount, setMessageCount] = useState(0);
   const [isLimitReached, setIsLimitReached] = useState(false);
@@ -22,13 +26,17 @@ export const useChat = () => {
   const [medicalAlertDetected, setMedicalAlertDetected] = useState<MedicalAlert | null>(null);
   const [isSchedulingFromAlert, setIsSchedulingFromAlert] = useState(false);
   const [showMedicalAlertBanner, setShowMedicalAlertBanner] = useState(false);
+  const [crisisDetected, setCrisisDetected] = useState(false);
+  const [showCrisisBanner, setShowCrisisBanner] = useState(false);
 
-  const remainingMessages = Math.max(0, MAX_MESSAGES - messageCount);
-  const isLastMessage = remainingMessages === 1;
+  const isPsychological = chatType === "psychological";
+  const remainingMessages = isPsychological ? Infinity : Math.max(0, MAX_MESSAGES - messageCount);
+  const isLastMessage = !isPsychological && remainingMessages === 1;
 
   // Cargar estado desde localStorage al iniciar
   useEffect(() => {
-    const savedState = localStorage.getItem("chatState");
+    const storageKey = `chatState_${chatType}`;
+    const savedState = localStorage.getItem(storageKey);
     if (savedState) {
       try {
         const parsed: ChatState = JSON.parse(savedState);
@@ -61,10 +69,11 @@ export const useChat = () => {
         setShowSuggestions(true);
       }
     }
-  }, []);
+  }, [chatType]);
 
   // Guardar estado en localStorage cuando cambie
   useEffect(() => {
+    const storageKey = `chatState_${chatType}`;
     const stateToSave: ChatState = {
       messages,
       messageCount,
@@ -72,8 +81,8 @@ export const useChat = () => {
       reportGenerated,
       currentSuggestions,
     };
-    localStorage.setItem("chatState", JSON.stringify(stateToSave));
-  }, [messages, messageCount, isLimitReached, reportGenerated, currentSuggestions]);
+    localStorage.setItem(storageKey, JSON.stringify(stateToSave));
+  }, [messages, messageCount, isLimitReached, reportGenerated, currentSuggestions, chatType]);
 
   // Función para generar reporte usando useCallback para evitar re-renderizados
   const generateMedicalReport = useCallback(async () => {
@@ -99,6 +108,7 @@ export const useChat = () => {
         body: JSON.stringify({
           content: report,
           messages: messages,
+          chatType,
         }),
       });
 
@@ -115,9 +125,10 @@ export const useChat = () => {
     }
   }, [messages, isGeneratingReport, reportGenerated]);
 
-  // Efecto para generar reporte cuando se alcanza el límite
+  // Efecto para generar reporte cuando se alcanza el límite (solo para chat médico)
   useEffect(() => {
     if (
+      !isPsychological &&
       messageCount >= MAX_MESSAGES &&
       !isLimitReached &&
       !reportGenerated &&
@@ -137,6 +148,7 @@ export const useChat = () => {
       }
     }
   }, [
+    isPsychological,
     messageCount,
     isLimitReached,
     reportGenerated,
@@ -179,6 +191,7 @@ export const useChat = () => {
         body: JSON.stringify({
           message: messageToSend,
           messages: [...messages, userMessage],
+          chatType,
         }),
       });
       
@@ -205,7 +218,7 @@ export const useChat = () => {
         const decoder = new TextDecoder();
         let buffer = "";
         let fullContent = "";
-        let metadata: { medicalAlert?: string; suggestions?: SuggestedPrompt[] } = {};
+        let metadata: { medicalAlert?: string; suggestions?: SuggestedPrompt[]; crisisDetected?: boolean } = {};
 
         if (!reader) {
           throw new Error("No se pudo obtener el reader del stream");
@@ -253,7 +266,8 @@ export const useChat = () => {
                     // Guardar metadatos
                     metadata = {
                       medicalAlert: parsed.medicalAlert,
-                      suggestions: parsed.suggestions
+                      suggestions: parsed.suggestions,
+                      crisisDetected: parsed.crisisDetected
                     };
                     console.log("📦 [CHAT] Metadatos recibidos:", metadata);
                   }
@@ -270,6 +284,13 @@ export const useChat = () => {
         const totalTime = Date.now() - startTime;
         console.log("✅ [CHAT] Respuesta completa procesada en:", totalTime, "ms");
 
+        // Detectar crisis en chat psicológico
+        if (isPsychological && metadata.crisisDetected) {
+          console.log("🚨 [CHAT] Crisis detectada en chat psicológico");
+          setCrisisDetected(true);
+          setShowCrisisBanner(true);
+        }
+
         // Detectar alerta médica
         if (metadata.medicalAlert && metadata.medicalAlert !== "NO_AGENDAR" && !medicalAlertDetected) {
           console.log("🚨 [CHAT] Alerta médica detectada:", metadata.medicalAlert);
@@ -292,7 +313,8 @@ export const useChat = () => {
         );
 
         // Mostrar sugerencias dinámicas después del streaming si no es el último mensaje
-        if (messageCount + 1 < MAX_MESSAGES && metadata.suggestions) {
+        // En chat psicológico no mostrar sugerencias dinámicas
+        if (!isPsychological && messageCount + 1 < MAX_MESSAGES && metadata.suggestions) {
           setCurrentSuggestions(metadata.suggestions);
           setShowDynamicSuggestions(true);
           setInputDisabledForSuggestions(true);
@@ -334,7 +356,8 @@ export const useChat = () => {
         setMessages((prev) => [...prev, fallbackMessage]);
         
         // Mostrar sugerencias dinámicas si no es el último mensaje
-        if (messageCount + 1 < MAX_MESSAGES) {
+        // En chat psicológico no mostrar sugerencias dinámicas
+        if (!isPsychological && messageCount + 1 < MAX_MESSAGES) {
           setCurrentSuggestions(fallbackMessage.suggestions || []);
           setShowDynamicSuggestions(true);
           setInputDisabledForSuggestions(true);
@@ -383,7 +406,8 @@ export const useChat = () => {
       setMessages((prev) => [...prev, fallbackMessage]);
       
       // Mostrar sugerencias dinámicas si no es el último mensaje
-      if (messageCount + 1 < MAX_MESSAGES) {
+      // En chat psicológico no mostrar sugerencias dinámicas
+      if (!isPsychological && messageCount + 1 < MAX_MESSAGES) {
         setCurrentSuggestions(fallbackMessage.suggestions || []);
         setShowDynamicSuggestions(true);
         setInputDisabledForSuggestions(true);
@@ -416,8 +440,11 @@ export const useChat = () => {
     setMedicalAlertDetected(null);
     setShowMedicalAlertBanner(false);
     setIsSchedulingFromAlert(false);
+    setCrisisDetected(false);
+    setShowCrisisBanner(false);
     // Limpiar localStorage
-    localStorage.removeItem("chatState");
+    const storageKey = `chatState_${chatType}`;
+    localStorage.removeItem(storageKey);
     notifications.chat.newConsultation();
   };
 
@@ -501,6 +528,10 @@ export const useChat = () => {
     setShowMedicalAlertBanner(false);
   };
 
+  const dismissCrisisBanner = () => {
+    setShowCrisisBanner(false);
+  };
+
   return {
     messages,
     messageCount,
@@ -530,5 +561,8 @@ export const useChat = () => {
     isSchedulingFromAlert,
     handleScheduleFromAlert,
     dismissMedicalAlertBanner,
+    crisisDetected,
+    showCrisisBanner,
+    dismissCrisisBanner,
   };
-};
+};;

+ 173 - 0
src/lib/chat-prompts.ts

@@ -0,0 +1,173 @@
+// System prompts para diferentes tipos de chat
+
+export const MEDICAL_SYSTEM_PROMPT = `Eres un asistente médico virtual llamado Ani Assistant especializado EXCLUSIVAMENTE en temas de salud y medicina. Tu función es:
+
+**TEMAS QUE SÍ PUEDES RESPONDER:**
+1. Información general sobre salud y bienestar
+2. Preguntas sobre síntomas comunes y su posible significado
+3. Consejos de estilo de vida saludable (ejercicio, nutrición, sueño)
+4. Información sobre medicamentos comunes y sus efectos
+5. Prevención de enfermedades
+6. Primeros auxilios básicos
+7. Salud mental y manejo del estrés
+8. Cuidados durante el embarazo
+9. Salud infantil básica
+10. Información sobre vacunas
+
+**TEMAS QUE NO PUEDES RESPONDER:**
+- Matemáticas, física, química (excepto si está directamente relacionado con medicina)
+- Historia, geografía, literatura
+- Tecnología, programación, ingeniería
+- Deportes (excepto ejercicio para la salud)
+- Entretenimiento, música, arte
+- Política, economía, finanzas
+- Cualquier tema que NO esté relacionado con salud o medicina
+
+**INSTRUCCIONES IMPORTANTES:**
+1. Si te preguntan algo que NO está relacionado con salud o medicina, responde: "Lo siento, soy un asistente médico virtual especializado únicamente en temas de salud. Solo puedo ayudarte con consultas médicas, síntomas, bienestar y cuidados de la salud. ¿Hay algún tema de salud en el que pueda asistirte?"
+2. NUNCA respondas preguntas sobre matemáticas, ciencias exactas, tecnología u otros temas no médicos
+3. Siempre aclara que NO puedes hacer diagnósticos médicos definitivos
+4. Recomienda consultar con un profesional médico para casos serios
+5. Sé empático y profesional en tus respuestas
+6. Responde siempre en español
+7. Mantén el enfoque estrictamente en salud y medicina
+
+**FORMATO DE RESPUESTA REQUERIDO:**
+Debes responder SIEMPRE en formato JSON válido. Tu respuesta DEBE ser ÚNICAMENTE el objeto JSON, sin texto adicional antes o después. Ejemplo:
+
+{
+  "response": "Tu respuesta médica aquí en formato markdown",
+  "medicalAlert": "NO_AGENDAR",
+  "suggestions": [
+    {
+      "title": "Título corto",
+      "emoji": "🩺",
+      "prompt": "Pregunta sugerida relacionada"
+    }
+  ]
+}
+
+**IMPORTANTE:**
+- NO agregues texto explicativo antes o después del JSON
+- NO uses comillas triples o bloques de código markdown
+- NO envuelvas el JSON en bloques de código
+- SOLO devuelve el objeto JSON válido sin formato adicional
+- Asegúrate de que todas las comillas estén correctamente escapadas
+- Tu respuesta debe comenzar directamente con { y terminar con }
+
+**SISTEMA DE ALERTAS MÉDICAS:**
+- NO_AGENDAR: Síntomas leves, información general, prevención
+- RECOMENDADO: Síntomas que ameritan consulta médica pero no urgente
+- URGENTE: Síntomas graves que requieren atención médica inmediata
+
+**SUGERENCIAS:**
+Incluye EXACTAMENTE 3 sugerencias específicas y útiles basadas en tu respuesta. Las sugerencias deben:
+1. Ser preguntas específicas y detalladas (no vagas como "Más información")
+2. Estar directamente relacionadas con el tema de tu respuesta
+3. Ayudar al usuario a profundizar en aspectos importantes del tema
+4. Usar emojis relevantes y descriptivos
+5. Ser preguntas que un paciente realmente haría
+
+Ejemplos de BUENAS sugerencias:
+- "¿Qué medicamentos de venta libre puedo tomar para el dolor de cabeza?"
+- "¿Cuándo debo preocuparme si la fiebre no baja?"
+- "¿Qué ejercicios son seguros durante el embarazo?"
+
+Ejemplos de MALAS sugerencias (evitar):
+- "Más información"
+- "¿Puedes darme más detalles?"
+- "Prevención"
+
+RECUERDA: Eres un asistente médico virtual, NO un asistente general. Tu especialidad es la salud y medicina únicamente.`;
+
+export const PSYCHOLOGICAL_SYSTEM_PROMPT = `Eres un asistente de apoyo psicológico llamado Ani Assistant. Tu función es proporcionar un espacio seguro y empático para que los estudiantes puedan hablar sobre sus emociones, pensamientos y bienestar mental.
+
+**TU ROL:**
+- Escuchar activamente y con empatía
+- Proporcionar apoyo emocional básico
+- Ayudar a normalizar experiencias emocionales comunes
+- Ofrecer estrategias de afrontamiento saludables
+- Validar los sentimientos del usuario
+
+**IMPORTANTE - NO ERES TERAPEUTA:**
+- NO puedes diagnosticar trastornos mentales
+- NO reemplazas terapia profesional
+- NO puedes prescribir tratamientos
+- Reconoce tus limitaciones claramente
+
+**TEMAS QUE PUEDES ABORDAR:**
+- Estrés académico y ansiedad
+- Adaptación a la vida universitaria
+- Problemas de sueño relacionados con estrés
+- Sentimientos de soledad o aislamiento
+- Problemas de autoestima
+- Dificultades en relaciones interpersonales
+- Manejo de emociones (tristeza, ira, frustración)
+- Técnicas de relajación y mindfulness
+- Balance vida-estudio
+- Procrastinación y motivación
+
+**DETECCIÓN DE CRISIS - MUY IMPORTANTE:**
+Si el usuario menciona ideas de:
+- Suicidio
+- Autolesión
+- Violencia hacia sí mismo o otros
+- Abuso (físico, sexual, emocional)
+
+DEBES responder inmediatamente con:
+"Me preocupa mucho lo que me estás contando. Esta situación requiere atención profesional inmediata. Por favor, contacta urgentemente a:
+
+🚨 **Líneas de Crisis 24/7:**
+- Cruz Roja: 132
+- Policía Nacional: 911
+- Línea de prevención del suicidio: (Tu país específico)
+
+También puedes acudir al servicio de salud de tu universidad o al hospital más cercano. Tu bienestar es lo más importante."
+
+**FORMATO DE RESPUESTA:**
+Debes responder SIEMPRE en formato JSON válido. Tu respuesta DEBE ser ÚNICAMENTE el objeto JSON. Ejemplo:
+
+{
+  "response": "Tu respuesta empática aquí en formato markdown",
+  "crisisDetected": false,
+  "suggestions": [
+    {
+      "title": "Título corto",
+      "emoji": "💭",
+      "prompt": "Pregunta o tema relacionado"
+    }
+  ]
+}
+
+**IMPORTANTE:**
+- NO agregues texto antes o después del JSON
+- Tu respuesta debe comenzar con { y terminar con }
+- Si detectas crisis, marca "crisisDetected": true
+
+**ESTILO DE COMUNICACIÓN:**
+- Usa un tono cálido, empático y no juzgador
+- Valida las emociones del usuario
+- Haz preguntas abiertas para fomentar la reflexión
+- Ofrece perspectivas constructivas
+- Sé genuino y auténtico
+- Evita consejos no solicitados
+- Responde siempre en español
+
+**SUGERENCIAS:**
+Incluye 3 sugerencias que:
+1. Inviten a profundizar en el tema
+2. Ofrezcan ángulos diferentes de exploración
+3. Sean sensibles y apropiadas al contexto emocional
+4. Usen emojis relacionados con bienestar: 💭💚🌱✨🤗
+
+Ejemplos:
+- "¿Hay algo específico que desencadena estos sentimientos?"
+- "¿Qué estrategias has probado antes que te hayan ayudado?"
+- "¿Cómo te gustaría sentirte idealmente en esta situación?"
+
+**RECORDATORIO CONSTANTE:**
+Siempre recuerda que eres un recurso de apoyo inicial, no un reemplazo de ayuda profesional. Anima a buscar apoyo profesional cuando sea apropiado.`;
+
+export function getSystemPrompt(chatType: "medical" | "psychological"): string {
+  return chatType === "psychological" ? PSYCHOLOGICAL_SYSTEM_PROMPT : MEDICAL_SYSTEM_PROMPT;
+}