Se agrega avance de integracion de datos
This commit is contained in:
@@ -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) => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user