wip
This commit is contained in:
5
bun.lock
5
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=="],
|
||||
|
||||
@@ -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",
|
||||
|
||||
18
scripts/update-types.ts
Normal file
18
scripts/update-types.ts
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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<React.SetStateAction<NewPlanWizardState>>
|
||||
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 (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
@@ -40,7 +54,7 @@ export function PasoBasicosForm({
|
||||
</Label>
|
||||
<Input
|
||||
id="nombrePlan"
|
||||
placeholder="Ej. Ingeniería en Sistemas 2026"
|
||||
placeholder="Ej. Ingeniería en Sistemas (2026)"
|
||||
value={wizard.datosBasicos.nombrePlan}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
onChange((w) => ({
|
||||
@@ -79,7 +93,7 @@ export function PasoBasicosForm({
|
||||
<SelectValue placeholder="Ej. Facultad de Ingeniería" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{FACULTADES.map((f) => (
|
||||
{facultadesList.map((f: any) => (
|
||||
<SelectItem key={f.id} value={f.id}>
|
||||
{f.nombre}
|
||||
</SelectItem>
|
||||
@@ -112,7 +126,7 @@ export function PasoBasicosForm({
|
||||
<SelectValue placeholder="Ej. Ingeniería en Cibernética y Sistemas Computacionales" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{carrerasFiltradas.map((c) => (
|
||||
{filteredCarreras.map((c: any) => (
|
||||
<SelectItem key={c.id} value={c.id}>
|
||||
{c.nombre}
|
||||
</SelectItem>
|
||||
@@ -125,11 +139,13 @@ export function PasoBasicosForm({
|
||||
<Label htmlFor="nivel">Nivel</Label>
|
||||
<Select
|
||||
value={wizard.datosBasicos.nivel}
|
||||
onValueChange={(value) =>
|
||||
onChange((w) => ({
|
||||
onValueChange={(value: NivelPlanEstudio) =>
|
||||
onChange(
|
||||
(w): NewPlanWizardState => ({
|
||||
...w,
|
||||
datosBasicos: { ...w.datosBasicos, nivel: value },
|
||||
}))
|
||||
}),
|
||||
)
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
@@ -157,7 +173,7 @@ export function PasoBasicosForm({
|
||||
<Label htmlFor="tipoCiclo">Tipo de ciclo</Label>
|
||||
<Select
|
||||
value={wizard.datosBasicos.tipoCiclo}
|
||||
onValueChange={(value) =>
|
||||
onValueChange={(value: TipoCiclo) =>
|
||||
onChange((w) => ({
|
||||
...w,
|
||||
datosBasicos: {
|
||||
@@ -180,8 +196,8 @@ export function PasoBasicosForm({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{TIPOS_CICLO.map((t) => (
|
||||
<SelectItem key={t.value} value={t.value}>
|
||||
{t.label}
|
||||
<SelectItem key={t} value={t}>
|
||||
{t}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -30,7 +30,7 @@ export function PasoDetallesPanel({
|
||||
onGenerarIA: () => void
|
||||
isLoading: boolean
|
||||
}) {
|
||||
if (wizard.modoCreacion === 'MANUAL') {
|
||||
if (wizard.tipoOrigen === 'MANUAL') {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -43,7 +43,7 @@ export function PasoDetallesPanel({
|
||||
)
|
||||
}
|
||||
|
||||
if (wizard.modoCreacion === 'IA') {
|
||||
if (wizard.tipoOrigen === 'IA') {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
@@ -162,10 +162,7 @@ export function PasoDetallesPanel({
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
wizard.modoCreacion === 'CLONADO' &&
|
||||
wizard.subModoClonado === 'INTERNO'
|
||||
) {
|
||||
if (wizard.tipoOrigen === 'CLONADO_INTERNO') {
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
@@ -269,10 +266,7 @@ export function PasoDetallesPanel({
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
wizard.modoCreacion === 'CLONADO' &&
|
||||
wizard.subModoClonado === 'TRADICIONAL'
|
||||
) {
|
||||
if (wizard.tipoOrigen === 'CLONADO_TRADICIONAL') {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
|
||||
@@ -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<React.SetStateAction<NewPlanWizardState>>
|
||||
}) {
|
||||
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({
|
||||
<Card
|
||||
className={isSelected('MANUAL') ? 'ring-ring ring-2' : ''}
|
||||
onClick={() =>
|
||||
onChange((w) => ({
|
||||
onChange(
|
||||
(w): NewPlanWizardState => ({
|
||||
...w,
|
||||
modoCreacion: 'MANUAL',
|
||||
subModoClonado: undefined,
|
||||
}))
|
||||
tipoOrigen: 'MANUAL',
|
||||
}),
|
||||
)
|
||||
}
|
||||
onKeyDown={(e: React.KeyboardEvent) =>
|
||||
handleKeyActivate(e, () =>
|
||||
onChange((w) => ({
|
||||
onChange(
|
||||
(w): NewPlanWizardState => ({
|
||||
...w,
|
||||
modoCreacion: 'MANUAL',
|
||||
subModoClonado: undefined,
|
||||
})),
|
||||
tipoOrigen: 'MANUAL',
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
role="button"
|
||||
@@ -70,19 +68,21 @@ export function PasoModoCardGroup({
|
||||
<Card
|
||||
className={isSelected('IA') ? 'ring-ring ring-2' : ''}
|
||||
onClick={() =>
|
||||
onChange((w) => ({
|
||||
onChange(
|
||||
(w): NewPlanWizardState => ({
|
||||
...w,
|
||||
modoCreacion: 'IA',
|
||||
subModoClonado: undefined,
|
||||
}))
|
||||
tipoOrigen: 'IA',
|
||||
}),
|
||||
)
|
||||
}
|
||||
onKeyDown={(e: React.KeyboardEvent) =>
|
||||
handleKeyActivate(e, () =>
|
||||
onChange((w) => ({
|
||||
onChange(
|
||||
(w): NewPlanWizardState => ({
|
||||
...w,
|
||||
modoCreacion: 'IA',
|
||||
subModoClonado: undefined,
|
||||
})),
|
||||
tipoOrigen: 'IA',
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
role="button"
|
||||
@@ -99,11 +99,13 @@ export function PasoModoCardGroup({
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
className={isSelected('CLONADO') ? 'ring-ring ring-2' : ''}
|
||||
onClick={() => 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({
|
||||
</CardTitle>
|
||||
<CardDescription>Desde un plan existente o archivos.</CardDescription>
|
||||
</CardHeader>
|
||||
{wizard.modoCreacion === 'CLONADO' && (
|
||||
{(wizard.tipoOrigen === 'OTRO' ||
|
||||
wizard.tipoOrigen === 'CLONADO_INTERNO' ||
|
||||
wizard.tipoOrigen === 'CLONADO_TRADICIONAL') && (
|
||||
<CardContent className="flex flex-col gap-3">
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={(e) => {
|
||||
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'
|
||||
} `}
|
||||
|
||||
@@ -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<T> = { data: Array<T>; count: number | null };
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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,17 +74,16 @@ 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") {
|
||||
if (wizard.tipoOrigen === "CLONADO_INTERNO") {
|
||||
return !!wizard.clonInterno?.planOrigenId;
|
||||
}
|
||||
if (wizard.subModoClonado === "TRADICIONAL") {
|
||||
if (wizard.tipoOrigen === "CLONADO_TRADICIONAL") {
|
||||
const t = wizard.clonTradicional;
|
||||
if (!t) return false;
|
||||
const tieneWord = !!t.archivoWordPlanId;
|
||||
@@ -91,7 +91,6 @@ export function useNuevoPlanWizard() {
|
||||
!!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 },
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user