From 6db7c1c0232369159afcfe9072341fe1d7f6b7f5 Mon Sep 17 00:00:00 2001 From: "Roberto.silva" Date: Tue, 24 Feb 2026 16:09:35 -0600 Subject: [PATCH] Que te permita renombrar los chats fix #96 --- src/data/api/ai.api.ts | 23 ++- src/data/hooks/useAI.ts | 19 ++- src/routes/planes/$planId/_detalle/iaplan.tsx | 161 +++++++++++------- 3 files changed, 128 insertions(+), 75 deletions(-) diff --git a/src/data/api/ai.api.ts b/src/data/api/ai.api.ts index ca0c080..16632f7 100644 --- a/src/data/api/ai.api.ts +++ b/src/data/api/ai.api.ts @@ -111,12 +111,6 @@ export async function create_conversation(planId: string) { ) if (error) throw error - - // LOG de depuración: Mira qué estructura trae 'data' - console.log('Respuesta creación conv:', data) - - // Si data es { id: "..." }, devolvemos data. - // Si data viene envuelto, asegúrate de retornar el objeto con el id. return data } @@ -181,3 +175,20 @@ export async function getConversationByPlan(planId: string) { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return data ?? [] } + +export async function update_conversation_title( + conversacionId: string, + nuevoTitulo: string, +) { + const supabase = supabaseBrowser() + + const { data, error } = await supabase + .from('conversaciones_plan') + .update({ nombre: nuevoTitulo }) // Asegúrate que la columna se llame 'title' o 'nombre' + .eq('id', conversacionId) + .select() + .single() + + if (error) throw error + return data +} diff --git a/src/data/hooks/useAI.ts b/src/data/hooks/useAI.ts index 5338a52..24231a1 100644 --- a/src/data/hooks/useAI.ts +++ b/src/data/hooks/useAI.ts @@ -10,6 +10,7 @@ import { getConversationByPlan, library_search, update_conversation_status, + update_conversation_title, } from '../api/ai.api' // eslint-disable-next-line node/prefer-node-protocol @@ -35,8 +36,6 @@ export function useAIPlanChat() { // CAMBIO AQUÍ: Accedemos a la estructura correcta según tu consola currentId = response.conversation_plan.id - - console.log('Nuevo ID extraído:', currentId) } // 2. Ahora enviamos el mensaje con el ID garantizado @@ -56,11 +55,8 @@ export function useChatHistory(conversacionId?: string) { return useQuery({ queryKey: ['chat-history', conversacionId], queryFn: async () => { - console.log('--- EJECUTANDO QUERY FN ---') - console.log('ID RECIBIDO:', conversacionId) return get_chat_history(conversacionId!) }, - // Simplificamos el enabled para probar enabled: Boolean(conversacionId), }) } @@ -102,3 +98,16 @@ export function useAISubjectChat() { export function useLibrarySearch() { return useMutation({ mutationFn: library_search }) } + +export function useUpdateConversationTitle() { + const qc = useQueryClient() + + return useMutation({ + mutationFn: ({ id, nombre }: { id: string; nombre: string }) => + update_conversation_title(id, nombre), + onSuccess: (_, variables) => { + // Invalidamos para que la lista de chats se refresque + qc.invalidateQueries({ queryKey: ['conversation-by-plan'] }) + }, + }) +} diff --git a/src/routes/planes/$planId/_detalle/iaplan.tsx b/src/routes/planes/$planId/_detalle/iaplan.tsx index a8d6049..8100208 100644 --- a/src/routes/planes/$planId/_detalle/iaplan.tsx +++ b/src/routes/planes/$planId/_detalle/iaplan.tsx @@ -30,6 +30,7 @@ import { useChatHistory, useConversationByPlan, useUpdateConversationStatus, + useUpdateConversationTitle, } from '@/data' import { usePlan } from '@/data/hooks/usePlans' @@ -104,8 +105,21 @@ function RouteComponent() { const [pendingSuggestion, setPendingSuggestion] = useState(null) const queryClient = useQueryClient() const scrollRef = useRef(null) - const [showArchived, setShowArchived] = useState(false) + const [editingChatId, setEditingChatId] = useState(null) + const editableRef = useRef(null) + const { mutate: updateTitleMutation } = useUpdateConversationTitle() + + const availableFields = useMemo(() => { + if (!data?.estructuras_plan?.definicion?.properties) return [] + return Object.entries(data.estructuras_plan.definicion.properties).map( + ([key, value]) => ({ + key, + label: value.title, + value: String(value.description || ''), + }), + ) + }, [data]) useEffect(() => { // 1. Si no hay ID o está cargando el historial, no hacemos nada @@ -153,9 +167,6 @@ function RouteComponent() { type: suggestions.length > 0 ? 'improvement-card' : 'text', } }) - - // Solo actualizamos si no estamos esperando la respuesta de un POST - // para evitar saltos visuales if (!isLoading) { setMessages(flattened.reverse()) } @@ -175,6 +186,20 @@ function RouteComponent() { } }, [lastConversation, activeChatId]) + useEffect(() => { + const state = routerState.location.state as any + if (!state?.campo_edit || availableFields.length === 0) return + const field = availableFields.find( + (f) => + f.value === state.campo_edit.label || f.key === state.campo_edit.clave, + ) + if (!field) return + setSelectedFields([field]) + setInput((prev) => + injectFieldsIntoInput(prev || 'Mejora este campo:', [field]), + ) + }, [availableFields]) + const createNewChat = () => { setActiveChatId(undefined) // Al ser undefined, el próximo handleSend creará uno nuevo setMessages([ @@ -224,37 +249,6 @@ function RouteComponent() { ) } - // 1. Transformar datos de la API para el menú de selección - const availableFields = useMemo(() => { - if (!data?.estructuras_plan?.definicion?.properties) return [] - return Object.entries(data.estructuras_plan.definicion.properties).map( - ([key, value]) => ({ - key, - label: value.title, - value: String(value.description || ''), - }), - ) - }, [data]) - - // 2. Manejar el estado inicial si viene de "Datos Generales" - useEffect(() => { - const state = routerState.location.state as any - if (!state?.campo_edit || availableFields.length === 0) return - - const field = availableFields.find( - (f) => - f.value === state.campo_edit.label || f.key === state.campo_edit.clave, - ) - - if (!field) return - - setSelectedFields([field]) - setInput((prev) => - injectFieldsIntoInput(prev || 'Mejora este campo:', [field]), - ) - }, [availableFields]) - - // 3. Lógica para el disparador ":" const handleInputChange = (e: React.ChangeEvent) => { const val = e.target.value setInput(val) @@ -290,17 +284,11 @@ function RouteComponent() { }) setInput((prev) => { - // 1. Eliminamos TODOS los ":" que existan en el texto actual - // 2. Quitamos espacios en blanco extra al final const cleanPrev = prev.replace(/:/g, '').trim() - // 3. Si el input resultante está vacío, solo ponemos la frase if (cleanPrev === '') { return `${field.label} ` } - - // 4. Si ya había algo, lo concatenamos con un espacio - // Usamos un espacio simple al final para que el usuario pueda seguir escribiendo return `${cleanPrev} ${field.label} ` }) @@ -329,8 +317,6 @@ function RouteComponent() { setMessages((prev) => [...prev, userMsg]) setInput('') - // setSelectedFields([]) - try { const payload: any = { planId: planId, @@ -454,7 +440,6 @@ function RouteComponent() {
{!showArchived ? ( - // --- LISTA DE CHATS ACTIVOS --- activeChats.map((chat) => (
- {/* Usamos el primer mensaje o un título por defecto */} - - {chat.title || `Chat ${chat.creado_en.split('T')[0]}`} - - + {chat.nombre || `Chat ${chat.creado_en.split('T')[0]}`} + + + {/* ACCIONES */} +
+ + +
)) ) : ( - // --- LISTA DE CHATS ARCHIVADOS --- + /* ... Resto del código de archivados (sin cambios) ... */

Archivados @@ -492,25 +533,17 @@ function RouteComponent() { > - {chat.title || + {chat.nombre || `Archivado ${chat.creado_en.split('T')[0]}`}

))} - {archivedChats.length === 0 && ( -
-

- No hay archivados -

-
- )}
)}