Refactor: unifica wizards con WizardLayout/WizardResponsiveHeader y convierte asignaturas en layout con Outlet

- Se introdujo un layout genérico de wizard (WizardLayout) con headerSlot/footerSlot y se migraron los modales de Nuevo Plan y Nueva Asignatura a esta estructura usando defineStepper.
- Se creó y reutilizó WizardResponsiveHeader para un encabezado responsivo consistente (progreso en móvil y navegación en escritorio) en ambos wizards.
- Se homologó WizardControls del wizard de asignaturas para alinearlo al patrón del wizard de planes (props onPrev/onNext, flags de disable, manejo de error/loading y creación).
- Se mejoró la captura de datos en el wizard de asignatura: créditos como flotante con 2 decimales, placeholders/estilos en inputs/selects y uso de catálogo real de estructuras vía useSubjectEstructuras con qk.estructurasAsignatura.
- Se reorganizó la sección de asignaturas del detalle del plan: el contenido del antiguo index se movió a asignaturas.tsx como layout y se agregó <Outlet />; navegación a “nueva asignatura” ajustada al path correcto.
This commit is contained in:
2026-02-04 13:36:46 -06:00
parent 1acb18711f
commit f3414f23f6
17 changed files with 834 additions and 441 deletions

View File

@@ -11,6 +11,7 @@ import type {
TipoAsignatura,
UUID,
} from '../types/domain'
import type { Database } from '@/types/supabase'
const EDGE = {
subjects_create_manual: 'subjects_create_manual',
@@ -226,3 +227,20 @@ export async function subjects_get_document(
subjectId,
})
}
export async function subjects_get_structure_catalog(): Promise<
Array<Database['public']['Tables']['estructuras_asignatura']['Row']>
> {
const supabase = supabaseBrowser()
const { data, error } = await supabase
.from('estructuras_asignatura')
.select('*')
.order('nombre', { ascending: true })
if (error) {
throw error
}
return data
}

View File

@@ -98,7 +98,7 @@ export function usePlanDocumento(planId: UUID | null | undefined) {
export function useCatalogosPlanes() {
return useQuery({
queryKey: ['catalogos_planes'],
queryKey: qk.estructurasPlan(),
queryFn: getCatalogos,
staleTime: 1000 * 60 * 60, // 1 hora de caché (estos datos casi no cambian)
})

View File

@@ -1,11 +1,5 @@
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 {
ai_generate_subject,
subjects_bibliografia_list,
@@ -14,6 +8,7 @@ import {
subjects_generate_document,
subjects_get,
subjects_get_document,
subjects_get_structure_catalog,
subjects_history,
subjects_import_from_file,
subjects_persist_from_ai,
@@ -21,6 +16,14 @@ import {
subjects_update_contenido,
subjects_update_fields,
} 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) {
return useQuery({
@@ -63,6 +66,13 @@ export function useSubjectDocumento(subjectId: UUID | null | undefined) {
})
}
export function useSubjectEstructuras() {
return useQuery({
queryKey: qk.estructurasAsignatura(),
queryFn: () => subjects_get_structure_catalog(),
})
}
/* ------------------ Mutations ------------------ */
export function useCreateSubjectManual() {
@@ -159,7 +169,7 @@ export function useUpdateSubjectContenido() {
const qc = useQueryClient()
return useMutation({
mutationFn: (vars: { subjectId: UUID; unidades: any[] }) =>
mutationFn: (vars: { subjectId: UUID; unidades: Array<any> }) =>
subjects_update_contenido(vars.subjectId, vars.unidades),
onSuccess: (updated) => {
qc.setQueryData(qk.asignatura(updated.id), updated)