- Se introdujo un layout genérico de wizard (WizardLayout) con headerSlot/footerSlot y se migraron los modales de Nuevo Plan y Nueva Asignatura a esta estructura usando defineStepper. - Se creó y reutilizó WizardResponsiveHeader para un encabezado responsivo consistente (progreso en móvil y navegación en escritorio) en ambos wizards. - Se homologó WizardControls del wizard de asignaturas para alinearlo al patrón del wizard de planes (props onPrev/onNext, flags de disable, manejo de error/loading y creación). - Se mejoró la captura de datos en el wizard de asignatura: créditos como flotante con 2 decimales, placeholders/estilos en inputs/selects y uso de catálogo real de estructuras vía useSubjectEstructuras con qk.estructurasAsignatura. - Se reorganizó la sección de asignaturas del detalle del plan: el contenido del antiguo index se movió a asignaturas.tsx como layout y se agregó <Outlet />; navegación a “nueva asignatura” ajustada al path correcto.
95 lines
2.5 KiB
TypeScript
95 lines
2.5 KiB
TypeScript
import { useState } from 'react'
|
|
|
|
import type { AsignaturaPreview, NewSubjectWizardState } from '../types'
|
|
|
|
export function useNuevaAsignaturaWizard(planId: string) {
|
|
const [wizard, setWizard] = useState<NewSubjectWizardState>({
|
|
step: 1,
|
|
plan_estudio_id: planId,
|
|
tipoOrigen: null,
|
|
datosBasicos: {
|
|
nombre: '',
|
|
codigo: '',
|
|
tipo: null,
|
|
creditos: null,
|
|
horasAcademicas: null,
|
|
horasIndependientes: null,
|
|
estructuraId: '',
|
|
},
|
|
clonInterno: {},
|
|
clonTradicional: {
|
|
archivoWordAsignaturaId: null,
|
|
archivosAdicionalesIds: [],
|
|
},
|
|
iaConfig: {
|
|
descripcionEnfoqueAcademico: '',
|
|
instruccionesAdicionalesIA: '',
|
|
archivosReferencia: [],
|
|
repositoriosReferencia: [],
|
|
archivosAdjuntos: [],
|
|
},
|
|
resumen: {},
|
|
isLoading: false,
|
|
errorMessage: null,
|
|
})
|
|
|
|
const canContinueDesdeMetodo =
|
|
wizard.tipoOrigen === 'MANUAL' ||
|
|
wizard.tipoOrigen === 'IA' ||
|
|
wizard.tipoOrigen === 'CLONADO_INTERNO' ||
|
|
wizard.tipoOrigen === 'CLONADO_TRADICIONAL'
|
|
|
|
const canContinueDesdeBasicos =
|
|
!!wizard.datosBasicos.nombre &&
|
|
wizard.datosBasicos.tipo !== null &&
|
|
wizard.datosBasicos.creditos !== null &&
|
|
wizard.datosBasicos.creditos > 0 &&
|
|
!!wizard.datosBasicos.estructuraId
|
|
|
|
const canContinueDesdeDetalles = (() => {
|
|
if (wizard.tipoOrigen === 'MANUAL') return true
|
|
if (wizard.tipoOrigen === 'IA') {
|
|
return !!wizard.iaConfig?.descripcionEnfoqueAcademico
|
|
}
|
|
if (wizard.tipoOrigen === 'CLONADO_INTERNO') {
|
|
return !!wizard.clonInterno?.asignaturaOrigenId
|
|
}
|
|
if (wizard.tipoOrigen === 'CLONADO_TRADICIONAL') {
|
|
return !!wizard.clonTradicional?.archivoWordAsignaturaId
|
|
}
|
|
return false
|
|
})()
|
|
|
|
const simularGeneracionIA = async () => {
|
|
setWizard((w) => ({ ...w, isLoading: true }))
|
|
await new Promise((r) => setTimeout(r, 1500))
|
|
setWizard((w) => ({
|
|
...w,
|
|
isLoading: false,
|
|
resumen: {
|
|
previewAsignatura: {
|
|
nombre: w.datosBasicos.nombre,
|
|
objetivo:
|
|
'Aplicar los fundamentos teóricos para la resolución de problemas...',
|
|
unidades: 5,
|
|
bibliografiaCount: 3,
|
|
} as AsignaturaPreview,
|
|
},
|
|
}))
|
|
}
|
|
|
|
const crearAsignatura = async () => {
|
|
await new Promise((r) => setTimeout(r, 1000))
|
|
}
|
|
|
|
return {
|
|
wizard,
|
|
setWizard,
|
|
canContinueDesdeMetodo,
|
|
canContinueDesdeBasicos,
|
|
canContinueDesdeDetalles,
|
|
simularGeneracionIA,
|
|
crearAsignatura,
|
|
}
|
|
}
|