diff --git a/src/components/asignaturas/detalle/IAAsignaturaTab.tsx b/src/components/asignaturas/detalle/IAAsignaturaTab.tsx index 4e087b8..45e5bd5 100644 --- a/src/components/asignaturas/detalle/IAAsignaturaTab.tsx +++ b/src/components/asignaturas/detalle/IAAsignaturaTab.tsx @@ -131,15 +131,45 @@ export function IAAsignaturaTab({ }, [todasConversaciones]) const availableFields = useMemo(() => { - if (!datosGenerales?.datos) return [] - const estructuraProps = - datosGenerales?.estructuras_asignatura?.definicion?.properties || {} - return Object.keys(datosGenerales.datos).map((key) => ({ - key, - label: - estructuraProps[key]?.title || key.replace(/_/g, ' ').toUpperCase(), - value: String(datosGenerales.datos[key] || ''), - })) + // 1. Obtenemos los campos dinámicos de la DB + const dynamicFields = datosGenerales?.datos + ? Object.keys(datosGenerales.datos).map((key) => { + const estructuraProps = + datosGenerales?.estructuras_asignatura?.definicion?.properties || {} + return { + key, + label: + estructuraProps[key]?.title || + key.replace(/_/g, ' ').toUpperCase(), + value: String(datosGenerales.datos[key] || ''), + } + }) + : [] + + // 2. Definimos tus campos manuales (hardcoded) + const hardcodedFields = [ + { + key: 'contenido_tematico', + label: 'Contenido temático', + value: '', // Puedes dejarlo vacío o buscarlo en datosGenerales si existiera + }, + { + key: 'criterios_de_evaluacion', + label: 'Criterios de evaluación', + value: '', + }, + ] + + // 3. Unimos ambos, filtrando duplicados por si acaso el backend ya los envía + const combined = [...dynamicFields] + + hardcodedFields.forEach((hf) => { + if (!combined.some((f) => f.key === hf.key)) { + combined.push(hf) + } + }) + + return combined }, [datosGenerales]) // --- PROCESAMIENTO DE MENSAJES --- @@ -269,7 +299,7 @@ export function IAAsignaturaTab({ } setInput('') - setSelectedFields([]) + // setSelectedFields([]) // Invalidamos la lista de conversaciones para que el nuevo chat aparezca en el historial (panel izquierdo) queryClient.invalidateQueries({ @@ -511,6 +541,7 @@ export function IAAsignaturaTab({ > {/* Texto del mensaje principal */}
{ + // Filtramos el array para conservar todos MENOS el que se aplicó + console.log(campoFinalizado) + console.log('campos:', selectedFields) + + setSelectedFields((prev) => + prev.filter((fieldObj) => { + // Accedemos a .key porque fieldObj es { key: "...", label: "..." } + return fieldObj.key !== campoFinalizado + }), + ) + }} /> ))}
diff --git a/src/components/asignaturas/detalle/SaveAsignatura/ImprovementCardProps.tsx b/src/components/asignaturas/detalle/SaveAsignatura/ImprovementCardProps.tsx index 83dac0d..6fabf3c 100644 --- a/src/components/asignaturas/detalle/SaveAsignatura/ImprovementCardProps.tsx +++ b/src/components/asignaturas/detalle/SaveAsignatura/ImprovementCardProps.tsx @@ -1,4 +1,4 @@ -import { Check, Loader2 } from 'lucide-react' +import { Check, Loader2, BookOpen, Clock, ListChecks } from 'lucide-react' import { useState } from 'react' import type { IASugerencia } from '@/types/asignatura' @@ -7,50 +7,65 @@ import { Button } from '@/components/ui/button' import { useUpdateAsignatura, useSubject, - useUpdateSubjectRecommendation, // Importamos tu nuevo hook + useUpdateSubjectRecommendation, } from '@/data' +import { cn } from '@/lib/utils' interface ImprovementCardProps { sug: IASugerencia asignaturaId: string + onApplied: (campoKey: string) => void } -export function ImprovementCard({ sug, asignaturaId }: ImprovementCardProps) { +export function ImprovementCard({ + sug, + asignaturaId, + onApplied, +}: ImprovementCardProps) { const { data: asignatura } = useSubject(asignaturaId) const updateAsignatura = useUpdateAsignatura() - - // Hook para marcar en la base de datos que la sugerencia fue aceptada const updateRecommendation = useUpdateSubjectRecommendation() const [isApplying, setIsApplying] = useState(false) const handleApply = async () => { - if (!asignatura?.datos) return + if (!asignatura) return setIsApplying(true) try { - // 1. Actualizar el contenido real de la asignatura (JSON datos) - const nuevosDatos = { - ...asignatura.datos, - [sug.campoKey]: sug.valorSugerido, + // 1. Identificar a qué columna debe ir el guardado + let patchData = {} + + if (sug.campoKey === 'contenido_tematico') { + // Se guarda directamente en la columna contenido_tematico + patchData = { contenido_tematico: sug.valorSugerido } + } else if (sug.campoKey === 'criterios_de_evaluacion') { + // Se guarda directamente en la columna criterios_de_evaluacion + patchData = { criterios_de_evaluacion: sug.valorSugerido } + } else { + // Otros campos (ciclo, fines, etc.) se siguen guardando en el JSON de la columna 'datos' + patchData = { + datos: { + ...asignatura.datos, + [sug.campoKey]: sug.valorSugerido, + }, + } } + // 2. Ejecutar la actualización con la estructura correcta await updateAsignatura.mutateAsync({ asignaturaId: asignaturaId as any, - patch: { - datos: nuevosDatos, - } as any, + patch: patchData as any, }) - // 2. Marcar la sugerencia como "aplicada: true" en la tabla de mensajes - // Usamos los datos que vienen en el objeto 'sug' + // 3. Marcar la recomendación como aplicada await updateRecommendation.mutateAsync({ mensajeId: sug.messageId, campoAfectado: sug.campoKey, }) + console.log(sug.campoKey) - // Al terminar, React Query invalidará 'subject-messages' - // y la card pasará automáticamente al estado "Aplicado" (gris) + onApplied(sug.campoKey) } catch (error) { console.error('Error al aplicar mejora:', error) } finally { @@ -58,10 +73,89 @@ export function ImprovementCard({ sug, asignaturaId }: ImprovementCardProps) { } } + // --- FUNCIÓN PARA RENDERIZAR EL CONTENIDO DE FORMA SEGURA --- + const renderContenido = (valor: any) => { + // Si no es un array, es texto simple + if (!Array.isArray(valor)) { + return

"{String(valor)}"

+ } + + // --- CASO 1: CONTENIDO TEMÁTICO (Detectamos si el primer objeto tiene 'unidad') --- + if (valor[0]?.hasOwnProperty('unidad')) { + return ( +
+ {valor.map((u: any, idx: number) => ( +
+
+ Unidad {u.unidad}: {u.titulo} +
+ +
+ ))} +
+ ) + } + + // --- CASO 2: CRITERIOS DE EVALUACIÓN (Detectamos si tiene 'criterio') --- + if (valor[0]?.hasOwnProperty('criterio')) { + return ( +
+
+ Desglose de evaluación +
+ {valor.map((c: any, idx: number) => ( +
+ + {c.criterio} + +
+ {c.porcentaje}% +
+
+ ))} + {/* Opcional: Suma total para verificar que de 100% */} +
+ Total:{' '} + {valor.reduce( + (acc: number, curr: any) => acc + (curr.porcentaje || 0), + 0, + )} + % +
+
+ ) + } + + // Caso por defecto (Array genérico) + return ( +
+        {/* JSON.stringify(valor, null, 2)*/ 'hola'}
+      
+ ) + } + // --- ESTADO APLICADO --- if (sug.aceptada) { return ( -
+
{sug.campoNombre} @@ -72,7 +166,7 @@ export function ImprovementCard({ sug, asignaturaId }: ImprovementCardProps) {
- "{sug.valorSugerido}" + {renderContenido(sug.valorSugerido)}
) @@ -101,8 +195,13 @@ export function ImprovementCard({ sug, asignaturaId }: ImprovementCardProps) { -
- "{sug.valorSugerido}" +
+ {renderContenido(sug.valorSugerido)}
) diff --git a/src/routes/planes/$planId/_detalle/iaplan.tsx b/src/routes/planes/$planId/_detalle/iaplan.tsx index ee3519d..a2e0ee8 100644 --- a/src/routes/planes/$planId/_detalle/iaplan.tsx +++ b/src/routes/planes/$planId/_detalle/iaplan.tsx @@ -227,7 +227,7 @@ function RouteComponent() { scrollToBottom() }, [chatMessages, isLoading]) - useEffect(() => { + /* useEffect(() => { // Verificamos cuáles campos de la lista "selectedFields" ya no están presentes en el texto del input const camposActualizados = selectedFields.filter((field) => input.includes(field.label), @@ -237,7 +237,7 @@ function RouteComponent() { if (camposActualizados.length !== selectedFields.length) { setSelectedFields(camposActualizados) } - }, [input, selectedFields]) + }, [input, selectedFields]) */ useEffect(() => { if (isLoadingConv || isSending) return @@ -297,7 +297,7 @@ function RouteComponent() { }, ]) setInput('') - setSelectedFields([]) + // setSelectedFields([]) } const archiveChat = (e: React.MouseEvent, id: string) => { @@ -405,7 +405,7 @@ function RouteComponent() { setIsSending(true) setOptimisticMessage(finalContent) setInput('') - setSelectedFields([]) + // setSelectedFields([]) try { const payload = {