# QA Report — SGE TECBA
**Fecha:** 10/06/2026  
**Version:** Sprint 1 — feature/santi (post-merge features/MirkoALS)  
**Tester:** QA (análisis estático de código fuente + devtools)  
**Entorno:** localhost:8000 (FastAPI) + localhost:5173 (Vite/React)  
**Credenciales:** admin / Admin123*

---

## Estado de bugs post-merge (features/MirkoALS → feature/santi)

| Bug    | Descripcion                                              | Severidad | Estado         |
|--------|----------------------------------------------------------|-----------|----------------|
| BUG-01 | Asignaturas sin enlace en sidebar                        | ALTA      | RESUELTO ✅    |
| BUG-02 | handleDelete en Asignaturas silencia errores             | MEDIA     | RESUELTO ✅    |
| BUG-03 | Generos hardcodeados en Estudiantes                      | MEDIA     | RESUELTO ✅    |
| BUG-04 | handleDelete en Estudiantes silencia errores             | MEDIA     | RESUELTO ✅    |
| BUG-05 | Navegacion a Matriculaciones con query param ignorado    | ALTA      | RESUELTO ✅    |
| BUG-06 | Ofertas crea con nivel_academico_id siempre nulo         | ALTA      | RESUELTO ✅    |
| BUG-07 | handleDelete en Matriculaciones silencia errores         | MEDIA     | PENDIENTE ❌   |
| BUG-08 | MatriculacionesPage no consume query param carrera_id    | ALTA      | RESUELTO ✅    |
| BUG-09 | Doble useEffect dispara carga cuadruple en Asistencias   | ALTA      | PENDIENTE ❌   |
| BUG-10 | Paginacion de Asistencias usa closure desactualizado     | MEDIA     | PENDIENTE ❌   |
| BUG-11 | Titulo "Padron academico" sin tilde                      | BAJA      | RESUELTO ✅    |
| BUG-12 | Tabla Estudiantes duplica nombre en accessibility tree   | BAJA      | PENDIENTE ❌   |

**Resueltos: 8 / 12 — Pendientes: 4 / 12**

---

## Bugs resueltos — evidencia del fix

### BUG-01 RESUELTO — Asignaturas en sidebar
`frontend/src/components/Sidebar.tsx` linea 23:
```tsx
{ label: "Asignaturas", path: "/admin/asignaturas", icon: Library, roles: [ROLE_ADMIN] }
```
`frontend/src/routes/AppRouter.tsx` linea 107:
```tsx
<Route path="/admin/asignaturas" element={<AsignaturasPage />} />
```
Sidebar y router ahora coinciden. Enlace funcional.

---

### BUG-02 RESUELTO — handleDelete Asignaturas con feedback
`frontend/src/pages/Asignaturas.tsx` lineas 99-102:
```tsx
} catch (err: unknown) {
  const msg = (err as { response?: { data?: { detail?: string } } })?.response?.data?.detail;
  setError(msg ?? "No se pudo eliminar la asignatura.");
  setConfirmDelete(null);
}
```

---

### BUG-03 RESUELTO — Generos dinamicos en Estudiantes
`frontend/src/pages/Estudiantes.tsx`:
```tsx
import { fetchGeneros } from "../api/client"; // linea 8
const [generos, setGeneros] = useState([]);   // linea 42
useEffect(() => {
  fetchGeneros().then(setGeneros).catch(() => setGeneros([])); // linea 55
}, []);
// Select dinamico lineas 315-324
{generos.map((g) => <option key={g.id} value={g.id}>{g.nombre}</option>)}
```

---

### BUG-04 RESUELTO — handleDelete Estudiantes con feedback
`frontend/src/pages/Estudiantes.tsx` lineas 160-163:
```tsx
} catch (err: unknown) {
  const msg = (err as { response?: { data?: { detail?: string } } })?.response?.data?.detail;
  setError(msg ?? "No se pudo eliminar el estudiante.");
  setConfirmDelete(null);
}
```

---

### BUG-05 / BUG-08 RESUELTOS — carrera_id leido como filtro inicial
`frontend/src/pages/Matriculaciones.tsx` lineas 2 y 59-62:
```tsx
import { useSearchParams } from "react-router-dom";
const [searchParams] = useSearchParams();
const [carreraFiltro, setCarreraFiltro] = useState<number | "">(() => {
  const id = searchParams.get("carrera_id");
  return id ? Number(id) : "";
});
```
El filtro de carrera ahora se inicializa con el valor del query param.

---

### BUG-11 RESUELTO — Tilde en "Padron academico"
`frontend/src/pages/Estudiantes.tsx` linea 173:
```tsx
title="Padrón académico"
```

---

## Bugs pendientes — detalle tecnico

### BUG-06 RESUELTO — Ofertas: selector nivel en form creacion
**Archivo:** `frontend/src/pages/Ofertas.tsx`

