Se agrega avance de integracion de datos

This commit is contained in:
2026-01-09 15:55:58 -06:00
parent 3e7d3385ec
commit bd0fcd5049
2 changed files with 97 additions and 30 deletions

View File

@@ -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 <div>No se encontró el plan de estudios.</div>
}
console.log(data);
// 1. Definimos los DATOS iniciales (Lo que antes venía por props)
const [campos, setCampos] = useState<DatosGeneralesField[]>([
{ 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<string | null>(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<DatosGeneralesField[]>([]);
const [editingId, setEditingId] = useState<string | null>(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) => {

View File

@@ -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<Materia[]>(INITIAL_MATERIAS);
const [lineas, setLineas] = useState<LineaCurricular[]>(INITIAL_LINEAS);
const [draggedMateria, setDraggedMateria] = useState<string | null>(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() {
</div>
{/* Filas por Línea */}
{lineas.map((linea, idx) => {
{lineasFinales.map((linea, idx) => {
const sub = getSubtotalLinea(linea.id);
return (
<div key={linea.id} className="grid gap-3 mb-3" style={{ gridTemplateColumns: `220px repeat(${ciclosTotales}, 1fr) 120px` }}>
@@ -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 => (
<MateriaCardItem key={m.id} materia={m} isDragging={draggedMateria === m.id} onDragStart={handleDragStart} onClick={() => { setSelectedMateria(m); setIsEditModalOpen(true); }} />
))}
</div>