From b08d58e2626526e7bf6fdfa642360ece4e5368ac Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Tue, 13 Jan 2026 14:30:57 -0600 Subject: [PATCH] wip --- bun.lock | 5 + package.json | 1 + scripts/update-types.ts | 18 ++++ .../PasoBasicosForm/PasoBasicosForm.tsx | 38 +++++-- .../PasoDetallesPanel/PasoDetallesPanel.tsx | 14 +-- .../planes/wizard/PasoModoCardGroup.tsx | 98 +++++++++++------- src/data/types/domain.ts | 10 +- .../planes/nuevo/NuevoPlanModalContainer.tsx | 20 +++- src/features/planes/nuevo/catalogs.ts | 18 ++-- .../planes/nuevo/hooks/useNuevoPlanWizard.ts | 47 ++++----- src/features/planes/nuevo/types.ts | 16 +-- src/types/supabase.ts | Bin 83786 -> 38590 bytes 12 files changed, 178 insertions(+), 107 deletions(-) create mode 100644 scripts/update-types.ts diff --git a/bun.lock b/bun.lock index c04b2fd..b722abb 100644 --- a/bun.lock +++ b/bun.lock @@ -45,6 +45,7 @@ "@tanstack/eslint-config": "^0.3.0", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.2.0", + "@types/bun": "^1.3.6", "@types/node": "^22.10.2", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", @@ -503,6 +504,8 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="], + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], @@ -663,6 +666,8 @@ "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], diff --git a/package.json b/package.json index aa6e263..5449226 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@tanstack/eslint-config": "^0.3.0", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.2.0", + "@types/bun": "^1.3.6", "@types/node": "^22.10.2", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", diff --git a/scripts/update-types.ts b/scripts/update-types.ts new file mode 100644 index 0000000..2124f6a --- /dev/null +++ b/scripts/update-types.ts @@ -0,0 +1,18 @@ +// scripts/update-types.ts +// Uso: +// bun run scripts/update-types.ts +import { $ } from "bun"; + +console.log("🔄 Generando tipos de Supabase..."); + +try { + // Ejecutamos el comando y capturamos la salida como texto + const output = await $`supabase gen types typescript --linked`.text(); + + // Escribimos el archivo directamente con Bun (garantiza UTF-8) + await Bun.write("src/types/supabase.ts", output); + + console.log("✅ Tipos actualizados correctamente con acentos."); +} catch (error) { + console.error("❌ Error generando tipos:", error); +} diff --git a/src/components/planes/wizard/PasoBasicosForm/PasoBasicosForm.tsx b/src/components/planes/wizard/PasoBasicosForm/PasoBasicosForm.tsx index bdc6b97..49059b5 100644 --- a/src/components/planes/wizard/PasoBasicosForm/PasoBasicosForm.tsx +++ b/src/components/planes/wizard/PasoBasicosForm/PasoBasicosForm.tsx @@ -1,5 +1,6 @@ import { TemplateSelectorCard } from './TemplateSelectorCard' +import type { NivelPlanEstudio, TipoCiclo } from '@/data/types/domain' import type { CARRERAS } from '@/features/planes/nuevo/catalogs' import type { NewPlanWizardState } from '@/features/planes/nuevo/types' @@ -13,6 +14,7 @@ import { SelectValue, } from '@/components/ui/select' import { Separator } from '@/components/ui/separator' +import { useCatalogosPlanes } from '@/data/hooks/usePlans' import { FACULTADES, NIVELES, @@ -31,6 +33,18 @@ export function PasoBasicosForm({ onChange: React.Dispatch> carrerasFiltradas: typeof CARRERAS }) { + const { data: catalogos } = useCatalogosPlanes() + + // Preferir los catálogos remotos si están disponibles; si no, usar los locales + const facultadesList = catalogos?.facultades ?? FACULTADES + const rawCarreras = catalogos?.carreras ?? carrerasFiltradas + + const filteredCarreras = rawCarreras.filter((c: any) => { + const facId = wizard.datosBasicos.facultadId + if (!facId) return true + // soportar ambos shapes: `facultad_id` (BD) o `facultadId` (local) + return c.facultad_id ? c.facultad_id === facId : c.facultadId === facId + }) return (
@@ -40,7 +54,7 @@ export function PasoBasicosForm({ ) => onChange((w) => ({ @@ -79,7 +93,7 @@ export function PasoBasicosForm({ - {FACULTADES.map((f) => ( + {facultadesList.map((f: any) => ( {f.nombre} @@ -112,7 +126,7 @@ export function PasoBasicosForm({ - {carrerasFiltradas.map((c) => ( + {filteredCarreras.map((c: any) => ( {c.nombre} @@ -125,11 +139,13 @@ export function PasoBasicosForm({ + onValueChange={(value: TipoCiclo) => onChange((w) => ({ ...w, datosBasicos: { @@ -180,8 +196,8 @@ export function PasoBasicosForm({ {TIPOS_CICLO.map((t) => ( - - {t.label} + + {t} ))} diff --git a/src/components/planes/wizard/PasoDetallesPanel/PasoDetallesPanel.tsx b/src/components/planes/wizard/PasoDetallesPanel/PasoDetallesPanel.tsx index ba3347e..0d3c7f5 100644 --- a/src/components/planes/wizard/PasoDetallesPanel/PasoDetallesPanel.tsx +++ b/src/components/planes/wizard/PasoDetallesPanel/PasoDetallesPanel.tsx @@ -30,7 +30,7 @@ export function PasoDetallesPanel({ onGenerarIA: () => void isLoading: boolean }) { - if (wizard.modoCreacion === 'MANUAL') { + if (wizard.tipoOrigen === 'MANUAL') { return ( @@ -43,7 +43,7 @@ export function PasoDetallesPanel({ ) } - if (wizard.modoCreacion === 'IA') { + if (wizard.tipoOrigen === 'IA') { return (
@@ -162,10 +162,7 @@ export function PasoDetallesPanel({ ) } - if ( - wizard.modoCreacion === 'CLONADO' && - wizard.subModoClonado === 'INTERNO' - ) { + if (wizard.tipoOrigen === 'CLONADO_INTERNO') { return (
@@ -269,10 +266,7 @@ export function PasoDetallesPanel({ ) } - if ( - wizard.modoCreacion === 'CLONADO' && - wizard.subModoClonado === 'TRADICIONAL' - ) { + if (wizard.tipoOrigen === 'CLONADO_TRADICIONAL') { return (
diff --git a/src/components/planes/wizard/PasoModoCardGroup.tsx b/src/components/planes/wizard/PasoModoCardGroup.tsx index e316f86..efde81b 100644 --- a/src/components/planes/wizard/PasoModoCardGroup.tsx +++ b/src/components/planes/wizard/PasoModoCardGroup.tsx @@ -1,10 +1,7 @@ import * as Icons from 'lucide-react' -import type { - NewPlanWizardState, - ModoCreacion, - SubModoClonado, -} from '@/features/planes/nuevo/types' +import type { TipoOrigen } from '@/data/types/domain' +import type { NewPlanWizardState } from '@/features/planes/nuevo/types' import { Card, @@ -21,8 +18,7 @@ export function PasoModoCardGroup({ wizard: NewPlanWizardState onChange: React.Dispatch> }) { - const isSelected = (m: ModoCreacion) => wizard.modoCreacion === m - const isSubSelected = (s: SubModoClonado) => wizard.subModoClonado === s + const isSelected = (m: TipoOrigen) => wizard.tipoOrigen === m const handleKeyActivate = (e: React.KeyboardEvent, cb: () => void) => { const key = e.key if ( @@ -41,19 +37,21 @@ export function PasoModoCardGroup({ - onChange((w) => ({ - ...w, - modoCreacion: 'MANUAL', - subModoClonado: undefined, - })) + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'MANUAL', + }), + ) } onKeyDown={(e: React.KeyboardEvent) => handleKeyActivate(e, () => - onChange((w) => ({ - ...w, - modoCreacion: 'MANUAL', - subModoClonado: undefined, - })), + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'MANUAL', + }), + ), ) } role="button" @@ -70,19 +68,21 @@ export function PasoModoCardGroup({ - onChange((w) => ({ - ...w, - modoCreacion: 'IA', - subModoClonado: undefined, - })) + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'IA', + }), + ) } onKeyDown={(e: React.KeyboardEvent) => handleKeyActivate(e, () => - onChange((w) => ({ - ...w, - modoCreacion: 'IA', - subModoClonado: undefined, - })), + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'IA', + }), + ), ) } role="button" @@ -99,11 +99,13 @@ export function PasoModoCardGroup({ onChange((w) => ({ ...w, modoCreacion: 'CLONADO' }))} + className={isSelected('OTRO') ? 'ring-ring ring-2' : ''} + onClick={() => + onChange((w): NewPlanWizardState => ({ ...w, tipoOrigen: 'OTRO' })) + } onKeyDown={(e: React.KeyboardEvent) => handleKeyActivate(e, () => - onChange((w) => ({ ...w, modoCreacion: 'CLONADO' })), + onChange((w): NewPlanWizardState => ({ ...w, tipoOrigen: 'OTRO' })), ) } role="button" @@ -115,22 +117,34 @@ export function PasoModoCardGroup({ Desde un plan existente o archivos. - {wizard.modoCreacion === 'CLONADO' && ( + {(wizard.tipoOrigen === 'OTRO' || + wizard.tipoOrigen === 'CLONADO_INTERNO' || + wizard.tipoOrigen === 'CLONADO_TRADICIONAL') && (
{ e.stopPropagation() - onChange((w) => ({ ...w, subModoClonado: 'INTERNO' })) + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'CLONADO_INTERNO', + }), + ) }} onKeyDown={(e: React.KeyboardEvent) => handleKeyActivate(e, () => - onChange((w) => ({ ...w, subModoClonado: 'INTERNO' })), + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'CLONADO_INTERNO', + }), + ), ) } className={`hover:border-primary/50 hover:bg-accent flex cursor-pointer flex-row items-center justify-center gap-2 rounded-lg border p-4 text-center transition-all sm:flex-col ${ - isSubSelected('INTERNO') + isSelected('CLONADO_INTERNO') ? 'border-primary bg-primary/5 ring-primary text-primary ring-1' : 'border-border text-muted-foreground' } `} @@ -144,15 +158,25 @@ export function PasoModoCardGroup({ tabIndex={0} onClick={(e) => { e.stopPropagation() - onChange((w) => ({ ...w, subModoClonado: 'TRADICIONAL' })) + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'CLONADO_TRADICIONAL', + }), + ) }} onKeyDown={(e: React.KeyboardEvent) => handleKeyActivate(e, () => - onChange((w) => ({ ...w, subModoClonado: 'TRADICIONAL' })), + onChange( + (w): NewPlanWizardState => ({ + ...w, + tipoOrigen: 'CLONADO_TRADICIONAL', + }), + ), ) } className={`hover:border-primary/50 hover:bg-accent flex cursor-pointer flex-row items-center justify-center gap-2 rounded-lg border p-4 text-center transition-all sm:flex-col ${ - isSubSelected('TRADICIONAL') + isSelected('CLONADO_TRADICIONAL') ? 'border-primary bg-primary/5 ring-primary text-primary ring-1' : 'border-border text-muted-foreground' } `} diff --git a/src/data/types/domain.ts b/src/data/types/domain.ts index 1f8c2c2..a66fdb7 100644 --- a/src/data/types/domain.ts +++ b/src/data/types/domain.ts @@ -1,4 +1,4 @@ -import type { Database, Enums, Tables } from "../../types/supabase"; +import type { Enums, Tables } from "../../types/supabase"; export type UUID = string; @@ -52,14 +52,14 @@ export type PlanDatosSep = { }; export type PlanEstudioWithRel = - & Database["public"]["Tables"]["planes_estudio"]["Row"] + & Tables<"planes_estudio"> & { carreras: - | Database["public"]["Tables"]["carreras"]["Row"] & { - facultades: Database["public"]["Tables"]["facultades"]["Row"] | null; + | Tables<"carreras"> & { + facultades: Tables<"facultades"> | null; } | null; - estados_plan: Database["public"]["Tables"]["estados_plan"]["Row"] | null; + estados_plan: Tables<"estados_plan"> | null; }; export type Paged = { data: Array; count: number | null }; diff --git a/src/features/planes/nuevo/NuevoPlanModalContainer.tsx b/src/features/planes/nuevo/NuevoPlanModalContainer.tsx index 2ca1e57..caafbeb 100644 --- a/src/features/planes/nuevo/NuevoPlanModalContainer.tsx +++ b/src/features/planes/nuevo/NuevoPlanModalContainer.tsx @@ -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} /> diff --git a/src/features/planes/nuevo/catalogs.ts b/src/features/planes/nuevo/catalogs.ts index 805193e..afe98be 100644 --- a/src/features/planes/nuevo/catalogs.ts +++ b/src/features/planes/nuevo/catalogs.ts @@ -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 = [ "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 = [ + "Semestre", + "Cuatrimestre", + "Trimestre", + "Otro", ]; export const PLANES_EXISTENTES = [ diff --git a/src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts b/src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts index 7ee4bfb..0700f81 100644 --- a/src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts +++ b/src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts @@ -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({ 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 }, diff --git a/src/features/planes/nuevo/types.ts b/src/features/planes/nuevo/types.ts index 6ae3de0..825715a 100644 --- a/src/features/planes/nuevo/types.ts +++ b/src/features/planes/nuevo/types.ts @@ -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; repositoriosReferencia?: Array; diff --git a/src/types/supabase.ts b/src/types/supabase.ts index 9953cdaaf58bc0a01cf33dc0b02daac3d9951456..8165023797e92bd172e4df3fe379ac37e584fd45 100644 GIT binary patch literal 38590 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 ayC%)Dbgh3kbi5P4;h%Rbt(bJeC;tyBr_nzE literal 83786 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