Chats de ia en segundo plano para asignaturas #160 #161
@@ -76,6 +76,17 @@ export function IAAsignaturaTab({
|
||||
const [isCreatingNewChat, setIsCreatingNewChat] = useState(false)
|
||||
const hasInitialSelected = useRef(false)
|
||||
|
||||
const isAiThinking = useMemo(() => {
|
||||
if (isSending) return true
|
||||
if (!rawMessages || rawMessages.length === 0) return false
|
||||
|
||||
// Verificamos si el último mensaje está en estado de procesamiento
|
||||
const lastMessage = rawMessages[rawMessages.length - 1]
|
||||
return (
|
||||
lastMessage.estado === 'PROCESANDO' || lastMessage.estado === 'PENDIENTE'
|
||||
)
|
||||
}, [isSending, rawMessages])
|
||||
|
||||
// --- AUTO-SCROLL ---
|
||||
useEffect(() => {
|
||||
const viewport = scrollRef.current?.querySelector(
|
||||
@@ -392,11 +403,23 @@ export function IAAsignaturaTab({
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{isSending && (
|
||||
<div className="flex animate-pulse gap-2 p-4">
|
||||
<div className="h-2 w-2 rounded-full bg-teal-400" />
|
||||
<div className="h-2 w-2 rounded-full bg-teal-400" />
|
||||
<div className="h-2 w-2 rounded-full bg-teal-400" />
|
||||
{isAiThinking && (
|
||||
<div className="animate-in fade-in flex flex-row items-start gap-3 duration-300">
|
||||
<Avatar className="h-8 w-8 shrink-0 border bg-teal-50">
|
||||
<AvatarFallback>
|
||||
<Sparkles
|
||||
size={14}
|
||||
className="animate-pulse text-teal-600"
|
||||
/>
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="rounded-2xl rounded-tl-none border bg-white p-4 shadow-sm">
|
||||
<div className="flex gap-1.5">
|
||||
<div className="h-2 w-2 animate-bounce rounded-full bg-teal-400 [animation-delay:-0.3s]" />
|
||||
<div className="h-2 w-2 animate-bounce rounded-full bg-teal-400 [animation-delay:-0.15s]" />
|
||||
<div className="h-2 w-2 animate-bounce rounded-full bg-teal-400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -301,7 +301,7 @@ export async function getConversationBySubject(subjectId: string) {
|
||||
export async function getMessagesBySubjectConversation(conversationId: string) {
|
||||
const supabase = supabaseBrowser()
|
||||
const { data, error } = await supabase
|
||||
.from('asignatura_mensajes_ia') // Tabla corregida
|
||||
.from('asignatura_mensajes_ia' as any)
|
||||
.select('*')
|
||||
.eq('conversacion_asignatura_id', conversationId)
|
||||
.order('fecha_creacion', { ascending: true })
|
||||
|
||||
@@ -243,15 +243,54 @@ export function useConversationBySubject(subjectId: string | null) {
|
||||
}
|
||||
|
||||
export function useMessagesBySubjectChat(conversationId: string | null) {
|
||||
return useQuery({
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ['subject-messages', conversationId],
|
||||
queryFn: () => {
|
||||
queryFn: async () => {
|
||||
if (!conversationId) throw new Error('Conversation ID is required')
|
||||
return getMessagesBySubjectConversation(conversationId)
|
||||
},
|
||||
enabled: !!conversationId,
|
||||
placeholderData: (previousData) => previousData,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!conversationId) return
|
||||
|
||||
const supabase = supabaseBrowser()
|
||||
|
||||
// Suscripción a cambios en la tabla específica para esta conversación
|
||||
const channel = supabase
|
||||
.channel(`subject_messages_${conversationId}`)
|
||||
.on(
|
||||
'postgres_changes',
|
||||
{
|
||||
event: 'UPDATE', // Solo nos interesan las actualizaciones (cuando pasa de PROCESANDO a COMPLETADO)
|
||||
schema: 'public',
|
||||
table: 'asignatura_mensajes_ia',
|
||||
filter: `conversacion_asignatura_id=eq.${conversationId}`,
|
||||
},
|
||||
(payload) => {
|
||||
// Si el mensaje se completó o dio error, invalidamos la caché para traer los datos nuevos
|
||||
if (
|
||||
payload.new.estado === 'COMPLETADO' ||
|
||||
payload.new.estado === 'ERROR'
|
||||
) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['subject-messages', conversationId],
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
.subscribe()
|
||||
|
||||
return () => {
|
||||
supabase.removeChannel(channel)
|
||||
}
|
||||
}, [conversationId, queryClient])
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
export function useUpdateSubjectRecommendation() {
|
||||
|
||||
Reference in New Issue
Block a user