feat: gestion de materias por rol - Bedelia, Docente y Alumno
- Nuevo modelo StudentEnrollment (tabla student_enrollments)
con inscripcion unica por alumno/comision, estado y notas
- Migracion automatica en init_db.py (CREATE TABLE IF NOT EXISTS)
- Rutas /admin/commissions/ para Bedelia y Admin:
listado con filtros, detalle, asignar docente,
inscribir/dar de baja alumnos, cambiar estado
- Blueprint my_subjects_bp en /mis-materias:
Docente ve solo sus comisiones asignadas
Alumno ve solo sus inscripciones activas
Admin/Bedelia ven todo
- Templates: admin/commissions/list.html, detail.html
my_subjects/list.html, detail.html
- Navbar actualizado por rol:
Docente y Alumno: 'Mis Materias'
Bedelia y Admin: 'Gestion de Comisiones'
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Comisión {{ commission.get_full_code() }} — Edu-Space{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-3">
|
||||
<ol class="breadcrumb small">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('main.dashboard') }}">Inicio</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('admin.commissions_list') }}">Comisiones</a></li>
|
||||
<li class="breadcrumb-item active">{{ commission.get_full_code() }}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Flash Messages -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Left: Commission Info + Teacher Assignment -->
|
||||
<div class="col-12 col-lg-4">
|
||||
|
||||
<!-- Commission Card -->
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-header border-0 bg-primary text-white rounded-top py-3">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi bi-diagram-3 fs-4"></i>
|
||||
<div>
|
||||
<div class="fw-bold font-monospace">{{ commission.get_full_code() }}</div>
|
||||
<small class="opacity-75">{{ commission.semester }} {{ commission.year }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="fw-bold mb-1">{{ commission.subject.name }}</h5>
|
||||
<p class="text-muted small mb-3">{{ commission.subject.code }}
|
||||
{% if commission.subject.career_obj %}— {{ commission.subject.career_obj.name }}{% endif %}
|
||||
</p>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2 mb-3">
|
||||
{% if commission.shift %}
|
||||
{% set shift_colors = {'Mañana': 'warning', 'Tarde': 'info', 'Vespertino': 'secondary', 'Noche': 'dark'} %}
|
||||
<span class="badge bg-{{ shift_colors.get(commission.shift, 'secondary') }}-subtle text-{{ shift_colors.get(commission.shift, 'secondary') }} border border-{{ shift_colors.get(commission.shift, 'secondary') }}-subtle">
|
||||
<i class="bi bi-clock me-1"></i>{{ commission.shift }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="badge {% if commission.active %}bg-success-subtle text-success{% else %}bg-secondary-subtle text-secondary{% endif %}">
|
||||
{{ 'Activa' if commission.active else 'Inactiva' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul class="list-unstyled small mb-0">
|
||||
<li class="d-flex justify-content-between py-1 border-bottom">
|
||||
<span class="text-muted">Cupo máximo</span>
|
||||
<strong>{{ commission.max_students }}</strong>
|
||||
</li>
|
||||
<li class="d-flex justify-content-between py-1 border-bottom">
|
||||
<span class="text-muted">Inscriptos</span>
|
||||
<strong class="{% if (commission.current_students or 0) >= commission.max_students %}text-danger{% else %}text-success{% endif %}">
|
||||
{{ commission.current_students or 0 }}
|
||||
</strong>
|
||||
</li>
|
||||
{% if commission.schedule %}
|
||||
<li class="d-flex justify-content-between py-1 border-bottom">
|
||||
<span class="text-muted">Horario</span>
|
||||
<span>{{ commission.schedule }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if commission.virtual_link %}
|
||||
<li class="py-1">
|
||||
<a href="{{ commission.virtual_link }}" target="_blank" class="btn btn-sm btn-outline-info w-100">
|
||||
<i class="bi bi-camera-video me-1"></i>Acceso Virtual
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Teacher Assignment -->
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header border-0 bg-transparent py-2 d-flex align-items-center gap-2">
|
||||
<i class="bi bi-person-video3 text-info"></i>
|
||||
<span class="fw-semibold">Docente Asignado</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if commission.teacher %}
|
||||
<div class="d-flex align-items-center gap-3 mb-3 p-2 rounded bg-info-subtle border border-info-subtle">
|
||||
<div class="avatar-sm bg-info rounded-circle d-flex align-items-center justify-content-center" style="width:40px;height:40px;flex-shrink:0;">
|
||||
<i class="bi bi-person-fill text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-semibold small">{{ commission.teacher.name }}</div>
|
||||
<div class="text-muted" style="font-size:.75rem;">{{ commission.teacher.email }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning py-2 small mb-3">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>Sin docente asignado
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_edit %}
|
||||
<form method="POST" action="{{ url_for('admin.commission_assign_teacher', id=commission.id) }}" id="assign-teacher-form">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold mb-1">Cambiar / Asignar Docente</label>
|
||||
<select name="teacher_id" class="form-select form-select-sm" id="teacher-select">
|
||||
<option value="">— Sin asignar —</option>
|
||||
{% for teacher in teachers %}
|
||||
<option value="{{ teacher.id }}" {% if commission.teacher_id == teacher.id %}selected{% endif %}>
|
||||
{{ teacher.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-info btn-sm w-100" id="assign-teacher-btn">
|
||||
<i class="bi bi-person-check me-1"></i>Guardar Docente
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right: Students -->
|
||||
<div class="col-12 col-lg-8">
|
||||
|
||||
<!-- Enrolled Students -->
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-header border-0 bg-transparent py-2 d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi bi-mortarboard text-success"></i>
|
||||
<span class="fw-semibold">Alumnos Inscriptos</span>
|
||||
<span class="badge bg-success-subtle text-success">{{ enrollments | length }}</span>
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-sm btn-success" data-bs-toggle="collapse" data-bs-target="#enroll-form" id="add-student-btn">
|
||||
<i class="bi bi-person-plus me-1"></i>Inscribir Alumno
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Enroll Student Form (collapsible) -->
|
||||
{% if can_edit %}
|
||||
<div class="collapse" id="enroll-form">
|
||||
<div class="card-body border-top bg-success-subtle py-3">
|
||||
<form method="POST" action="{{ url_for('admin.commission_enroll_student', id=commission.id) }}" id="enroll-student-form">
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label small fw-semibold mb-1">Seleccionar Alumno</label>
|
||||
<select name="student_id" class="form-select form-select-sm" required id="student-select">
|
||||
<option value="">— Elegir alumno —</option>
|
||||
{% for student in students %}
|
||||
{% if student.id not in enrolled_ids %}
|
||||
<option value="{{ student.id }}">{{ student.name }} — {{ student.email }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label class="form-label small fw-semibold mb-1">Observaciones (opcional)</label>
|
||||
<input type="text" name="notes" class="form-control form-control-sm" placeholder="Notas…" id="enroll-notes">
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<button type="submit" class="btn btn-success btn-sm w-100" id="enroll-submit-btn">
|
||||
<i class="bi bi-plus-circle me-1"></i>Inscribir
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card-body p-0">
|
||||
{% if enrollments %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle" id="enrollments-table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th class="ps-3">#</th>
|
||||
<th>Alumno</th>
|
||||
<th class="text-center">Estado</th>
|
||||
<th class="text-center">Fecha Inscripción</th>
|
||||
<th class="text-center">Notas</th>
|
||||
{% if can_edit %}<th class="text-end pe-3">Acciones</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for e in enrollments %}
|
||||
<tr id="enrollment-row-{{ e.student_id }}">
|
||||
<td class="ps-3 text-muted small">{{ loop.index }}</td>
|
||||
<td>
|
||||
<div class="fw-semibold small">{{ e.student.name if e.student else '—' }}</div>
|
||||
<div class="text-muted" style="font-size:.75rem;">{{ e.student.email if e.student else '' }}</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{% set st_colors = {'activo': 'success', 'retirado': 'danger', 'condicional': 'warning'} %}
|
||||
<span class="badge bg-{{ st_colors.get(e.status, 'secondary') }}-subtle text-{{ st_colors.get(e.status, 'secondary') }} border border-{{ st_colors.get(e.status, 'secondary') }}-subtle">
|
||||
{{ e.status | capitalize }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center small text-muted">
|
||||
{{ e.enrolled_at.strftime('%d/%m/%Y') if e.enrolled_at else '—' }}
|
||||
</td>
|
||||
<td class="text-center small text-muted">{{ e.notes or '—' }}</td>
|
||||
{% if can_edit %}
|
||||
<td class="text-end pe-3">
|
||||
<div class="d-flex gap-1 justify-content-end">
|
||||
<!-- Quick status update -->
|
||||
<form method="POST" action="{{ url_for('admin.commission_update_enrollment', id=commission.id, student_id=e.student_id) }}" class="d-inline">
|
||||
<select name="status" class="form-select form-select-sm d-inline-block w-auto"
|
||||
onchange="this.form.submit()" title="Cambiar estado"
|
||||
id="status-select-{{ e.student_id }}">
|
||||
<option value="activo" {% if e.status == 'activo' %}selected{% endif %}>Activo</option>
|
||||
<option value="condicional" {% if e.status == 'condicional' %}selected{% endif %}>Condicional</option>
|
||||
<option value="retirado" {% if e.status == 'retirado' %}selected{% endif %}>Retirado</option>
|
||||
</select>
|
||||
</form>
|
||||
<!-- Remove button -->
|
||||
<form method="POST" action="{{ url_for('admin.commission_unenroll_student', id=commission.id, student_id=e.student_id) }}"
|
||||
onsubmit="return confirm('¿Dar de baja a {{ e.student.name if e.student else 'este alumno' }} de la comisión?')">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="Dar de baja"
|
||||
id="unenroll-btn-{{ e.student_id }}">
|
||||
<i class="bi bi-person-dash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-people display-5 text-muted opacity-40"></i>
|
||||
<p class="text-muted mt-3 small">No hay alumnos inscriptos en esta comisión.</p>
|
||||
{% if can_edit %}
|
||||
<button class="btn btn-outline-success btn-sm" data-bs-toggle="collapse" data-bs-target="#enroll-form">
|
||||
<i class="bi bi-person-plus me-1"></i>Inscribir primer alumno
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Capacity Bar -->
|
||||
{% set enrolled_count = enrollments | length %}
|
||||
{% set max_students = commission.max_students or 1 %}
|
||||
{% set cap_pct = [((enrolled_count / max_students) * 100) | round | int, 100] | min %}
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body py-3">
|
||||
<div class="d-flex justify-content-between small fw-semibold mb-1">
|
||||
<span><i class="bi bi-bar-chart-fill me-1 text-primary"></i>Ocupación del cupo</span>
|
||||
<span>{{ enrolled_count }} / {{ max_students }} ({{ cap_pct }}%)</span>
|
||||
</div>
|
||||
<div class="progress" style="height:10px;">
|
||||
<div class="progress-bar {% if cap_pct >= 90 %}bg-danger{% elif cap_pct >= 70 %}bg-warning{% else %}bg-success{% endif %}"
|
||||
style="width:{{ cap_pct }}%;" role="progressbar"
|
||||
aria-valuenow="{{ cap_pct }}" aria-valuemin="0" aria-valuemax="100">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,209 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Gestión de Comisiones — Edu-Space{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid py-4">
|
||||
<!-- Header -->
|
||||
<div class="d-flex align-items-center justify-content-between mb-4 flex-wrap gap-2">
|
||||
<div>
|
||||
<h1 class="h3 fw-bold mb-1">
|
||||
<i class="bi bi-diagram-3 me-2 text-primary"></i>Gestión de Comisiones
|
||||
</h1>
|
||||
<p class="text-muted mb-0 small">Asignación de docentes e inscripción de alumnos por comisión</p>
|
||||
</div>
|
||||
<a href="{{ url_for('admin.subjects_list') }}" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-book me-1"></i>Ver Asignaturas
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Flash Messages -->
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Filters Card -->
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body py-3">
|
||||
<form method="GET" class="row g-2 align-items-end" id="filter-form">
|
||||
<div class="col-12 col-md-3">
|
||||
<label class="form-label small fw-semibold mb-1">Buscar</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
||||
<input type="text" name="search" class="form-control" placeholder="Código o asignatura…"
|
||||
value="{{ search or '' }}" id="search-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<label class="form-label small fw-semibold mb-1">Asignatura</label>
|
||||
<select name="subject_id" class="form-select form-select-sm" id="subject-filter">
|
||||
<option value="">Todas</option>
|
||||
{% for subj in subjects %}
|
||||
<option value="{{ subj.id }}" {% if subject_filter == subj.id %}selected{% endif %}>
|
||||
{{ subj.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<label class="form-label small fw-semibold mb-1">Turno</label>
|
||||
<select name="shift" class="form-select form-select-sm" id="shift-filter">
|
||||
<option value="">Todos</option>
|
||||
{% for shift in ['Mañana', 'Tarde', 'Vespertino', 'Noche'] %}
|
||||
<option value="{{ shift }}" {% if shift_filter == shift %}selected{% endif %}>{{ shift }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-6 col-md-2">
|
||||
<label class="form-label small fw-semibold mb-1">Estado</label>
|
||||
<select name="active" class="form-select form-select-sm" id="active-filter">
|
||||
<option value="true" {% if active_filter == 'true' %}selected{% endif %}>Activas</option>
|
||||
<option value="false" {% if active_filter == 'false' %}selected{% endif %}>Inactivas</option>
|
||||
<option value="" {% if not active_filter %}selected{% endif %}>Todas</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-6 col-md-2 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm flex-fill" id="filter-btn">
|
||||
<i class="bi bi-funnel me-1"></i>Filtrar
|
||||
</button>
|
||||
<a href="{{ url_for('admin.commissions_list') }}" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-x"></i>
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Commissions Table -->
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-transparent border-bottom d-flex align-items-center justify-content-between py-2">
|
||||
<span class="fw-semibold small">
|
||||
<i class="bi bi-list-ul me-1 text-primary"></i>
|
||||
{{ pagination.total }} comisión(es) encontrada(s)
|
||||
</span>
|
||||
<span class="badge bg-primary-subtle text-primary">Página {{ pagination.page }} / {{ pagination.pages or 1 }}</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if commissions %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle" id="commissions-table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th class="ps-3">Comisión</th>
|
||||
<th>Asignatura</th>
|
||||
<th>Docente</th>
|
||||
<th class="text-center">Turno</th>
|
||||
<th class="text-center">Alumnos</th>
|
||||
<th class="text-center">Estado</th>
|
||||
<th class="text-end pe-3">Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for comm in commissions %}
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<span class="badge bg-primary-subtle text-primary fw-semibold font-monospace">
|
||||
{{ comm.get_full_code() }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold">{{ comm.subject.name }}</div>
|
||||
<small class="text-muted">{{ comm.subject.code }} — {{ comm.semester }} {{ comm.year }}</small>
|
||||
</td>
|
||||
<td>
|
||||
{% if comm.teacher %}
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="avatar-xs bg-info-subtle rounded-circle d-flex align-items-center justify-content-center" style="width:28px;height:28px;">
|
||||
<i class="bi bi-person-video3 small text-info"></i>
|
||||
</div>
|
||||
<span class="small">{{ comm.teacher.name }}</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted small fst-italic"><i class="bi bi-exclamation-circle me-1 text-warning"></i>Sin asignar</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{% if comm.shift %}
|
||||
{% set shift_colors = {'Mañana': 'warning', 'Tarde': 'info', 'Vespertino': 'secondary', 'Noche': 'dark'} %}
|
||||
<span class="badge bg-{{ shift_colors.get(comm.shift, 'secondary') }}-subtle text-{{ shift_colors.get(comm.shift, 'secondary') }} border border-{{ shift_colors.get(comm.shift, 'secondary') }}-subtle">
|
||||
{{ comm.shift }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-muted small">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="fw-semibold">{{ comm.current_students or 0 }}</span>
|
||||
<span class="text-muted small">/ {{ comm.max_students }}</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{% if comm.active %}
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle">Activa</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary-subtle text-secondary border border-secondary-subtle">Inactiva</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end pe-3">
|
||||
<a href="{{ url_for('admin.commission_detail', id=comm.id) }}"
|
||||
class="btn btn-sm btn-outline-primary" title="Ver y gestionar comisión">
|
||||
<i class="bi bi-gear me-1"></i>Gestionar
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if pagination.pages > 1 %}
|
||||
<div class="d-flex justify-content-center py-3">
|
||||
<nav>
|
||||
<ul class="pagination pagination-sm mb-0">
|
||||
{% if pagination.has_prev %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ url_for('admin.commissions_list', page=pagination.prev_num, search=search, subject_id=subject_filter, shift=shift_filter, active=active_filter) }}">
|
||||
<i class="bi bi-chevron-left"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for pg in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
|
||||
{% if pg %}
|
||||
<li class="page-item {% if pg == pagination.page %}active{% endif %}">
|
||||
<a class="page-link" href="{{ url_for('admin.commissions_list', page=pg, search=search, subject_id=subject_filter, shift=shift_filter, active=active_filter) }}">{{ pg }}</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled"><span class="page-link">…</span></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if pagination.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ url_for('admin.commissions_list', page=pagination.next_num, search=search, subject_id=subject_filter, shift=shift_filter, active=active_filter) }}">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-diagram-3 display-4 text-muted opacity-50"></i>
|
||||
<p class="mt-3 text-muted">No se encontraron comisiones con los filtros aplicados.</p>
|
||||
<a href="{{ url_for('admin.commissions_list') }}" class="btn btn-outline-primary btn-sm">
|
||||
<i class="bi bi-arrow-clockwise me-1"></i>Limpiar filtros
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user