import { createFileRoute, useRouterState } from '@tanstack/react-router' import { Sparkles, Send, Target, UserCheck, Lightbulb, FileText, GraduationCap, BookOpen, Check, X, } from 'lucide-react' import { useState, useEffect, useRef, useMemo } from 'react' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { ScrollArea } from '@/components/ui/scroll-area' import { Textarea } from '@/components/ui/textarea' import { usePlan } from '@/data/hooks/usePlans' const PRESETS = [ { id: 'objetivo', label: 'Mejorar objetivo general', icon: Target, prompt: 'Mejora la redacción del objetivo general...', }, { id: 'perfil-egreso', label: 'Redactar perfil de egreso', icon: GraduationCap, prompt: 'Genera un perfil de egreso detallado...', }, { id: 'competencias', label: 'Sugerir competencias', icon: BookOpen, prompt: 'Genera una lista de competencias...', }, { id: 'pertinencia', label: 'Justificar pertinencia', icon: FileText, prompt: 'Redacta una justificación de pertinencia...', }, ] // --- Tipado y Helpers --- interface SelectedField { key: string label: string value: string } const formatLabel = (key: string) => { const result = key.replace(/_/g, ' ') return result.charAt(0).toUpperCase() + result.slice(1) } export const Route = createFileRoute('/planes/$planId/_detalle/iaplan')({ component: RouteComponent, }) function RouteComponent() { const { planId } = Route.useParams() // Usamos el ID dinámico del plan o el hardcoded según tu necesidad const { data } = usePlan('0e0aea4d-b8b4-4e75-8279-6224c3ac769f') const routerState = useRouterState() // ESTADOS PRINCIPALES const [messages, setMessages] = useState>([ { id: '1', role: 'assistant', content: '¡Hola! Soy tu asistente de IA. ¿Qué campos deseas mejorar? Puedes escribir ":" para seleccionar uno.', }, ]) const [input, setInput] = useState('') const [selectedFields, setSelectedFields] = useState>([]) const [showSuggestions, setShowSuggestions] = useState(false) const [isLoading, setIsLoading] = useState(false) const [pendingSuggestion, setPendingSuggestion] = useState(null) const scrollRef = useRef(null) // 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 && !selectedFields.some((sf) => sf.key === field.key)) { setSelectedFields([field]) } setInput((prev) => injectFieldsIntoInput(prev || 'Mejora este campo:', field ? [field] : []), ) }, [availableFields]) // 3. Lógica para el disparador ":" const handleInputChange = (e: React.ChangeEvent) => { const val = e.target.value setInput(val) if (val.endsWith(':')) { setShowSuggestions(true) } else { setShowSuggestions(false) } } const injectFieldsIntoInput = ( input: string, fields: Array, ) => { const baseText = input.replace(/\[[^\]]+]/g, '').trim() const tags = fields.map((f) => `${f.label}`).join(' ') return `${baseText} ${tags}`.trim() } const toggleField = (field: SelectedField) => { setSelectedFields((prev) => { let nextFields if (prev.find((f) => f.key === field.key)) { nextFields = prev.filter((f) => f.key !== field.key) } else { nextFields = [...prev, field] } setInput((prevInput) => injectFieldsIntoInput(prevInput || 'Mejora este campo:', nextFields), ) return nextFields }) setShowSuggestions(false) } const buildPrompt = (userInput: string) => { if (selectedFields.length === 0) return userInput const fieldsText = selectedFields .map((f) => `- ${f.label}: ${f.value || '(sin contenido)'}`) .join('\n') return ` ${userInput || 'Mejora los siguientes campos:'} Campos a analizar: ${fieldsText} `.trim() } const handleSend = async (promptOverride?: string) => { const rawText = promptOverride || input if (!rawText.trim() && selectedFields.length === 0) return const finalPrompt = buildPrompt(rawText) const userMsg = { id: Date.now().toString(), role: 'user', content: finalPrompt, } setMessages((prev) => [...prev, userMsg]) setInput('') setIsLoading(true) setTimeout(() => { const mockText = 'Sugerencia generada basada en los campos seleccionados...' setMessages((prev) => [ ...prev, { id: Date.now().toString(), role: 'assistant', content: `He analizado ${selectedFields .map((f) => f.label) .join(', ')}. Aquí tienes una propuesta:\n\n${mockText}`, }, ]) setPendingSuggestion({ text: mockText }) setIsLoading(false) }, 1200) } return (
{/* PANEL DE CHAT PRINCIPAL */}
{/* NUEVO: Barra superior de campos seleccionados */}
Mejorar con IA
{/* CONTENIDO DEL CHAT */}
{messages.map((msg) => (
{msg.role === 'assistant' ? ( ) : ( )}
{msg.content}
))} {isLoading && (
)}
{/* Botones flotantes de aplicación */} {pendingSuggestion && !isLoading && (
)}
{/* INPUT FIJO AL FONDO CON SUGERENCIAS : */} {/* INPUT FIJO AL FONDO CON SUGERENCIAS : */}
{/* MENÚ DE SUGERENCIAS FLOTANTE (Se mantiene igual) */} {showSuggestions && (
Seleccionar campo para IA
{availableFields.map((field) => ( ))}
)} {/* CONTENEDOR DEL INPUT TRANSFORMADO */}
{/* 1. Visualización de campos dentro del input (Tags) */} {selectedFields.length > 0 && (
{selectedFields.map((field) => (
Campo: {field.label}
))}
)} {/* 2. Área de escritura */}