From ef177a3f92731fea2ab45b2e1fa43ea3081a11c8 Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Tue, 6 Jan 2026 13:46:57 -0600 Subject: [PATCH 01/11] wip --- .../shadcn-studio/checkbox/checkbox-13.tsx | 32 +++ src/components/shadcn-studio/tabs/tabs-03.tsx | 76 +++++++ src/components/ui/checkbox.tsx | 30 +++ src/components/ui/tabs.tsx | 64 ++++++ .../nueva/NuevaAsignaturaModalContainer.tsx | 127 +++++++++++ src/features/asignaturas/nueva/catalogs.ts | 56 +++++ .../nueva/hooks/useNuevaAsignaturaWizard.ts | 90 ++++++++ src/features/asignaturas/nueva/types.ts | 45 ++++ .../planes/nuevo/NuevoPlanModalContainer.tsx | 205 ++++++++++++++++++ src/features/planes/nuevo/catalogs.ts | 116 ++++++++++ .../planes/nuevo/hooks/useNuevoPlanWizard.ts | 115 ++++++++++ src/features/planes/nuevo/types.ts | 41 ++++ 12 files changed, 997 insertions(+) create mode 100644 src/components/shadcn-studio/checkbox/checkbox-13.tsx create mode 100644 src/components/shadcn-studio/tabs/tabs-03.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/features/asignaturas/nueva/NuevaAsignaturaModalContainer.tsx create mode 100644 src/features/asignaturas/nueva/catalogs.ts create mode 100644 src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts create mode 100644 src/features/asignaturas/nueva/types.ts create mode 100644 src/features/planes/nuevo/NuevoPlanModalContainer.tsx create mode 100644 src/features/planes/nuevo/catalogs.ts create mode 100644 src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts create mode 100644 src/features/planes/nuevo/types.ts diff --git a/src/components/shadcn-studio/checkbox/checkbox-13.tsx b/src/components/shadcn-studio/checkbox/checkbox-13.tsx new file mode 100644 index 0000000..2c3e8f8 --- /dev/null +++ b/src/components/shadcn-studio/checkbox/checkbox-13.tsx @@ -0,0 +1,32 @@ +import { Checkbox } from '@/components/ui/checkbox' +import { Label } from '@/components/ui/label' + +const CheckboxCardDemo = () => { + return ( +
+ + +
+ ) +} + +export default CheckboxCardDemo diff --git a/src/components/shadcn-studio/tabs/tabs-03.tsx b/src/components/shadcn-studio/tabs/tabs-03.tsx new file mode 100644 index 0000000..f1cd723 --- /dev/null +++ b/src/components/shadcn-studio/tabs/tabs-03.tsx @@ -0,0 +1,76 @@ +import { BookIcon, GiftIcon, HeartIcon } from 'lucide-react' + +import CheckboxCardDemo from '../checkbox/checkbox-13' + +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' + +const tabs = [ + { + name: 'Explore', + value: 'explore', + icon: BookIcon, + content: ( + <> + + + ), + }, + { + name: 'Favorites', + value: 'favorites', + icon: HeartIcon, + content: ( + <> + All your{' '} + favorites are + saved here. Revisit articles, collections, and moments you love, any + time you want a little inspiration. + + ), + }, + { + name: 'Surprise', + value: 'surprise', + icon: GiftIcon, + content: ( + <> + Surprise!{' '} + Here's something unexpected—a fun fact, a quirky tip, or a daily + challenge. Come back for a new surprise every day! + + ), + }, +] + +const TabsWithIconDemo = () => { + return ( +
+ + + {tabs.map(({ icon: Icon, name, value }) => ( + + + {name} + + ))} + + + {tabs.map((tab) => ( + +

{tab.content}

+
+ ))} +
+
+ ) +} + +export default TabsWithIconDemo diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..0e2a6cd --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { CheckIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Checkbox({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ) +} + +export { Checkbox } diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx new file mode 100644 index 0000000..eeec6d4 --- /dev/null +++ b/src/components/ui/tabs.tsx @@ -0,0 +1,64 @@ +import * as TabsPrimitive from '@radix-ui/react-tabs' +import * as React from 'react' + +import { cn } from '@/lib/utils' + +function Tabs({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/src/features/asignaturas/nueva/NuevaAsignaturaModalContainer.tsx b/src/features/asignaturas/nueva/NuevaAsignaturaModalContainer.tsx new file mode 100644 index 0000000..ba66cb1 --- /dev/null +++ b/src/features/asignaturas/nueva/NuevaAsignaturaModalContainer.tsx @@ -0,0 +1,127 @@ +import { useNavigate } from '@tanstack/react-router' + +import { useNuevaAsignaturaWizard } from './hooks/useNuevaAsignaturaWizard' + +import { PasoBasicosForm } from '@/components/asignaturas/wizard/PasoBasicosForm' +import { PasoConfiguracionPanel } from '@/components/asignaturas/wizard/PasoConfiguracionPanel' +import { PasoMetodoCardGroup } from '@/components/asignaturas/wizard/PasoMetodoCardGroup' +import { PasoResumenCard } from '@/components/asignaturas/wizard/PasoResumenCard' +import { VistaSinPermisos } from '@/components/asignaturas/wizard/VistaSinPermisos' +import { WizardControls } from '@/components/asignaturas/wizard/WizardControls' +import { WizardHeader } from '@/components/asignaturas/wizard/WizardHeader' +import { defineStepper } from '@/components/stepper' +import { Dialog, DialogContent } from '@/components/ui/dialog' + +const Wizard = defineStepper( + { + id: 'metodo', + title: 'Método', + description: 'Manual, IA o Clonado', + }, + { + id: 'basicos', + title: 'Datos básicos', + description: 'Nombre y estructura', + }, + { + id: 'configuracion', + title: 'Configuración', + description: 'Detalles según modo', + }, + { + id: 'resumen', + title: 'Resumen', + description: 'Confirmar creación', + }, +) + +const auth_get_current_user_role = () => 'JEFE_CARRERA' as const + +export function NuevaAsignaturaModalContainer({ planId }: { planId: string }) { + const navigate = useNavigate() + const role = auth_get_current_user_role() + + const { + wizard, + setWizard, + canContinueDesdeMetodo, + canContinueDesdeBasicos, + canContinueDesdeConfig, + simularGeneracionIA, + crearAsignatura, + } = useNuevaAsignaturaWizard(planId) + + const handleClose = () => { + navigate({ to: `/planes/${planId}/asignaturas`, resetScroll: false }) + } + + return ( + !open && handleClose()}> + e.preventDefault()} + > + {role !== 'JEFE_CARRERA' ? ( + + ) : ( + + {({ methods }) => ( + <> + + +
+
+ {Wizard.utils.getIndex(methods.current.id) === 0 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 1 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 2 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 3 && ( + + + + )} +
+
+ + crearAsignatura(handleClose)} + /> + + )} +
+ )} +
+
+ ) +} diff --git a/src/features/asignaturas/nueva/catalogs.ts b/src/features/asignaturas/nueva/catalogs.ts new file mode 100644 index 0000000..97ea60a --- /dev/null +++ b/src/features/asignaturas/nueva/catalogs.ts @@ -0,0 +1,56 @@ +import type { TipoAsignatura } from "./types"; + +export const ESTRUCTURAS_SEP = [ + { id: "sep-lic-2025", label: "Licenciatura SEP v2025" }, + { id: "sep-pos-2023", label: "Posgrado SEP v2023" }, + { id: "ulsa-int-2024", label: "Estándar Interno ULSA 2024" }, +]; + +export const TIPOS_MATERIA: Array<{ value: TipoAsignatura; label: string }> = [ + { value: "OBLIGATORIA", label: "Obligatoria" }, + { value: "OPTATIVA", label: "Optativa" }, + { value: "TRONCAL", label: "Troncal / Eje común" }, + { value: "OTRO", label: "Otro" }, +]; + +export const FACULTADES = [ + { id: "ing", nombre: "Facultad de Ingeniería" }, + { id: "med", nombre: "Facultad de Medicina" }, + { id: "neg", nombre: "Facultad de Negocios" }, +]; + +export const CARRERAS = [ + { id: "sis", nombre: "Ing. en Sistemas", facultadId: "ing" }, + { id: "ind", nombre: "Ing. Industrial", facultadId: "ing" }, + { id: "medico", nombre: "Médico Cirujano", facultadId: "med" }, + { id: "act", nombre: "Actuaría", facultadId: "neg" }, +]; + +export const PLANES_MOCK = [ + { id: "p1", nombre: "Plan 2010 Sistemas", carreraId: "sis" }, + { id: "p2", nombre: "Plan 2016 Sistemas", carreraId: "sis" }, + { id: "p3", nombre: "Plan 2015 Industrial", carreraId: "ind" }, +]; + +export const MATERIAS_MOCK = [ + { + id: "m1", + nombre: "Programación Orientada a Objetos", + creditos: 8, + clave: "POO-101", + }, + { id: "m2", nombre: "Cálculo Diferencial", creditos: 6, clave: "MAT-101" }, + { id: "m3", nombre: "Ética Profesional", creditos: 4, clave: "HUM-302" }, + { + id: "m4", + nombre: "Bases de Datos Avanzadas", + creditos: 8, + clave: "BD-201", + }, +]; + +export const ARCHIVOS_SISTEMA_MOCK = [ + { id: "doc1", name: "Sílabo_Base_Ingenieria.pdf" }, + { id: "doc2", name: "Competencias_Egreso_2025.docx" }, + { id: "doc3", name: "Reglamento_Academico.pdf" }, +]; diff --git a/src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts b/src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts new file mode 100644 index 0000000..dd5c14c --- /dev/null +++ b/src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts @@ -0,0 +1,90 @@ +import { useState } from "react"; + +import type { AsignaturaPreview, NewSubjectWizardState } from "../types"; + +export function useNuevaAsignaturaWizard(planId: string) { + const [wizard, setWizard] = useState({ + step: 1, + planId, + modoCreacion: null, + datosBasicos: { + nombre: "", + clave: "", + tipo: "OBLIGATORIA", + creditos: 0, + horasSemana: 0, + estructuraId: "", + }, + clonInterno: {}, + clonTradicional: { + archivoWordAsignaturaId: null, + archivosAdicionalesIds: [], + }, + iaConfig: { + descripcionEnfoque: "", + notasAdicionales: "", + archivosExistentesIds: [], + }, + resumen: {}, + isLoading: false, + errorMessage: null, + }); + + const canContinueDesdeMetodo = wizard.modoCreacion === "MANUAL" || + wizard.modoCreacion === "IA" || + (wizard.modoCreacion === "CLONADO" && !!wizard.subModoClonado); + + const canContinueDesdeBasicos = !!wizard.datosBasicos.nombre && + wizard.datosBasicos.creditos > 0 && + !!wizard.datosBasicos.estructuraId; + + const canContinueDesdeConfig = (() => { + if (wizard.modoCreacion === "MANUAL") return true; + if (wizard.modoCreacion === "IA") { + return !!wizard.iaConfig?.descripcionEnfoque; + } + if (wizard.modoCreacion === "CLONADO") { + if (wizard.subModoClonado === "INTERNO") { + return !!wizard.clonInterno?.asignaturaOrigenId; + } + if (wizard.subModoClonado === "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 (onCreated: () => void) => { + setWizard((w) => ({ ...w, isLoading: true })); + await new Promise((r) => setTimeout(r, 1000)); + onCreated(); + }; + + return { + wizard, + setWizard, + canContinueDesdeMetodo, + canContinueDesdeBasicos, + canContinueDesdeConfig, + simularGeneracionIA, + crearAsignatura, + }; +} diff --git a/src/features/asignaturas/nueva/types.ts b/src/features/asignaturas/nueva/types.ts new file mode 100644 index 0000000..799b0d5 --- /dev/null +++ b/src/features/asignaturas/nueva/types.ts @@ -0,0 +1,45 @@ +export type ModoCreacion = "MANUAL" | "IA" | "CLONADO"; +export type SubModoClonado = "INTERNO" | "TRADICIONAL"; +export type TipoAsignatura = "OBLIGATORIA" | "OPTATIVA" | "TRONCAL" | "OTRO"; + +export type AsignaturaPreview = { + nombre: string; + objetivo: string; + unidades: number; + bibliografiaCount: number; +}; + +export type NewSubjectWizardState = { + step: 1 | 2 | 3 | 4; + planId: string; + modoCreacion: ModoCreacion | null; + subModoClonado?: SubModoClonado; + datosBasicos: { + nombre: string; + clave?: string; + tipo: TipoAsignatura; + creditos: number; + horasSemana?: number; + estructuraId: string; + }; + clonInterno?: { + facultadId?: string; + carreraId?: string; + planOrigenId?: string; + asignaturaOrigenId?: string | null; + }; + clonTradicional?: { + archivoWordAsignaturaId: string | null; + archivosAdicionalesIds: Array; + }; + iaConfig?: { + descripcionEnfoque: string; + notasAdicionales: string; + archivosExistentesIds: Array; + }; + resumen: { + previewAsignatura?: AsignaturaPreview; + }; + isLoading: boolean; + errorMessage: string | null; +}; diff --git a/src/features/planes/nuevo/NuevoPlanModalContainer.tsx b/src/features/planes/nuevo/NuevoPlanModalContainer.tsx new file mode 100644 index 0000000..8f985da --- /dev/null +++ b/src/features/planes/nuevo/NuevoPlanModalContainer.tsx @@ -0,0 +1,205 @@ +import { useNavigate } from '@tanstack/react-router' +import * as Icons from 'lucide-react' + +import { useNuevoPlanWizard } from './hooks/useNuevoPlanWizard' + +import { PasoBasicosForm } from '@/components/planes/wizard/PasoBasicosForm' +import { PasoDetallesPanel } from '@/components/planes/wizard/PasoDetallesPanel' +import { PasoModoCardGroup } from '@/components/planes/wizard/PasoModoCardGroup' +import { PasoResumenCard } from '@/components/planes/wizard/PasoResumenCard' +import { WizardControls } from '@/components/planes/wizard/WizardControls' +import { WizardHeader } from '@/components/planes/wizard/WizardHeader' +import { defineStepper } from '@/components/stepper' +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' + +// Mock de permisos/rol +const auth_get_current_user_role = () => 'JEFE_CARRERA' as const + +const Wizard = defineStepper( + { + id: 'modo', + title: 'Método', + description: 'Selecciona cómo crearás el plan', + }, + { + id: 'basicos', + title: 'Datos básicos', + description: 'Nombre, carrera, nivel y ciclos', + }, + { id: 'detalles', title: 'Detalles', description: 'IA, clonado o archivos' }, + { id: 'resumen', title: 'Resumen', description: 'Confirma y crea el plan' }, +) + +export default function NuevoPlanModalContainer() { + const navigate = useNavigate() + const role = auth_get_current_user_role() + + const { + wizard, + setWizard, + carrerasFiltradas, + canContinueDesdeModo, + canContinueDesdeBasicos, + canContinueDesdeDetalles, + generarPreviewIA, + } = useNuevoPlanWizard() + + const handleClose = () => { + navigate({ to: '/planes', resetScroll: false }) + } + + const crearPlan = async () => { + setWizard((w) => ({ ...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' + return 'plan_new_import_001' + })() + navigate({ to: `/planes/${nuevoId}` }) + } + + return ( + !open && handleClose()}> + { + e.preventDefault() + }} + > + {role !== 'JEFE_CARRERA' ? ( + <> + + Nuevo plan de estudios + +
+ + + + + Sin permisos + + + No tienes permisos para crear planes de estudio. + + + + + + +
+ + ) : ( + + {({ methods }) => { + const currentIndex = Wizard.utils.getIndex(methods.current.id) + 1 + const totalSteps = Wizard.steps.length + const nextStep = Wizard.steps[currentIndex] + + return ( + <> + + +
+
+ {Wizard.utils.getIndex(methods.current.id) === 0 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 1 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 2 && ( + + + + )} + {Wizard.utils.getIndex(methods.current.id) === 3 && ( + + + + )} +
+
+ +
+ + methods.prev()} + onNext={() => methods.next()} + onCreate={crearPlan} + disablePrev={ + Wizard.utils.getIndex(methods.current.id) === 0 || + wizard.isLoading + } + disableNext={ + wizard.isLoading || + (Wizard.utils.getIndex(methods.current.id) === 0 && + !canContinueDesdeModo) || + (Wizard.utils.getIndex(methods.current.id) === 1 && + !canContinueDesdeBasicos) || + (Wizard.utils.getIndex(methods.current.id) === 2 && + !canContinueDesdeDetalles) + } + disableCreate={wizard.isLoading} + isLastStep={ + Wizard.utils.getIndex(methods.current.id) >= + Wizard.steps.length - 1 + } + /> + +
+ + ) + }} +
+ )} +
+
+ ) +} diff --git a/src/features/planes/nuevo/catalogs.ts b/src/features/planes/nuevo/catalogs.ts new file mode 100644 index 0000000..28c3d57 --- /dev/null +++ b/src/features/planes/nuevo/catalogs.ts @@ -0,0 +1,116 @@ +import type { TipoCiclo } from "./types"; + +export const FACULTADES = [ + { id: "ing", nombre: "Facultad de Ingeniería" }, + { + id: "med", + nombre: "Facultad de Medicina en medicina en medicina en medicina", + }, + { id: "neg", nombre: "Facultad de Negocios" }, +]; + +export const CARRERAS = [ + { id: "sis", nombre: "Ing. en Sistemas", facultadId: "ing" }, + { id: "ind", nombre: "Ing. Industrial", facultadId: "ing" }, + { id: "medico", nombre: "Médico Cirujano", facultadId: "med" }, + { id: "act", nombre: "Actuaría", facultadId: "neg" }, +]; + +export const NIVELES = [ + "Licenciatura", + "Especialidad", + "Maestría", + "Doctorado", +]; +export const TIPOS_CICLO: Array<{ value: TipoCiclo; label: string }> = [ + { value: "SEMESTRE", label: "Semestre" }, + { value: "CUATRIMESTRE", label: "Cuatrimestre" }, + { value: "TRIMESTRE", label: "Trimestre" }, +]; + +export const PLANES_EXISTENTES = [ + { + id: "plan-2021-sis", + nombre: "ISC 2021", + estado: "Aprobado", + anio: 2021, + facultadId: "ing", + carreraId: "sis", + }, + { + id: "plan-2020-ind", + nombre: "I. Industrial 2020", + estado: "Aprobado", + anio: 2020, + facultadId: "ing", + carreraId: "ind", + }, + { + id: "plan-2019-med", + nombre: "Medicina 2019", + estado: "Vigente", + anio: 2019, + facultadId: "med", + carreraId: "medico", + }, +]; + +export const ARCHIVOS = [ + { + id: "file-1", + nombre: "Sílabo POO 2023.docx", + tipo: "docx", + tamaño: "245 KB", + }, + { + id: "file-2", + nombre: "Guía de prácticas BD.pdf", + tipo: "pdf", + tamaño: "1.2 MB", + }, + { + id: "file-3", + nombre: "Rúbrica evaluación proyectos.xlsx", + tipo: "xlsx", + tamaño: "89 KB", + }, + { + id: "file-4", + nombre: "Banco de reactivos IA.docx", + tipo: "docx", + tamaño: "567 KB", + }, + { + id: "file-5", + nombre: "Material didáctico Web.pdf", + tipo: "pdf", + tamaño: "3.4 MB", + }, +]; + +export const REPOSITORIOS = [ + { + id: "repo-1", + nombre: "Materiales ISC 2024", + descripcion: "Documentos de referencia para Ingeniería en Sistemas", + cantidadArchivos: 45, + }, + { + id: "repo-2", + nombre: "Lineamientos SEP", + descripcion: "Documentos oficiales y normativas SEP actualizadas", + cantidadArchivos: 12, + }, + { + id: "repo-3", + nombre: "Bibliografía Digital", + descripcion: "Recursos bibliográficos digitalizados", + cantidadArchivos: 128, + }, + { + id: "repo-4", + nombre: "Plantillas Institucionales", + descripcion: "Formatos y plantillas oficiales ULSA", + cantidadArchivos: 23, + }, +]; diff --git a/src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts b/src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts new file mode 100644 index 0000000..b94d332 --- /dev/null +++ b/src/features/planes/nuevo/hooks/useNuevoPlanWizard.ts @@ -0,0 +1,115 @@ +import { useMemo, useState } from "react"; + +import { CARRERAS } from "../catalogs"; + +import type { NewPlanWizardState, PlanPreview, TipoCiclo } from "../types"; + +export function useNuevoPlanWizard() { + const [wizard, setWizard] = useState({ + step: 1, + modoCreacion: null, + datosBasicos: { + nombrePlan: "", + carreraId: "", + facultadId: "", + nivel: "", + tipoCiclo: "", + numCiclos: undefined, + }, + clonInterno: { planOrigenId: null }, + clonTradicional: { + archivoWordPlanId: null, + archivoMapaExcelId: null, + archivoAsignaturasExcelId: null, + }, + iaConfig: { + descripcionEnfoque: "", + poblacionObjetivo: "", + notasAdicionales: "", + archivosReferencia: [], + }, + resumen: {}, + isLoading: false, + errorMessage: null, + }); + + const carrerasFiltradas = useMemo(() => { + const fac = wizard.datosBasicos.facultadId; + 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 canContinueDesdeBasicos = !!wizard.datosBasicos.nombrePlan && + !!wizard.datosBasicos.carreraId && + !!wizard.datosBasicos.facultadId && + !!wizard.datosBasicos.nivel && + (wizard.datosBasicos.numCiclos !== undefined && + wizard.datosBasicos.numCiclos > 0); + + const canContinueDesdeDetalles = (() => { + if (wizard.modoCreacion === "MANUAL") return true; + if (wizard.modoCreacion === "IA") { + return !!wizard.iaConfig?.descripcionEnfoque; + } + 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; + } + } + return false; + })(); + + const generarPreviewIA = async () => { + setWizard((w) => ({ ...w, isLoading: true, errorMessage: null })); + await new Promise((r) => setTimeout(r, 800)); + // Ensure preview has the stricter types required by `PlanPreview`. + let tipoCicloSafe: TipoCiclo; + if (wizard.datosBasicos.tipoCiclo === "") { + tipoCicloSafe = "SEMESTRE"; + } else { + tipoCicloSafe = wizard.datosBasicos.tipoCiclo; + } + const numCiclosSafe: number = + typeof wizard.datosBasicos.numCiclos === "number" + ? wizard.datosBasicos.numCiclos + : 1; + + const preview: PlanPreview = { + nombrePlan: wizard.datosBasicos.nombrePlan || "Plan sin nombre", + nivel: wizard.datosBasicos.nivel || "Licenciatura", + tipoCiclo: tipoCicloSafe, + numCiclos: numCiclosSafe, + numAsignaturasAprox: numCiclosSafe * 6, + secciones: [ + { id: "obj", titulo: "Objetivos", resumen: "Borrador de objetivos…" }, + { id: "perfil", titulo: "Perfil de egreso", resumen: "Borrador…" }, + ], + }; + setWizard((w) => ({ + ...w, + isLoading: false, + resumen: { previewPlan: preview }, + })); + }; + + return { + wizard, + setWizard, + carrerasFiltradas, + canContinueDesdeModo, + canContinueDesdeBasicos, + canContinueDesdeDetalles, + generarPreviewIA, + }; +} diff --git a/src/features/planes/nuevo/types.ts b/src/features/planes/nuevo/types.ts new file mode 100644 index 0000000..49c08b1 --- /dev/null +++ b/src/features/planes/nuevo/types.ts @@ -0,0 +1,41 @@ +export type TipoCiclo = "SEMESTRE" | "CUATRIMESTRE" | "TRIMESTRE"; +export type ModoCreacion = "MANUAL" | "IA" | "CLONADO"; +export type SubModoClonado = "INTERNO" | "TRADICIONAL"; + +export type PlanPreview = { + nombrePlan: string; + nivel: string; + tipoCiclo: TipoCiclo; + numCiclos: number; + numAsignaturasAprox?: number; + secciones?: Array<{ id: string; titulo: string; resumen: string }>; +}; + +export type NewPlanWizardState = { + step: 1 | 2 | 3 | 4; + modoCreacion: ModoCreacion | null; + subModoClonado?: SubModoClonado; + datosBasicos: { + nombrePlan: string; + carreraId: string; + facultadId: string; + nivel: string; + tipoCiclo: TipoCiclo | ""; + numCiclos: number | undefined; + }; + clonInterno?: { planOrigenId: string | null }; + clonTradicional?: { + archivoWordPlanId: string | null; + archivoMapaExcelId: string | null; + archivoAsignaturasExcelId: string | null; + }; + iaConfig?: { + descripcionEnfoque: string; + poblacionObjetivo: string; + notasAdicionales: string; + archivosReferencia: Array; + }; + resumen: { previewPlan?: PlanPreview }; + isLoading: boolean; + errorMessage: string | null; +}; From 69119aeaa6472fd2039081144173c979ae049f6d Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Tue, 6 Jan 2026 17:02:55 -0600 Subject: [PATCH 02/11] =?UTF-8?q?finalizada=20secci=C3=B3n=20de=20Referenc?= =?UTF-8?q?ias=20para=20la=20IA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lock | 42 +- components.json | 10 +- package.json | 7 +- .../asignaturas/wizard/PasoBasicosForm.tsx | 4 +- .../wizard/PasoConfiguracionPanel.tsx | 4 +- .../wizard/PasoMetodoCardGroup.tsx | 2 +- .../asignaturas/wizard/PasoResumenCard.tsx | 4 +- .../asignaturas/wizard/WizardControls.tsx | 2 +- .../planes/wizard/PasoBasicosForm.tsx | 9 +- .../planes/wizard/PasoDetallesPanel.tsx | 31 +- .../wizard/PasoDetallesPanel/FileDropZone.tsx | 186 ++++++ .../PasoDetallesPanel/ReferenciasParaIA.tsx | 138 +++++ .../planes/wizard/PasoModoCardGroup.tsx | 48 +- .../planes/wizard/PasoResumenCard.tsx | 2 +- .../shadcn-studio/checkbox/checkbox-13.tsx | 4 +- src/components/shadcn-studio/tabs/tabs-27.tsx | 72 +++ src/components/ui/motion-highlight.tsx | 549 ++++++++++++++++++ src/components/ui/motion-tabs.tsx | 261 +++++++++ .../new/NuevaAsignaturaModalContainer.tsx | 127 ---- src/features/asignaturas/new/catalogs.ts | 56 -- .../new/hooks/useNuevaAsignaturaWizard.ts | 90 --- src/features/asignaturas/new/types.ts | 45 -- .../planes/new/NuevoPlanModalContainer.tsx | 205 ------- src/features/planes/new/catalogs.ts | 56 -- .../planes/new/hooks/useNuevoPlanWizard.ts | 102 ---- src/features/planes/new/types.ts | 41 -- .../planes/nuevo/hooks/useNuevoPlanWizard.ts | 12 +- .../$planId/asignaturas/_lista/nueva.tsx | 2 +- src/routes/planes/_lista/nuevo.tsx | 2 +- 29 files changed, 1337 insertions(+), 776 deletions(-) create mode 100644 src/components/planes/wizard/PasoDetallesPanel/FileDropZone.tsx create mode 100644 src/components/planes/wizard/PasoDetallesPanel/ReferenciasParaIA.tsx create mode 100644 src/components/shadcn-studio/tabs/tabs-27.tsx create mode 100644 src/components/ui/motion-highlight.tsx create mode 100644 src/components/ui/motion-tabs.tsx delete mode 100644 src/features/asignaturas/new/NuevaAsignaturaModalContainer.tsx delete mode 100644 src/features/asignaturas/new/catalogs.ts delete mode 100644 src/features/asignaturas/new/hooks/useNuevaAsignaturaWizard.ts delete mode 100644 src/features/asignaturas/new/types.ts delete mode 100644 src/features/planes/new/NuevoPlanModalContainer.tsx delete mode 100644 src/features/planes/new/catalogs.ts delete mode 100644 src/features/planes/new/hooks/useNuevoPlanWizard.ts delete mode 100644 src/features/planes/new/types.ts diff --git a/bun.lock b/bun.lock index b08e698..a195a69 100644 --- a/bun.lock +++ b/bun.lock @@ -1,17 +1,18 @@ { "lockfileVersion": 1, - "configVersion": 1, "workspaces": { "": { "name": "acad-ia-2", "dependencies": { "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@stepperize/react": "^5.1.9", "@tailwindcss/vite": "^4.0.6", @@ -24,10 +25,11 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", - "lucide-react": "^0.561.0", + "lucide-react": "^0.562.0", + "motion": "^12.24.7", "react": "^19.2.0", "react-dom": "^19.2.0", - "tailwind-merge": "^3.0.2", + "tailwind-merge": "^3.4.0", "tailwindcss": "^4.0.6", "tw-animate-css": "^1.3.6", }, @@ -254,6 +256,8 @@ "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.11", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q=="], + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], @@ -284,12 +288,16 @@ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g=="], "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], @@ -804,6 +812,8 @@ "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + "framer-motion": ["framer-motion@12.24.7", "", { "dependencies": { "motion-dom": "^12.24.3", "motion-utils": "^12.23.28", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-EolFLm7NdEMhWO/VTMZ0LlR4fLHGDiJItTx3i8dlyQooOOBoYAaysK4paGD4PrwqnoDdeDOS+TxnSBIAnNHs3w=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -994,7 +1004,7 @@ "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], - "lucide-react": ["lucide-react@0.561.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Y59gMY38tl4/i0qewcqohPdEbieBy7SovpBL9IFebhc2mDd8x4PZSOsiFRkpPcOq6bj1r/mjH/Rk73gSlIJP2A=="], + "lucide-react": ["lucide-react@0.562.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -1008,6 +1018,12 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "motion": ["motion@12.24.7", "", { "dependencies": { "framer-motion": "^12.24.7", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-0jOoqFlQ7JBvAcRhRv28pwUgZ1xw9WS4+tCU6aqYjxgiNVZCVi34ED2cihW3EgjIIWPBoZJis5og1mx/LmQWVQ=="], + + "motion-dom": ["motion-dom@12.24.3", "", { "dependencies": { "motion-utils": "^12.23.28" } }, "sha512-ZjMZCwhTglim0LM64kC1iFdm4o+2P9IKk3rl/Nb4RKsb5p4O9HJ1C2LWZXOFdsRtp6twpqWRXaFKOduF30ntow=="], + + "motion-utils": ["motion-utils@12.23.28", "", {}, "sha512-0W6cWd5Okoyf8jmessVK3spOmbyE0yTdNKujHctHH9XdAE4QDuZ1/LjSXC68rrhsJU+TkzXURC5OdSWh9ibOwQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], @@ -1310,6 +1326,10 @@ "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-checkbox/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-checkbox/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-collection/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], @@ -1338,12 +1358,20 @@ "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-roving-focus/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-select/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], "@radix-ui/react-select/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-tabs/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-tooltip/@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], "@radix-ui/react-tooltip/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], @@ -1414,6 +1442,8 @@ "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-checkbox/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], "@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], @@ -1422,6 +1452,10 @@ "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-roving-focus/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], "@tanstack/devtools/@tanstack/devtools-client/@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.3.5", "", {}, "sha512-RL1f5ZlfZMpghrCIdzl6mLOFLTuhqmPNblZgBaeKfdtk5rfbjykurv+VfYydOFXj0vxVIoA2d/zT7xfD7Ph8fw=="], diff --git a/components.json b/components.json index 6998bdf..5eeb81e 100644 --- a/components.json +++ b/components.json @@ -17,5 +17,11 @@ "lib": "@/lib", "hooks": "@/hooks" }, - "iconLibrary": "lucide" -} \ No newline at end of file + "iconLibrary": "lucide", + "registries": { + "@shadcn-studio": "https://shadcnstudio.com/r/{name}.json", + "@ss-components": "https://shadcnstudio.com/r/components/{name}.json", + "@ss-blocks": "https://shadcnstudio.com/r/blocks/{name}.json", + "@ss-themes": "https://shadcnstudio.com/r/themes/{name}.json" + } +} diff --git a/package.json b/package.json index 3a6b10f..e70e08b 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,14 @@ }, "dependencies": { "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@stepperize/react": "^5.1.9", "@tailwindcss/vite": "^4.0.6", @@ -36,10 +38,11 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", - "lucide-react": "^0.561.0", + "lucide-react": "^0.562.0", + "motion": "^12.24.7", "react": "^19.2.0", "react-dom": "^19.2.0", - "tailwind-merge": "^3.0.2", + "tailwind-merge": "^3.4.0", "tailwindcss": "^4.0.6", "tw-animate-css": "^1.3.6" }, diff --git a/src/components/asignaturas/wizard/PasoBasicosForm.tsx b/src/components/asignaturas/wizard/PasoBasicosForm.tsx index 4fdeacf..166907f 100644 --- a/src/components/asignaturas/wizard/PasoBasicosForm.tsx +++ b/src/components/asignaturas/wizard/PasoBasicosForm.tsx @@ -1,7 +1,7 @@ import type { NewSubjectWizardState, TipoAsignatura, -} from '@/features/asignaturas/new/types' +} from '@/features/asignaturas/nueva/types' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' @@ -15,7 +15,7 @@ import { import { ESTRUCTURAS_SEP, TIPOS_MATERIA, -} from '@/features/asignaturas/new/catalogs' +} from '@/features/asignaturas/nueva/catalogs' export function PasoBasicosForm({ wizard, diff --git a/src/components/asignaturas/wizard/PasoConfiguracionPanel.tsx b/src/components/asignaturas/wizard/PasoConfiguracionPanel.tsx index 947869d..a6e8429 100644 --- a/src/components/asignaturas/wizard/PasoConfiguracionPanel.tsx +++ b/src/components/asignaturas/wizard/PasoConfiguracionPanel.tsx @@ -1,6 +1,6 @@ import * as Icons from 'lucide-react' -import type { NewSubjectWizardState } from '@/features/asignaturas/new/types' +import type { NewSubjectWizardState } from '@/features/asignaturas/nueva/types' import { Button } from '@/components/ui/button' import { @@ -25,7 +25,7 @@ import { FACULTADES, MATERIAS_MOCK, PLANES_MOCK, -} from '@/features/asignaturas/new/catalogs' +} from '@/features/asignaturas/nueva/catalogs' export function PasoConfiguracionPanel({ wizard, diff --git a/src/components/asignaturas/wizard/PasoMetodoCardGroup.tsx b/src/components/asignaturas/wizard/PasoMetodoCardGroup.tsx index b5f1dc3..1ddcf4b 100644 --- a/src/components/asignaturas/wizard/PasoMetodoCardGroup.tsx +++ b/src/components/asignaturas/wizard/PasoMetodoCardGroup.tsx @@ -4,7 +4,7 @@ import type { ModoCreacion, NewSubjectWizardState, SubModoClonado, -} from '@/features/asignaturas/new/types' +} from '@/features/asignaturas/nueva/types' import { Card, diff --git a/src/components/asignaturas/wizard/PasoResumenCard.tsx b/src/components/asignaturas/wizard/PasoResumenCard.tsx index ef708dc..f6f64df 100644 --- a/src/components/asignaturas/wizard/PasoResumenCard.tsx +++ b/src/components/asignaturas/wizard/PasoResumenCard.tsx @@ -1,6 +1,6 @@ import * as Icons from 'lucide-react' -import type { NewSubjectWizardState } from '@/features/asignaturas/new/types' +import type { NewSubjectWizardState } from '@/features/asignaturas/nueva/types' import { Card, @@ -9,7 +9,7 @@ import { CardHeader, CardTitle, } from '@/components/ui/card' -import { ESTRUCTURAS_SEP } from '@/features/asignaturas/new/catalogs' +import { ESTRUCTURAS_SEP } from '@/features/asignaturas/nueva/catalogs' export function PasoResumenCard({ wizard }: { wizard: NewSubjectWizardState }) { return ( diff --git a/src/components/asignaturas/wizard/WizardControls.tsx b/src/components/asignaturas/wizard/WizardControls.tsx index 73b6e38..9e39b0e 100644 --- a/src/components/asignaturas/wizard/WizardControls.tsx +++ b/src/components/asignaturas/wizard/WizardControls.tsx @@ -1,4 +1,4 @@ -import type { NewSubjectWizardState } from '@/features/asignaturas/new/types' +import type { NewSubjectWizardState } from '@/features/asignaturas/nueva/types' import { Button } from '@/components/ui/button' diff --git a/src/components/planes/wizard/PasoBasicosForm.tsx b/src/components/planes/wizard/PasoBasicosForm.tsx index e7f16c1..8892b5a 100644 --- a/src/components/planes/wizard/PasoBasicosForm.tsx +++ b/src/components/planes/wizard/PasoBasicosForm.tsx @@ -1,5 +1,5 @@ -import type { CARRERAS } from '@/features/planes/new/catalogs' -import type { NewPlanWizardState } from '@/features/planes/new/types' +import type { CARRERAS } from '@/features/planes/nuevo/catalogs' +import type { NewPlanWizardState } from '@/features/planes/nuevo/types' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' @@ -14,7 +14,7 @@ import { FACULTADES, NIVELES, TIPOS_CICLO, -} from '@/features/planes/new/catalogs' +} from '@/features/planes/nuevo/catalogs' export function PasoBasicosForm({ wizard, @@ -146,7 +146,7 @@ export function PasoBasicosForm({ id="tipoCiclo" className="w-full min-w-0 [&>span]:block! [&>span]:truncate!" > - + {TIPOS_CICLO.map((t) => ( @@ -174,6 +174,7 @@ export function PasoBasicosForm({ }, })) } + placeholder="1" /> diff --git a/src/components/planes/wizard/PasoDetallesPanel.tsx b/src/components/planes/wizard/PasoDetallesPanel.tsx index 5ef68e0..b4ead0f 100644 --- a/src/components/planes/wizard/PasoDetallesPanel.tsx +++ b/src/components/planes/wizard/PasoDetallesPanel.tsx @@ -1,4 +1,6 @@ -import type { NewPlanWizardState } from '@/features/planes/new/types' +import ReferenciasParaIA from './PasoDetallesPanel/ReferenciasParaIA' + +import type { NewPlanWizardState } from '@/features/planes/nuevo/types' import { Button } from '@/components/ui/button' import { @@ -14,7 +16,7 @@ import { CARRERAS, FACULTADES, PLANES_EXISTENTES, -} from '@/features/planes/new/catalogs' +} from '@/features/planes/nuevo/catalogs' export function PasoDetallesPanel({ wizard, @@ -42,8 +44,8 @@ export function PasoDetallesPanel({ if (wizard.modoCreacion === 'IA') { return ( -
-
+
+