Nuevo Filtro: renews - Estudiantes que Renuevan¶
Fecha: 2025-10-02
Endpoint: GET /api/students/administrative
Versión: 1.2.0
Nueva Funcionalidad¶
Agregamos un nuevo parámetro renews que permite filtrar estudiantes según si están renovando matrícula en el curso escolar actual.
Parámetro renews¶
| Parámetro | Tipo | Valores | Descripción |
|---|---|---|---|
renews |
boolean |
true, false, (omitido) |
Filtrar por estado de renovación |
Valores posibles:¶
renews=true→ Solo estudiantes que SÍ renuevan (tienen matrículas activas)renews=false→ Solo estudiantes que NO renuevan (sin matrículas)- Sin parámetro → Todos los estudiantes (comportamiento por defecto)
Ejemplos de Uso¶
1. Obtener solo estudiantes que SÍ renuevan¶
Request:
Response:
{
"data": [
{
"id": 6,
"name": "Teresa",
"lastname": "Merino Bobillo",
"status": "active",
"renews": true,
"email": "tmerinobobillo@hotmail.com"
},
{
"id": 45,
"name": "Juan",
"lastname": "García López",
"status": "active",
"renews": true,
"email": "juan.garcia@example.com"
}
// ... más estudiantes que SÍ renuevan
],
"meta": {
"totalItems": 120,
"totalPages": 6,
"currentPage": 1,
"pageSize": 20
}
}
2. Obtener solo estudiantes que NO renuevan¶
Request:
Response:
{
"data": [
{
"id": 795,
"name": "Teresa",
"lastname": "Barazal Díaz",
"status": "restricted",
"renews": false,
"email": "teresa.20160608@musicaymaestro.com"
},
{
"id": 13,
"name": "Teresa",
"lastname": "Escobar Moriega",
"status": "inactive",
"renews": false,
"email": "teresaes2009@hotmail.com"
}
// ... más estudiantes que NO renuevan
],
"meta": {
"totalItems": 45,
"totalPages": 3,
"currentPage": 1,
"pageSize": 20
}
}
3. Combinar con otros filtros¶
Request:
Esto devuelve: - ✅ Estudiantes con estado "active" - ✅ Que NO tienen matrículas activas este año
4. Búsqueda + Filtro de renovación¶
Request:
Esto devuelve: - ✅ Estudiantes con "Maria" en nombre o apellido - ✅ Que SÍ están renovando matrícula
Casos de Uso Frontend¶
Ejemplo 1: Tabs para filtrar por renovación¶
import { useState } from 'react';
type RenewsFilter = 'all' | 'renews' | 'not_renews';
function StudentsPage() {
const [activeTab, setActiveTab] = useState<RenewsFilter>('all');
const [students, setStudents] = useState([]);
const fetchStudents = async (renewsFilter: RenewsFilter) => {
let url = '/api/students/administrative?page=1&limit=20';
if (renewsFilter === 'renews') {
url += '&renews=true';
} else if (renewsFilter === 'not_renews') {
url += '&renews=false';
}
const response = await fetch(url);
const data = await response.json();
setStudents(data.data);
};
return (
<div>
<div className="tabs">
<button
className={activeTab === 'all' ? 'active' : ''}
onClick={() => {
setActiveTab('all');
fetchStudents('all');
}}
>
Todos ({totalStudents})
</button>
<button
className={activeTab === 'renews' ? 'active' : ''}
onClick={() => {
setActiveTab('renews');
fetchStudents('renews');
}}
>
Renuevan ({studentsRenewing})
</button>
<button
className={activeTab === 'not_renews' ? 'active' : ''}
onClick={() => {
setActiveTab('not_renews');
fetchStudents('not_renews');
}}
>
No Renuevan ({studentsNotRenewing})
</button>
</div>
<table>
{/* Tabla de estudiantes */}
</table>
</div>
);
}
Ejemplo 2: Select dropdown para filtrar¶
function RenewsFilterDropdown({ onChange }: { onChange: (value: string) => void }) {
return (
<select
className="form-select"
onChange={(e) => onChange(e.target.value)}
defaultValue=""
>
<option value="">Todos los estudiantes</option>
<option value="true">Solo los que renuevan</option>
<option value="false">Solo los que NO renuevan</option>
</select>
);
}
// Uso:
function StudentsList() {
const handleRenewsChange = (value: string) => {
const url = value
? `/api/students/administrative?renews=${value}`
: '/api/students/administrative';
fetchStudents(url);
};
return (
<div>
<RenewsFilterDropdown onChange={handleRenewsChange} />
{/* ... resto del componente */}
</div>
);
}
Ejemplo 3: Dashboard con estadísticas¶
interface DashboardStats {
total: number;
renewing: number;
notRenewing: number;
renewalRate: number;
}
async function getDashboardStats(): Promise<DashboardStats> {
// Obtener todos
const allResponse = await fetch('/api/students/administrative?page=1&limit=1');
const allData = await allResponse.json();
// Obtener los que renuevan
const renewingResponse = await fetch('/api/students/administrative?renews=true&page=1&limit=1');
const renewingData = await renewingResponse.json();
// Obtener los que NO renuevan
const notRenewingResponse = await fetch('/api/students/administrative?renews=false&page=1&limit=1');
const notRenewingData = await notRenewingResponse.json();
const total = allData.meta.totalItems;
const renewing = renewingData.meta.totalItems;
const notRenewing = notRenewingData.meta.totalItems;
return {
total,
renewing,
notRenewing,
renewalRate: (renewing / total) * 100
};
}
function Dashboard() {
const [stats, setStats] = useState<DashboardStats | null>(null);
useEffect(() => {
getDashboardStats().then(setStats);
}, []);
if (!stats) return <div>Cargando...</div>;
return (
<div className="dashboard">
<div className="stat-card">
<h3>Total Estudiantes</h3>
<p className="stat-number">{stats.total}</p>
</div>
<div className="stat-card success">
<h3>Renuevan</h3>
<p className="stat-number">{stats.renewing}</p>
</div>
<div className="stat-card warning">
<h3>No Renuevan</h3>
<p className="stat-number">{stats.notRenewing}</p>
</div>
<div className="stat-card info">
<h3>Tasa de Renovación</h3>
<p className="stat-number">{stats.renewalRate.toFixed(1)}%</p>
</div>
</div>
);
}
Cómo Funciona Internamente¶
renews=true (Solo estudiantes que renuevan)¶
-- Equivalente SQL (simplificado)
SELECT DISTINCT students.*
FROM students
INNER JOIN student_registrations
ON students.id = student_registrations.studentId
WHERE student_registrations.schoolCoursesId = [curso_actual]
renews=false (Solo estudiantes que NO renuevan)¶
-- Equivalente SQL (simplificado)
SELECT students.*
FROM students
WHERE students.id NOT IN (
SELECT DISTINCT studentId
FROM student_registrations
WHERE schoolCoursesId = [curso_actual]
)
Sin parámetro (Todos los estudiantes)¶
-- Equivalente SQL (simplificado)
SELECT students.*, student_registrations.*
FROM students
LEFT JOIN student_registrations
ON students.id = student_registrations.studentId
AND student_registrations.schoolCoursesId = [curso_actual]
Notas Importantes¶
⚠️ Diferencia entre renews (query param) y renews (propiedad)¶
renewscomo query parameter → Filtro para la búsquedarenewscomo propiedad en response → Valor calculado para cada estudiante
Ejemplo:
# Request con filtro
GET /api/students/administrative?renews=true
# Response - TODOS tienen renews: true porque el filtro los seleccionó
{
"data": [
{ "id": 1, "renews": true },
{ "id": 2, "renews": true },
{ "id": 3, "renews": true }
]
}
📊 Paginación funciona correctamente¶
La paginación se aplica después del filtro, así que:
Te devolverá la página 2 de los estudiantes que NO renuevan (elementos 21-40).🔍 Combinación con otros filtros¶
Puedes combinar renews con cualquier otro filtro:
# Activos que NO renuevan
?status=active&renews=false
# Inactivos buscando "Maria"
?search=Maria&status=inactive&renews=false
# Con asignatura específica que renuevan
?subjectId=5&renews=true
TypeScript Types Actualizados¶
interface StudentQueryParams {
page?: number;
limit?: number;
search?: string;
status?: string;
subjectId?: number;
schoolCourseId?: number;
renews?: boolean; // ✨ NUEVO
}
// Ejemplo de uso
const params: StudentQueryParams = {
page: 1,
limit: 20,
renews: true,
status: 'active'
};
const queryString = new URLSearchParams({
page: params.page.toString(),
limit: params.limit.toString(),
renews: params.renews.toString(),
status: params.status
}).toString();
const url = `/api/students/administrative?${queryString}`;
Changelog¶
v1.2.0 (2025-10-02)¶
- ✨ NUEVO: Parámetro
renewspara filtrar estudiantes por renovación renews=true→ Solo estudiantes que renuevanrenews=false→ Solo estudiantes que NO renuevan- 🔧 Paginación optimizada para el filtro de renovación
- 📝 Documentación completa con ejemplos
v1.1.0 (2025-10-02)¶
- ✨ Agregada propiedad
renewsen la respuesta
v1.0.0¶
- 🎉 Versión inicial
Soporte¶
¿Preguntas sobre este filtro? - 📧 Email: backend@musicaymaestro.com - 💬 Slack: #backend-support
Última actualización: 2025-10-02