Se mandan generar sugerencias de asignaturas junto con el id del plan, el enfoque que se le quiere dar, la cantidad de sugerencias, y las sugerencias conservadas
This commit is contained in:
@@ -17,11 +17,9 @@ export default function PasoSugerenciasForm({
|
||||
wizard: NewSubjectWizardState
|
||||
onChange: Dispatch<SetStateAction<NewSubjectWizardState>>
|
||||
}) {
|
||||
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<NonNullable<NewSubjectWizardState['iaMultiple']>>,
|
||||
@@ -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<string>()
|
||||
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 (
|
||||
<div className="p-6">
|
||||
<>
|
||||
{/* --- BLOQUE SUPERIOR: PARÁMETROS --- */}
|
||||
<div className="border-border/60 bg-muted/30 mb-4 rounded-xl border p-4">
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
@@ -138,17 +160,47 @@ export default function PasoSugerenciasForm({
|
||||
onChange={(e) => setIaMultiple({ enfoque: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex w-full flex-col items-end gap-3 md:flex-row">
|
||||
<div className="w-full md:w-44">
|
||||
<Label className="text-muted-foreground mb-1 block text-xs">
|
||||
Cantidad de sugerencias
|
||||
</Label>
|
||||
<Input
|
||||
placeholder="Ej. 10"
|
||||
value={cantidadDeSugerencias}
|
||||
type="number"
|
||||
min={1}
|
||||
max={50}
|
||||
step={1}
|
||||
inputMode="numeric"
|
||||
onKeyDown={(e) => {
|
||||
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 })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Botón Refrescar */}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="h-9 gap-1.5"
|
||||
onClick={onMasSugerencias}
|
||||
onClick={onGenerarSugerencias}
|
||||
disabled={wizard.isLoading}
|
||||
>
|
||||
<RefreshCw className="h-3.5 w-3.5" />
|
||||
Más sugerencias
|
||||
Generar sugerencias
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -170,12 +222,12 @@ export default function PasoSugerenciasForm({
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-muted text-foreground inline-flex items-center rounded-full px-2.5 py-0.5 text-sm font-semibold">
|
||||
{selectedIds.length} seleccionadas
|
||||
{wizard.sugerencias.filter((s) => s.selected).length} seleccionadas
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* --- LISTA DE ASIGNATURAS --- */}
|
||||
<div className="max-h-80 space-y-1 overflow-y-auto pr-1">
|
||||
<div className="max-h-100 space-y-1 overflow-y-auto pr-1">
|
||||
{wizard.sugerencias.map((asignatura) => {
|
||||
const isSelected = asignatura.selected
|
||||
|
||||
@@ -223,7 +275,7 @@ export default function PasoSugerenciasForm({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground mt-1 text-xs">
|
||||
<p className="text-muted-foreground mt-1 text-sm">
|
||||
{asignatura.descripcion}
|
||||
</p>
|
||||
</div>
|
||||
@@ -231,6 +283,6 @@ export default function PasoSugerenciasForm({
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export function WizardControls({
|
||||
Anterior
|
||||
</Button>
|
||||
|
||||
<div className="flex-1">
|
||||
<div className="mx-2 flex-1">
|
||||
{(errorMessage ?? wizard.errorMessage) && (
|
||||
<span className="text-destructive text-sm font-medium">
|
||||
{errorMessage ?? wizard.errorMessage}
|
||||
|
||||
Reference in New Issue
Block a user