- {campos.map((campo, key) => {
+ {campos.map((campo) => {
const isEditing = editingId === campo.id
- console.log(campo)
return (
({
- ciclo: search.ciclo ?? null,
- }),
})
function MapaCurricularPage() {
const { planId } = Route.useParams() // Idealmente usa el ID de la ruta
- const { ciclo } = Route.useSearch()
+ const { data } = usePlan(planId)
+ const [ciclo, setCiclo] = useState(0)
const [editingLineaId, setEditingLineaId] = useState
(null)
const [tempNombreLinea, setTempNombreLinea] = useState('')
const { mutate: createLinea } = useCreateLinea()
const { mutate: updateLineaApi } = useUpdateLinea()
const { mutate: deleteLineaApi } = useDeleteLinea()
- // 1. Fetch de Datos
const { data: asignaturasApi, isLoading: loadingAsig } =
usePlanAsignaturas(planId)
const { data: lineasApi, isLoading: loadingLineas } = usePlanLineas(planId)
-
- // 2. Estado Local (Para interactividad)
const [asignaturas, setAsignaturas] = useState>([])
const [lineas, setLineas] = useState>([])
const [draggedAsignatura, setDraggedAsignatura] = useState(
null,
)
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
- const [selectedAsignatura, setSelectedAsignatura] =
- useState(null)
- const [hasAreaComun, setHasAreaComun] = useState(false)
const [nombreNuevaLinea, setNombreNuevaLinea] = useState('') // Para el input de nombre personalizado
- const { mutate: updateAsignatura, isPending } = useUpdateAsignatura()
+ const { mutate: updateAsignatura } = useUpdateAsignatura()
+ const [seriacionValue, setSeriacionValue] = useState('unassigned')
+
+ useEffect(() => {
+ if (data?.numero_ciclos) {
+ setCiclo(data.numero_ciclos)
+ }
+ }, [data])
const manejarAgregarLinea = (nombre: string) => {
const nombreNormalizado = nombre.trim()
-
- // 1. Validar vacío
if (!nombreNormalizado) return
-
- // 2. Validar duplicados en el estado local (Insensible a mayúsculas/acentos)
const nombreBusqueda = nombreNormalizado
.toLowerCase()
.normalize('NFD')
@@ -219,12 +215,9 @@ function MapaCurricularPage() {
if (yaExiste) {
alert(`La línea "${nombreNormalizado}" ya existe en este plan.`)
- return // DETIENE la ejecución aquí, no llega a la mutación
+ return
}
-
- // 3. Si pasa las validaciones, procedemos con la persistencia
const maxOrden = lineas.reduce((max, l) => Math.max(max, l.orden || 0), 0)
-
createLinea(
{
nombre: nombreNormalizado,
@@ -234,7 +227,6 @@ function MapaCurricularPage() {
},
{
onSuccess: (nueva) => {
- // Sincronización local que ya teníamos
const mapeada = {
id: nueva.id,
nombre: nueva.nombre,
@@ -280,7 +272,6 @@ function MapaCurricularPage() {
)
}, [lineas])
- // 3. Sincronizar API -> Estado Local
useEffect(() => {
if (asignaturasApi)
setAsignaturas(mapAsignaturasToAsignaturas(asignaturasApi))
@@ -292,20 +283,27 @@ function MapaCurricularPage() {
const ciclosTotales = Number(ciclo)
const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1)
-
- // Nuevo estado para controlar los datos temporales del modal de edición
const [editingData, setEditingData] = useState(null)
+ const handleDecimalChange = (value: string, max?: number): string | null => {
+ if (value === '') return ''
- // 1. FUNCION DE GUARDAR MODAL
+ const val = value.replace(',', '.')
+ const regex = /^\d*\.?\d{0,2}$/
+ if (!regex.test(val)) return null
+ if (max !== undefined) {
+ const num = Number(val)
+ if (!isNaN(num) && num > max) {
+ return max.toFixed(2)
+ }
+ }
+
+ return val
+ }
const handleSaveChanges = () => {
if (!editingData) return
- console.log(asignaturas)
-
setAsignaturas((prev) =>
prev.map((m) => (m.id === editingData.id ? { ...editingData } : m)),
)
- // setIsEditModalOpen(false)
- // Preparamos el patch con la estructura de tu tabla
const patch = {
nombre: editingData.nombre,
codigo: editingData.clave,
@@ -332,22 +330,11 @@ function MapaCurricularPage() {
},
)
}
-
- // 2. MODIFICACIÓN: Zona de soltado siempre visible
- // Cambiamos la condición: Mostramos la sección si hay asignaturas sin asignar
- // O si simplemente queremos tener el "depósito" disponible.
const unassignedAsignaturas = asignaturas.filter(
(m) => m.ciclo === null || m.lineaCurricularId === null,
)
- // --- Lógica de Gestión ---
- const agregarLinea = (nombre: string) => {
- const nueva = { id: crypto.randomUUID(), nombre, orden: lineas.length + 1 }
- setLineas([...lineas, nueva])
- }
-
const borrarLinea = (id: string) => {
- // 1. Opcional: Confirmación de seguridad
if (
!confirm(
'¿Estás seguro de eliminar esta línea? Las materias asignadas volverán a la bandeja de entrada.',
@@ -356,11 +343,8 @@ function MapaCurricularPage() {
return
}
- // 2. Llamada a la API
deleteLineaApi(id, {
onSuccess: () => {
- // 3. Actualización instantánea del estado local
-
// Primero: Las materias que estaban en esa línea pasan a ser "huérfanas"
setAsignaturas((prev) =>
prev.map((asig) =>
@@ -369,8 +353,6 @@ function MapaCurricularPage() {
: asig,
),
)
-
- // Segundo: Quitamos la línea del estado
setLineas((prev) => prev.filter((l) => l.id !== id))
},
onError: (error) => {
@@ -427,8 +409,6 @@ function MapaCurricularPage() {
: m,
),
)
-
- // 2. Persistir en la API
const patch = {
numero_ciclo: ciclo,
linea_plan_id: lineaId,
@@ -774,13 +754,17 @@ function MapaCurricularPage() {
- setEditingData({
- ...editingData,
- creditos: Number(e.target.value),
- })
- }
+ onChange={(e) => {
+ const val = handleDecimalChange(e.target.value, 10)
+ if (val !== null) {
+ setEditingData({
+ ...editingData,
+ creditos: val === '' ? 0 : Number(val),
+ })
+ }
+ }}
/>
@@ -790,12 +774,15 @@ function MapaCurricularPage() {
- setEditingData({
- ...editingData,
- hd: Number(e.target.value),
- })
- }
+ onChange={(e) => {
+ const val = handleDecimalChange(e.target.value, 10)
+ if (val !== null) {
+ setEditingData({
+ ...editingData,
+ hd: Number(e.target.value),
+ })
+ }
+ }}
/>
@@ -805,12 +792,15 @@ function MapaCurricularPage() {
- setEditingData({
- ...editingData,
- hi: Number(e.target.value),
- })
- }
+ onChange={(e) => {
+ const val = handleDecimalChange(e.target.value, 10)
+ if (val !== null) {
+ setEditingData({
+ ...editingData,
+ hi: Number(e.target.value),
+ })
+ }
+ }}
/>
@@ -882,23 +872,29 @@ function MapaCurricularPage() {
Seriación (Prerrequisitos)