Compare commits
5 Commits
45952cbdc8
...
4950f7efbf
| Author | SHA1 | Date | |
|---|---|---|---|
| 4950f7efbf | |||
| aa867e4612 | |||
| 0fddcfdc65 | |||
| b5e6565ae1 | |||
| 254f6383d7 |
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"bradlc.vscode-tailwindcss"
|
||||||
|
]
|
||||||
|
}
|
||||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -22,5 +22,11 @@
|
|||||||
],
|
],
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.css": "tailwindcss"
|
"*.css": "tailwindcss"
|
||||||
}
|
},
|
||||||
|
"[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||||
|
"[javascriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||||
|
"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||||
|
"[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||||
|
"[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
|
||||||
|
"prettier.requireConfig": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function PasoBasicosForm({
|
|||||||
<div className="grid gap-4 sm:grid-cols-2">
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
<div className="grid gap-1 sm:col-span-2">
|
<div className="grid gap-1 sm:col-span-2">
|
||||||
<Label htmlFor="nombrePlan">
|
<Label htmlFor="nombrePlan">
|
||||||
Nombre del plan <span className="text-destructive">*</span>
|
Nombre del plan {/* <span className="text-destructive">*</span> */}
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
id="nombrePlan"
|
id="nombrePlan"
|
||||||
|
|||||||
@@ -65,7 +65,12 @@ export function PasoDetallesPanel({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<Label htmlFor="notas">Notas adicionales</Label>
|
<Label htmlFor="notas">
|
||||||
|
Notas adicionales
|
||||||
|
<span className="text-xs font-normal text-gray-500 dark:text-gray-400">
|
||||||
|
(Opcional)
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
<textarea
|
<textarea
|
||||||
id="notas"
|
id="notas"
|
||||||
className="bg-background text-foreground ring-offset-background focus-visible:ring-ring min-h-24 w-full rounded-md border px-3 py-2 text-sm shadow-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none"
|
className="bg-background text-foreground ring-offset-background focus-visible:ring-ring min-h-24 w-full rounded-md border px-3 py-2 text-sm shadow-sm focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none"
|
||||||
|
|||||||
@@ -1,24 +1,105 @@
|
|||||||
|
import { useNavigate } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
import type { NewPlanWizardState } from '@/features/planes/nuevo/types'
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { useGeneratePlanAI } from '@/data/hooks/usePlans'
|
||||||
|
|
||||||
export function WizardControls({
|
export function WizardControls({
|
||||||
errorMessage,
|
errorMessage,
|
||||||
onPrev,
|
onPrev,
|
||||||
onNext,
|
onNext,
|
||||||
onCreate,
|
|
||||||
disablePrev,
|
disablePrev,
|
||||||
disableNext,
|
disableNext,
|
||||||
disableCreate,
|
disableCreate,
|
||||||
isLastStep,
|
isLastStep,
|
||||||
|
wizard,
|
||||||
|
setWizard,
|
||||||
}: {
|
}: {
|
||||||
errorMessage?: string | null
|
errorMessage?: string | null
|
||||||
onPrev: () => void
|
onPrev: () => void
|
||||||
onNext: () => void
|
onNext: () => void
|
||||||
onCreate: () => void
|
|
||||||
disablePrev: boolean
|
disablePrev: boolean
|
||||||
disableNext: boolean
|
disableNext: boolean
|
||||||
disableCreate: boolean
|
disableCreate: boolean
|
||||||
isLastStep: boolean
|
isLastStep: boolean
|
||||||
|
wizard: NewPlanWizardState
|
||||||
|
setWizard: React.Dispatch<React.SetStateAction<NewPlanWizardState>>
|
||||||
}) {
|
}) {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const generatePlanAI = useGeneratePlanAI()
|
||||||
|
// const persistPlanFromAI = usePersistPlanFromAI()
|
||||||
|
|
||||||
|
const handleCreate = async () => {
|
||||||
|
// Start loading
|
||||||
|
setWizard(
|
||||||
|
(w: NewPlanWizardState): NewPlanWizardState => ({
|
||||||
|
...w,
|
||||||
|
isLoading: true,
|
||||||
|
errorMessage: null,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (wizard.tipoOrigen === 'IA') {
|
||||||
|
const tipoCicloSafe = (wizard.datosBasicos.tipoCiclo ||
|
||||||
|
'Semestre') as any
|
||||||
|
const numCiclosSafe =
|
||||||
|
typeof wizard.datosBasicos.numCiclos === 'number'
|
||||||
|
? wizard.datosBasicos.numCiclos
|
||||||
|
: 1
|
||||||
|
|
||||||
|
const aiInput = {
|
||||||
|
datosBasicos: {
|
||||||
|
nombrePlan: wizard.datosBasicos.nombrePlan,
|
||||||
|
carreraId: wizard.datosBasicos.carreraId,
|
||||||
|
facultadId: wizard.datosBasicos.facultadId || undefined,
|
||||||
|
nivel: wizard.datosBasicos.nivel as string,
|
||||||
|
tipoCiclo: tipoCicloSafe,
|
||||||
|
numCiclos: numCiclosSafe,
|
||||||
|
estructuraPlanId: wizard.datosBasicos.estructuraPlanId as string,
|
||||||
|
},
|
||||||
|
iaConfig: {
|
||||||
|
descripcionEnfoque: wizard.iaConfig?.descripcionEnfoque || '',
|
||||||
|
notasAdicionales: wizard.iaConfig?.notasAdicionales || '',
|
||||||
|
archivosReferencia: wizard.iaConfig?.archivosReferencia || [],
|
||||||
|
repositoriosIds: wizard.iaConfig?.repositoriosReferencia || [],
|
||||||
|
archivosAdjuntos: wizard.iaConfig?.archivosAdjuntos || [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${new Date().toISOString()} - Enviando a generar plan IA`)
|
||||||
|
|
||||||
|
const data = await generatePlanAI.mutateAsync(aiInput as any)
|
||||||
|
console.log(`${new Date().toISOString()} - Plan IA generado`, data)
|
||||||
|
|
||||||
|
navigate({ to: `/planes/${data.plan.id}` })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback mocks for non-IA origins
|
||||||
|
await new Promise((r) => setTimeout(r, 900))
|
||||||
|
const nuevoId = (() => {
|
||||||
|
if (wizard.tipoOrigen === 'MANUAL') return 'plan_new_manual_001'
|
||||||
|
if (
|
||||||
|
wizard.tipoOrigen === 'CLONADO_INTERNO' ||
|
||||||
|
wizard.tipoOrigen === 'CLONADO_TRADICIONAL'
|
||||||
|
)
|
||||||
|
return 'plan_new_clone_001'
|
||||||
|
return 'plan_new_import_001'
|
||||||
|
})()
|
||||||
|
navigate({ to: `/planes/${nuevoId}` })
|
||||||
|
} catch (err: any) {
|
||||||
|
setWizard((w) => ({
|
||||||
|
...w,
|
||||||
|
isLoading: false,
|
||||||
|
errorMessage: err?.message ?? 'Error generando el plan',
|
||||||
|
}))
|
||||||
|
} finally {
|
||||||
|
setWizard((w) => ({ ...w, isLoading: false }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
@@ -33,7 +114,7 @@ export function WizardControls({
|
|||||||
Anterior
|
Anterior
|
||||||
</Button>
|
</Button>
|
||||||
{isLastStep ? (
|
{isLastStep ? (
|
||||||
<Button onClick={onCreate} disabled={disableCreate}>
|
<Button onClick={handleCreate} disabled={disableCreate}>
|
||||||
Crear plan
|
Crear plan
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type { UploadedFile } from '@/components/planes/wizard/PasoDetallesPanel/
|
|||||||
|
|
||||||
const EDGE = {
|
const EDGE = {
|
||||||
plans_create_manual: 'plans_create_manual',
|
plans_create_manual: 'plans_create_manual',
|
||||||
ai_generate_plan: 'ai_generate_plan',
|
ai_generate_plan: 'ai-generate-plan',
|
||||||
plans_persist_from_ai: 'plans_persist_from_ai',
|
plans_persist_from_ai: 'plans_persist_from_ai',
|
||||||
plans_clone_from_existing: 'plans_clone_from_existing',
|
plans_clone_from_existing: 'plans_clone_from_existing',
|
||||||
|
|
||||||
@@ -213,10 +213,10 @@ export type AIGeneratePlanInput = {
|
|||||||
nivel: string
|
nivel: string
|
||||||
tipoCiclo: TipoCiclo
|
tipoCiclo: TipoCiclo
|
||||||
numCiclos: number
|
numCiclos: number
|
||||||
|
estructuraPlanId: UUID
|
||||||
}
|
}
|
||||||
iaConfig: {
|
iaConfig: {
|
||||||
descripcionEnfoque: string
|
descripcionEnfoque: string
|
||||||
poblacionObjetivo?: string
|
|
||||||
notasAdicionales?: string
|
notasAdicionales?: string
|
||||||
archivosReferencia?: Array<UUID>
|
archivosReferencia?: Array<UUID>
|
||||||
repositoriosIds?: Array<UUID>
|
repositoriosIds?: Array<UUID>
|
||||||
@@ -228,7 +228,27 @@ export type AIGeneratePlanInput = {
|
|||||||
export async function ai_generate_plan(
|
export async function ai_generate_plan(
|
||||||
input: AIGeneratePlanInput,
|
input: AIGeneratePlanInput,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return invokeEdge<any>(EDGE.ai_generate_plan, input)
|
console.log('input ai generate', input)
|
||||||
|
|
||||||
|
const edgeFunctionBody = new FormData()
|
||||||
|
edgeFunctionBody.append('datosBasicos', JSON.stringify(input.datosBasicos))
|
||||||
|
edgeFunctionBody.append(
|
||||||
|
'iaConfig',
|
||||||
|
JSON.stringify({
|
||||||
|
...input.iaConfig,
|
||||||
|
archivosAdjuntos: undefined, // los manejamos aparte
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
input.iaConfig.archivosAdjuntos.forEach((file, index) => {
|
||||||
|
edgeFunctionBody.append(`archivosAdjuntos`, file.file)
|
||||||
|
})
|
||||||
|
|
||||||
|
return invokeEdge<any>(
|
||||||
|
EDGE.ai_generate_plan,
|
||||||
|
edgeFunctionBody,
|
||||||
|
undefined,
|
||||||
|
supabaseBrowser(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function plans_persist_from_ai(payload: {
|
export async function plans_persist_from_ai(payload: {
|
||||||
|
|||||||
@@ -116,11 +116,27 @@ export function useCreatePlanManual() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useGeneratePlanAI() {
|
export function useGeneratePlanAI() {
|
||||||
|
const qc = useQueryClient()
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: ai_generate_plan,
|
mutationFn: ai_generate_plan,
|
||||||
|
onSuccess: (data) => {
|
||||||
|
// Asumiendo que la Edge Function devuelve { ok: true, plan: { id: ... } }
|
||||||
|
console.log('success de ai_generate_plan')
|
||||||
|
|
||||||
|
const newPlan = data.plan
|
||||||
|
|
||||||
|
if (newPlan) {
|
||||||
|
// 1. Invalidar la lista para que aparezca el nuevo plan
|
||||||
|
qc.invalidateQueries({ queryKey: ['planes', 'list'] })
|
||||||
|
|
||||||
|
// 2. (Opcional) Pre-cargar el dato individual para que la navegación sea instantánea
|
||||||
|
// qc.setQueryData(["planes", "detail", newPlan.id], newPlan);
|
||||||
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funcion obsoleta porque ahora el plan se persiste directamente en useGeneratePlanAI
|
||||||
export function usePersistPlanFromAI() {
|
export function usePersistPlanFromAI() {
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import * as Icons from 'lucide-react'
|
|||||||
|
|
||||||
import { useNuevoPlanWizard } from './hooks/useNuevoPlanWizard'
|
import { useNuevoPlanWizard } from './hooks/useNuevoPlanWizard'
|
||||||
|
|
||||||
import type { NewPlanWizardState } from './types'
|
// import type { NewPlanWizardState } from './types'
|
||||||
|
|
||||||
import { PasoBasicosForm } from '@/components/planes/wizard/PasoBasicosForm/PasoBasicosForm'
|
import { PasoBasicosForm } from '@/components/planes/wizard/PasoBasicosForm/PasoBasicosForm'
|
||||||
import { PasoDetallesPanel } from '@/components/planes/wizard/PasoDetallesPanel/PasoDetallesPanel'
|
import { PasoDetallesPanel } from '@/components/planes/wizard/PasoDetallesPanel/PasoDetallesPanel'
|
||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog'
|
} 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 = () => 'JEFE_CARRERA' as const
|
||||||
@@ -48,8 +48,7 @@ const Wizard = defineStepper(
|
|||||||
export default function NuevoPlanModalContainer() {
|
export default function NuevoPlanModalContainer() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const role = auth_get_current_user_role()
|
const role = auth_get_current_user_role()
|
||||||
const generatePlanAI = useGeneratePlanAI()
|
// const generatePlanAI = useGeneratePlanAI()
|
||||||
// const persistPlanFromAI = usePersistPlanFromAI()
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
wizard,
|
wizard,
|
||||||
@@ -63,74 +62,7 @@ export default function NuevoPlanModalContainer() {
|
|||||||
navigate({ to: '/planes', resetScroll: false })
|
navigate({ to: '/planes', resetScroll: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
const crearPlan = async () => {
|
// Crear plan: ahora la lógica vive en WizardControls
|
||||||
setWizard(
|
|
||||||
(w: NewPlanWizardState): NewPlanWizardState => ({
|
|
||||||
...w,
|
|
||||||
isLoading: true,
|
|
||||||
errorMessage: null,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (wizard.tipoOrigen === 'IA') {
|
|
||||||
const tipoCicloSafe = (wizard.datosBasicos.tipoCiclo ||
|
|
||||||
'Semestre') as any
|
|
||||||
const numCiclosSafe =
|
|
||||||
typeof wizard.datosBasicos.numCiclos === 'number'
|
|
||||||
? wizard.datosBasicos.numCiclos
|
|
||||||
: 1
|
|
||||||
|
|
||||||
const aiInput = {
|
|
||||||
datosBasicos: {
|
|
||||||
nombrePlan: wizard.datosBasicos.nombrePlan,
|
|
||||||
carreraId: wizard.datosBasicos.carreraId,
|
|
||||||
facultadId: wizard.datosBasicos.facultadId || undefined,
|
|
||||||
nivel: wizard.datosBasicos.nivel as string,
|
|
||||||
tipoCiclo: tipoCicloSafe,
|
|
||||||
numCiclos: numCiclosSafe,
|
|
||||||
estructuraPlanId: wizard.datosBasicos.estructuraPlanId as string,
|
|
||||||
},
|
|
||||||
iaConfig: {
|
|
||||||
descripcionEnfoque: wizard.iaConfig?.descripcionEnfoque || '',
|
|
||||||
notasAdicionales: wizard.iaConfig?.notasAdicionales || '',
|
|
||||||
archivosReferencia: wizard.iaConfig?.archivosReferencia || [],
|
|
||||||
repositoriosIds: wizard.iaConfig?.repositoriosReferencia || [],
|
|
||||||
archivosAdjuntos: wizard.iaConfig?.archivosAdjuntos || [],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await generatePlanAI.mutateAsync(aiInput as any)
|
|
||||||
// const createdPlan = await persistPlanFromAI.mutateAsync({
|
|
||||||
// jsonPlan: generatedJson,
|
|
||||||
// })
|
|
||||||
// navigate({ to: `/planes/${createdPlan.id}` })
|
|
||||||
console.log('Plan generado por IA:', response)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: comportamiento previo para otros modos (mock IDs)
|
|
||||||
await new Promise((r) => setTimeout(r, 900))
|
|
||||||
const nuevoId = (() => {
|
|
||||||
if (wizard.tipoOrigen === 'MANUAL') return 'plan_new_manual_001'
|
|
||||||
if (
|
|
||||||
wizard.tipoOrigen === 'CLONADO_INTERNO' ||
|
|
||||||
wizard.tipoOrigen === 'CLONADO_TRADICIONAL'
|
|
||||||
)
|
|
||||||
return 'plan_new_clone_001'
|
|
||||||
return 'plan_new_import_001'
|
|
||||||
})()
|
|
||||||
navigate({ to: `/planes/${nuevoId}` })
|
|
||||||
} catch (err: any) {
|
|
||||||
setWizard((w) => ({
|
|
||||||
...w,
|
|
||||||
isLoading: false,
|
|
||||||
errorMessage: err?.message ?? 'Error generando el plan con IA',
|
|
||||||
}))
|
|
||||||
} finally {
|
|
||||||
setWizard((w) => ({ ...w, isLoading: false }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={true} onOpenChange={(open) => !open && handleClose()}>
|
<Dialog open={true} onOpenChange={(open) => !open && handleClose()}>
|
||||||
@@ -233,7 +165,6 @@ export default function NuevoPlanModalContainer() {
|
|||||||
errorMessage={wizard.errorMessage}
|
errorMessage={wizard.errorMessage}
|
||||||
onPrev={() => methods.prev()}
|
onPrev={() => methods.prev()}
|
||||||
onNext={() => methods.next()}
|
onNext={() => methods.next()}
|
||||||
onCreate={crearPlan}
|
|
||||||
disablePrev={
|
disablePrev={
|
||||||
Wizard.utils.getIndex(methods.current.id) === 0 ||
|
Wizard.utils.getIndex(methods.current.id) === 0 ||
|
||||||
wizard.isLoading
|
wizard.isLoading
|
||||||
@@ -252,6 +183,8 @@ export default function NuevoPlanModalContainer() {
|
|||||||
Wizard.utils.getIndex(methods.current.id) >=
|
Wizard.utils.getIndex(methods.current.id) >=
|
||||||
Wizard.steps.length - 1
|
Wizard.steps.length - 1
|
||||||
}
|
}
|
||||||
|
wizard={wizard}
|
||||||
|
setWizard={setWizard}
|
||||||
/>
|
/>
|
||||||
</Wizard.Stepper.Controls>
|
</Wizard.Stepper.Controls>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { useState } from "react";
|
import { useState } from 'react'
|
||||||
|
|
||||||
import type { NewPlanWizardState } from "../types";
|
import type { NewPlanWizardState } from '../types'
|
||||||
|
|
||||||
export function useNuevoPlanWizard() {
|
export function useNuevoPlanWizard() {
|
||||||
const [wizard, setWizard] = useState<NewPlanWizardState>({
|
const [wizard, setWizard] = useState<NewPlanWizardState>({
|
||||||
step: 1,
|
step: 1,
|
||||||
tipoOrigen: null,
|
tipoOrigen: null,
|
||||||
datosBasicos: {
|
datosBasicos: {
|
||||||
nombrePlan: "",
|
nombrePlan: '',
|
||||||
carreraId: "",
|
carreraId: '',
|
||||||
facultadId: "",
|
facultadId: '',
|
||||||
nivel: "",
|
nivel: '',
|
||||||
tipoCiclo: "",
|
tipoCiclo: '',
|
||||||
numCiclos: undefined,
|
numCiclos: undefined,
|
||||||
estructuraPlanId: null,
|
estructuraPlanId: null,
|
||||||
},
|
},
|
||||||
@@ -34,8 +34,8 @@ export function useNuevoPlanWizard() {
|
|||||||
archivoAsignaturasExcelId: null,
|
archivoAsignaturasExcelId: null,
|
||||||
},
|
},
|
||||||
iaConfig: {
|
iaConfig: {
|
||||||
descripcionEnfoque: "",
|
descripcionEnfoque: '',
|
||||||
notasAdicionales: "",
|
notasAdicionales: '',
|
||||||
archivosReferencia: [],
|
archivosReferencia: [],
|
||||||
repositoriosReferencia: [],
|
repositoriosReferencia: [],
|
||||||
archivosAdjuntos: [],
|
archivosAdjuntos: [],
|
||||||
@@ -43,42 +43,43 @@ export function useNuevoPlanWizard() {
|
|||||||
resumen: {},
|
resumen: {},
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
});
|
})
|
||||||
|
|
||||||
const canContinueDesdeModo = wizard.tipoOrigen === "MANUAL" ||
|
const canContinueDesdeModo =
|
||||||
wizard.tipoOrigen === "IA" ||
|
wizard.tipoOrigen === 'MANUAL' ||
|
||||||
(wizard.tipoOrigen === "CLONADO_INTERNO" ||
|
wizard.tipoOrigen === 'IA' ||
|
||||||
wizard.tipoOrigen === "CLONADO_TRADICIONAL");
|
wizard.tipoOrigen === 'CLONADO_INTERNO' ||
|
||||||
|
wizard.tipoOrigen === 'CLONADO_TRADICIONAL'
|
||||||
|
|
||||||
const canContinueDesdeBasicos = !!wizard.datosBasicos.nombrePlan &&
|
const canContinueDesdeBasicos =
|
||||||
|
!!wizard.datosBasicos.nombrePlan &&
|
||||||
!!wizard.datosBasicos.carreraId &&
|
!!wizard.datosBasicos.carreraId &&
|
||||||
!!wizard.datosBasicos.facultadId &&
|
!!wizard.datosBasicos.facultadId &&
|
||||||
!!wizard.datosBasicos.nivel &&
|
!!wizard.datosBasicos.nivel &&
|
||||||
(wizard.datosBasicos.numCiclos !== undefined &&
|
wizard.datosBasicos.numCiclos !== undefined &&
|
||||||
wizard.datosBasicos.numCiclos > 0) &&
|
wizard.datosBasicos.numCiclos > 0 &&
|
||||||
// Requerir ambas plantillas (plan y mapa) con versión
|
// Requerir ambas plantillas (plan y mapa) con versión
|
||||||
!!wizard.datosBasicos.estructuraPlanId;
|
!!wizard.datosBasicos.estructuraPlanId
|
||||||
|
|
||||||
const canContinueDesdeDetalles = (() => {
|
const canContinueDesdeDetalles = (() => {
|
||||||
if (wizard.tipoOrigen === "MANUAL") return true;
|
if (wizard.tipoOrigen === 'MANUAL') return true
|
||||||
if (wizard.tipoOrigen === "IA") {
|
if (wizard.tipoOrigen === 'IA') {
|
||||||
// Requerimos descripción del enfoque y notas adicionales
|
// Requerimos descripción del enfoque y notas adicionales
|
||||||
return !!wizard.iaConfig?.descripcionEnfoque &&
|
return !!wizard.iaConfig?.descripcionEnfoque
|
||||||
!!wizard.iaConfig.notasAdicionales;
|
|
||||||
}
|
}
|
||||||
if (wizard.tipoOrigen === "CLONADO_INTERNO") {
|
if (wizard.tipoOrigen === 'CLONADO_INTERNO') {
|
||||||
return !!wizard.clonInterno?.planOrigenId;
|
return !!wizard.clonInterno?.planOrigenId
|
||||||
}
|
}
|
||||||
if (wizard.tipoOrigen === "CLONADO_TRADICIONAL") {
|
if (wizard.tipoOrigen === 'CLONADO_TRADICIONAL') {
|
||||||
const t = wizard.clonTradicional;
|
const t = wizard.clonTradicional
|
||||||
if (!t) return false;
|
if (!t) return false
|
||||||
const tieneWord = !!t.archivoWordPlanId;
|
const tieneWord = !!t.archivoWordPlanId
|
||||||
const tieneAlMenosUnExcel = !!t.archivoMapaExcelId ||
|
const tieneAlMenosUnExcel =
|
||||||
!!t.archivoAsignaturasExcelId;
|
!!t.archivoMapaExcelId || !!t.archivoAsignaturasExcelId
|
||||||
return tieneWord && tieneAlMenosUnExcel;
|
return tieneWord && tieneAlMenosUnExcel
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
})();
|
})()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wizard,
|
wizard,
|
||||||
@@ -86,5 +87,5 @@ export function useNuevoPlanWizard() {
|
|||||||
canContinueDesdeModo,
|
canContinueDesdeModo,
|
||||||
canContinueDesdeBasicos,
|
canContinueDesdeBasicos,
|
||||||
canContinueDesdeDetalles,
|
canContinueDesdeDetalles,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +1,61 @@
|
|||||||
import type { UploadedFile } from "@/components/planes/wizard/PasoDetallesPanel/FileDropZone";
|
import type { UploadedFile } from '@/components/planes/wizard/PasoDetallesPanel/FileDropZone'
|
||||||
import type {
|
import type {
|
||||||
NivelPlanEstudio,
|
NivelPlanEstudio,
|
||||||
TipoCiclo,
|
TipoCiclo,
|
||||||
TipoOrigen,
|
TipoOrigen,
|
||||||
} from "@/data/types/domain";
|
} from '@/data/types/domain'
|
||||||
|
|
||||||
export type PlanPreview = {
|
export type PlanPreview = {
|
||||||
nombrePlan: string;
|
nombrePlan: string
|
||||||
nivel: NivelPlanEstudio;
|
nivel: NivelPlanEstudio
|
||||||
tipoCiclo: TipoCiclo;
|
tipoCiclo: TipoCiclo
|
||||||
numCiclos: number;
|
numCiclos: number
|
||||||
numAsignaturasAprox?: number;
|
numAsignaturasAprox?: number
|
||||||
secciones?: Array<{ id: string; titulo: string; resumen: string }>;
|
secciones?: Array<{ id: string; titulo: string; resumen: string }>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NewPlanWizardState = {
|
export type NewPlanWizardState = {
|
||||||
step: 1 | 2 | 3 | 4;
|
step: 1 | 2 | 3 | 4
|
||||||
tipoOrigen: TipoOrigen | null;
|
tipoOrigen: TipoOrigen | null
|
||||||
datosBasicos: {
|
datosBasicos: {
|
||||||
nombrePlan: string;
|
nombrePlan: string
|
||||||
carreraId: string;
|
carreraId: string
|
||||||
facultadId: string;
|
facultadId: string
|
||||||
nivel: NivelPlanEstudio | "";
|
nivel: NivelPlanEstudio | ''
|
||||||
tipoCiclo: TipoCiclo | "";
|
tipoCiclo: TipoCiclo | ''
|
||||||
numCiclos: number | undefined;
|
numCiclos: number | undefined
|
||||||
// Selección de plantillas (obligatorias)
|
// Selección de plantillas (obligatorias)
|
||||||
estructuraPlanId: string | null;
|
estructuraPlanId: string | null
|
||||||
};
|
}
|
||||||
clonInterno?: { planOrigenId: string | null };
|
clonInterno?: { planOrigenId: string | null }
|
||||||
clonTradicional?: {
|
clonTradicional?: {
|
||||||
archivoWordPlanId:
|
archivoWordPlanId: {
|
||||||
| {
|
id: string
|
||||||
id: string;
|
name: string
|
||||||
name: string;
|
size: string
|
||||||
size: string;
|
type: string
|
||||||
type: string;
|
} | null
|
||||||
}
|
|
||||||
| null;
|
|
||||||
archivoMapaExcelId: {
|
archivoMapaExcelId: {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
size: string;
|
size: string
|
||||||
type: string;
|
type: string
|
||||||
} | null;
|
} | null
|
||||||
archivoAsignaturasExcelId: {
|
archivoAsignaturasExcelId: {
|
||||||
id: string;
|
id: string
|
||||||
name: string;
|
name: string
|
||||||
size: string;
|
size: string
|
||||||
type: string;
|
type: string
|
||||||
} | null;
|
} | null
|
||||||
};
|
}
|
||||||
iaConfig?: {
|
iaConfig?: {
|
||||||
descripcionEnfoque: string;
|
descripcionEnfoque: string
|
||||||
notasAdicionales: string;
|
notasAdicionales?: string
|
||||||
archivosReferencia: Array<string>;
|
archivosReferencia: Array<string>
|
||||||
repositoriosReferencia?: Array<string>;
|
repositoriosReferencia?: Array<string>
|
||||||
archivosAdjuntos?: Array<
|
archivosAdjuntos?: Array<UploadedFile>
|
||||||
UploadedFile
|
}
|
||||||
>;
|
resumen: { previewPlan?: PlanPreview }
|
||||||
};
|
isLoading: boolean
|
||||||
resumen: { previewPlan?: PlanPreview };
|
errorMessage: string | null
|
||||||
isLoading: boolean;
|
}
|
||||||
errorMessage: string | null;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ 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 PlanesPlanesListRouteRouteImport } from './routes/planes/PlanesListRoute'
|
|
||||||
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 PlanesListaRouteRouteImport } from './routes/planes/_lista/route'
|
||||||
|
import { Route as PlanesPlanIdIndexRouteImport } from './routes/planes/$planId/index'
|
||||||
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 PlanesPlanIdAsignaturasRouteRouteImport } from './routes/planes/$planId/asignaturas/route'
|
||||||
import { Route as PlanesPlanIdDetalleRouteRouteImport } from './routes/planes/$planId/_detalle/route'
|
import { Route as PlanesPlanIdDetalleRouteRouteImport } from './routes/planes/$planId/_detalle/route'
|
||||||
@@ -45,11 +45,6 @@ const IndexRoute = IndexRouteImport.update({
|
|||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const PlanesPlanesListRouteRoute = PlanesPlanesListRouteRouteImport.update({
|
|
||||||
id: '/planes/PlanesListRoute',
|
|
||||||
path: '/planes/PlanesListRoute',
|
|
||||||
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',
|
||||||
@@ -60,6 +55,11 @@ const PlanesListaRouteRoute = PlanesListaRouteRouteImport.update({
|
|||||||
path: '/planes',
|
path: '/planes',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
|
const PlanesPlanIdIndexRoute = PlanesPlanIdIndexRouteImport.update({
|
||||||
|
id: '/planes/$planId/',
|
||||||
|
path: '/planes/$planId/',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
const PlanesListaNuevoRoute = PlanesListaNuevoRouteImport.update({
|
const PlanesListaNuevoRoute = PlanesListaNuevoRouteImport.update({
|
||||||
id: '/nuevo',
|
id: '/nuevo',
|
||||||
path: '/nuevo',
|
path: '/nuevo',
|
||||||
@@ -148,8 +148,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes': typeof PlanesListaRouteRouteWithChildren
|
'/planes': typeof PlanesListaRouteRouteWithChildren
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/planes/PlanesListRoute': typeof PlanesPlanesListRouteRoute
|
'/planes/$planId': typeof PlanesPlanIdIndexRoute
|
||||||
'/planes/$planId': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
||||||
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
||||||
@@ -169,8 +168,7 @@ export interface FileRoutesByTo {
|
|||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes': typeof PlanesListaRouteRouteWithChildren
|
'/planes': typeof PlanesListaRouteRouteWithChildren
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/planes/PlanesListRoute': typeof PlanesPlanesListRouteRoute
|
'/planes/$planId': typeof PlanesPlanIdIndexRoute
|
||||||
'/planes/$planId': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
|
||||||
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasIndexRoute
|
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasIndexRoute
|
||||||
@@ -190,10 +188,10 @@ export interface FileRoutesById {
|
|||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes/_lista': typeof PlanesListaRouteRouteWithChildren
|
'/planes/_lista': typeof PlanesListaRouteRouteWithChildren
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/planes/PlanesListRoute': typeof PlanesPlanesListRouteRoute
|
|
||||||
'/planes/$planId/_detalle': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
'/planes/$planId/_detalle': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
||||||
'/planes/_lista/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/_lista/nuevo': typeof PlanesListaNuevoRoute
|
||||||
|
'/planes/$planId/': typeof PlanesPlanIdIndexRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
||||||
'/planes/$planId/asignaturas/_lista': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
'/planes/$planId/asignaturas/_lista': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
||||||
'/planes/$planId/_detalle/datos': typeof PlanesPlanIdDetalleDatosRoute
|
'/planes/$planId/_detalle/datos': typeof PlanesPlanIdDetalleDatosRoute
|
||||||
@@ -214,7 +212,6 @@ export interface FileRouteTypes {
|
|||||||
| '/login'
|
| '/login'
|
||||||
| '/planes'
|
| '/planes'
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
| '/planes/PlanesListRoute'
|
|
||||||
| '/planes/$planId'
|
| '/planes/$planId'
|
||||||
| '/planes/$planId/asignaturas'
|
| '/planes/$planId/asignaturas'
|
||||||
| '/planes/nuevo'
|
| '/planes/nuevo'
|
||||||
@@ -235,7 +232,6 @@ export interface FileRouteTypes {
|
|||||||
| '/login'
|
| '/login'
|
||||||
| '/planes'
|
| '/planes'
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
| '/planes/PlanesListRoute'
|
|
||||||
| '/planes/$planId'
|
| '/planes/$planId'
|
||||||
| '/planes/nuevo'
|
| '/planes/nuevo'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
@@ -255,10 +251,10 @@ export interface FileRouteTypes {
|
|||||||
| '/login'
|
| '/login'
|
||||||
| '/planes/_lista'
|
| '/planes/_lista'
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
| '/planes/PlanesListRoute'
|
|
||||||
| '/planes/$planId/_detalle'
|
| '/planes/$planId/_detalle'
|
||||||
| '/planes/$planId/asignaturas'
|
| '/planes/$planId/asignaturas'
|
||||||
| '/planes/_lista/nuevo'
|
| '/planes/_lista/nuevo'
|
||||||
|
| '/planes/$planId/'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
| '/planes/$planId/asignaturas/_lista'
|
| '/planes/$planId/asignaturas/_lista'
|
||||||
| '/planes/$planId/_detalle/datos'
|
| '/planes/$planId/_detalle/datos'
|
||||||
@@ -278,9 +274,9 @@ export interface RootRouteChildren {
|
|||||||
LoginRoute: typeof LoginRoute
|
LoginRoute: typeof LoginRoute
|
||||||
PlanesListaRouteRoute: typeof PlanesListaRouteRouteWithChildren
|
PlanesListaRouteRoute: typeof PlanesListaRouteRouteWithChildren
|
||||||
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
|
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
|
||||||
PlanesPlanesListRouteRoute: typeof PlanesPlanesListRouteRoute
|
|
||||||
PlanesPlanIdDetalleRouteRoute: typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
PlanesPlanIdDetalleRouteRoute: typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
||||||
PlanesPlanIdAsignaturasRouteRoute: typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
PlanesPlanIdAsignaturasRouteRoute: typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
||||||
|
PlanesPlanIdIndexRoute: typeof PlanesPlanIdIndexRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
@@ -306,13 +302,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof IndexRouteImport
|
preLoaderRoute: typeof IndexRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/planes/PlanesListRoute': {
|
|
||||||
id: '/planes/PlanesListRoute'
|
|
||||||
path: '/planes/PlanesListRoute'
|
|
||||||
fullPath: '/planes/PlanesListRoute'
|
|
||||||
preLoaderRoute: typeof PlanesPlanesListRouteRouteImport
|
|
||||||
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'
|
||||||
@@ -327,6 +316,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof PlanesListaRouteRouteImport
|
preLoaderRoute: typeof PlanesListaRouteRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
|
'/planes/$planId/': {
|
||||||
|
id: '/planes/$planId/'
|
||||||
|
path: '/planes/$planId'
|
||||||
|
fullPath: '/planes/$planId'
|
||||||
|
preLoaderRoute: typeof PlanesPlanIdIndexRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
'/planes/_lista/nuevo': {
|
'/planes/_lista/nuevo': {
|
||||||
id: '/planes/_lista/nuevo'
|
id: '/planes/_lista/nuevo'
|
||||||
path: '/nuevo'
|
path: '/nuevo'
|
||||||
@@ -506,10 +502,10 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
LoginRoute: LoginRoute,
|
LoginRoute: LoginRoute,
|
||||||
PlanesListaRouteRoute: PlanesListaRouteRouteWithChildren,
|
PlanesListaRouteRoute: PlanesListaRouteRouteWithChildren,
|
||||||
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
|
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
|
||||||
PlanesPlanesListRouteRoute: PlanesPlanesListRouteRoute,
|
|
||||||
PlanesPlanIdDetalleRouteRoute: PlanesPlanIdDetalleRouteRouteWithChildren,
|
PlanesPlanIdDetalleRouteRoute: PlanesPlanIdDetalleRouteRouteWithChildren,
|
||||||
PlanesPlanIdAsignaturasRouteRoute:
|
PlanesPlanIdAsignaturasRouteRoute:
|
||||||
PlanesPlanIdAsignaturasRouteRouteWithChildren,
|
PlanesPlanIdAsignaturasRouteRouteWithChildren,
|
||||||
|
PlanesPlanIdIndexRoute: PlanesPlanIdIndexRoute,
|
||||||
}
|
}
|
||||||
export const routeTree = rootRouteImport
|
export const routeTree = rootRouteImport
|
||||||
._addFileChildren(rootRouteChildren)
|
._addFileChildren(rootRouteChildren)
|
||||||
|
|||||||
@@ -161,12 +161,9 @@ function MapaCurricularPage() {
|
|||||||
const { planId } = Route.useParams() // Idealmente usa el ID de la ruta
|
const { planId } = Route.useParams() // Idealmente usa el ID de la ruta
|
||||||
|
|
||||||
// 1. Fetch de Datos
|
// 1. Fetch de Datos
|
||||||
const { data: asignaturasApi, isLoading: loadingAsig } = usePlanAsignaturas(
|
const { data: asignaturasApi, isLoading: loadingAsig } =
|
||||||
/* planId*/ '0e0aea4d-b8b4-4e75-8279-6224c3ac769f',
|
usePlanAsignaturas(planId)
|
||||||
)
|
const { data: lineasApi, isLoading: loadingLineas } = usePlanLineas(planId)
|
||||||
const { data: lineasApi, isLoading: loadingLineas } = usePlanLineas(
|
|
||||||
/* planId*/ '0e0aea4d-b8b4-4e75-8279-6224c3ac769f',
|
|
||||||
)
|
|
||||||
|
|
||||||
// 2. Estado Local (Para interactividad)
|
// 2. Estado Local (Para interactividad)
|
||||||
const [materias, setMaterias] = useState<Array<Materia>>([])
|
const [materias, setMaterias] = useState<Array<Materia>>([])
|
||||||
|
|||||||
@@ -71,12 +71,9 @@ function MateriasPage() {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
// 1. Fetch de datos reales
|
// 1. Fetch de datos reales
|
||||||
const { data: asignaturasApi, isLoading: loadingAsig } = usePlanAsignaturas(
|
const { data: asignaturasApi, isLoading: loadingAsig } =
|
||||||
'0e0aea4d-b8b4-4e75-8279-6224c3ac769f',
|
usePlanAsignaturas(planId)
|
||||||
)
|
const { data: lineasApi, isLoading: loadingLineas } = usePlanLineas(planId)
|
||||||
const { data: lineasApi, isLoading: loadingLineas } = usePlanLineas(
|
|
||||||
'0e0aea4d-b8b4-4e75-8279-6224c3ac769f',
|
|
||||||
)
|
|
||||||
|
|
||||||
// 2. Estados de filtrado
|
// 2. Estados de filtrado
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
@@ -232,7 +229,7 @@ function MateriasPage() {
|
|||||||
to: '/planes/$planId/asignaturas/$asignaturaId',
|
to: '/planes/$planId/asignaturas/$asignaturaId',
|
||||||
params: {
|
params: {
|
||||||
planId,
|
planId,
|
||||||
asignaturaId: 'asignatura', // 👈 puede ser índice, consecutivo o slug
|
asignaturaId: materia.id, // 👈 puede ser índice, consecutivo o slug
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
realId: materia.id, // 👈 ID largo oculto
|
realId: materia.id, // 👈 ID largo oculto
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
import { useMemo, useState } from 'react'
|
|
||||||
|
|
||||||
import { usePlanes } from '@/data'
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/planes/PlanesListRoute')({
|
|
||||||
component: RouteComponent,
|
|
||||||
})
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
const [search, setSearch] = useState('')
|
|
||||||
|
|
||||||
const filters = useMemo(
|
|
||||||
() => ({ search, limit: 20, offset: 0, activo: true }),
|
|
||||||
[search],
|
|
||||||
)
|
|
||||||
|
|
||||||
const { data, isLoading, isError, error } = usePlanes(filters)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ padding: 16 }}>
|
|
||||||
<h1>Planes</h1>
|
|
||||||
|
|
||||||
<input
|
|
||||||
value={search}
|
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
|
||||||
placeholder="Buscar…"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{isLoading && <div>Cargando…</div>}
|
|
||||||
{isError && <div>Error: {(error as any).message}</div>}
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
{(data?.data ?? []).map((p) => (
|
|
||||||
<li key={p.id}>
|
|
||||||
<pre>{JSON.stringify(p, null, 2)}</pre>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user