This commit is contained in:
2026-01-13 14:30:57 -06:00
parent 55c37b83b4
commit b08d58e262
12 changed files with 178 additions and 107 deletions

View File

@@ -3,6 +3,8 @@ import * as Icons from 'lucide-react'
import { useNuevoPlanWizard } from './hooks/useNuevoPlanWizard'
import type { NewPlanWizardState } from './types'
import { PasoBasicosForm } from '@/components/planes/wizard/PasoBasicosForm/PasoBasicosForm'
import { PasoDetallesPanel } from '@/components/planes/wizard/PasoDetallesPanel/PasoDetallesPanel'
import { PasoModoCardGroup } from '@/components/planes/wizard/PasoModoCardGroup'
@@ -61,12 +63,20 @@ export default function NuevoPlanModalContainer() {
}
const crearPlan = async () => {
setWizard((w) => ({ ...w, isLoading: true, errorMessage: null }))
setWizard((w: NewPlanWizardState) => ({
...w,
isLoading: true,
errorMessage: null,
}))
await new Promise((r) => setTimeout(r, 900))
const nuevoId = (() => {
if (wizard.modoCreacion === 'MANUAL') return 'plan_new_manual_001'
if (wizard.modoCreacion === 'IA') return 'plan_new_ai_001'
if (wizard.subModoClonado === 'INTERNO') return 'plan_new_clone_001'
if (wizard.tipoOrigen === 'MANUAL') return 'plan_new_manual_001'
if (wizard.tipoOrigen === 'IA') return 'plan_new_ai_001'
if (
wizard.tipoOrigen === 'CLONADO_INTERNO' ||
wizard.tipoOrigen === 'CLONADO_TRADICIONAL'
)
return 'plan_new_clone_001'
return 'plan_new_import_001'
})()
navigate({ to: `/planes/${nuevoId}` })
@@ -124,7 +134,7 @@ export default function NuevoPlanModalContainer() {
totalSteps={totalSteps}
currentTitle={methods.current.title}
currentDescription={methods.current.description}
nextTitle={nextStep?.title}
nextTitle={nextStep.title}
onClose={handleClose}
Wizard={Wizard}
/>

View File

@@ -1,4 +1,4 @@
import type { TipoCiclo } from "./types";
import type { NivelPlanEstudio, TipoCiclo } from "@/data/types/domain";
export const FACULTADES = [
{ id: "ing", nombre: "Facultad de Ingeniería" },
@@ -16,16 +16,20 @@ export const CARRERAS = [
{ id: "act", nombre: "Actuaría", facultadId: "neg" },
];
export const NIVELES = [
export const NIVELES: Array<NivelPlanEstudio> = [
"Licenciatura",
"Especialidad",
"Maestría",
"Doctorado",
"Especialidad",
"Diplomado",
"Otro",
];
export const TIPOS_CICLO: Array<{ value: TipoCiclo; label: string }> = [
{ value: "SEMESTRE", label: "Semestre" },
{ value: "CUATRIMESTRE", label: "Cuatrimestre" },
{ value: "TRIMESTRE", label: "Trimestre" },
export const TIPOS_CICLO: Array<TipoCiclo> = [
"Semestre",
"Cuatrimestre",
"Trimestre",
"Otro",
];
export const PLANES_EXISTENTES = [

View File

@@ -2,12 +2,13 @@ import { useMemo, useState } from "react";
import { CARRERAS } from "../catalogs";
import type { NewPlanWizardState, PlanPreview, TipoCiclo } from "../types";
import type { NewPlanWizardState, PlanPreview } from "../types";
import type { NivelPlanEstudio, TipoCiclo } from "@/data/types/domain";
export function useNuevoPlanWizard() {
const [wizard, setWizard] = useState<NewPlanWizardState>({
step: 1,
modoCreacion: null,
tipoOrigen: null,
datosBasicos: {
nombrePlan: "",
carreraId: "",
@@ -40,7 +41,6 @@ export function useNuevoPlanWizard() {
},
iaConfig: {
descripcionEnfoque: "",
poblacionObjetivo: "",
notasAdicionales: "",
archivosReferencia: [],
repositoriosReferencia: [],
@@ -56,9 +56,10 @@ export function useNuevoPlanWizard() {
return fac ? CARRERAS.filter((c) => c.facultadId === fac) : CARRERAS;
}, [wizard.datosBasicos.facultadId]);
const canContinueDesdeModo = wizard.modoCreacion === "MANUAL" ||
wizard.modoCreacion === "IA" ||
(wizard.modoCreacion === "CLONADO" && !!wizard.subModoClonado);
const canContinueDesdeModo = wizard.tipoOrigen === "MANUAL" ||
wizard.tipoOrigen === "IA" ||
(wizard.tipoOrigen === "CLONADO_INTERNO" ||
wizard.tipoOrigen === "CLONADO_TRADICIONAL");
const canContinueDesdeBasicos = !!wizard.datosBasicos.nombrePlan &&
!!wizard.datosBasicos.carreraId &&
@@ -73,24 +74,22 @@ export function useNuevoPlanWizard() {
!!wizard.datosBasicos.plantillaMapaVersion;
const canContinueDesdeDetalles = (() => {
if (wizard.modoCreacion === "MANUAL") return true;
if (wizard.modoCreacion === "IA") {
if (wizard.tipoOrigen === "MANUAL") return true;
if (wizard.tipoOrigen === "IA") {
// Requerimos descripción del enfoque y notas adicionales
return !!wizard.iaConfig?.descripcionEnfoque &&
!!wizard.iaConfig?.notasAdicionales;
!!wizard.iaConfig.notasAdicionales;
}
if (wizard.modoCreacion === "CLONADO") {
if (wizard.subModoClonado === "INTERNO") {
return !!wizard.clonInterno?.planOrigenId;
}
if (wizard.subModoClonado === "TRADICIONAL") {
const t = wizard.clonTradicional;
if (!t) return false;
const tieneWord = !!t.archivoWordPlanId;
const tieneAlMenosUnExcel = !!t.archivoMapaExcelId ||
!!t.archivoAsignaturasExcelId;
return tieneWord && tieneAlMenosUnExcel;
}
if (wizard.tipoOrigen === "CLONADO_INTERNO") {
return !!wizard.clonInterno?.planOrigenId;
}
if (wizard.tipoOrigen === "CLONADO_TRADICIONAL") {
const t = wizard.clonTradicional;
if (!t) return false;
const tieneWord = !!t.archivoWordPlanId;
const tieneAlMenosUnExcel = !!t.archivoMapaExcelId ||
!!t.archivoAsignaturasExcelId;
return tieneWord && tieneAlMenosUnExcel;
}
return false;
})();
@@ -101,7 +100,7 @@ export function useNuevoPlanWizard() {
// Ensure preview has the stricter types required by `PlanPreview`.
let tipoCicloSafe: TipoCiclo;
if (wizard.datosBasicos.tipoCiclo === "") {
tipoCicloSafe = "SEMESTRE";
tipoCicloSafe = "Semestre";
} else {
tipoCicloSafe = wizard.datosBasicos.tipoCiclo;
}
@@ -112,7 +111,7 @@ export function useNuevoPlanWizard() {
const preview: PlanPreview = {
nombrePlan: wizard.datosBasicos.nombrePlan || "Plan sin nombre",
nivel: wizard.datosBasicos.nivel || "Licenciatura",
nivel: wizard.datosBasicos.nivel as NivelPlanEstudio,
tipoCiclo: tipoCicloSafe,
numCiclos: numCiclosSafe,
numAsignaturasAprox: numCiclosSafe * 6,
@@ -121,7 +120,7 @@ export function useNuevoPlanWizard() {
{ id: "perfil", titulo: "Perfil de egreso", resumen: "Borrador…" },
],
};
setWizard((w) => ({
setWizard((w: NewPlanWizardState) => ({
...w,
isLoading: false,
resumen: { previewPlan: preview },

View File

@@ -1,10 +1,12 @@
export type TipoCiclo = "SEMESTRE" | "CUATRIMESTRE" | "TRIMESTRE";
export type ModoCreacion = "MANUAL" | "IA" | "CLONADO";
export type SubModoClonado = "INTERNO" | "TRADICIONAL";
import type {
NivelPlanEstudio,
TipoCiclo,
TipoOrigen,
} from "@/data/types/domain";
export type PlanPreview = {
nombrePlan: string;
nivel: string;
nivel: NivelPlanEstudio;
tipoCiclo: TipoCiclo;
numCiclos: number;
numAsignaturasAprox?: number;
@@ -13,13 +15,12 @@ export type PlanPreview = {
export type NewPlanWizardState = {
step: 1 | 2 | 3 | 4;
modoCreacion: ModoCreacion | null;
subModoClonado?: SubModoClonado;
tipoOrigen: TipoOrigen | null;
datosBasicos: {
nombrePlan: string;
carreraId: string;
facultadId: string;
nivel: string;
nivel: NivelPlanEstudio | "";
tipoCiclo: TipoCiclo | "";
numCiclos: number | undefined;
// Selección de plantillas (obligatorias)
@@ -53,7 +54,6 @@ export type NewPlanWizardState = {
};
iaConfig?: {
descripcionEnfoque: string;
poblacionObjetivo: string;
notasAdicionales: string;
archivosReferencia: Array<string>;
repositoriosReferencia?: Array<string>;