) : (
<>
+ {/* CLAVE 2: 'truncate' y 'min-w-0' en el span para que ceda ante los botones */}
-
{
- e.stopPropagation()
- setEditingId(chat.id)
- setTempName(chat.nombre || chat.titulo || '')
- }}
- className="p-1 hover:text-teal-600"
- >
-
-
+ {/* CLAVE 3: 'shrink-0' asegura que los botones NUNCA desaparezcan */}
+
+
+
+
+ {
+ e.stopPropagation()
+ setEditingId(chat.id)
+ setTempName(chat.nombre || chat.titulo || '')
+ }}
+ className="rounded-md p-1 transition-colors hover:bg-slate-200 hover:text-teal-600"
+ >
+
+
+
+
+ Editar nombre
+
+
- {/* Botón para Archivar/Desarchivar dinámico */}
- {
- e.stopPropagation()
- // Si el estado actual es ACTIVA, mandamos ARCHIVADA. Si no, viceversa.
- const nuevoEstado =
- chat.estado === 'ACTIVA' ? 'ARCHIVADA' : 'ACTIVA'
- updateStatus({ id: chat.id, estado: nuevoEstado })
- }}
- className={cn(
- 'p-1 transition-colors',
- chat.estado === 'ACTIVA'
- ? 'hover:text-red-500'
- : 'hover:text-teal-600',
- )}
- title={
- chat.estado === 'ACTIVA'
- ? 'Archivar chat'
- : 'Desarchivar chat'
- }
- >
- {chat.estado === 'ACTIVA' ? (
-
- ) : (
- /* Icono de Desarchivar */
-
- )}
-
+
+
+ {
+ e.stopPropagation()
+ const nuevoEstado =
+ chat.estado === 'ACTIVA'
+ ? 'ARCHIVADA'
+ : 'ACTIVA'
+ updateStatus({
+ id: chat.id,
+ estado: nuevoEstado,
+ })
+ }}
+ className={cn(
+ 'rounded-md p-1 transition-colors hover:bg-slate-200',
+ chat.estado === 'ACTIVA'
+ ? 'hover:text-red-500'
+ : 'hover:text-teal-600',
+ )}
+ >
+ {chat.estado === 'ACTIVA' ? (
+
+ ) : (
+
+ )}
+
+
+
+ {chat.estado === 'ACTIVA'
+ ? 'Archivar'
+ : 'Desarchivar'}
+
+
+
>
)}
diff --git a/src/routes/planes/$planId/_detalle/iaplan.tsx b/src/routes/planes/$planId/_detalle/iaplan.tsx
index a2e0ee8..e42398b 100644
--- a/src/routes/planes/$planId/_detalle/iaplan.tsx
+++ b/src/routes/planes/$planId/_detalle/iaplan.tsx
@@ -13,8 +13,8 @@ import {
X,
MessageSquarePlus,
Archive,
- RotateCcw,
Loader2,
+ Sparkles,
} from 'lucide-react'
import { useState, useEffect, useRef, useMemo } from 'react'
@@ -22,10 +22,17 @@ import type { UploadedFile } from '@/components/planes/wizard/PasoDetallesPanel/
import { ImprovementCard } from '@/components/planes/detalle/Ia/ImprovementCard'
import ReferenciasParaIA from '@/components/planes/wizard/PasoDetallesPanel/ReferenciasParaIA'
+import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { Drawer, DrawerContent } from '@/components/ui/drawer'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Textarea } from '@/components/ui/textarea'
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from '@/components/ui/tooltip'
import {
useAIPlanChat,
useConversationByPlan,
@@ -507,76 +514,99 @@ function RouteComponent() {
setActiveChatId(chat.id)}
- className={`group relative flex w-full cursor-pointer items-center gap-3 rounded-lg px-3 py-3 text-sm transition-colors ${
+ className={`group relative flex w-full items-center justify-between overflow-hidden rounded-lg px-3 py-3 text-sm transition-colors ${
activeChatId === chat.id
? 'bg-slate-100 font-medium text-slate-900'
: 'text-slate-600 hover:bg-slate-50'
}`}
>
-
+ {/* LADO IZQUIERDO: Icono + Texto con Tooltip */}
+
+
-
+
+
+ {/* Este contenedor es el que obliga al span a truncarse */}
+
+ {
+ e.stopPropagation()
+ setEditingChatId(chat.id)
+ }}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ e.currentTarget.blur()
+ }
+ if (e.key === 'Escape') {
+ setEditingChatId(null)
+ e.currentTarget.textContent =
+ chat.nombre || ''
+ }
+ }}
+ onBlur={(e) => {
+ if (editingChatId === chat.id) {
+ const newTitle =
+ e.currentTarget.textContent?.trim() || ''
+ if (newTitle && newTitle !== chat.nombre) {
+ updateTitleMutation({
+ id: chat.id,
+ nombre: newTitle,
+ })
+ }
+ setEditingChatId(null)
+ }
+ }}
+ >
+ {chat.nombre ||
+ `Chat ${chat.creado_en.split('T')[0]}`}
+
+
+
+
+ {/* Tooltip: Solo aparece si no estás editando y el texto es largo */}
+ {editingChatId !== chat.id && (
+
+ {chat.nombre || 'Conversación'}
+
+ )}
+
+
+
+
+ {/* LADO DERECHO: Acciones con shrink-0 para que no se muevan */}
+
{
- e.stopPropagation()
- setEditingChatId(chat.id)
- }}
- onKeyDown={(e) => {
- if (e.key === 'Enter') {
- e.preventDefault()
- const newTitle = e.currentTarget.textContent || ''
- updateTitleMutation(
- { id: chat.id, nombre: newTitle },
- {
- onSuccess: () => setEditingChatId(null),
- },
- )
- }
- if (e.key === 'Escape') {
- setEditingChatId(null)
-
- e.currentTarget.textContent = chat.nombre || ''
- }
- }}
- onBlur={(e) => {
- if (editingChatId === chat.id) {
- const newTitle = e.currentTarget.textContent || ''
- if (newTitle !== chat.nombre) {
- updateTitleMutation({ id: chat.id, nombre: newTitle })
- }
- setEditingChatId(null)
- }
- }}
- onClick={(e) => {
- if (editingChatId === chat.id) e.stopPropagation()
- }}
>
- {chat.nombre || `Chat ${chat.creado_en.split('T')[0]}`}
-
-
- {/* ACCIONES */}
-
{
e.stopPropagation()
setEditingChatId(chat.id)
- // Pequeño timeout para asegurar que el DOM se actualice antes de enfocar
setTimeout(() => editableRef.current?.focus(), 50)
}}
- className="p-1 text-slate-400 hover:text-teal-600"
+ className="rounded-md p-1 text-slate-400 transition-colors hover:text-teal-600"
>
archiveChat(e, chat.id)}
- className="p-1 text-slate-400 hover:text-amber-600"
+ className="rounded-md p-1 text-slate-400 transition-colors hover:text-amber-600"
>
@@ -584,24 +614,26 @@ function RouteComponent() {
))
) : (
- /* ... Resto del código de archivados (sin cambios) ... */
-
+ /* Sección de archivados */
+
Archivados
{archivedChats.map((chat) => (
-
-
- {chat.nombre ||
- `Archivado ${chat.creado_en.split('T')[0]}`}
-
+
+
+
+ {chat.nombre ||
+ `Archivado ${chat.creado_en.split('T')[0]}`}
+
+
unarchiveChat(e, chat.id)}
- className="absolute right-2 p-1 opacity-0 group-hover:opacity-100 hover:text-teal-600"
+ className="ml-2 shrink-0 rounded bg-slate-50/80 p-1 opacity-0 transition-opacity group-hover:opacity-100 hover:text-teal-600"
>
@@ -721,33 +753,24 @@ function RouteComponent() {
)
})}
- {(isSending || isSyncing) &&
- optimisticMessage &&
- !chatMessages.some(
- (m) => m.content === optimisticMessage,
- ) && (
-
-
- {optimisticMessage}
-
-
- )}
-
{(isSending || isSyncing) && (
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
- {isSyncing
- ? 'Actualizando historial...'
- : 'Esperando respuesta...'}
-
+
+ La IA está analizando tu solicitud...
+
)}