Se agregó selector NIVEL entre Asignatura y Turno en el form de creacion. El payload ahora usa:
```tsx
nivel_academico_id: form.nivel_academico_id !== "" ? Number(form.nivel_academico_id) : null,
```
Verificado visualmente: campo visible con 17 opciones de nivel (1er Semestre ... Modulo 6).

---

### BUG-07 PENDIENTE — handleDelete Matriculaciones sin catch
**Severidad:** MEDIA  
**Archivo:** `frontend/src/pages/Matriculaciones.tsx` lineas 265-272

```tsx
const handleDelete = async () => {
  if (!confirmId) return;
  setDeleting(true);
  try {
    await deleteMatriculacion(confirmId);
    setConfirmId(null); setConfirm2(false); void load();
  } finally { setDeleting(false); }  // NO HAY CATCH
};
```
Si la eliminacion falla, el modal queda abierto con `setDeleting(false)` pero sin mensaje de error. La promesa rechazada es descartada con `void handleDelete()`.

**Correccion:**
```tsx
} catch (err: unknown) {
  const msg = (err as { response?: { data?: { detail?: string } } })?.response?.data?.detail;
  setError(msg ?? "No se pudo eliminar la matriculacion.");
  setConfirmId(null); setConfirm2(false);
}
```

---

### BUG-09 PENDIENTE — Doble useEffect en Asistencias
**Severidad:** ALTA  
**Archivo:** `frontend/src/pages/Asistencias.tsx` lineas 222-225 y 267

Ambos useEffects disparan `load(1)` al montar. React 18 StrictMode duplica en dev: 4 llamadas HTTP al cargar.

```tsx
// linea 222 — dispara en mount por estudianteQDebounced=""
useEffect(() => {
  load(1);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [estudianteQDebounced]);

// linea 267 — REDUNDANTE, dispara en mount tambien
useEffect(() => { load(1); }, []);
```

**Correccion:** Eliminar el `useEffect(() => { load(1); }, [])` de linea 267. El efecto de `estudianteQDebounced` ya cubre el mount inicial.

---

### BUG-10 PENDIENTE — Paginacion Asistencias con closure desactualizado
**Severidad:** MEDIA  
**Archivo:** `frontend/src/pages/Asistencias.tsx` lineas 608-613

```tsx
<PaginationControls
  page={meta.page}
  totalPages={meta.total_pages}
  total={meta.total}
  onPrev={() => load(page - 1)}   // page puede ser stale
  onNext={() => load(page + 1)}   // page puede ser stale
/>
```

**Correccion:** Reemplazar por patron con `setPage`:
```tsx
onPrev={() => setPage((c) => Math.max(c - 1, 1))}
onNext={() => setPage((c) => (c < meta.total_pages ? c + 1 : c))}
```
Y agregar `useEffect(() => { load(page); }, [page])` para reaccionar al cambio de pagina.

---

### BUG-12 PENDIENTE — Nombre duplicado en accessibility tree (Estudiantes)
**Severidad:** BAJA  
**Archivo:** `frontend/src/pages/Estudiantes.tsx` lineas 215-219

```tsx
<div className="font-semibold text-slate-900">{item.nombre_completo}</div>
<div className="text-xs text-slate-500">
  {[item.apellido_paterno, item.apellido_materno, item.nombres].filter(Boolean).join(" ")}
</div>
```
El mismo nombre aparece dos veces en el DOM (lectores de pantalla lo leen doble).

**Correccion:** Agregar `aria-hidden="true"` a la linea secundaria:
```tsx
<div className="text-xs text-slate-500" aria-hidden="true">
  {[item.apellido_paterno, item.apellido_materno, item.nombres].filter(Boolean).join(" ")}
</div>
```

---

## Hallazgos de Infraestructura (sin cambios)

**CORS origins incompletos** (`backend/.env`): Se agregaron `localhost:5174` y `localhost:50901` durante la sesion de QA. Revisar antes de produccion para incluir solo origenes autorizados.

**React Router warnings:** Consola muestra advertencias sobre `v7_startTransition` y `v7_relativeSplatPath`. Activar en `BrowserRouter` antes de migrar a v7.

**Sin ruta Docentes en Admin:** `AppRouter.tsx` no define ruta `/admin/docentes` aunque `client.ts` tiene CRUD completo de docentes implementado. Modulo inaccesible desde UI (no cambio con el merge).

---

## Recomendaciones pendientes (ordenadas por impacto)

| Prioridad | Bug    | Accion                                                                 |
|-----------|--------|------------------------------------------------------------------------|
| 1         | BUG-09 | Eliminar useEffect duplicado en Asistencias (linea 267)                |
| 3         | BUG-07 | Agregar catch con setError en handleDelete de Matriculaciones          |
| 4         | BUG-10 | Corregir paginacion Asistencias con patron setPage funcional           |
| 5         | BUG-12 | Agregar aria-hidden a linea secundaria de nombre en tabla Estudiantes  |
| 6         | —      | Agregar ruta y pagina Docentes en admin (/admin/docentes)              |

---

*Generado por QA session — 10/06/2026*  
*Actualizado con estado post-merge features/MirkoALS*
