From bd0fcd5049a8f4db016917f2bd9755202fcecf20 Mon Sep 17 00:00:00 2001 From: "Roberto.silva" Date: Fri, 9 Jan 2026 15:55:58 -0600 Subject: [PATCH] Se agrega avance de integracion de datos --- src/routes/planes/$planId/_detalle/datos.tsx | 51 ++++++++----- src/routes/planes/$planId/_detalle/mapa.tsx | 76 ++++++++++++++++---- 2 files changed, 97 insertions(+), 30 deletions(-) diff --git a/src/routes/planes/$planId/_detalle/datos.tsx b/src/routes/planes/$planId/_detalle/datos.tsx index d10d268..433db03 100644 --- a/src/routes/planes/$planId/_detalle/datos.tsx +++ b/src/routes/planes/$planId/_detalle/datos.tsx @@ -1,6 +1,6 @@ import { usePlan } from '@/data'; import { createFileRoute } from '@tanstack/react-router' -import { useState } from 'react' +import { useState, useEffect } from 'react' import type { DatosGeneralesField } from '@/types/plan' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' @@ -17,23 +17,40 @@ export const Route = createFileRoute('/planes/$planId/_detalle/datos')({ component: DatosGeneralesPage, }) -function DatosGeneralesPage() { - const {data, isFetching} = usePlan('0e0aea4d-b8b4-4e75-8279-6224c3ac769f'); - if(!isFetching && !data) { - return
No se encontró el plan de estudios.
- } - console.log(data); - - // 1. Definimos los DATOS iniciales (Lo que antes venía por props) - const [campos, setCampos] = useState([ - { id: '1', label: 'Objetivo General', value: 'Formar profesionales...', requerido: true, tipo: 'texto' }, - { id: '2', label: 'Perfil de Ingreso', value: 'Interés por la tecnología...', requerido: true, tipo: 'lista' }, - { id: '3', label: 'Perfil de Egreso', value: '', requerido: true, tipo: 'texto' }, - ]) +const formatLabel = (key: string) => { + const result = key.replace(/_/g, ' '); + return result.charAt(0).toUpperCase() + result.slice(1); +}; - // 2. Estados de edición - const [editingId, setEditingId] = useState(null) - const [editValue, setEditValue] = useState('') +function DatosGeneralesPage() { + const { data, isFetching } = usePlan('0e0aea4d-b8b4-4e75-8279-6224c3ac769f'); + + // Inicializamos campos como un arreglo vacío + const [campos, setCampos] = useState([]); + const [editingId, setEditingId] = useState(null); + const [editValue, setEditValue] = useState(''); + + + // Efecto para transformar data?.datos en el arreglo de campos + useEffect(() => { + if (data) { + // Si data es directamente el objeto que mostraste, usamos data. + // Si viene dentro de .datos, usamos data.datos. + const sourceData = data?.datos; + + const datosTransformados: DatosGeneralesField[] = Object.entries(sourceData).map( + ([key, value], index) => ({ + id: (index + 1).toString(), // Id basado en index (1, 2, 3...) + label: formatLabel(key), // "perfil_de_ingreso" -> "Perfil de ingreso" + value: value?.toString() || '', // Manejo de nulls + requerido: true, + tipo: 'texto' // Todos como texto según tu instrucción + }) + ); + + setCampos(datosTransformados); + } + }, [data]); // 3. Manejadores de acciones (Ahora como funciones locales) const handleEdit = (campo: DatosGeneralesField) => { diff --git a/src/routes/planes/$planId/_detalle/mapa.tsx b/src/routes/planes/$planId/_detalle/mapa.tsx index 3999584..9954a57 100644 --- a/src/routes/planes/$planId/_detalle/mapa.tsx +++ b/src/routes/planes/$planId/_detalle/mapa.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from '@tanstack/react-router' -import { useState } from 'react' +import { useMemo, useState } from 'react' import { Badge } from '@/components/ui/badge' import { Input } from '@/components/ui/input' import { @@ -20,22 +20,14 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" +import { usePlanAsignaturas, usePlanLineas } from '@/data'; + export const Route = createFileRoute('/planes/$planId/_detalle/mapa')({ component: MapaCurricularPage, }) -// --- Constantes de Estilo y Datos --- -const INITIAL_LINEAS: LineaCurricular[] = [ - { id: 'l1', nombre: 'Formación Básica', orden: 1 }, - { id: 'l2', nombre: 'Ciencias de la Computación', orden: 2 }, -]; -const INITIAL_MATERIAS: Materia[] = [ - { id: "1", clave: 'MAT101', nombre: 'Cálculo Diferencial', creditos: 8, hd: 4, hi: 4, ciclo: 1, lineaCurricularId: 'l1', tipo: 'obligatoria', estado: 'aprobada' }, - { id: "2", clave: 'FIS101', nombre: 'Física Mecánica', creditos: 6, hd: 3, hi: 3, ciclo: 1, lineaCurricularId: 'l1', tipo: 'obligatoria', estado: 'aprobada' }, - { id: "3", clave: 'PRO101', nombre: 'Fundamentos de Programación', creditos: 8, hd: 4, hi: 4, ciclo: null, lineaCurricularId: null, tipo: 'obligatoria', estado: 'borrador' }, -]; const lineColors = [ 'bg-blue-50 border-blue-200 text-blue-700', @@ -94,6 +86,24 @@ function MateriaCardItem({ materia, onDragStart, isDragging, onClick }: { // --- Componente Principal --- function MapaCurricularPage() { + + + const { data: asignaturas, isFetching: loadingAsig } = usePlanAsignaturas('0e0aea4d-b8b4-4e75-8279-6224c3ac769f'); + const { data: lineas2, isFetching: loadingLineas } = usePlanLineas('0e0aea4d-b8b4-4e75-8279-6224c3ac769f'); + console.log(asignaturas); + console.log(lineas2); + // --- Constantes de Estilo y Datos --- +const INITIAL_LINEAS: LineaCurricular[] = [ + { id: 'l1', nombre: 'Formación Básica', orden: 1 }, + { id: 'l2', nombre: 'Ciencias de la Computación', orden: 2 }, +]; + +const INITIAL_MATERIAS: Materia[] = [ + { id: "1", clave: 'MAT101', nombre: 'Cálculo Diferencial', creditos: 8, hd: 4, hi: 4, ciclo: 1, lineaCurricularId: 'l1', tipo: 'obligatoria', estado: 'aprobada' }, + { id: "2", clave: 'FIS101', nombre: 'Física Mecánica', creditos: 6, hd: 3, hi: 3, ciclo: 1, lineaCurricularId: 'l1', tipo: 'obligatoria', estado: 'aprobada' }, + { id: "3", clave: 'PRO101', nombre: 'Fundamentos de Programación', creditos: 8, hd: 4, hi: 4, ciclo: null, lineaCurricularId: null, tipo: 'obligatoria', estado: 'borrador' }, +]; + const [materias, setMaterias] = useState(INITIAL_MATERIAS); const [lineas, setLineas] = useState(INITIAL_LINEAS); const [draggedMateria, setDraggedMateria] = useState(null); @@ -103,6 +113,46 @@ function MapaCurricularPage() { const ciclosTotales = 9; const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1); + const mapLineasToLineaCurricular = (lineasApi = []): LineaCurricular[] => { + return lineasApi.map((linea: any) => ({ + id: linea.id, + nombre: linea.nombre, + orden: linea.orden ?? 0, + color: '#1976d2', // default aceptado + })); +}; + +const mapAsignaturasToMaterias = (asigApi = []): Materia[] => { + return asigApi.map((asig: any) => ({ + id: asig.id, + clave: asig.codigo, + nombre: asig.nombre, + creditos: asig.creditos ?? 0, + ciclo: asig.numero_ciclo ?? null, + lineaCurricularId: asig.linea_plan_id ?? null, + tipo: asig.tipo === 'OBLIGATORIA' ? 'obligatoria' : 'optativa', + estado: 'borrador', // default válido + orden: asig.orden_celda ?? 0, + hd: Math.floor((asig.horas_semana ?? 0) / 2), + hi: Math.ceil((asig.horas_semana ?? 0) / 2), + })); +}; + +const lineasFinales: LineaCurricular[] = useMemo(() => { + return [ + ...INITIAL_LINEAS, + ...mapLineasToLineaCurricular(lineas2), + ]; +}, [lineas2]); + +const materiasFinales: Materia[] = useMemo(() => { + return [ + ...INITIAL_MATERIAS, + ...mapAsignaturasToMaterias(asignaturas), + ]; +}, [asignaturas]); + + // --- Lógica de Gestión --- const agregarLinea = (nombre: string) => { const nueva = { id: crypto.randomUUID(), nombre, orden: lineas.length + 1 }; @@ -192,7 +242,7 @@ function MapaCurricularPage() { {/* Filas por Línea */} - {lineas.map((linea, idx) => { + {lineasFinales.map((linea, idx) => { const sub = getSubtotalLinea(linea.id); return (
@@ -208,7 +258,7 @@ function MapaCurricularPage() { onDrop={(e) => handleDrop(e, ciclo, linea.id)} className="min-h-[140px] p-2 rounded-xl border-2 border-dashed border-slate-100 bg-slate-50/20 space-y-2" > - {materias.filter(m => m.ciclo === ciclo && m.lineaCurricularId === linea.id).map(m => ( + {materiasFinales.filter(m => m.ciclo === ciclo && m.lineaCurricularId === linea.id).map(m => ( { setSelectedMateria(m); setIsEditModalOpen(true); }} /> ))}