import { createFileRoute, useNavigate, useLocation, } from '@tanstack/react-router' // import confetti from 'canvas-confetti' import { Pencil, Check, X, Sparkles, AlertCircle } from 'lucide-react' import { useState, useEffect } from 'react' import type { DatosGeneralesField } from '@/types/plan' import { Button } from '@/components/ui/button' import { lateralConfetti } from '@/components/ui/lateral-confetti' import { Textarea } from '@/components/ui/textarea' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' import { usePlan } from '@/data' // import { toast } from 'sonner' // Asegúrate de tener sonner instalado o quita la línea export const Route = createFileRoute('/planes/$planId/_detalle/')({ component: DatosGeneralesPage, }) const formatLabel = (key: string) => { const result = key.replace(/_/g, ' ') return result.charAt(0).toUpperCase() + result.slice(1) } function DatosGeneralesPage() { const { planId } = Route.useParams() const { data, isLoading } = usePlan(planId) const navigate = useNavigate() // Inicializamos campos como un arreglo vacío const [campos, setCampos] = useState>([]) const [editingId, setEditingId] = useState(null) const [editValue, setEditValue] = useState('') const location = useLocation() // Confetti al llegar desde creación useEffect(() => { if (location.state.showConfetti) { lateralConfetti() window.history.replaceState({}, document.title) // Limpiar el estado para que no se repita } }, [location.state]) // Efecto para transformar data?.datos en el arreglo de campos useEffect(() => { const definicion = data?.estructuras_plan?.definicion as any const properties = definicion?.properties const requiredOrder = definicion?.required as Array | undefined const valores = (data?.datos as Record) || {} if (properties && typeof properties === 'object') { let keys = Object.keys(properties) // Ordenar llaves basado en la lista "required" si existe if (Array.isArray(requiredOrder)) { keys = keys.sort((a, b) => { const indexA = requiredOrder.indexOf(a) const indexB = requiredOrder.indexOf(b) // Si 'a' está en la lista y 'b' no -> 'a' primero (-1) if (indexA !== -1 && indexB === -1) return -1 // Si 'b' está en la lista y 'a' no -> 'b' primero (1) if (indexA === -1 && indexB !== -1) return 1 // Si ambos están, comparar índices if (indexA !== -1 && indexB !== -1) return indexA - indexB // Ninguno en la lista, mantener orden relativo return 0 }) } const datosTransformados: Array = keys.map( (key, index) => { const schema = properties[key] const rawValue = valores[key] return { id: (index + 1).toString(), label: schema?.title || formatLabel(key), helperText: schema?.description || '', holder: schema?.examples || '', value: rawValue !== undefined && rawValue !== null ? String(rawValue) : '', requerido: true, // 👇 TIPO DE CAMPO tipo: Array.isArray(schema?.enum) ? 'select' : schema?.type === 'number' ? 'number' : 'texto', opciones: schema?.enum || [], } }, ) setCampos(datosTransformados) } console.log(properties) }, [data]) // 3. Manejadores de acciones (Ahora como funciones locales) const handleEdit = (campo: DatosGeneralesField) => { setEditingId(campo.id) setEditValue(campo.value) } const handleCancel = () => { setEditingId(null) setEditValue('') } const handleSave = (id: string) => { // Actualizamos el estado local de la lista setCampos((prev) => prev.map((c) => (c.id === id ? { ...c, value: editValue } : c)), ) setEditingId(null) setEditValue('') // toast.success('Cambios guardados localmente') } const handleIARequest = (descripcion: string) => { navigate({ to: '/planes/$planId/iaplan', params: { planId: planId, // o dinámico }, state: { prefill: descripcion, } as any, }) } return (

Datos Generales del Plan

Información estructural y descriptiva del plan de estudios

{campos.map((campo) => { const isEditing = editingId === campo.id return (
{/* Header de la Card */}

{campo.label}

{campo.helperText || 'Información del campo'}
{campo.requerido && ( * )}
{!isEditing && (
Generar con IA Editar campo
)}
{/* Contenido de la Card */}
{isEditing ? (