diff --git a/src/components/asignaturas/wizard/PasoBasicosForm/PasoSugerenciasForm.tsx b/src/components/asignaturas/wizard/PasoBasicosForm/PasoSugerenciasForm.tsx index 2c0cdba..d703a6d 100644 --- a/src/components/asignaturas/wizard/PasoBasicosForm/PasoSugerenciasForm.tsx +++ b/src/components/asignaturas/wizard/PasoBasicosForm/PasoSugerenciasForm.tsx @@ -17,11 +17,9 @@ export default function PasoSugerenciasForm({ wizard: NewSubjectWizardState onChange: Dispatch> }) { - const selectedIds = wizard.sugerencias - .filter((s) => s.selected) - .map((s) => s.id) const ciclo = wizard.iaMultiple?.ciclo ?? '' const enfoque = wizard.iaMultiple?.enfoque ?? '' + const cantidadDeSugerencias = wizard.iaMultiple?.cantidadDeSugerencias ?? 10 const setIaMultiple = ( patch: Partial>, @@ -32,6 +30,7 @@ export default function PasoSugerenciasForm({ iaMultiple: { ciclo: w.iaMultiple?.ciclo ?? null, enfoque: w.iaMultiple?.enfoque ?? '', + cantidadDeSugerencias: w.iaMultiple?.cantidadDeSugerencias ?? 10, ...patch, }, }), @@ -48,7 +47,7 @@ export default function PasoSugerenciasForm({ })) } - const onMasSugerencias = async () => { + const onGenerarSugerencias = async () => { const sugerenciasConservadas = wizard.sugerencias.filter((s) => s.selected) onChange((w) => ({ @@ -59,21 +58,44 @@ export default function PasoSugerenciasForm({ })) try { - const nuevasSugerencias = await generate_subject_suggestions() - const merged = [...nuevasSugerencias, ...sugerenciasConservadas] + const numeroCiclo = wizard.iaMultiple?.ciclo + if (!numeroCiclo || !Number.isFinite(numeroCiclo) || numeroCiclo <= 0) { + onChange((w) => ({ + ...w, + isLoading: false, + errorMessage: 'Ingresa un número de ciclo válido.', + })) + return + } - const seen = new Set() - const deduped = merged.filter((s) => { - if (seen.has(s.id)) return false - seen.add(s.id) - return true + const cantidad = wizard.iaMultiple?.cantidadDeSugerencias ?? 10 + if (!Number.isFinite(cantidad) || cantidad <= 0 || cantidad > 50) { + onChange((w) => ({ + ...w, + isLoading: false, + errorMessage: 'La cantidad de sugerencias debe ser entre 1 y 50.', + })) + return + } + + const enfoqueTrim = wizard.iaMultiple?.enfoque.trim() ?? '' + + const nuevasSugerencias = await generate_subject_suggestions({ + plan_estudio_id: wizard.plan_estudio_id, + numero_de_ciclo: numeroCiclo, + enfoque: enfoqueTrim ? enfoqueTrim : undefined, + cantidad_de_sugerencias: cantidad, + sugerencias_conservadas: sugerenciasConservadas.map((s) => ({ + nombre: s.nombre, + descripcion: s.descripcion, + })), }) onChange( (w): NewSubjectWizardState => ({ ...w, isLoading: false, - sugerencias: deduped, + sugerencias: [...nuevasSugerencias, ...sugerenciasConservadas], }), ) } catch (err) { @@ -90,7 +112,7 @@ export default function PasoSugerenciasForm({ } return ( -
+ <> {/* --- BLOQUE SUPERIOR: PARÁMETROS --- */}
@@ -138,17 +160,47 @@ export default function PasoSugerenciasForm({ onChange={(e) => setIaMultiple({ enfoque: e.target.value })} />
+
+ +
+
+ + { + if (['.', ',', '-', 'e', 'E', '+'].includes(e.key)) { + e.preventDefault() + } + }} + onChange={(e) => { + const raw = e.target.value + if (raw === '') return + const asNumber = Number(raw) + if (!Number.isFinite(asNumber)) return + const n = Math.floor(Math.abs(asNumber)) + const capped = Math.min(n >= 1 ? n : 1, 50) + setIaMultiple({ cantidadDeSugerencias: capped }) + }} + /> +
- {/* Botón Refrescar */}
@@ -170,12 +222,12 @@ export default function PasoSugerenciasForm({

- {selectedIds.length} seleccionadas + {wizard.sugerencias.filter((s) => s.selected).length} seleccionadas
{/* --- LISTA DE ASIGNATURAS --- */} -
+
{wizard.sugerencias.map((asignatura) => { const isSelected = asignatura.selected @@ -223,7 +275,7 @@ export default function PasoSugerenciasForm({
-

+

{asignatura.descripcion}

@@ -231,6 +283,6 @@ export default function PasoSugerenciasForm({ ) })} - + ) } diff --git a/src/components/asignaturas/wizard/WizardControls.tsx b/src/components/asignaturas/wizard/WizardControls.tsx index fd26612..9188df1 100644 --- a/src/components/asignaturas/wizard/WizardControls.tsx +++ b/src/components/asignaturas/wizard/WizardControls.tsx @@ -94,7 +94,7 @@ export function WizardControls({ Anterior -
+
{(errorMessage ?? wizard.errorMessage) && ( {errorMessage ?? wizard.errorMessage} diff --git a/src/data/api/subjects.api.ts b/src/data/api/subjects.api.ts index 0d06769..590f170 100644 --- a/src/data/api/subjects.api.ts +++ b/src/data/api/subjects.api.ts @@ -138,31 +138,38 @@ export type AIGenerateSubjectInput = { } } -export async function generate_subject_suggestions(): Promise< - Array -> { +export type GenerateSubjectSuggestionsInput = { + plan_estudio_id: UUID + numero_de_ciclo: number + enfoque?: string + cantidad_de_sugerencias: number + sugerencias_conservadas: Array<{ nombre: string; descripcion: string }> +} + +export async function generate_subject_suggestions( + input: GenerateSubjectSuggestionsInput, +): Promise> { const raw = await invokeEdge>( EDGE.generate_subject_suggestions, - {}, + input, + { headers: { 'Content-Type': 'application/json' } }, ) - const arr = raw.map( + return raw.map( (s): AsignaturaSugerida => ({ id: crypto.randomUUID(), selected: false, source: 'IA', + estructuraId: null, nombre: s.nombre, codigo: s.codigo, tipo: s.tipo ?? null, creditos: s.creditos ?? null, horasAcademicas: s.horasAcademicas ?? null, horasIndependientes: s.horasIndependientes ?? null, - estructuraId: null, descripcion: s.descripcion, }), ) - - return arr } export async function ai_generate_subject( diff --git a/src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts b/src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts index 45e9546..d920134 100644 --- a/src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts +++ b/src/features/asignaturas/nueva/hooks/useNuevaAsignaturaWizard.ts @@ -32,6 +32,7 @@ export function useNuevaAsignaturaWizard(planId: string) { iaMultiple: { ciclo: null, enfoque: '', + cantidadDeSugerencias: 10, }, resumen: {}, isLoading: false, diff --git a/src/features/asignaturas/nueva/types.ts b/src/features/asignaturas/nueva/types.ts index 56d9943..d529f13 100644 --- a/src/features/asignaturas/nueva/types.ts +++ b/src/features/asignaturas/nueva/types.ts @@ -18,7 +18,7 @@ export type DataAsignaturaSugerida = { creditos: Asignatura['creditos'] | null horasAcademicas?: number | null horasIndependientes?: number | null - descripcion?: string + descripcion: string } export type AsignaturaSugerida = { @@ -67,6 +67,7 @@ export type NewSubjectWizardState = { iaMultiple?: { ciclo: number | null enfoque: string + cantidadDeSugerencias: number } resumen: { previewAsignatura?: AsignaturaPreview