closes #63: - Añadido AIGenerateSubjectInput y nueva implementación ai_generate_subject que envía FormData (soporta archivosAdjuntos) al Edge Function. - Creado hook useGenerateSubjectAI (mutation) y usado en WizardControls de asignaturas para generar la asignatura vía IA. - WizardControls (asignaturas) construye el payload IA, invoca la mutación y navega al detalle de la asignatura creada pasando state.showConfetti para lanzar confetti. - Ajustes en subjects.api.ts (nombres de endpoint, tipos y envío de datos) y sincronización de tipos en WizardControls (plan y campos básicos). - Ruta de detalle de asignatura ($asignaturaId) ahora lee location.state.showConfetti y dispara lateralConfetti al entrar. - Eliminado el prop onCreate del modal de nueva asignatura (la creación IA se gestiona internamente).
117 lines
3.4 KiB
TypeScript
117 lines
3.4 KiB
TypeScript
import { useNavigate } from '@tanstack/react-router'
|
|
|
|
import type { AIGenerateSubjectInput } from '@/data'
|
|
import type { NewSubjectWizardState } from '@/features/asignaturas/nueva/types'
|
|
|
|
import { Button } from '@/components/ui/button'
|
|
import { useGenerateSubjectAI } from '@/data'
|
|
|
|
export function WizardControls({
|
|
wizard,
|
|
setWizard,
|
|
errorMessage,
|
|
onPrev,
|
|
onNext,
|
|
disablePrev,
|
|
disableNext,
|
|
disableCreate,
|
|
isLastStep,
|
|
}: {
|
|
wizard: NewSubjectWizardState
|
|
setWizard: React.Dispatch<React.SetStateAction<NewSubjectWizardState>>
|
|
errorMessage?: string | null
|
|
onPrev: () => void
|
|
onNext: () => void
|
|
disablePrev: boolean
|
|
disableNext: boolean
|
|
disableCreate: boolean
|
|
isLastStep: boolean
|
|
}) {
|
|
const navigate = useNavigate()
|
|
const generateSubjectAI = useGenerateSubjectAI()
|
|
const handleCreate = async () => {
|
|
setWizard((w) => ({
|
|
...w,
|
|
isLoading: true,
|
|
errorMessage: null,
|
|
}))
|
|
|
|
try {
|
|
if (wizard.tipoOrigen === 'IA') {
|
|
const aiInput: AIGenerateSubjectInput = {
|
|
plan_estudio_id: wizard.plan_estudio_id,
|
|
datosBasicos: {
|
|
nombre: wizard.datosBasicos.nombre,
|
|
codigo: wizard.datosBasicos.codigo,
|
|
tipo: wizard.datosBasicos.tipo!,
|
|
creditos: wizard.datosBasicos.creditos!,
|
|
horasIndependientes: wizard.datosBasicos.horasIndependientes,
|
|
horasAcademicas: wizard.datosBasicos.horasAcademicas,
|
|
estructuraId: wizard.datosBasicos.estructuraId!,
|
|
},
|
|
iaConfig: {
|
|
descripcionEnfoqueAcademico:
|
|
wizard.iaConfig!.descripcionEnfoqueAcademico,
|
|
instruccionesAdicionalesIA:
|
|
wizard.iaConfig!.instruccionesAdicionalesIA,
|
|
archivosReferencia: wizard.iaConfig!.archivosReferencia,
|
|
repositoriosReferencia:
|
|
wizard.iaConfig!.repositoriosReferencia || [],
|
|
archivosAdjuntos: wizard.iaConfig!.archivosAdjuntos || [],
|
|
},
|
|
}
|
|
|
|
console.log(
|
|
`${new Date().toISOString()} - Enviando a generar asignatura con IA`,
|
|
)
|
|
|
|
const asignatura = await generateSubjectAI.mutateAsync(aiInput)
|
|
console.log(
|
|
`${new Date().toISOString()} - Asignatura IA generada`,
|
|
asignatura,
|
|
)
|
|
|
|
navigate({
|
|
to: `/planes/${wizard.plan_estudio_id}/asignaturas/${asignatura.id}`,
|
|
state: { showConfetti: true },
|
|
})
|
|
return
|
|
}
|
|
} catch (err: any) {
|
|
setWizard((w) => ({
|
|
...w,
|
|
isLoading: false,
|
|
errorMessage: err?.message ?? 'Error creando la asignatura',
|
|
}))
|
|
} finally {
|
|
setWizard((w) => ({ ...w, isLoading: false }))
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="flex grow items-center justify-between">
|
|
<Button variant="secondary" onClick={onPrev} disabled={disablePrev}>
|
|
Anterior
|
|
</Button>
|
|
|
|
<div className="flex-1">
|
|
{(errorMessage ?? wizard.errorMessage) && (
|
|
<span className="text-destructive text-sm font-medium">
|
|
{errorMessage ?? wizard.errorMessage}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{isLastStep ? (
|
|
<Button onClick={handleCreate} disabled={disableCreate}>
|
|
{wizard.isLoading ? 'Creando...' : 'Crear Asignatura'}
|
|
</Button>
|
|
) : (
|
|
<Button onClick={onNext} disabled={disableNext}>
|
|
Siguiente
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|