Compare commits
6 Commits
c27f05c5f6
...
bd8bef142a
| Author | SHA1 | Date | |
|---|---|---|---|
| bd8bef142a | |||
| 261dec7fa9 | |||
| 1acb18711f | |||
| f046bdcc04 | |||
| 12c572a442 | |||
| 64d9aa336f |
@@ -442,20 +442,13 @@ function DatosGenerales({
|
|||||||
? config.examples[0]
|
? config.examples[0]
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
// 2. CONTENIDO REAL (Viene de data.datos -> valoresActuales)
|
|
||||||
// El problema: Si 'description' en 'datos' es igual a la de la 'estructura',
|
|
||||||
// el usuario aún no ha redactado nada real.
|
|
||||||
|
|
||||||
const valActual = valoresActuales[key]
|
const valActual = valoresActuales[key]
|
||||||
|
|
||||||
// Lógica para determinar si mostrar el contenido o dejarlo vacío (para que salga el placeholder)
|
|
||||||
// Si el contenido en 'datos' es idéntico a la instrucción de la 'estructura',
|
|
||||||
// asumimos que no hay contenido real todavía.
|
|
||||||
const isContentEmpty =
|
const isContentEmpty =
|
||||||
!valActual?.description ||
|
!valActual?.description ||
|
||||||
valActual.description === config.description
|
valActual.description === config.description
|
||||||
|
|
||||||
const currentContent = valActual?.description ?? ''
|
const currentContent = valActual ?? ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InfoCard
|
<InfoCard
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export function WizardControls({
|
|||||||
const isLast = idx >= Wizard.steps.length - 1
|
const isLast = idx >= Wizard.steps.length - 1
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-none border-t bg-white p-6">
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
{wizard.errorMessage && (
|
{wizard.errorMessage && (
|
||||||
@@ -61,6 +60,5 @@ export function WizardControls({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
41
src/components/wizard/StepWithTooltip.tsx
Normal file
41
src/components/wizard/StepWithTooltip.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from '@/components/ui/tooltip'
|
||||||
|
|
||||||
|
export function StepWithTooltip({
|
||||||
|
title,
|
||||||
|
desc,
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
desc: string
|
||||||
|
}) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipProvider delayDuration={0}>
|
||||||
|
<Tooltip open={isOpen} onOpenChange={setIsOpen}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span
|
||||||
|
className="cursor-help decoration-dotted underline-offset-4 hover:underline"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setIsOpen((prev) => !prev)
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => setIsOpen(true)}
|
||||||
|
onMouseLeave={() => setIsOpen(false)}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="max-w-50 text-xs">
|
||||||
|
<p>{desc}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
52
src/components/wizard/WizardLayout.tsx
Normal file
52
src/components/wizard/WizardLayout.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import * as Icons from 'lucide-react'
|
||||||
|
|
||||||
|
import { CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
|
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
||||||
|
|
||||||
|
export function WizardLayout({
|
||||||
|
title,
|
||||||
|
onClose,
|
||||||
|
headerSlot,
|
||||||
|
footerSlot,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
onClose: () => void
|
||||||
|
headerSlot?: React.ReactNode
|
||||||
|
footerSlot?: React.ReactNode
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog open={true} onOpenChange={(open) => !open && onClose()}>
|
||||||
|
<DialogContent
|
||||||
|
className="flex h-[90vh] w-[calc(100%-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl"
|
||||||
|
onInteractOutside={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="z-10 flex-none border-b bg-white">
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between gap-4 p-6 pb-4">
|
||||||
|
<CardTitle>{title}</CardTitle>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none"
|
||||||
|
>
|
||||||
|
<Icons.X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Cerrar</span>
|
||||||
|
</button>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
{headerSlot ? <div className="px-6 pb-6">{headerSlot}</div> : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto bg-gray-50/30 p-6">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{footerSlot ? (
|
||||||
|
<div className="flex-none border-t bg-white p-6">{footerSlot}</div>
|
||||||
|
) : null}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
59
src/components/wizard/WizardResponsiveHeader.tsx
Normal file
59
src/components/wizard/WizardResponsiveHeader.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { CircularProgress } from '@/components/CircularProgress'
|
||||||
|
import { StepWithTooltip } from '@/components/wizard/StepWithTooltip'
|
||||||
|
|
||||||
|
export function WizardResponsiveHeader({
|
||||||
|
wizard,
|
||||||
|
methods,
|
||||||
|
}: {
|
||||||
|
wizard: any
|
||||||
|
methods: any
|
||||||
|
}) {
|
||||||
|
const idx = wizard.utils.getIndex(methods.current.id)
|
||||||
|
const totalSteps = wizard.steps.length
|
||||||
|
const currentIndex = idx + 1
|
||||||
|
const hasNextStep = idx < totalSteps - 1
|
||||||
|
const nextStep = wizard.steps[currentIndex]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="block sm:hidden">
|
||||||
|
<div className="flex items-center gap-5">
|
||||||
|
<CircularProgress current={currentIndex} total={totalSteps} />
|
||||||
|
<div className="flex flex-col justify-center">
|
||||||
|
<h2 className="text-lg font-bold text-slate-900">
|
||||||
|
<StepWithTooltip
|
||||||
|
title={methods.current.title}
|
||||||
|
desc={methods.current.description}
|
||||||
|
/>
|
||||||
|
</h2>
|
||||||
|
{hasNextStep && nextStep ? (
|
||||||
|
<p className="text-sm text-slate-400">
|
||||||
|
Siguiente: {nextStep.title}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm font-medium text-green-500">
|
||||||
|
¡Último paso!
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden sm:block">
|
||||||
|
<wizard.Stepper.Navigation className="border-border/60 rounded-xl border bg-slate-50 p-2">
|
||||||
|
{wizard.steps.map((step: any) => (
|
||||||
|
<wizard.Stepper.Step
|
||||||
|
key={step.id}
|
||||||
|
of={step.id}
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
>
|
||||||
|
<wizard.Stepper.Title>
|
||||||
|
<StepWithTooltip title={step.title} desc={step.description} />
|
||||||
|
</wizard.Stepper.Title>
|
||||||
|
</wizard.Stepper.Step>
|
||||||
|
))}
|
||||||
|
</wizard.Stepper.Navigation>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -165,7 +165,7 @@ export async function plan_asignaturas_list(
|
|||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('asignaturas')
|
.from('asignaturas')
|
||||||
.select(
|
.select(
|
||||||
'id,plan_estudio_id,estructura_id,facultad_propietaria_id,codigo,nombre,tipo,creditos,horas_semana,numero_ciclo,linea_plan_id,orden_celda,datos,contenido_tematico,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en',
|
'id,plan_estudio_id,horas_academicas, horas_independientes,estructura_id,codigo,nombre,tipo,creditos,numero_ciclo,linea_plan_id,orden_celda,datos,contenido_tematico,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en',
|
||||||
)
|
)
|
||||||
.eq('plan_estudio_id', planId)
|
.eq('plan_estudio_id', planId)
|
||||||
.order('numero_ciclo', { ascending: true, nullsFirst: false })
|
.order('numero_ciclo', { ascending: true, nullsFirst: false })
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { supabaseBrowser } from '../supabase/client'
|
import { supabaseBrowser } from '../supabase/client'
|
||||||
import { invokeEdge } from '../supabase/invokeEdge'
|
import { invokeEdge } from '../supabase/invokeEdge'
|
||||||
|
|
||||||
import { throwIfError, requireData } from './_helpers'
|
import { throwIfError, requireData } from './_helpers'
|
||||||
|
|
||||||
|
import type { DocumentoResult } from './plans.api'
|
||||||
import type {
|
import type {
|
||||||
Asignatura,
|
Asignatura,
|
||||||
BibliografiaAsignatura,
|
BibliografiaAsignatura,
|
||||||
@@ -8,7 +11,6 @@ import type {
|
|||||||
TipoAsignatura,
|
TipoAsignatura,
|
||||||
UUID,
|
UUID,
|
||||||
} from '../types/domain'
|
} from '../types/domain'
|
||||||
import type { DocumentoResult } from './plans.api'
|
|
||||||
|
|
||||||
const EDGE = {
|
const EDGE = {
|
||||||
subjects_create_manual: 'subjects_create_manual',
|
subjects_create_manual: 'subjects_create_manual',
|
||||||
@@ -32,7 +34,7 @@ export async function subjects_get(subjectId: UUID): Promise<Asignatura> {
|
|||||||
.from('asignaturas')
|
.from('asignaturas')
|
||||||
.select(
|
.select(
|
||||||
`
|
`
|
||||||
id,plan_estudio_id,estructura_id,facultad_propietaria_id,codigo,nombre,tipo,creditos,horas_semana,numero_ciclo,linea_plan_id,orden_celda,datos,contenido_tematico,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,
|
id,plan_estudio_id,estructura_id,codigo,nombre,tipo,creditos,numero_ciclo,linea_plan_id,orden_celda,datos,contenido_tematico,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,
|
||||||
planes_estudio(
|
planes_estudio(
|
||||||
id,carrera_id,estructura_id,nombre,nivel,tipo_ciclo,numero_ciclos,datos,estado_actual_id,activo,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,
|
id,carrera_id,estructura_id,nombre,nivel,tipo_ciclo,numero_ciclos,datos,estado_actual_id,activo,tipo_origen,meta_origen,creado_por,actualizado_por,creado_en,actualizado_en,
|
||||||
carreras(id,facultad_id,nombre,nombre_corto,clave_sep,activa, facultades(id,nombre,nombre_corto,color,icono))
|
carreras(id,facultad_id,nombre,nombre_corto,clave_sep,activa, facultades(id,nombre,nombre_corto,color,icono))
|
||||||
@@ -49,7 +51,7 @@ export async function subjects_get(subjectId: UUID): Promise<Asignatura> {
|
|||||||
|
|
||||||
export async function subjects_history(
|
export async function subjects_history(
|
||||||
subjectId: UUID,
|
subjectId: UUID,
|
||||||
): Promise<CambioAsignatura[]> {
|
): Promise<Array<CambioAsignatura>> {
|
||||||
const supabase = supabaseBrowser()
|
const supabase = supabaseBrowser()
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('cambios_asignatura')
|
.from('cambios_asignatura')
|
||||||
@@ -65,7 +67,7 @@ export async function subjects_history(
|
|||||||
|
|
||||||
export async function subjects_bibliografia_list(
|
export async function subjects_bibliografia_list(
|
||||||
subjectId: UUID,
|
subjectId: UUID,
|
||||||
): Promise<BibliografiaAsignatura[]> {
|
): Promise<Array<BibliografiaAsignatura>> {
|
||||||
const supabase = supabaseBrowser()
|
const supabase = supabaseBrowser()
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('bibliografia_asignatura')
|
.from('bibliografia_asignatura')
|
||||||
@@ -112,9 +114,9 @@ export async function ai_generate_subject(payload: {
|
|||||||
iaConfig: {
|
iaConfig: {
|
||||||
descripcionEnfoque: string
|
descripcionEnfoque: string
|
||||||
notasAdicionales?: string
|
notasAdicionales?: string
|
||||||
archivosExistentesIds?: UUID[]
|
archivosExistentesIds?: Array<UUID>
|
||||||
repositoriosIds?: UUID[]
|
repositoriosIds?: Array<UUID>
|
||||||
archivosAdhocIds?: UUID[]
|
archivosAdhocIds?: Array<UUID>
|
||||||
usarMCP?: boolean
|
usarMCP?: boolean
|
||||||
}
|
}
|
||||||
}): Promise<any> {
|
}): Promise<any> {
|
||||||
@@ -145,7 +147,7 @@ export async function subjects_clone_from_existing(payload: {
|
|||||||
export async function subjects_import_from_file(payload: {
|
export async function subjects_import_from_file(payload: {
|
||||||
planId: UUID
|
planId: UUID
|
||||||
archivoWordAsignaturaId: UUID
|
archivoWordAsignaturaId: UUID
|
||||||
archivosAdicionalesIds?: UUID[]
|
archivosAdicionalesIds?: Array<UUID>
|
||||||
}): Promise<Asignatura> {
|
}): Promise<Asignatura> {
|
||||||
return invokeEdge<Asignatura>(EDGE.subjects_import_from_file, payload)
|
return invokeEdge<Asignatura>(EDGE.subjects_import_from_file, payload)
|
||||||
}
|
}
|
||||||
@@ -175,7 +177,7 @@ export async function subjects_update_fields(
|
|||||||
|
|
||||||
export async function subjects_update_contenido(
|
export async function subjects_update_contenido(
|
||||||
subjectId: UUID,
|
subjectId: UUID,
|
||||||
unidades: any[],
|
unidades: Array<any>,
|
||||||
): Promise<Asignatura> {
|
): Promise<Asignatura> {
|
||||||
return invokeEdge<Asignatura>(EDGE.subjects_update_contenido, {
|
return invokeEdge<Asignatura>(EDGE.subjects_update_contenido, {
|
||||||
subjectId,
|
subjectId,
|
||||||
@@ -224,3 +226,20 @@ export async function subjects_get_document(
|
|||||||
subjectId,
|
subjectId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function asignaturas_update(
|
||||||
|
asignaturaId: UUID,
|
||||||
|
patch: Partial<Asignatura>, // O tu tipo específico para el Patch de materias
|
||||||
|
): Promise<Asignatura> {
|
||||||
|
const supabase = supabaseBrowser()
|
||||||
|
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('asignaturas')
|
||||||
|
.update(patch)
|
||||||
|
.eq('id', asignaturaId)
|
||||||
|
.select() // Trae la materia actualizada
|
||||||
|
.single()
|
||||||
|
|
||||||
|
throwIfError(error)
|
||||||
|
return requireData(data, 'No se pudo actualizar la asignatura.')
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
import { qk } from '../query/keys'
|
|
||||||
import type { UUID } from '../types/domain'
|
|
||||||
import type {
|
|
||||||
BibliografiaUpsertInput,
|
|
||||||
SubjectsCreateManualInput,
|
|
||||||
SubjectsUpdateFieldsPatch,
|
|
||||||
} from '../api/subjects.api'
|
|
||||||
import {
|
import {
|
||||||
ai_generate_subject,
|
ai_generate_subject,
|
||||||
|
asignaturas_update,
|
||||||
subjects_bibliografia_list,
|
subjects_bibliografia_list,
|
||||||
subjects_clone_from_existing,
|
subjects_clone_from_existing,
|
||||||
subjects_create_manual,
|
subjects_create_manual,
|
||||||
@@ -21,6 +16,14 @@ import {
|
|||||||
subjects_update_contenido,
|
subjects_update_contenido,
|
||||||
subjects_update_fields,
|
subjects_update_fields,
|
||||||
} from '../api/subjects.api'
|
} from '../api/subjects.api'
|
||||||
|
import { qk } from '../query/keys'
|
||||||
|
|
||||||
|
import type {
|
||||||
|
BibliografiaUpsertInput,
|
||||||
|
SubjectsCreateManualInput,
|
||||||
|
SubjectsUpdateFieldsPatch,
|
||||||
|
} from '../api/subjects.api'
|
||||||
|
import type { UUID } from '../types/domain'
|
||||||
|
|
||||||
export function useSubject(subjectId: UUID | null | undefined) {
|
export function useSubject(subjectId: UUID | null | undefined) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -159,7 +162,7 @@ export function useUpdateSubjectContenido() {
|
|||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (vars: { subjectId: UUID; unidades: any[] }) =>
|
mutationFn: (vars: { subjectId: UUID; unidades: Array<any> }) =>
|
||||||
subjects_update_contenido(vars.subjectId, vars.unidades),
|
subjects_update_contenido(vars.subjectId, vars.unidades),
|
||||||
onSuccess: (updated) => {
|
onSuccess: (updated) => {
|
||||||
qc.setQueryData(qk.asignatura(updated.id), updated)
|
qc.setQueryData(qk.asignatura(updated.id), updated)
|
||||||
@@ -194,3 +197,28 @@ export function useGenerateSubjectDocumento() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useUpdateAsignatura() {
|
||||||
|
const qc = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (vars: {
|
||||||
|
asignaturaId: UUID
|
||||||
|
patch: Partial<SubjectsUpdateFieldsPatch>
|
||||||
|
}) => asignaturas_update(vars.asignaturaId, vars.patch),
|
||||||
|
|
||||||
|
onSuccess: (updated) => {
|
||||||
|
// 1. Actualizamos la materia específica en la caché si tienes un query de "detalle"
|
||||||
|
qc.setQueryData(['asignatura', updated.id], updated)
|
||||||
|
|
||||||
|
// 2. IMPORTANTÍSIMO: Invalidamos la lista de materias del plan
|
||||||
|
// para que el mapa curricular vea los cambios (créditos, horas, nombre, etc.)
|
||||||
|
qc.invalidateQueries({
|
||||||
|
queryKey: ['plan_asignaturas', updated.plan_estudio_id],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 3. Si tienes una lista general de asignaturas, también la invalidamos
|
||||||
|
qc.invalidateQueries({ queryKey: ['asignaturas', 'list'] })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,31 +1,33 @@
|
|||||||
export const qk = {
|
export const qk = {
|
||||||
auth: ["auth"] as const,
|
auth: ['auth'] as const,
|
||||||
session: () => ["auth", "session"] as const,
|
session: () => ['auth', 'session'] as const,
|
||||||
meProfile: () => ["auth", "meProfile"] as const,
|
meProfile: () => ['auth', 'meProfile'] as const,
|
||||||
|
|
||||||
facultades: () => ["meta", "facultades"] as const,
|
facultades: () => ['meta', 'facultades'] as const,
|
||||||
carreras: (facultadId?: string | null) =>
|
carreras: (facultadId?: string | null) =>
|
||||||
["meta", "carreras", { facultadId: facultadId ?? null }] as const,
|
['meta', 'carreras', { facultadId: facultadId ?? null }] as const,
|
||||||
estructurasPlan: (nivel?: string | null) =>
|
estructurasPlan: (nivel?: string | null) =>
|
||||||
["meta", "estructurasPlan", { nivel: nivel ?? null }] as const,
|
['meta', 'estructurasPlan', { nivel: nivel ?? null }] as const,
|
||||||
estructurasAsignatura: () => ["meta", "estructurasAsignatura"] as const,
|
estructurasAsignatura: () => ['meta', 'estructurasAsignatura'] as const,
|
||||||
estadosPlan: () => ["meta", "estadosPlan"] as const,
|
estadosPlan: () => ['meta', 'estadosPlan'] as const,
|
||||||
|
|
||||||
planesList: (filters: unknown) => ["planes", "list", filters] as const,
|
planesList: (filters: unknown) => ['planes', 'list', filters] as const,
|
||||||
plan: (planId: string) => ["planes", "detail", planId] as const,
|
plan: (planId: string) => ['planes', 'detail', planId] as const,
|
||||||
planLineas: (planId: string) => ["planes", planId, "lineas"] as const,
|
planLineas: (planId: string) => ['planes', planId, 'lineas'] as const,
|
||||||
planAsignaturas: (planId: string) => ["planes", planId, "asignaturas"] as const,
|
planAsignaturas: (planId: string) =>
|
||||||
planHistorial: (planId: string) => ["planes", planId, "historial"] as const,
|
['planes', planId, 'asignaturas'] as const,
|
||||||
planDocumento: (planId: string) => ["planes", planId, "documento"] as const,
|
planHistorial: (planId: string) => ['planes', planId, 'historial'] as const,
|
||||||
|
planDocumento: (planId: string) => ['planes', planId, 'documento'] as const,
|
||||||
|
|
||||||
asignatura: (asignaturaId: string) => ["asignaturas", "detail", asignaturaId] as const,
|
asignatura: (asignaturaId: string) =>
|
||||||
|
['asignaturas', 'detail', asignaturaId] as const,
|
||||||
asignaturaBibliografia: (asignaturaId: string) =>
|
asignaturaBibliografia: (asignaturaId: string) =>
|
||||||
["asignaturas", asignaturaId, "bibliografia"] as const,
|
['asignaturas', asignaturaId, 'bibliografia'] as const,
|
||||||
asignaturaHistorial: (asignaturaId: string) =>
|
asignaturaHistorial: (asignaturaId: string) =>
|
||||||
["asignaturas", asignaturaId, "historial"] as const,
|
['asignaturas', asignaturaId, 'historial'] as const,
|
||||||
asignaturaDocumento: (asignaturaId: string) =>
|
asignaturaDocumento: (asignaturaId: string) =>
|
||||||
["asignaturas", asignaturaId, "documento"] as const,
|
['asignaturas', asignaturaId, 'documento'] as const,
|
||||||
|
|
||||||
tareas: () => ["tareas", "mias"] as const,
|
tareas: () => ['tareas', 'mias'] as const,
|
||||||
notificaciones: () => ["notificaciones", "mias"] as const,
|
notificaciones: () => ['notificaciones', 'mias'] as const,
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useNavigate } from '@tanstack/react-router'
|
import { useNavigate } from '@tanstack/react-router'
|
||||||
|
import * as Icons from 'lucide-react'
|
||||||
|
|
||||||
import { useNuevaAsignaturaWizard } from './hooks/useNuevaAsignaturaWizard'
|
import { useNuevaAsignaturaWizard } from './hooks/useNuevaAsignaturaWizard'
|
||||||
|
|
||||||
@@ -6,11 +7,20 @@ import { PasoBasicosForm } from '@/components/asignaturas/wizard/PasoBasicosForm
|
|||||||
import { PasoConfiguracionPanel } from '@/components/asignaturas/wizard/PasoConfiguracionPanel'
|
import { PasoConfiguracionPanel } from '@/components/asignaturas/wizard/PasoConfiguracionPanel'
|
||||||
import { PasoMetodoCardGroup } from '@/components/asignaturas/wizard/PasoMetodoCardGroup'
|
import { PasoMetodoCardGroup } from '@/components/asignaturas/wizard/PasoMetodoCardGroup'
|
||||||
import { PasoResumenCard } from '@/components/asignaturas/wizard/PasoResumenCard'
|
import { PasoResumenCard } from '@/components/asignaturas/wizard/PasoResumenCard'
|
||||||
import { VistaSinPermisos } from '@/components/asignaturas/wizard/VistaSinPermisos'
|
|
||||||
import { WizardControls } from '@/components/asignaturas/wizard/WizardControls'
|
import { WizardControls } from '@/components/asignaturas/wizard/WizardControls'
|
||||||
import { WizardHeader } from '@/components/asignaturas/wizard/WizardHeader'
|
|
||||||
import { defineStepper } from '@/components/stepper'
|
import { defineStepper } from '@/components/stepper'
|
||||||
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@/components/ui/card'
|
||||||
|
import { WizardLayout } from '@/components/wizard/WizardLayout'
|
||||||
|
import { WizardResponsiveHeader } from '@/components/wizard/WizardResponsiveHeader'
|
||||||
|
|
||||||
|
const auth_get_current_user_role = (): string => 'JEFE_CARRERA'
|
||||||
|
|
||||||
const Wizard = defineStepper(
|
const Wizard = defineStepper(
|
||||||
{
|
{
|
||||||
@@ -24,8 +34,8 @@ const Wizard = defineStepper(
|
|||||||
description: 'Nombre y estructura',
|
description: 'Nombre y estructura',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'configuracion',
|
id: 'detalles',
|
||||||
title: 'Configuración',
|
title: 'Detalles',
|
||||||
description: 'Detalles según modo',
|
description: 'Detalles según modo',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -35,8 +45,6 @@ const Wizard = defineStepper(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const auth_get_current_user_role = () => 'JEFE_CARRERA' as const
|
|
||||||
|
|
||||||
export function NuevaAsignaturaModalContainer({ planId }: { planId: string }) {
|
export function NuevaAsignaturaModalContainer({ planId }: { planId: string }) {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const role = auth_get_current_user_role()
|
const role = auth_get_current_user_role()
|
||||||
@@ -55,59 +63,46 @@ export function NuevaAsignaturaModalContainer({ planId }: { planId: string }) {
|
|||||||
navigate({ to: `/planes/${planId}/asignaturas`, resetScroll: false })
|
navigate({ to: `/planes/${planId}/asignaturas`, resetScroll: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role !== 'JEFE_CARRERA') {
|
||||||
|
return (
|
||||||
|
<WizardLayout title="Nueva Asignatura" onClose={handleClose}>
|
||||||
|
<Card className="border-destructive/40">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-destructive flex items-center gap-2">
|
||||||
|
<Icons.ShieldAlert className="h-5 w-5" />
|
||||||
|
Sin permisos
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Solo el Jefe de Carrera puede crear asignaturas.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex justify-end">
|
||||||
|
<Button variant="secondary" onClick={handleClose}>
|
||||||
|
Volver
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</WizardLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onOpenChange={(open) => !open && handleClose()}>
|
|
||||||
<DialogContent
|
|
||||||
className="flex h-[90vh] w-[calc(100%-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl"
|
|
||||||
onInteractOutside={(e) => e.preventDefault()}
|
|
||||||
>
|
|
||||||
{role !== 'JEFE_CARRERA' ? (
|
|
||||||
<VistaSinPermisos onClose={handleClose} />
|
|
||||||
) : (
|
|
||||||
<Wizard.Stepper.Provider
|
<Wizard.Stepper.Provider
|
||||||
initialStep={Wizard.utils.getFirst().id}
|
initialStep={Wizard.utils.getFirst().id}
|
||||||
className="flex h-full flex-col"
|
className="flex h-full flex-col"
|
||||||
>
|
>
|
||||||
{({ methods }) => (
|
{({ methods }) => {
|
||||||
<>
|
const idx = Wizard.utils.getIndex(methods.current.id)
|
||||||
<WizardHeader
|
|
||||||
|
return (
|
||||||
|
<WizardLayout
|
||||||
title="Nueva Asignatura"
|
title="Nueva Asignatura"
|
||||||
Wizard={Wizard}
|
onClose={handleClose}
|
||||||
methods={{ ...methods, onClose: handleClose }}
|
headerSlot={
|
||||||
/>
|
<WizardResponsiveHeader wizard={Wizard} methods={methods} />
|
||||||
|
}
|
||||||
<div className="flex-1 overflow-y-auto bg-gray-50/30 p-6">
|
footerSlot={
|
||||||
<div className="mx-auto max-w-3xl">
|
<Wizard.Stepper.Controls>
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 0 && (
|
|
||||||
<Wizard.Stepper.Panel>
|
|
||||||
<PasoMetodoCardGroup
|
|
||||||
wizard={wizard}
|
|
||||||
onChange={setWizard}
|
|
||||||
/>
|
|
||||||
</Wizard.Stepper.Panel>
|
|
||||||
)}
|
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 1 && (
|
|
||||||
<Wizard.Stepper.Panel>
|
|
||||||
<PasoBasicosForm wizard={wizard} onChange={setWizard} />
|
|
||||||
</Wizard.Stepper.Panel>
|
|
||||||
)}
|
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 2 && (
|
|
||||||
<Wizard.Stepper.Panel>
|
|
||||||
<PasoConfiguracionPanel
|
|
||||||
wizard={wizard}
|
|
||||||
onChange={setWizard}
|
|
||||||
onGenerarIA={simularGeneracionIA}
|
|
||||||
/>
|
|
||||||
</Wizard.Stepper.Panel>
|
|
||||||
)}
|
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 3 && (
|
|
||||||
<Wizard.Stepper.Panel>
|
|
||||||
<PasoResumenCard wizard={wizard} />
|
|
||||||
</Wizard.Stepper.Panel>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<WizardControls
|
<WizardControls
|
||||||
Wizard={Wizard}
|
Wizard={Wizard}
|
||||||
methods={methods}
|
methods={methods}
|
||||||
@@ -117,11 +112,41 @@ export function NuevaAsignaturaModalContainer({ planId }: { planId: string }) {
|
|||||||
canContinueDesdeConfig={canContinueDesdeConfig}
|
canContinueDesdeConfig={canContinueDesdeConfig}
|
||||||
onCreate={() => crearAsignatura(handleClose)}
|
onCreate={() => crearAsignatura(handleClose)}
|
||||||
/>
|
/>
|
||||||
</>
|
</Wizard.Stepper.Controls>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="mx-auto max-w-3xl">
|
||||||
|
{idx === 0 && (
|
||||||
|
<Wizard.Stepper.Panel>
|
||||||
|
<PasoMetodoCardGroup wizard={wizard} onChange={setWizard} />
|
||||||
|
</Wizard.Stepper.Panel>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{idx === 1 && (
|
||||||
|
<Wizard.Stepper.Panel>
|
||||||
|
<PasoBasicosForm wizard={wizard} onChange={setWizard} />
|
||||||
|
</Wizard.Stepper.Panel>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{idx === 2 && (
|
||||||
|
<Wizard.Stepper.Panel>
|
||||||
|
<PasoConfiguracionPanel
|
||||||
|
wizard={wizard}
|
||||||
|
onChange={setWizard}
|
||||||
|
onGenerarIA={simularGeneracionIA}
|
||||||
|
/>
|
||||||
|
</Wizard.Stepper.Panel>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{idx === 3 && (
|
||||||
|
<Wizard.Stepper.Panel>
|
||||||
|
<PasoResumenCard wizard={wizard} />
|
||||||
|
</Wizard.Stepper.Panel>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</WizardLayout>
|
||||||
|
)
|
||||||
|
}}
|
||||||
</Wizard.Stepper.Provider>
|
</Wizard.Stepper.Provider>
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { PasoDetallesPanel } from '@/components/planes/wizard/PasoDetallesPanel/
|
|||||||
import { PasoModoCardGroup } from '@/components/planes/wizard/PasoModoCardGroup'
|
import { PasoModoCardGroup } from '@/components/planes/wizard/PasoModoCardGroup'
|
||||||
import { PasoResumenCard } from '@/components/planes/wizard/PasoResumenCard'
|
import { PasoResumenCard } from '@/components/planes/wizard/PasoResumenCard'
|
||||||
import { WizardControls } from '@/components/planes/wizard/WizardControls'
|
import { WizardControls } from '@/components/planes/wizard/WizardControls'
|
||||||
import { WizardHeader } from '@/components/planes/wizard/WizardHeader'
|
|
||||||
import { defineStepper } from '@/components/stepper'
|
import { defineStepper } from '@/components/stepper'
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -19,16 +18,12 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/components/ui/card'
|
} from '@/components/ui/card'
|
||||||
import {
|
import { WizardLayout } from '@/components/wizard/WizardLayout'
|
||||||
Dialog,
|
import { WizardResponsiveHeader } from '@/components/wizard/WizardResponsiveHeader'
|
||||||
DialogContent,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from '@/components/ui/dialog'
|
|
||||||
// import { useGeneratePlanAI } from '@/data/hooks/usePlans'
|
// import { useGeneratePlanAI } from '@/data/hooks/usePlans'
|
||||||
|
|
||||||
// Mock de permisos/rol
|
// Mock de permisos/rol
|
||||||
const auth_get_current_user_role = () => 'JEFE_CARRERA' as const
|
const auth_get_current_user_role = (): string => 'JEFE_CARRERA'
|
||||||
|
|
||||||
const Wizard = defineStepper(
|
const Wizard = defineStepper(
|
||||||
{
|
{
|
||||||
@@ -64,20 +59,9 @@ export default function NuevoPlanModalContainer() {
|
|||||||
|
|
||||||
// Crear plan: ahora la lógica vive en WizardControls
|
// Crear plan: ahora la lógica vive en WizardControls
|
||||||
|
|
||||||
|
if (role !== 'JEFE_CARRERA') {
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onOpenChange={(open) => !open && handleClose()}>
|
<WizardLayout title="Nuevo plan de estudios" onClose={handleClose}>
|
||||||
<DialogContent
|
|
||||||
className="flex h-[90vh] w-[calc(100%-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl"
|
|
||||||
onInteractOutside={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{role !== 'JEFE_CARRERA' ? (
|
|
||||||
<>
|
|
||||||
<DialogHeader className="flex-none border-b p-6">
|
|
||||||
<DialogTitle>Nuevo plan de estudios</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="flex-1 p-6">
|
|
||||||
<Card className="border-destructive/40">
|
<Card className="border-destructive/40">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
@@ -97,52 +81,58 @@ export default function NuevoPlanModalContainer() {
|
|||||||
</button>
|
</button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</WizardLayout>
|
||||||
</>
|
)
|
||||||
) : (
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<Wizard.Stepper.Provider
|
<Wizard.Stepper.Provider
|
||||||
initialStep={Wizard.utils.getFirst().id}
|
initialStep={Wizard.utils.getFirst().id}
|
||||||
className="flex h-full flex-col"
|
className="flex h-full flex-col"
|
||||||
>
|
>
|
||||||
{({ methods }) => {
|
{({ methods }) => {
|
||||||
const currentIndex = Wizard.utils.getIndex(methods.current.id) + 1
|
const idx = Wizard.utils.getIndex(methods.current.id)
|
||||||
const totalSteps = Wizard.steps.length
|
|
||||||
const nextStep = Wizard.steps[currentIndex] ?? {
|
|
||||||
title: '',
|
|
||||||
description: '',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<WizardLayout
|
||||||
<WizardHeader
|
title="Nuevo plan de estudios"
|
||||||
currentIndex={currentIndex}
|
|
||||||
totalSteps={totalSteps}
|
|
||||||
currentTitle={methods.current.title}
|
|
||||||
currentDescription={methods.current.description}
|
|
||||||
nextTitle={nextStep.title}
|
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
Wizard={Wizard}
|
headerSlot={
|
||||||
|
<WizardResponsiveHeader wizard={Wizard} methods={methods} />
|
||||||
|
}
|
||||||
|
footerSlot={
|
||||||
|
<Wizard.Stepper.Controls>
|
||||||
|
<WizardControls
|
||||||
|
errorMessage={wizard.errorMessage}
|
||||||
|
onPrev={() => methods.prev()}
|
||||||
|
onNext={() => methods.next()}
|
||||||
|
disablePrev={idx === 0 || wizard.isLoading}
|
||||||
|
disableNext={
|
||||||
|
wizard.isLoading ||
|
||||||
|
(idx === 0 && !canContinueDesdeModo) ||
|
||||||
|
(idx === 1 && !canContinueDesdeBasicos) ||
|
||||||
|
(idx === 2 && !canContinueDesdeDetalles)
|
||||||
|
}
|
||||||
|
disableCreate={wizard.isLoading}
|
||||||
|
isLastStep={idx >= Wizard.steps.length - 1}
|
||||||
|
wizard={wizard}
|
||||||
|
setWizard={setWizard}
|
||||||
/>
|
/>
|
||||||
|
</Wizard.Stepper.Controls>
|
||||||
<div className="flex-1 overflow-y-auto bg-gray-50/30 p-6">
|
}
|
||||||
|
>
|
||||||
<div className="mx-auto max-w-3xl">
|
<div className="mx-auto max-w-3xl">
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 0 && (
|
{idx === 0 && (
|
||||||
<Wizard.Stepper.Panel>
|
<Wizard.Stepper.Panel>
|
||||||
<PasoModoCardGroup
|
<PasoModoCardGroup wizard={wizard} onChange={setWizard} />
|
||||||
wizard={wizard}
|
|
||||||
onChange={setWizard}
|
|
||||||
/>
|
|
||||||
</Wizard.Stepper.Panel>
|
</Wizard.Stepper.Panel>
|
||||||
)}
|
)}
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 1 && (
|
{idx === 1 && (
|
||||||
<Wizard.Stepper.Panel>
|
<Wizard.Stepper.Panel>
|
||||||
<PasoBasicosForm
|
<PasoBasicosForm wizard={wizard} onChange={setWizard} />
|
||||||
wizard={wizard}
|
|
||||||
onChange={setWizard}
|
|
||||||
/>
|
|
||||||
</Wizard.Stepper.Panel>
|
</Wizard.Stepper.Panel>
|
||||||
)}
|
)}
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 2 && (
|
{idx === 2 && (
|
||||||
<Wizard.Stepper.Panel>
|
<Wizard.Stepper.Panel>
|
||||||
<PasoDetallesPanel
|
<PasoDetallesPanel
|
||||||
wizard={wizard}
|
wizard={wizard}
|
||||||
@@ -151,49 +141,15 @@ export default function NuevoPlanModalContainer() {
|
|||||||
/>
|
/>
|
||||||
</Wizard.Stepper.Panel>
|
</Wizard.Stepper.Panel>
|
||||||
)}
|
)}
|
||||||
{Wizard.utils.getIndex(methods.current.id) === 3 && (
|
{idx === 3 && (
|
||||||
<Wizard.Stepper.Panel>
|
<Wizard.Stepper.Panel>
|
||||||
<PasoResumenCard wizard={wizard} />
|
<PasoResumenCard wizard={wizard} />
|
||||||
</Wizard.Stepper.Panel>
|
</Wizard.Stepper.Panel>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</WizardLayout>
|
||||||
|
|
||||||
<div className="flex-none border-t bg-white p-6">
|
|
||||||
<Wizard.Stepper.Controls>
|
|
||||||
<WizardControls
|
|
||||||
errorMessage={wizard.errorMessage}
|
|
||||||
onPrev={() => methods.prev()}
|
|
||||||
onNext={() => methods.next()}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
wizard={wizard}
|
|
||||||
setWizard={setWizard}
|
|
||||||
/>
|
|
||||||
</Wizard.Stepper.Controls>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</Wizard.Stepper.Provider>
|
</Wizard.Stepper.Provider>
|
||||||
)}
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,21 +12,19 @@ import { Route as rootRouteImport } from './routes/__root'
|
|||||||
import { Route as LoginRouteImport } from './routes/login'
|
import { Route as LoginRouteImport } from './routes/login'
|
||||||
import { Route as DashboardRouteImport } from './routes/dashboard'
|
import { Route as DashboardRouteImport } from './routes/dashboard'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
|
import { Route as PlanesListaRouteImport } from './routes/planes/_lista'
|
||||||
import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query'
|
import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query'
|
||||||
import { Route as PlanesListaRouteRouteImport } from './routes/planes/_lista/route'
|
|
||||||
import { Route as PlanesListaNuevoRouteImport } from './routes/planes/_lista/nuevo'
|
import { Route as PlanesListaNuevoRouteImport } from './routes/planes/_lista/nuevo'
|
||||||
import { Route as PlanesPlanIdAsignaturasRouteRouteImport } from './routes/planes/$planId/asignaturas/route'
|
import { Route as PlanesPlanIdDetalleRouteImport } from './routes/planes/$planId/_detalle'
|
||||||
import { Route as PlanesPlanIdDetalleRouteRouteImport } from './routes/planes/$planId/_detalle/route'
|
|
||||||
import { Route as PlanesPlanIdDetalleIndexRouteImport } from './routes/planes/$planId/_detalle/index'
|
import { Route as PlanesPlanIdDetalleIndexRouteImport } from './routes/planes/$planId/_detalle/index'
|
||||||
|
import { Route as PlanesPlanIdAsignaturasAsignaturaIdRouteImport } from './routes/planes/$planId/asignaturas/$asignaturaId'
|
||||||
import { Route as PlanesPlanIdDetalleMapaRouteImport } from './routes/planes/$planId/_detalle/mapa'
|
import { Route as PlanesPlanIdDetalleMapaRouteImport } from './routes/planes/$planId/_detalle/mapa'
|
||||||
import { Route as PlanesPlanIdDetalleIaplanRouteImport } from './routes/planes/$planId/_detalle/iaplan'
|
import { Route as PlanesPlanIdDetalleIaplanRouteImport } from './routes/planes/$planId/_detalle/iaplan'
|
||||||
import { Route as PlanesPlanIdDetalleHistorialRouteImport } from './routes/planes/$planId/_detalle/historial'
|
import { Route as PlanesPlanIdDetalleHistorialRouteImport } from './routes/planes/$planId/_detalle/historial'
|
||||||
import { Route as PlanesPlanIdDetalleFlujoRouteImport } from './routes/planes/$planId/_detalle/flujo'
|
import { Route as PlanesPlanIdDetalleFlujoRouteImport } from './routes/planes/$planId/_detalle/flujo'
|
||||||
import { Route as PlanesPlanIdDetalleDocumentoRouteImport } from './routes/planes/$planId/_detalle/documento'
|
import { Route as PlanesPlanIdDetalleDocumentoRouteImport } from './routes/planes/$planId/_detalle/documento'
|
||||||
import { Route as PlanesPlanIdDetalleAsignaturasRouteImport } from './routes/planes/$planId/_detalle/asignaturas'
|
import { Route as PlanesPlanIdDetalleAsignaturasIndexRouteImport } from './routes/planes/$planId/_detalle/asignaturas/index'
|
||||||
import { Route as PlanesPlanIdAsignaturasListaRouteRouteImport } from './routes/planes/$planId/asignaturas/_lista/route'
|
import { Route as PlanesPlanIdDetalleAsignaturasNuevaRouteImport } from './routes/planes/$planId/_detalle/asignaturas/nueva'
|
||||||
import { Route as PlanesPlanIdAsignaturasAsignaturaIdRouteRouteImport } from './routes/planes/$planId/asignaturas/$asignaturaId/route'
|
|
||||||
import { Route as PlanesPlanIdAsignaturasListaNuevaRouteImport } from './routes/planes/$planId/asignaturas/_lista/nueva'
|
|
||||||
|
|
||||||
const LoginRoute = LoginRouteImport.update({
|
const LoginRoute = LoginRouteImport.update({
|
||||||
id: '/login',
|
id: '/login',
|
||||||
@@ -43,147 +41,133 @@ const IndexRoute = IndexRouteImport.update({
|
|||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
|
const PlanesListaRoute = PlanesListaRouteImport.update({
|
||||||
|
id: '/planes/_lista',
|
||||||
|
path: '/planes',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
const DemoTanstackQueryRoute = DemoTanstackQueryRouteImport.update({
|
const DemoTanstackQueryRoute = DemoTanstackQueryRouteImport.update({
|
||||||
id: '/demo/tanstack-query',
|
id: '/demo/tanstack-query',
|
||||||
path: '/demo/tanstack-query',
|
path: '/demo/tanstack-query',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesListaRouteRoute = PlanesListaRouteRouteImport.update({
|
|
||||||
id: '/planes/_lista',
|
|
||||||
path: '/planes',
|
|
||||||
getParentRoute: () => rootRouteImport,
|
|
||||||
} as any)
|
|
||||||
const PlanesListaNuevoRoute = PlanesListaNuevoRouteImport.update({
|
const PlanesListaNuevoRoute = PlanesListaNuevoRouteImport.update({
|
||||||
id: '/nuevo',
|
id: '/nuevo',
|
||||||
path: '/nuevo',
|
path: '/nuevo',
|
||||||
getParentRoute: () => PlanesListaRouteRoute,
|
getParentRoute: () => PlanesListaRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdAsignaturasRouteRoute =
|
const PlanesPlanIdDetalleRoute = PlanesPlanIdDetalleRouteImport.update({
|
||||||
PlanesPlanIdAsignaturasRouteRouteImport.update({
|
|
||||||
id: '/planes/$planId/asignaturas',
|
|
||||||
path: '/planes/$planId/asignaturas',
|
|
||||||
getParentRoute: () => rootRouteImport,
|
|
||||||
} as any)
|
|
||||||
const PlanesPlanIdDetalleRouteRoute =
|
|
||||||
PlanesPlanIdDetalleRouteRouteImport.update({
|
|
||||||
id: '/planes/$planId/_detalle',
|
id: '/planes/$planId/_detalle',
|
||||||
path: '/planes/$planId',
|
path: '/planes/$planId',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdDetalleIndexRoute =
|
const PlanesPlanIdDetalleIndexRoute =
|
||||||
PlanesPlanIdDetalleIndexRouteImport.update({
|
PlanesPlanIdDetalleIndexRouteImport.update({
|
||||||
id: '/',
|
id: '/',
|
||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => PlanesPlanIdDetalleRouteRoute,
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
|
} as any)
|
||||||
|
const PlanesPlanIdAsignaturasAsignaturaIdRoute =
|
||||||
|
PlanesPlanIdAsignaturasAsignaturaIdRouteImport.update({
|
||||||
|
id: '/planes/$planId/asignaturas/$asignaturaId',
|
||||||
|
path: '/planes/$planId/asignaturas/$asignaturaId',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdDetalleMapaRoute = PlanesPlanIdDetalleMapaRouteImport.update({
|
const PlanesPlanIdDetalleMapaRoute = PlanesPlanIdDetalleMapaRouteImport.update({
|
||||||
id: '/mapa',
|
id: '/mapa',
|
||||||
path: '/mapa',
|
path: '/mapa',
|
||||||
getParentRoute: () => PlanesPlanIdDetalleRouteRoute,
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdDetalleIaplanRoute =
|
const PlanesPlanIdDetalleIaplanRoute =
|
||||||
PlanesPlanIdDetalleIaplanRouteImport.update({
|
PlanesPlanIdDetalleIaplanRouteImport.update({
|
||||||
id: '/iaplan',
|
id: '/iaplan',
|
||||||
path: '/iaplan',
|
path: '/iaplan',
|
||||||
getParentRoute: () => PlanesPlanIdDetalleRouteRoute,
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdDetalleHistorialRoute =
|
const PlanesPlanIdDetalleHistorialRoute =
|
||||||
PlanesPlanIdDetalleHistorialRouteImport.update({
|
PlanesPlanIdDetalleHistorialRouteImport.update({
|
||||||
id: '/historial',
|
id: '/historial',
|
||||||
path: '/historial',
|
path: '/historial',
|
||||||
getParentRoute: () => PlanesPlanIdDetalleRouteRoute,
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdDetalleFlujoRoute =
|
const PlanesPlanIdDetalleFlujoRoute =
|
||||||
PlanesPlanIdDetalleFlujoRouteImport.update({
|
PlanesPlanIdDetalleFlujoRouteImport.update({
|
||||||
id: '/flujo',
|
id: '/flujo',
|
||||||
path: '/flujo',
|
path: '/flujo',
|
||||||
getParentRoute: () => PlanesPlanIdDetalleRouteRoute,
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdDetalleDocumentoRoute =
|
const PlanesPlanIdDetalleDocumentoRoute =
|
||||||
PlanesPlanIdDetalleDocumentoRouteImport.update({
|
PlanesPlanIdDetalleDocumentoRouteImport.update({
|
||||||
id: '/documento',
|
id: '/documento',
|
||||||
path: '/documento',
|
path: '/documento',
|
||||||
getParentRoute: () => PlanesPlanIdDetalleRouteRoute,
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdDetalleAsignaturasRoute =
|
const PlanesPlanIdDetalleAsignaturasIndexRoute =
|
||||||
PlanesPlanIdDetalleAsignaturasRouteImport.update({
|
PlanesPlanIdDetalleAsignaturasIndexRouteImport.update({
|
||||||
id: '/asignaturas',
|
id: '/asignaturas/',
|
||||||
path: '/asignaturas',
|
path: '/asignaturas/',
|
||||||
getParentRoute: () => PlanesPlanIdDetalleRouteRoute,
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanIdAsignaturasListaRouteRoute =
|
const PlanesPlanIdDetalleAsignaturasNuevaRoute =
|
||||||
PlanesPlanIdAsignaturasListaRouteRouteImport.update({
|
PlanesPlanIdDetalleAsignaturasNuevaRouteImport.update({
|
||||||
id: '/_lista',
|
id: '/asignaturas/nueva',
|
||||||
getParentRoute: () => PlanesPlanIdAsignaturasRouteRoute,
|
path: '/asignaturas/nueva',
|
||||||
} as any)
|
getParentRoute: () => PlanesPlanIdDetalleRoute,
|
||||||
const PlanesPlanIdAsignaturasAsignaturaIdRouteRoute =
|
|
||||||
PlanesPlanIdAsignaturasAsignaturaIdRouteRouteImport.update({
|
|
||||||
id: '/$asignaturaId',
|
|
||||||
path: '/$asignaturaId',
|
|
||||||
getParentRoute: () => PlanesPlanIdAsignaturasRouteRoute,
|
|
||||||
} as any)
|
|
||||||
const PlanesPlanIdAsignaturasListaNuevaRoute =
|
|
||||||
PlanesPlanIdAsignaturasListaNuevaRouteImport.update({
|
|
||||||
id: '/nueva',
|
|
||||||
path: '/nueva',
|
|
||||||
getParentRoute: () => PlanesPlanIdAsignaturasListaRouteRoute,
|
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/dashboard': typeof DashboardRoute
|
'/dashboard': typeof DashboardRoute
|
||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes': typeof PlanesListaRouteRouteWithChildren
|
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/planes/$planId': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
'/planes': typeof PlanesListaRouteWithChildren
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdDetalleAsignaturasRoute
|
'/planes/$planId': typeof PlanesPlanIdDetalleRouteWithChildren
|
||||||
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
|
||||||
'/planes/$planId/documento': typeof PlanesPlanIdDetalleDocumentoRoute
|
'/planes/$planId/documento': typeof PlanesPlanIdDetalleDocumentoRoute
|
||||||
'/planes/$planId/flujo': typeof PlanesPlanIdDetalleFlujoRoute
|
'/planes/$planId/flujo': typeof PlanesPlanIdDetalleFlujoRoute
|
||||||
'/planes/$planId/historial': typeof PlanesPlanIdDetalleHistorialRoute
|
'/planes/$planId/historial': typeof PlanesPlanIdDetalleHistorialRoute
|
||||||
'/planes/$planId/iaplan': typeof PlanesPlanIdDetalleIaplanRoute
|
'/planes/$planId/iaplan': typeof PlanesPlanIdDetalleIaplanRoute
|
||||||
'/planes/$planId/mapa': typeof PlanesPlanIdDetalleMapaRoute
|
'/planes/$planId/mapa': typeof PlanesPlanIdDetalleMapaRoute
|
||||||
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRoute
|
||||||
'/planes/$planId/': typeof PlanesPlanIdDetalleIndexRoute
|
'/planes/$planId/': typeof PlanesPlanIdDetalleIndexRoute
|
||||||
'/planes/$planId/asignaturas/nueva': typeof PlanesPlanIdAsignaturasListaNuevaRoute
|
'/planes/$planId/asignaturas/nueva': typeof PlanesPlanIdDetalleAsignaturasNuevaRoute
|
||||||
|
'/planes/$planId/asignaturas/': typeof PlanesPlanIdDetalleAsignaturasIndexRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/dashboard': typeof DashboardRoute
|
'/dashboard': typeof DashboardRoute
|
||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes': typeof PlanesListaRouteRouteWithChildren
|
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdDetalleAsignaturasRoute
|
'/planes': typeof PlanesListaRouteWithChildren
|
||||||
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
|
||||||
'/planes/$planId/documento': typeof PlanesPlanIdDetalleDocumentoRoute
|
'/planes/$planId/documento': typeof PlanesPlanIdDetalleDocumentoRoute
|
||||||
'/planes/$planId/flujo': typeof PlanesPlanIdDetalleFlujoRoute
|
'/planes/$planId/flujo': typeof PlanesPlanIdDetalleFlujoRoute
|
||||||
'/planes/$planId/historial': typeof PlanesPlanIdDetalleHistorialRoute
|
'/planes/$planId/historial': typeof PlanesPlanIdDetalleHistorialRoute
|
||||||
'/planes/$planId/iaplan': typeof PlanesPlanIdDetalleIaplanRoute
|
'/planes/$planId/iaplan': typeof PlanesPlanIdDetalleIaplanRoute
|
||||||
'/planes/$planId/mapa': typeof PlanesPlanIdDetalleMapaRoute
|
'/planes/$planId/mapa': typeof PlanesPlanIdDetalleMapaRoute
|
||||||
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRoute
|
||||||
'/planes/$planId': typeof PlanesPlanIdDetalleIndexRoute
|
'/planes/$planId': typeof PlanesPlanIdDetalleIndexRoute
|
||||||
'/planes/$planId/asignaturas/nueva': typeof PlanesPlanIdAsignaturasListaNuevaRoute
|
'/planes/$planId/asignaturas/nueva': typeof PlanesPlanIdDetalleAsignaturasNuevaRoute
|
||||||
|
'/planes/$planId/asignaturas': typeof PlanesPlanIdDetalleAsignaturasIndexRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport
|
__root__: typeof rootRouteImport
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/dashboard': typeof DashboardRoute
|
'/dashboard': typeof DashboardRoute
|
||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes/_lista': typeof PlanesListaRouteRouteWithChildren
|
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/planes/$planId/_detalle': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
'/planes/_lista': typeof PlanesListaRouteWithChildren
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
'/planes/$planId/_detalle': typeof PlanesPlanIdDetalleRouteWithChildren
|
||||||
'/planes/_lista/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/_lista/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
|
||||||
'/planes/$planId/asignaturas/_lista': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
|
||||||
'/planes/$planId/_detalle/asignaturas': typeof PlanesPlanIdDetalleAsignaturasRoute
|
|
||||||
'/planes/$planId/_detalle/documento': typeof PlanesPlanIdDetalleDocumentoRoute
|
'/planes/$planId/_detalle/documento': typeof PlanesPlanIdDetalleDocumentoRoute
|
||||||
'/planes/$planId/_detalle/flujo': typeof PlanesPlanIdDetalleFlujoRoute
|
'/planes/$planId/_detalle/flujo': typeof PlanesPlanIdDetalleFlujoRoute
|
||||||
'/planes/$planId/_detalle/historial': typeof PlanesPlanIdDetalleHistorialRoute
|
'/planes/$planId/_detalle/historial': typeof PlanesPlanIdDetalleHistorialRoute
|
||||||
'/planes/$planId/_detalle/iaplan': typeof PlanesPlanIdDetalleIaplanRoute
|
'/planes/$planId/_detalle/iaplan': typeof PlanesPlanIdDetalleIaplanRoute
|
||||||
'/planes/$planId/_detalle/mapa': typeof PlanesPlanIdDetalleMapaRoute
|
'/planes/$planId/_detalle/mapa': typeof PlanesPlanIdDetalleMapaRoute
|
||||||
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRoute
|
||||||
'/planes/$planId/_detalle/': typeof PlanesPlanIdDetalleIndexRoute
|
'/planes/$planId/_detalle/': typeof PlanesPlanIdDetalleIndexRoute
|
||||||
'/planes/$planId/asignaturas/_lista/nueva': typeof PlanesPlanIdAsignaturasListaNuevaRoute
|
'/planes/$planId/_detalle/asignaturas/nueva': typeof PlanesPlanIdDetalleAsignaturasNuevaRoute
|
||||||
|
'/planes/$planId/_detalle/asignaturas/': typeof PlanesPlanIdDetalleAsignaturasIndexRoute
|
||||||
}
|
}
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
fileRoutesByFullPath: FileRoutesByFullPath
|
fileRoutesByFullPath: FileRoutesByFullPath
|
||||||
@@ -191,66 +175,64 @@ export interface FileRouteTypes {
|
|||||||
| '/'
|
| '/'
|
||||||
| '/dashboard'
|
| '/dashboard'
|
||||||
| '/login'
|
| '/login'
|
||||||
| '/planes'
|
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
|
| '/planes'
|
||||||
| '/planes/$planId'
|
| '/planes/$planId'
|
||||||
| '/planes/$planId/asignaturas'
|
|
||||||
| '/planes/nuevo'
|
| '/planes/nuevo'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
|
||||||
| '/planes/$planId/documento'
|
| '/planes/$planId/documento'
|
||||||
| '/planes/$planId/flujo'
|
| '/planes/$planId/flujo'
|
||||||
| '/planes/$planId/historial'
|
| '/planes/$planId/historial'
|
||||||
| '/planes/$planId/iaplan'
|
| '/planes/$planId/iaplan'
|
||||||
| '/planes/$planId/mapa'
|
| '/planes/$planId/mapa'
|
||||||
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
| '/planes/$planId/'
|
| '/planes/$planId/'
|
||||||
| '/planes/$planId/asignaturas/nueva'
|
| '/planes/$planId/asignaturas/nueva'
|
||||||
|
| '/planes/$planId/asignaturas/'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to:
|
to:
|
||||||
| '/'
|
| '/'
|
||||||
| '/dashboard'
|
| '/dashboard'
|
||||||
| '/login'
|
| '/login'
|
||||||
| '/planes'
|
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
| '/planes/$planId/asignaturas'
|
| '/planes'
|
||||||
| '/planes/nuevo'
|
| '/planes/nuevo'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
|
||||||
| '/planes/$planId/documento'
|
| '/planes/$planId/documento'
|
||||||
| '/planes/$planId/flujo'
|
| '/planes/$planId/flujo'
|
||||||
| '/planes/$planId/historial'
|
| '/planes/$planId/historial'
|
||||||
| '/planes/$planId/iaplan'
|
| '/planes/$planId/iaplan'
|
||||||
| '/planes/$planId/mapa'
|
| '/planes/$planId/mapa'
|
||||||
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
| '/planes/$planId'
|
| '/planes/$planId'
|
||||||
| '/planes/$planId/asignaturas/nueva'
|
| '/planes/$planId/asignaturas/nueva'
|
||||||
|
| '/planes/$planId/asignaturas'
|
||||||
id:
|
id:
|
||||||
| '__root__'
|
| '__root__'
|
||||||
| '/'
|
| '/'
|
||||||
| '/dashboard'
|
| '/dashboard'
|
||||||
| '/login'
|
| '/login'
|
||||||
| '/planes/_lista'
|
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
|
| '/planes/_lista'
|
||||||
| '/planes/$planId/_detalle'
|
| '/planes/$planId/_detalle'
|
||||||
| '/planes/$planId/asignaturas'
|
|
||||||
| '/planes/_lista/nuevo'
|
| '/planes/_lista/nuevo'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
|
||||||
| '/planes/$planId/asignaturas/_lista'
|
|
||||||
| '/planes/$planId/_detalle/asignaturas'
|
|
||||||
| '/planes/$planId/_detalle/documento'
|
| '/planes/$planId/_detalle/documento'
|
||||||
| '/planes/$planId/_detalle/flujo'
|
| '/planes/$planId/_detalle/flujo'
|
||||||
| '/planes/$planId/_detalle/historial'
|
| '/planes/$planId/_detalle/historial'
|
||||||
| '/planes/$planId/_detalle/iaplan'
|
| '/planes/$planId/_detalle/iaplan'
|
||||||
| '/planes/$planId/_detalle/mapa'
|
| '/planes/$planId/_detalle/mapa'
|
||||||
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
| '/planes/$planId/_detalle/'
|
| '/planes/$planId/_detalle/'
|
||||||
| '/planes/$planId/asignaturas/_lista/nueva'
|
| '/planes/$planId/_detalle/asignaturas/nueva'
|
||||||
|
| '/planes/$planId/_detalle/asignaturas/'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
DashboardRoute: typeof DashboardRoute
|
DashboardRoute: typeof DashboardRoute
|
||||||
LoginRoute: typeof LoginRoute
|
LoginRoute: typeof LoginRoute
|
||||||
PlanesListaRouteRoute: typeof PlanesListaRouteRouteWithChildren
|
|
||||||
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
|
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
|
||||||
PlanesPlanIdDetalleRouteRoute: typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
PlanesListaRoute: typeof PlanesListaRouteWithChildren
|
||||||
PlanesPlanIdAsignaturasRouteRoute: typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
PlanesPlanIdDetalleRoute: typeof PlanesPlanIdDetalleRouteWithChildren
|
||||||
|
PlanesPlanIdAsignaturasAsignaturaIdRoute: typeof PlanesPlanIdAsignaturasAsignaturaIdRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
@@ -276,6 +258,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof IndexRouteImport
|
preLoaderRoute: typeof IndexRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
|
'/planes/_lista': {
|
||||||
|
id: '/planes/_lista'
|
||||||
|
path: '/planes'
|
||||||
|
fullPath: '/planes'
|
||||||
|
preLoaderRoute: typeof PlanesListaRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
'/demo/tanstack-query': {
|
'/demo/tanstack-query': {
|
||||||
id: '/demo/tanstack-query'
|
id: '/demo/tanstack-query'
|
||||||
path: '/demo/tanstack-query'
|
path: '/demo/tanstack-query'
|
||||||
@@ -283,32 +272,18 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof DemoTanstackQueryRouteImport
|
preLoaderRoute: typeof DemoTanstackQueryRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/planes/_lista': {
|
|
||||||
id: '/planes/_lista'
|
|
||||||
path: '/planes'
|
|
||||||
fullPath: '/planes'
|
|
||||||
preLoaderRoute: typeof PlanesListaRouteRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
|
||||||
'/planes/_lista/nuevo': {
|
'/planes/_lista/nuevo': {
|
||||||
id: '/planes/_lista/nuevo'
|
id: '/planes/_lista/nuevo'
|
||||||
path: '/nuevo'
|
path: '/nuevo'
|
||||||
fullPath: '/planes/nuevo'
|
fullPath: '/planes/nuevo'
|
||||||
preLoaderRoute: typeof PlanesListaNuevoRouteImport
|
preLoaderRoute: typeof PlanesListaNuevoRouteImport
|
||||||
parentRoute: typeof PlanesListaRouteRoute
|
parentRoute: typeof PlanesListaRoute
|
||||||
}
|
|
||||||
'/planes/$planId/asignaturas': {
|
|
||||||
id: '/planes/$planId/asignaturas'
|
|
||||||
path: '/planes/$planId/asignaturas'
|
|
||||||
fullPath: '/planes/$planId/asignaturas'
|
|
||||||
preLoaderRoute: typeof PlanesPlanIdAsignaturasRouteRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle': {
|
'/planes/$planId/_detalle': {
|
||||||
id: '/planes/$planId/_detalle'
|
id: '/planes/$planId/_detalle'
|
||||||
path: '/planes/$planId'
|
path: '/planes/$planId'
|
||||||
fullPath: '/planes/$planId'
|
fullPath: '/planes/$planId'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleRouteRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle/': {
|
'/planes/$planId/_detalle/': {
|
||||||
@@ -316,153 +291,115 @@ declare module '@tanstack/react-router' {
|
|||||||
path: '/'
|
path: '/'
|
||||||
fullPath: '/planes/$planId/'
|
fullPath: '/planes/$planId/'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleIndexRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleIndexRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdDetalleRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
|
}
|
||||||
|
'/planes/$planId/asignaturas/$asignaturaId': {
|
||||||
|
id: '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
|
path: '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
|
fullPath: '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
|
preLoaderRoute: typeof PlanesPlanIdAsignaturasAsignaturaIdRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle/mapa': {
|
'/planes/$planId/_detalle/mapa': {
|
||||||
id: '/planes/$planId/_detalle/mapa'
|
id: '/planes/$planId/_detalle/mapa'
|
||||||
path: '/mapa'
|
path: '/mapa'
|
||||||
fullPath: '/planes/$planId/mapa'
|
fullPath: '/planes/$planId/mapa'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleMapaRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleMapaRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdDetalleRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle/iaplan': {
|
'/planes/$planId/_detalle/iaplan': {
|
||||||
id: '/planes/$planId/_detalle/iaplan'
|
id: '/planes/$planId/_detalle/iaplan'
|
||||||
path: '/iaplan'
|
path: '/iaplan'
|
||||||
fullPath: '/planes/$planId/iaplan'
|
fullPath: '/planes/$planId/iaplan'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleIaplanRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleIaplanRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdDetalleRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle/historial': {
|
'/planes/$planId/_detalle/historial': {
|
||||||
id: '/planes/$planId/_detalle/historial'
|
id: '/planes/$planId/_detalle/historial'
|
||||||
path: '/historial'
|
path: '/historial'
|
||||||
fullPath: '/planes/$planId/historial'
|
fullPath: '/planes/$planId/historial'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleHistorialRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleHistorialRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdDetalleRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle/flujo': {
|
'/planes/$planId/_detalle/flujo': {
|
||||||
id: '/planes/$planId/_detalle/flujo'
|
id: '/planes/$planId/_detalle/flujo'
|
||||||
path: '/flujo'
|
path: '/flujo'
|
||||||
fullPath: '/planes/$planId/flujo'
|
fullPath: '/planes/$planId/flujo'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleFlujoRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleFlujoRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdDetalleRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle/documento': {
|
'/planes/$planId/_detalle/documento': {
|
||||||
id: '/planes/$planId/_detalle/documento'
|
id: '/planes/$planId/_detalle/documento'
|
||||||
path: '/documento'
|
path: '/documento'
|
||||||
fullPath: '/planes/$planId/documento'
|
fullPath: '/planes/$planId/documento'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleDocumentoRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleDocumentoRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdDetalleRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
}
|
}
|
||||||
'/planes/$planId/_detalle/asignaturas': {
|
'/planes/$planId/_detalle/asignaturas/': {
|
||||||
id: '/planes/$planId/_detalle/asignaturas'
|
id: '/planes/$planId/_detalle/asignaturas/'
|
||||||
path: '/asignaturas'
|
path: '/asignaturas'
|
||||||
fullPath: '/planes/$planId/asignaturas'
|
fullPath: '/planes/$planId/asignaturas/'
|
||||||
preLoaderRoute: typeof PlanesPlanIdDetalleAsignaturasRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleAsignaturasIndexRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdDetalleRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
}
|
}
|
||||||
'/planes/$planId/asignaturas/_lista': {
|
'/planes/$planId/_detalle/asignaturas/nueva': {
|
||||||
id: '/planes/$planId/asignaturas/_lista'
|
id: '/planes/$planId/_detalle/asignaturas/nueva'
|
||||||
path: ''
|
path: '/asignaturas/nueva'
|
||||||
fullPath: '/planes/$planId/asignaturas'
|
|
||||||
preLoaderRoute: typeof PlanesPlanIdAsignaturasListaRouteRouteImport
|
|
||||||
parentRoute: typeof PlanesPlanIdAsignaturasRouteRoute
|
|
||||||
}
|
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': {
|
|
||||||
id: '/planes/$planId/asignaturas/$asignaturaId'
|
|
||||||
path: '/$asignaturaId'
|
|
||||||
fullPath: '/planes/$planId/asignaturas/$asignaturaId'
|
|
||||||
preLoaderRoute: typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRouteImport
|
|
||||||
parentRoute: typeof PlanesPlanIdAsignaturasRouteRoute
|
|
||||||
}
|
|
||||||
'/planes/$planId/asignaturas/_lista/nueva': {
|
|
||||||
id: '/planes/$planId/asignaturas/_lista/nueva'
|
|
||||||
path: '/nueva'
|
|
||||||
fullPath: '/planes/$planId/asignaturas/nueva'
|
fullPath: '/planes/$planId/asignaturas/nueva'
|
||||||
preLoaderRoute: typeof PlanesPlanIdAsignaturasListaNuevaRouteImport
|
preLoaderRoute: typeof PlanesPlanIdDetalleAsignaturasNuevaRouteImport
|
||||||
parentRoute: typeof PlanesPlanIdAsignaturasListaRouteRoute
|
parentRoute: typeof PlanesPlanIdDetalleRoute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlanesListaRouteRouteChildren {
|
interface PlanesListaRouteChildren {
|
||||||
PlanesListaNuevoRoute: typeof PlanesListaNuevoRoute
|
PlanesListaNuevoRoute: typeof PlanesListaNuevoRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlanesListaRouteRouteChildren: PlanesListaRouteRouteChildren = {
|
const PlanesListaRouteChildren: PlanesListaRouteChildren = {
|
||||||
PlanesListaNuevoRoute: PlanesListaNuevoRoute,
|
PlanesListaNuevoRoute: PlanesListaNuevoRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlanesListaRouteRouteWithChildren =
|
const PlanesListaRouteWithChildren = PlanesListaRoute._addFileChildren(
|
||||||
PlanesListaRouteRoute._addFileChildren(PlanesListaRouteRouteChildren)
|
PlanesListaRouteChildren,
|
||||||
|
)
|
||||||
|
|
||||||
interface PlanesPlanIdDetalleRouteRouteChildren {
|
interface PlanesPlanIdDetalleRouteChildren {
|
||||||
PlanesPlanIdDetalleAsignaturasRoute: typeof PlanesPlanIdDetalleAsignaturasRoute
|
|
||||||
PlanesPlanIdDetalleDocumentoRoute: typeof PlanesPlanIdDetalleDocumentoRoute
|
PlanesPlanIdDetalleDocumentoRoute: typeof PlanesPlanIdDetalleDocumentoRoute
|
||||||
PlanesPlanIdDetalleFlujoRoute: typeof PlanesPlanIdDetalleFlujoRoute
|
PlanesPlanIdDetalleFlujoRoute: typeof PlanesPlanIdDetalleFlujoRoute
|
||||||
PlanesPlanIdDetalleHistorialRoute: typeof PlanesPlanIdDetalleHistorialRoute
|
PlanesPlanIdDetalleHistorialRoute: typeof PlanesPlanIdDetalleHistorialRoute
|
||||||
PlanesPlanIdDetalleIaplanRoute: typeof PlanesPlanIdDetalleIaplanRoute
|
PlanesPlanIdDetalleIaplanRoute: typeof PlanesPlanIdDetalleIaplanRoute
|
||||||
PlanesPlanIdDetalleMapaRoute: typeof PlanesPlanIdDetalleMapaRoute
|
PlanesPlanIdDetalleMapaRoute: typeof PlanesPlanIdDetalleMapaRoute
|
||||||
PlanesPlanIdDetalleIndexRoute: typeof PlanesPlanIdDetalleIndexRoute
|
PlanesPlanIdDetalleIndexRoute: typeof PlanesPlanIdDetalleIndexRoute
|
||||||
|
PlanesPlanIdDetalleAsignaturasNuevaRoute: typeof PlanesPlanIdDetalleAsignaturasNuevaRoute
|
||||||
|
PlanesPlanIdDetalleAsignaturasIndexRoute: typeof PlanesPlanIdDetalleAsignaturasIndexRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlanesPlanIdDetalleRouteRouteChildren: PlanesPlanIdDetalleRouteRouteChildren =
|
const PlanesPlanIdDetalleRouteChildren: PlanesPlanIdDetalleRouteChildren = {
|
||||||
{
|
|
||||||
PlanesPlanIdDetalleAsignaturasRoute: PlanesPlanIdDetalleAsignaturasRoute,
|
|
||||||
PlanesPlanIdDetalleDocumentoRoute: PlanesPlanIdDetalleDocumentoRoute,
|
PlanesPlanIdDetalleDocumentoRoute: PlanesPlanIdDetalleDocumentoRoute,
|
||||||
PlanesPlanIdDetalleFlujoRoute: PlanesPlanIdDetalleFlujoRoute,
|
PlanesPlanIdDetalleFlujoRoute: PlanesPlanIdDetalleFlujoRoute,
|
||||||
PlanesPlanIdDetalleHistorialRoute: PlanesPlanIdDetalleHistorialRoute,
|
PlanesPlanIdDetalleHistorialRoute: PlanesPlanIdDetalleHistorialRoute,
|
||||||
PlanesPlanIdDetalleIaplanRoute: PlanesPlanIdDetalleIaplanRoute,
|
PlanesPlanIdDetalleIaplanRoute: PlanesPlanIdDetalleIaplanRoute,
|
||||||
PlanesPlanIdDetalleMapaRoute: PlanesPlanIdDetalleMapaRoute,
|
PlanesPlanIdDetalleMapaRoute: PlanesPlanIdDetalleMapaRoute,
|
||||||
PlanesPlanIdDetalleIndexRoute: PlanesPlanIdDetalleIndexRoute,
|
PlanesPlanIdDetalleIndexRoute: PlanesPlanIdDetalleIndexRoute,
|
||||||
}
|
PlanesPlanIdDetalleAsignaturasNuevaRoute:
|
||||||
|
PlanesPlanIdDetalleAsignaturasNuevaRoute,
|
||||||
const PlanesPlanIdDetalleRouteRouteWithChildren =
|
PlanesPlanIdDetalleAsignaturasIndexRoute:
|
||||||
PlanesPlanIdDetalleRouteRoute._addFileChildren(
|
PlanesPlanIdDetalleAsignaturasIndexRoute,
|
||||||
PlanesPlanIdDetalleRouteRouteChildren,
|
|
||||||
)
|
|
||||||
|
|
||||||
interface PlanesPlanIdAsignaturasListaRouteRouteChildren {
|
|
||||||
PlanesPlanIdAsignaturasListaNuevaRoute: typeof PlanesPlanIdAsignaturasListaNuevaRoute
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlanesPlanIdAsignaturasListaRouteRouteChildren: PlanesPlanIdAsignaturasListaRouteRouteChildren =
|
const PlanesPlanIdDetalleRouteWithChildren =
|
||||||
{
|
PlanesPlanIdDetalleRoute._addFileChildren(PlanesPlanIdDetalleRouteChildren)
|
||||||
PlanesPlanIdAsignaturasListaNuevaRoute:
|
|
||||||
PlanesPlanIdAsignaturasListaNuevaRoute,
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlanesPlanIdAsignaturasListaRouteRouteWithChildren =
|
|
||||||
PlanesPlanIdAsignaturasListaRouteRoute._addFileChildren(
|
|
||||||
PlanesPlanIdAsignaturasListaRouteRouteChildren,
|
|
||||||
)
|
|
||||||
|
|
||||||
interface PlanesPlanIdAsignaturasRouteRouteChildren {
|
|
||||||
PlanesPlanIdAsignaturasAsignaturaIdRouteRoute: typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
|
||||||
PlanesPlanIdAsignaturasListaRouteRoute: typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlanesPlanIdAsignaturasRouteRouteChildren: PlanesPlanIdAsignaturasRouteRouteChildren =
|
|
||||||
{
|
|
||||||
PlanesPlanIdAsignaturasAsignaturaIdRouteRoute:
|
|
||||||
PlanesPlanIdAsignaturasAsignaturaIdRouteRoute,
|
|
||||||
PlanesPlanIdAsignaturasListaRouteRoute:
|
|
||||||
PlanesPlanIdAsignaturasListaRouteRouteWithChildren,
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlanesPlanIdAsignaturasRouteRouteWithChildren =
|
|
||||||
PlanesPlanIdAsignaturasRouteRoute._addFileChildren(
|
|
||||||
PlanesPlanIdAsignaturasRouteRouteChildren,
|
|
||||||
)
|
|
||||||
|
|
||||||
const rootRouteChildren: RootRouteChildren = {
|
const rootRouteChildren: RootRouteChildren = {
|
||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
DashboardRoute: DashboardRoute,
|
DashboardRoute: DashboardRoute,
|
||||||
LoginRoute: LoginRoute,
|
LoginRoute: LoginRoute,
|
||||||
PlanesListaRouteRoute: PlanesListaRouteRouteWithChildren,
|
|
||||||
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
|
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
|
||||||
PlanesPlanIdDetalleRouteRoute: PlanesPlanIdDetalleRouteRouteWithChildren,
|
PlanesListaRoute: PlanesListaRouteWithChildren,
|
||||||
PlanesPlanIdAsignaturasRouteRoute:
|
PlanesPlanIdDetalleRoute: PlanesPlanIdDetalleRouteWithChildren,
|
||||||
PlanesPlanIdAsignaturasRouteRouteWithChildren,
|
PlanesPlanIdAsignaturasAsignaturaIdRoute:
|
||||||
|
PlanesPlanIdAsignaturasAsignaturaIdRoute,
|
||||||
}
|
}
|
||||||
export const routeTree = rootRouteImport
|
export const routeTree = rootRouteImport
|
||||||
._addFileChildren(rootRouteChildren)
|
._addFileChildren(rootRouteChildren)
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export const Route = createFileRoute('/planes/$planId/_detalle')({
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// PGRST116: The result contains 0 rows
|
// PGRST116: The result contains 0 rows
|
||||||
if (e?.code === 'PGRST116') {
|
if (e?.code === 'PGRST116') {
|
||||||
|
console.log('not found on', Route.path)
|
||||||
|
|
||||||
throw notFound()
|
throw notFound()
|
||||||
}
|
}
|
||||||
throw e
|
throw e
|
||||||
@@ -219,7 +221,11 @@ function RouteComponent() {
|
|||||||
<Tab to="/planes/$planId/" params={{ planId }}>
|
<Tab to="/planes/$planId/" params={{ planId }}>
|
||||||
Datos Generales
|
Datos Generales
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab to="/planes/$planId/mapa" params={{ planId }}>
|
<Tab
|
||||||
|
to="/planes/$planId/mapa"
|
||||||
|
params={{ planId }}
|
||||||
|
search={{ ciclo: data?.numero_ciclos }}
|
||||||
|
>
|
||||||
Mapa Curricular
|
Mapa Curricular
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab to="/planes/$planId/asignaturas" params={{ planId }}>
|
<Tab to="/planes/$planId/asignaturas" params={{ planId }}>
|
||||||
@@ -234,7 +240,13 @@ function RouteComponent() {
|
|||||||
<Tab to="/planes/$planId/documento" params={{ planId }}>
|
<Tab to="/planes/$planId/documento" params={{ planId }}>
|
||||||
Documento
|
Documento
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab to="/planes/$planId/historial" params={{ planId }}>
|
<Tab
|
||||||
|
to="/planes/$planId/historial"
|
||||||
|
params={{ planId }}
|
||||||
|
search={{
|
||||||
|
structure: data?.estructuras_plan?.definicion?.properties,
|
||||||
|
}}
|
||||||
|
>
|
||||||
Historial
|
Historial
|
||||||
</Tab>
|
</Tab>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -288,16 +300,20 @@ const InfoCard = forwardRef<
|
|||||||
function Tab({
|
function Tab({
|
||||||
to,
|
to,
|
||||||
params,
|
params,
|
||||||
|
search,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
to: string
|
to: string
|
||||||
params?: any
|
params?: any
|
||||||
|
search?: any
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
|
console.log(search)
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={to}
|
to={to}
|
||||||
params={params}
|
params={params}
|
||||||
|
search={search}
|
||||||
className="border-b-2 border-transparent pb-3 text-sm font-medium text-slate-500 transition-all hover:text-slate-800"
|
className="border-b-2 border-transparent pb-3 text-sm font-medium text-slate-500 transition-all hover:text-slate-800"
|
||||||
activeProps={{ className: 'border-teal-600 text-teal-700 font-bold' }}
|
activeProps={{ className: 'border-teal-600 text-teal-700 font-bold' }}
|
||||||
activeOptions={{
|
activeOptions={{
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { useState, useMemo } from 'react'
|
import { useState, useMemo } from 'react'
|
||||||
|
|
||||||
import type { Asignatura } from '@/types/plan'
|
import type { Asignatura, AsignaturaStatus, TipoAsignatura } from '@/types/plan'
|
||||||
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
@@ -33,17 +33,24 @@ import {
|
|||||||
import { usePlanAsignaturas, usePlanLineas } from '@/data'
|
import { usePlanAsignaturas, usePlanLineas } from '@/data'
|
||||||
|
|
||||||
// --- Configuración de Estilos ---
|
// --- Configuración de Estilos ---
|
||||||
const statusConfig: Record<string, { label: string; className: string }> = {
|
const statusConfig: Record<
|
||||||
|
AsignaturaStatus,
|
||||||
|
{ label: string; className: string }
|
||||||
|
> = {
|
||||||
borrador: { label: 'Borrador', className: 'bg-slate-100 text-slate-600' },
|
borrador: { label: 'Borrador', className: 'bg-slate-100 text-slate-600' },
|
||||||
revisada: { label: 'Revisada', className: 'bg-amber-100 text-amber-700' },
|
revisada: { label: 'Revisada', className: 'bg-amber-100 text-amber-700' },
|
||||||
aprobada: { label: 'Aprobada', className: 'bg-emerald-100 text-emerald-700' },
|
aprobada: { label: 'Aprobada', className: 'bg-emerald-100 text-emerald-700' },
|
||||||
}
|
}
|
||||||
|
|
||||||
const tipoConfig: Record<string, { label: string; className: string }> = {
|
const tipoConfig: Record<TipoAsignatura, { label: string; className: string }> =
|
||||||
obligatoria: { label: 'Obligatoria', className: 'bg-blue-100 text-blue-700' },
|
{
|
||||||
|
obligatoria: {
|
||||||
|
label: 'Obligatoria',
|
||||||
|
className: 'bg-blue-100 text-blue-700',
|
||||||
|
},
|
||||||
optativa: { label: 'Optativa', className: 'bg-purple-100 text-purple-700' },
|
optativa: { label: 'Optativa', className: 'bg-purple-100 text-purple-700' },
|
||||||
troncal: { label: 'Troncal', className: 'bg-slate-100 text-slate-700' },
|
troncal: { label: 'Troncal', className: 'bg-slate-100 text-slate-700' },
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Mapeadores de API ---
|
// --- Mapeadores de API ---
|
||||||
const mapAsignaturas = (asigApi: Array<any> = []): Array<Asignatura> => {
|
const mapAsignaturas = (asigApi: Array<any> = []): Array<Asignatura> => {
|
||||||
@@ -59,10 +66,13 @@ const mapAsignaturas = (asigApi: Array<any> = []): Array<Asignatura> => {
|
|||||||
estado: 'borrador', // O el campo que venga de tu API
|
estado: 'borrador', // O el campo que venga de tu API
|
||||||
hd: Math.floor((asig.horas_semana ?? 0) / 2),
|
hd: Math.floor((asig.horas_semana ?? 0) / 2),
|
||||||
hi: Math.ceil((asig.horas_semana ?? 0) / 2),
|
hi: Math.ceil((asig.horas_semana ?? 0) / 2),
|
||||||
|
prerrequisitos: Array.isArray(asig.prerrequisitos)
|
||||||
|
? asig.prerrequisitos
|
||||||
|
: [],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Route = createFileRoute('/planes/$planId/_detalle/asignaturas')({
|
export const Route = createFileRoute('/planes/$planId/_detalle/asignaturas/')({
|
||||||
component: AsignaturasPage,
|
component: AsignaturasPage,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -131,7 +141,17 @@ function AsignaturasPage() {
|
|||||||
<Button variant="outline" size="sm">
|
<Button variant="outline" size="sm">
|
||||||
<Copy className="mr-2 h-4 w-4" /> Clonar
|
<Copy className="mr-2 h-4 w-4" /> Clonar
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="bg-emerald-700 hover:bg-emerald-800">
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
console.log('planId desde asignaturas', planId)
|
||||||
|
|
||||||
|
navigate({
|
||||||
|
to: `/planes/${planId}/asignaturas/nueva`,
|
||||||
|
resetScroll: false,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
className="ring-offset-background bg-primary text-primary-foreground hover:bg-primary/90 inline-flex h-11 items-center justify-center gap-2 rounded-md px-8 text-sm font-medium shadow-md transition-colors"
|
||||||
|
>
|
||||||
<Plus className="mr-2 h-4 w-4" /> Nueva Asignatura
|
<Plus className="mr-2 h-4 w-4" /> Nueva Asignatura
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -262,17 +282,17 @@ function AsignaturasPage() {
|
|||||||
<TableCell>
|
<TableCell>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={`capitalize shadow-sm ${tipoConfig[asignatura.tipo]?.className}`}
|
className={`capitalize shadow-sm ${tipoConfig[asignatura.tipo].className}`}
|
||||||
>
|
>
|
||||||
{tipoConfig[asignatura.tipo]?.label}
|
{tipoConfig[asignatura.tipo].label}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={`capitalize shadow-sm ${statusConfig[asignatura.estado]?.className}`}
|
className={`capitalize shadow-sm ${statusConfig[asignatura.estado].className}`}
|
||||||
>
|
>
|
||||||
{statusConfig[asignatura.estado]?.label}
|
{statusConfig[asignatura.estado].label}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
@@ -3,12 +3,13 @@ import { createFileRoute } from '@tanstack/react-router'
|
|||||||
import { NuevaAsignaturaModalContainer } from '@/features/asignaturas/nueva/NuevaAsignaturaModalContainer'
|
import { NuevaAsignaturaModalContainer } from '@/features/asignaturas/nueva/NuevaAsignaturaModalContainer'
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/planes/$planId/asignaturas/_lista/nueva',
|
'/planes/$planId/_detalle/asignaturas/nueva',
|
||||||
)({
|
)({
|
||||||
component: NuevaAsignaturaModal,
|
component: NuevaAsignaturaModal,
|
||||||
})
|
})
|
||||||
|
|
||||||
function NuevaAsignaturaModal() {
|
function NuevaAsignaturaModal() {
|
||||||
const { planId } = Route.useParams()
|
const { planId } = Route.useParams()
|
||||||
|
console.log('planId desde nueva', planId)
|
||||||
return <NuevaAsignaturaModalContainer planId={planId} />
|
return <NuevaAsignaturaModalContainer planId={planId} />
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,9 @@ import { usePlanHistorial } from '@/data/hooks/usePlans'
|
|||||||
|
|
||||||
export const Route = createFileRoute('/planes/$planId/_detalle/historial')({
|
export const Route = createFileRoute('/planes/$planId/_detalle/historial')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
validateSearch: (search: { structure?: any }) => ({
|
||||||
|
structure: search.structure ?? null,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const getEventConfig = (tipo: string, campo: string) => {
|
const getEventConfig = (tipo: string, campo: string) => {
|
||||||
@@ -58,6 +61,9 @@ const getEventConfig = (tipo: string, campo: string) => {
|
|||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { planId } = Route.useParams()
|
const { planId } = Route.useParams()
|
||||||
const { data: rawData, isLoading } = usePlanHistorial(planId)
|
const { data: rawData, isLoading } = usePlanHistorial(planId)
|
||||||
|
const { structure } = Route.useSearch()
|
||||||
|
console.log(structure?.vigencia?.title)
|
||||||
|
console.log(structure)
|
||||||
|
|
||||||
// ESTADOS PARA EL MODAL
|
// ESTADOS PARA EL MODAL
|
||||||
const [selectedEvent, setSelectedEvent] = useState<any>(null)
|
const [selectedEvent, setSelectedEvent] = useState<any>(null)
|
||||||
@@ -77,7 +83,9 @@ function RouteComponent() {
|
|||||||
description:
|
description:
|
||||||
item.campo === 'datos'
|
item.campo === 'datos'
|
||||||
? `Actualización general de: ${item.valor_nuevo?.nombre || 'información del plan'}`
|
? `Actualización general de: ${item.valor_nuevo?.nombre || 'información del plan'}`
|
||||||
: `Se modificó el campo ${item.campo}`,
|
: `Se modificó el campo ${
|
||||||
|
structure?.[item.campo]?.title ?? item.campo
|
||||||
|
}`,
|
||||||
date: parseISO(item.cambiado_en),
|
date: parseISO(item.cambiado_en),
|
||||||
icon: config.icon,
|
icon: config.icon,
|
||||||
campo: item.campo,
|
campo: item.campo,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from '@/components/ui/tooltip'
|
} from '@/components/ui/tooltip'
|
||||||
import { usePlan } from '@/data'
|
import { usePlan, useUpdatePlanFields } from '@/data'
|
||||||
|
|
||||||
// import { toast } from 'sonner' // Asegúrate de tener sonner instalado o quita la línea
|
// import { toast } from 'sonner' // Asegúrate de tener sonner instalado o quita la línea
|
||||||
export const Route = createFileRoute('/planes/$planId/_detalle/')({
|
export const Route = createFileRoute('/planes/$planId/_detalle/')({
|
||||||
@@ -39,7 +39,7 @@ function DatosGeneralesPage() {
|
|||||||
const [editingId, setEditingId] = useState<string | null>(null)
|
const [editingId, setEditingId] = useState<string | null>(null)
|
||||||
const [editValue, setEditValue] = useState('')
|
const [editValue, setEditValue] = useState('')
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const updatePlan = useUpdatePlanFields()
|
||||||
// Confetti al llegar desde creación
|
// Confetti al llegar desde creación
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (location.state.showConfetti) {
|
if (location.state.showConfetti) {
|
||||||
@@ -122,14 +122,47 @@ function DatosGeneralesPage() {
|
|||||||
setEditValue('')
|
setEditValue('')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = (id: string) => {
|
const handleSave = (campo: DatosGeneralesField) => {
|
||||||
// Actualizamos el estado local de la lista
|
if (!data?.datos) return
|
||||||
|
|
||||||
|
const currentValue = (data.datos as any)[campo.clave]
|
||||||
|
|
||||||
|
let newValue: any
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof currentValue === 'object' &&
|
||||||
|
currentValue !== null &&
|
||||||
|
'description' in currentValue
|
||||||
|
) {
|
||||||
|
// Caso 1: objeto con description
|
||||||
|
newValue = {
|
||||||
|
...currentValue,
|
||||||
|
description: editValue,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Caso 2: valor plano (string, number, etc)
|
||||||
|
newValue = editValue
|
||||||
|
}
|
||||||
|
|
||||||
|
const datosActualizados = {
|
||||||
|
...data.datos,
|
||||||
|
[campo.clave]: newValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePlan.mutate({
|
||||||
|
planId,
|
||||||
|
patch: {
|
||||||
|
datos: datosActualizados,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// UI optimista
|
||||||
setCampos((prev) =>
|
setCampos((prev) =>
|
||||||
prev.map((c) => (c.id === id ? { ...c, value: editValue } : c)),
|
prev.map((c) => (c.id === campo.id ? { ...c, value: editValue } : c)),
|
||||||
)
|
)
|
||||||
|
|
||||||
setEditingId(null)
|
setEditingId(null)
|
||||||
setEditValue('')
|
setEditValue('')
|
||||||
// toast.success('Cambios guardados localmente')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleIARequest = (clave: string) => {
|
const handleIARequest = (clave: string) => {
|
||||||
@@ -245,7 +278,7 @@ function DatosGeneralesPage() {
|
|||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
className="bg-teal-600 hover:bg-teal-700"
|
className="bg-teal-600 hover:bg-teal-700"
|
||||||
onClick={() => handleSave(campo.id)}
|
onClick={() => handleSave(campo)}
|
||||||
>
|
>
|
||||||
<Check size={14} className="mr-1" /> Guardar
|
<Check size={14} className="mr-1" /> Guardar
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
import { usePlanAsignaturas, usePlanLineas } from '@/data'
|
import { usePlanAsignaturas, usePlanLineas, useUpdateAsignatura } from '@/data'
|
||||||
|
|
||||||
// --- Mapeadores (Fuera del componente para mayor limpieza) ---
|
// --- Mapeadores (Fuera del componente para mayor limpieza) ---
|
||||||
const mapLineasToLineaCurricular = (
|
const mapLineasToLineaCurricular = (
|
||||||
@@ -60,8 +60,9 @@ const mapAsignaturasToAsignaturas = (
|
|||||||
tipo: asig.tipo === 'OBLIGATORIA' ? 'obligatoria' : 'optativa',
|
tipo: asig.tipo === 'OBLIGATORIA' ? 'obligatoria' : 'optativa',
|
||||||
estado: 'borrador',
|
estado: 'borrador',
|
||||||
orden: asig.orden_celda ?? 0,
|
orden: asig.orden_celda ?? 0,
|
||||||
hd: Math.floor((asig.horas_semana ?? 0) / 2),
|
// Mapeo directo de los nuevos campos de la API
|
||||||
hi: Math.ceil((asig.horas_semana ?? 0) / 2),
|
hd: asig.horas_academicas ?? 0,
|
||||||
|
hi: asig.horas_independientes ?? 0,
|
||||||
prerrequisitos: [],
|
prerrequisitos: [],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -157,10 +158,15 @@ function AsignaturaCardItem({
|
|||||||
|
|
||||||
export const Route = createFileRoute('/planes/$planId/_detalle/mapa')({
|
export const Route = createFileRoute('/planes/$planId/_detalle/mapa')({
|
||||||
component: MapaCurricularPage,
|
component: MapaCurricularPage,
|
||||||
|
validateSearch: (search: { ciclo?: number }) => ({
|
||||||
|
ciclo: search.ciclo ?? null,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
function MapaCurricularPage() {
|
function MapaCurricularPage() {
|
||||||
const { planId } = Route.useParams() // Idealmente usa el ID de la ruta
|
const { planId } = Route.useParams() // Idealmente usa el ID de la ruta
|
||||||
|
const { ciclo } = Route.useSearch()
|
||||||
|
console.log(ciclo)
|
||||||
|
|
||||||
// 1. Fetch de Datos
|
// 1. Fetch de Datos
|
||||||
const { data: asignaturasApi, isLoading: loadingAsig } =
|
const { data: asignaturasApi, isLoading: loadingAsig } =
|
||||||
@@ -178,6 +184,7 @@ function MapaCurricularPage() {
|
|||||||
useState<Asignatura | null>(null)
|
useState<Asignatura | null>(null)
|
||||||
const [hasAreaComun, setHasAreaComun] = useState(false)
|
const [hasAreaComun, setHasAreaComun] = useState(false)
|
||||||
const [nombreNuevaLinea, setNombreNuevaLinea] = useState('') // Para el input de nombre personalizado
|
const [nombreNuevaLinea, setNombreNuevaLinea] = useState('') // Para el input de nombre personalizado
|
||||||
|
const { mutate: updateAsignatura, isPending } = useUpdateAsignatura()
|
||||||
|
|
||||||
const manejarAgregarLinea = (nombre: string) => {
|
const manejarAgregarLinea = (nombre: string) => {
|
||||||
const nombreNormalizado = nombre.trim()
|
const nombreNormalizado = nombre.trim()
|
||||||
@@ -249,7 +256,7 @@ function MapaCurricularPage() {
|
|||||||
if (lineasApi) setLineas(mapLineasToLineaCurricular(lineasApi))
|
if (lineasApi) setLineas(mapLineasToLineaCurricular(lineasApi))
|
||||||
}, [lineasApi])
|
}, [lineasApi])
|
||||||
|
|
||||||
const ciclosTotales = 9
|
const ciclosTotales = Number(ciclo)
|
||||||
const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1)
|
const ciclosArray = Array.from({ length: ciclosTotales }, (_, i) => i + 1)
|
||||||
|
|
||||||
// Nuevo estado para controlar los datos temporales del modal de edición
|
// Nuevo estado para controlar los datos temporales del modal de edición
|
||||||
@@ -263,13 +270,41 @@ function MapaCurricularPage() {
|
|||||||
setAsignaturas((prev) =>
|
setAsignaturas((prev) =>
|
||||||
prev.map((m) => (m.id === editingData.id ? { ...editingData } : m)),
|
prev.map((m) => (m.id === editingData.id ? { ...editingData } : m)),
|
||||||
)
|
)
|
||||||
|
// setIsEditModalOpen(false)
|
||||||
|
// Preparamos el patch con la estructura de tu tabla
|
||||||
|
const patch = {
|
||||||
|
nombre: editingData.nombre,
|
||||||
|
codigo: editingData.clave,
|
||||||
|
creditos: editingData.creditos,
|
||||||
|
horas_academicas: editingData.hd,
|
||||||
|
horas_independientes: editingData.hi,
|
||||||
|
numero_ciclo: editingData.ciclo,
|
||||||
|
linea_plan_id: editingData.lineaCurricularId,
|
||||||
|
tipo: editingData.tipo.toUpperCase(), // Asegurar que coincida con el ENUM (OBLIGATORIA/OPTATIVA)
|
||||||
|
// datos: editingData.datos, // Si editaste algo del JSONB
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAsignatura(
|
||||||
|
{ asignaturaId: editingData.id, patch },
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
setIsEditModalOpen(false)
|
setIsEditModalOpen(false)
|
||||||
|
// Opcional: Mostrar un toast de éxito
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('Error al guardar:', error)
|
||||||
|
alert('Hubo un error al guardar los cambios.')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. MODIFICACIÓN: Zona de soltado siempre visible
|
// 2. MODIFICACIÓN: Zona de soltado siempre visible
|
||||||
// Cambiamos la condición: Mostramos la sección si hay asignaturas sin asignar
|
// Cambiamos la condición: Mostramos la sección si hay asignaturas sin asignar
|
||||||
// O si simplemente queremos tener el "depósito" disponible.
|
// O si simplemente queremos tener el "depósito" disponible.
|
||||||
const unassignedAsignaturas = asignaturas.filter((m) => m.ciclo === null)
|
const unassignedAsignaturas = asignaturas.filter(
|
||||||
|
(m) => m.ciclo === null || m.lineaCurricularId === null,
|
||||||
|
)
|
||||||
|
|
||||||
// --- Lógica de Gestión ---
|
// --- Lógica de Gestión ---
|
||||||
const agregarLinea = (nombre: string) => {
|
const agregarLinea = (nombre: string) => {
|
||||||
@@ -304,7 +339,7 @@ function MapaCurricularPage() {
|
|||||||
|
|
||||||
const getSubtotalLinea = (lineaId: string) => {
|
const getSubtotalLinea = (lineaId: string) => {
|
||||||
return asignaturas
|
return asignaturas
|
||||||
.filter((m) => m.lineaCurricularId === lineaId && m.ciclo !== null)
|
.filter((m) => m.lineaCurricularId === lineaId && m.ciclo !== null) // Aseguramos que pertenezca a la línea Y tenga ciclo
|
||||||
.reduce(
|
.reduce(
|
||||||
(acc, m) => ({
|
(acc, m) => ({
|
||||||
cr: acc.cr + (m.creditos || 0),
|
cr: acc.cr + (m.creditos || 0),
|
||||||
@@ -327,6 +362,7 @@ function MapaCurricularPage() {
|
|||||||
) => {
|
) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (draggedAsignatura) {
|
if (draggedAsignatura) {
|
||||||
|
// 1. Actualización optimista del UI
|
||||||
setAsignaturas((prev) =>
|
setAsignaturas((prev) =>
|
||||||
prev.map((m) =>
|
prev.map((m) =>
|
||||||
m.id === draggedAsignatura
|
m.id === draggedAsignatura
|
||||||
@@ -334,6 +370,23 @@ function MapaCurricularPage() {
|
|||||||
: m,
|
: m,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 2. Persistir en la API
|
||||||
|
const patch = {
|
||||||
|
numero_ciclo: ciclo,
|
||||||
|
linea_plan_id: lineaId,
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAsignatura(
|
||||||
|
{ asignaturaId: draggedAsignatura, patch },
|
||||||
|
{
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('Error al mover:', error)
|
||||||
|
// Opcional: Revertir el estado local si falla
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
setDraggedAsignatura(null)
|
setDraggedAsignatura(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,10 +421,15 @@ function MapaCurricularPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{asignaturas.filter((m) => !m.ciclo).length > 0 && (
|
{asignaturas.filter((m) => !m.ciclo || !m.lineaCurricularId).length >
|
||||||
|
0 && (
|
||||||
<Badge className="border-amber-100 bg-amber-50 text-amber-600 hover:bg-amber-50">
|
<Badge className="border-amber-100 bg-amber-50 text-amber-600 hover:bg-amber-50">
|
||||||
<AlertTriangle size={14} className="mr-1" />{' '}
|
<AlertTriangle size={14} className="mr-1" />{' '}
|
||||||
{asignaturas.filter((m) => !m.ciclo).length} sin asignar
|
{
|
||||||
|
asignaturas.filter((m) => !m.ciclo || !m.lineaCurricularId)
|
||||||
|
.length
|
||||||
|
}{' '}
|
||||||
|
sin asignar
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -477,6 +535,8 @@ function MapaCurricularPage() {
|
|||||||
<div
|
<div
|
||||||
key={ciclo}
|
key={ciclo}
|
||||||
onDragOver={handleDragOver}
|
onDragOver={handleDragOver}
|
||||||
|
// ANTES: onDrop={(e) => handleDrop(e, null, null)}
|
||||||
|
// AHORA: Usamos las variables 'ciclo' y 'linea.id' del map
|
||||||
onDrop={(e) => handleDrop(e, ciclo, linea.id)}
|
onDrop={(e) => handleDrop(e, ciclo, linea.id)}
|
||||||
className="min-h-[140px] space-y-2 rounded-xl border-2 border-dashed border-slate-100 bg-slate-50/20 p-2"
|
className="min-h-[140px] space-y-2 rounded-xl border-2 border-dashed border-slate-100 bg-slate-50/20 p-2"
|
||||||
>
|
>
|
||||||
@@ -588,7 +648,10 @@ function MapaCurricularPage() {
|
|||||||
|
|
||||||
{/* Modal de Edición */}
|
{/* Modal de Edición */}
|
||||||
<Dialog open={isEditModalOpen} onOpenChange={setIsEditModalOpen}>
|
<Dialog open={isEditModalOpen} onOpenChange={setIsEditModalOpen}>
|
||||||
<DialogContent className="sm:max-w-[550px]">
|
<DialogContent
|
||||||
|
className="sm:max-w-[550px]"
|
||||||
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="font-bold text-slate-700">
|
<DialogTitle className="font-bold text-slate-700">
|
||||||
Editar Asignatura
|
Editar Asignatura
|
||||||
@@ -734,32 +797,61 @@ function MapaCurricularPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Fila 4: Seriación (Igual a tu imagen) */}
|
{/* Fila 4: Seriación (Prerrequisitos) */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-xs font-bold text-slate-500 uppercase">
|
<label className="text-xs font-bold text-slate-500 uppercase">
|
||||||
Seriación (Prerrequisitos)
|
Seriación (Prerrequisitos)
|
||||||
</label>
|
</label>
|
||||||
<Select>
|
<Select
|
||||||
|
onValueChange={(val) => {
|
||||||
|
// Evitamos duplicados en la lista de prerrequisitos local
|
||||||
|
if (!editingData.prerrequisitos.includes(val)) {
|
||||||
|
setEditingData({
|
||||||
|
...editingData,
|
||||||
|
prerrequisitos: [...editingData.prerrequisitos, val],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder="Seleccionar asignatura..." />
|
<SelectValue placeholder="Seleccionar asignatura..." />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{asignaturas.map((m) => (
|
{/* FILTRO CLAVE:
|
||||||
|
Solo mostramos materias cuyo ID sea diferente al de la materia que estamos editando
|
||||||
|
*/}
|
||||||
|
{asignaturas
|
||||||
|
.filter((m) => m.id !== editingData.id)
|
||||||
|
.map((m) => (
|
||||||
<SelectItem key={m.id} value={m.clave}>
|
<SelectItem key={m.id} value={m.clave}>
|
||||||
{m.nombre}
|
{m.nombre} ({m.clave})
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<div className="mt-2 flex gap-2">
|
|
||||||
{/* Aquí usamos el array vacío que inicializamos en el mapeador */}
|
{/* Visualización de los prerrequisitos seleccionados */}
|
||||||
|
<div className="mt-2 flex flex-wrap gap-2">
|
||||||
{editingData.prerrequisitos.map((pre) => (
|
{editingData.prerrequisitos.map((pre) => (
|
||||||
<Badge
|
<Badge
|
||||||
key={pre}
|
key={pre}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="bg-slate-100 text-slate-600"
|
className="bg-slate-100 text-slate-600"
|
||||||
>
|
>
|
||||||
{pre} <span className="ml-1 cursor-pointer">×</span>
|
{pre}
|
||||||
|
<button
|
||||||
|
className="ml-1 hover:text-red-500"
|
||||||
|
onClick={() => {
|
||||||
|
setEditingData({
|
||||||
|
...editingData,
|
||||||
|
prerrequisitos: editingData.prerrequisitos.filter(
|
||||||
|
(p) => p !== pre,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
44
src/routes/planes/$planId/asignaturas/$asignaturaId.tsx
Normal file
44
src/routes/planes/$planId/asignaturas/$asignaturaId.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { createFileRoute, notFound } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
import AsignaturaDetailPage from '@/components/asignaturas/detalle/AsignaturaDetailPage'
|
||||||
|
import { NotFoundPage } from '@/components/ui/NotFoundPage'
|
||||||
|
import { subjects_get } from '@/data/api/subjects.api'
|
||||||
|
import { qk } from '@/data/query/keys'
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
'/planes/$planId/asignaturas/$asignaturaId',
|
||||||
|
)({
|
||||||
|
loader: async ({ context: { queryClient }, params: { asignaturaId } }) => {
|
||||||
|
try {
|
||||||
|
await queryClient.ensureQueryData({
|
||||||
|
queryKey: qk.asignatura(asignaturaId),
|
||||||
|
queryFn: () => subjects_get(asignaturaId),
|
||||||
|
})
|
||||||
|
} catch (e: any) {
|
||||||
|
// PGRST116: The result contains 0 rows (Supabase Single response error)
|
||||||
|
if (e?.code === 'PGRST116') {
|
||||||
|
throw notFound()
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
},
|
||||||
|
notFoundComponent: () => {
|
||||||
|
return (
|
||||||
|
<NotFoundPage
|
||||||
|
title="Materia no encontrada"
|
||||||
|
message="La asignatura que buscas no existe o fue eliminada."
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
component: RouteComponent,
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
// const { planId, asignaturaId } = Route.useParams()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AsignaturaDetailPage></AsignaturaDetailPage>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import AsignaturaDetailPage from '@/components/asignaturas/detalle/AsignaturaDetailPage'
|
|
||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
'/planes/$planId/asignaturas/$asignaturaId',
|
|
||||||
)({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
//const { planId, asignaturaId } = Route.useParams()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<AsignaturaDetailPage></AsignaturaDetailPage>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/planes/$planId/asignaturas/_lista')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
return (
|
|
||||||
<main className="bg-background min-h-screen w-full">
|
|
||||||
<div className="mx-auto flex w-full max-w-7xl flex-col gap-4 px-4 py-6 md:px-6 lg:px-8">
|
|
||||||
<h1 className="text-foreground text-2xl font-semibold">Asignaturas</h1>
|
|
||||||
<Outlet />
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { createFileRoute, Outlet, notFound } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
import { NotFoundPage } from '@/components/ui/NotFoundPage'
|
|
||||||
import { plans_get } from '@/data/api/plans.api'
|
|
||||||
import { qk } from '@/data/query/keys'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/planes/$planId/asignaturas')({
|
|
||||||
loader: async ({ context: { queryClient }, params: { planId } }) => {
|
|
||||||
try {
|
|
||||||
await queryClient.ensureQueryData({
|
|
||||||
queryKey: qk.plan(planId),
|
|
||||||
queryFn: () => plans_get(planId),
|
|
||||||
})
|
|
||||||
} catch (e: any) {
|
|
||||||
if (e?.code === 'PGRST116') {
|
|
||||||
throw notFound()
|
|
||||||
}
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
},
|
|
||||||
notFoundComponent: () => {
|
|
||||||
return (
|
|
||||||
<NotFoundPage
|
|
||||||
title="Plan de Estudios no encontrado"
|
|
||||||
message="El plan de estudios que intentas consultar no existe o no tienes permisos para verlo."
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
component: AsignaturasLayout,
|
|
||||||
})
|
|
||||||
|
|
||||||
function AsignaturasLayout() {
|
|
||||||
return <Outlet />
|
|
||||||
}
|
|
||||||
@@ -125,7 +125,11 @@ function RouteComponent() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate({ to: '/planes/nuevo' })}
|
onClick={() => {
|
||||||
|
console.log('planId')
|
||||||
|
|
||||||
|
navigate({ to: '/planes/nuevo', resetScroll: false })
|
||||||
|
}}
|
||||||
className="ring-offset-background bg-primary text-primary-foreground hover:bg-primary/90 inline-flex h-11 items-center justify-center gap-2 rounded-md px-8 text-sm font-medium shadow-md transition-colors"
|
className="ring-offset-background bg-primary text-primary-foreground hover:bg-primary/90 inline-flex h-11 items-center justify-center gap-2 rounded-md px-8 text-sm font-medium shadow-md transition-colors"
|
||||||
>
|
>
|
||||||
<Icons.Plus /> Nuevo plan de estudios
|
<Icons.Plus /> Nuevo plan de estudios
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user