feat(i18n & ux): auditoria, traduccion completa, correcciones ux y gestion de idioma en perfil

This commit is contained in:
Carlos Tello
2026-09-02 23:45:13 -03:00
parent 3d4e9992f8
commit d1d4be0428
31 changed files with 2356 additions and 488 deletions
+27 -24
View File
@@ -1,31 +1,32 @@
{% extends "base.html" %}
{% block title %}Schedule Calendar - Edu-Space Admin{% endblock %}
{% block title %}{% trans %}Reservations Calendar{% endtrans %} - Edu-Space Admin{% endblock %}
{% block content %}
<div class="container-fluid pt-5 mt-4">
<div class="row mb-4">
<div class="col-12">
<h1 class="h3 mb-0">
<i class="bi bi-calendar3 text-primary"></i> Schedule Calendar
<small class="text-muted ms-2">View & Manage Reservations</small>
<i class="bi bi-calendar3 text-primary"></i> {% trans %}Reservations Calendar{% endtrans %}
<small class="text-muted ms-2">{% trans %}View & Manage Reservations{% endtrans %}</small>
</h1>
</div>
</div>
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0">
<div class="card-header bg-body-tertiary border-0 py-3">
<div class="row align-items-center">
<div class="col-md-6">
<h5 class="mb-0">
<i class="bi bi-calendar-week text-info"></i> Reservations Calendar
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-calendar-week text-info me-2"></i> {% trans %}Reservations Calendar{% endtrans %}
</h5>
</div>
<div class="col-md-6 text-end">
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Add Reservation
<i class="bi bi-plus-circle"></i> {% trans %}Add Reservation{% endtrans %}
</a>
<a href="{{ url_for('schedule.list_reservations') }}" class="btn btn-outline-secondary">
<i class="bi bi-list"></i> List View
<i class="bi bi-list"></i> {% trans %}List View{% endtrans %}
</a>
</div>
</div>
@@ -36,11 +37,11 @@
<div class="d-flex gap-3 flex-wrap">
<div class="d-flex align-items-center">
<span class="badge bg-success me-2" style="width: 20px; height: 20px;"></span>
<small class="text-muted">Confirmed</small>
<small class="text-muted">{% trans %}Confirmed{% endtrans %}</small>
</div>
<div class="d-flex align-items-center">
<span class="badge bg-warning me-2" style="width: 20px; height: 20px;"></span>
<small class="text-muted">Pending</small>
<small class="text-muted">{% trans %}Pending{% endtrans %}</small>
</div>
</div>
</div>
@@ -55,18 +56,19 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Reservation Details</h5>
<h5 class="modal-title">{% trans %}Reservation Details{% endtrans %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="reservationDetails">
<!-- Content will be loaded dynamically -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans %}Close{% endtrans %}</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_css %}
@@ -95,11 +97,14 @@
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.0/main.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.0/locales-all.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var currentLang = '{{ session.get("language", "es") }}';
var calendar = new FullCalendar.Calendar(calendarEl, {
locale: currentLang,
initialView: 'timeGridWeek',
headerToolbar: {
left: 'prev,next today',
@@ -115,16 +120,13 @@ document.addEventListener('DOMContentLoaded', function() {
slotMaxTime: '22:00:00',
height: 'auto',
eventClick: function(info) {
// Prevent default link behavior
if (info.event.url) {
// Load reservation details in modal instead of navigation
loadReservationDetails(info.event.url);
return false;
}
},
events: '{{ url_for("schedule.calendar_data") }}',
loading: function(bool) {
// Show loading indicator if needed
}
});
@@ -133,38 +135,39 @@ document.addEventListener('DOMContentLoaded', function() {
function loadReservationDetails(url) {
$.get(url)
.done(function(data) {
var isEs = currentLang === 'es';
var html = `
<div class="row">
<div class="col-md-6">
<h6 class="text-muted mb-2">Classroom</h6>
<p class="fw-bold">${data.classroom.code} - ${data.classroom.name}</p>
<p><i class="bi bi-building text-primary"></i> ${data.classroom.building} - Floor ${data.classroom.floor}</p>
<h6 class="text-muted mb-2">${isEs ? 'Aula' : 'Classroom'}</h6>
<p class="fw-bold">${data.classroom.code} - ${data.classroom.name || ''}</p>
<p><i class="bi bi-building text-primary"></i> ${data.classroom.building} - ${isEs ? 'Piso' : 'Floor'} ${data.classroom.floor}</p>
</div>
<div class="col-md-6">
<h6 class="text-muted mb-2">Subject</h6>
<h6 class="text-muted mb-2">${isEs ? 'Materia' : 'Subject'}</h6>
<p class="fw-bold">${data.commission.subject.code} - ${data.commission.subject.name}</p>
<p><i class="bi bi-person text-info"></i> ${data.commission.teacher_name}</p>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<h6 class="text-muted mb-2">Schedule</h6>
<h6 class="text-muted mb-2">${isEs ? 'Horario' : 'Schedule'}</h6>
<p><i class="bi bi-clock text-warning"></i> ${new Date(data.start_time).toLocaleString()}</p>
<p><i class="bi bi-clock text-success"></i> ${new Date(data.end_time).toLocaleString()}</p>
</div>
<div class="col-md-6">
<h6 class="text-muted mb-2">Details</h6>
<p><i class="bi bi-people text-primary"></i> ${data.expected_attendees} attendees</p>
<h6 class="text-muted mb-2">${isEs ? 'Detalles' : 'Details'}</h6>
<p><i class="bi bi-people text-primary"></i> ${data.expected_attendees} ${isEs ? 'asistentes' : 'attendees'}</p>
<p><span class="badge bg-${data.status === 'CONFIRMED' ? 'success' : 'warning'}">${data.status}</span></p>
</div>
</div>
${data.notes ? `<div class="row mt-3"><div class="col-12"><h6 class="text-muted mb-2">Notes</h6><p>${data.notes}</p></div></div>` : ''}
${data.notes ? `<div class="row mt-3"><div class="col-12"><h6 class="text-muted mb-2">${isEs ? 'Notas' : 'Notes'}</h6><p>${data.notes}</p></div></div>` : ''}
`;
$('#reservationDetails').html(html);
$('#reservationModal').modal('show');
})
.fail(function() {
$('#reservationDetails').html('<p class="text-danger">Error loading reservation details.</p>');
$('#reservationDetails').html('<p class="text-danger">${isEs ? "Error al cargar los detalles de la reserva." : "Error loading reservation details."}</p>');
$('#reservationModal').modal('show');
});
}
+3 -3
View File
@@ -168,7 +168,7 @@
{% if subject.description %}
<div class="card-footer bg-light">
<small class="text-muted">
<i class="bi bi-info-circle"></i> {{ subject.description|truncatewords(20) }}
<i class="bi bi-info-circle"></i> {{ subject.description|truncate(100) }}
</small>
</div>
{% endif %}
@@ -180,8 +180,8 @@
<i class="bi bi-inbox fs-1 text-muted d-block mb-3"></i>
<h4 class="text-muted">No Subjects Found</h4>
<p class="text-muted">There are no active subjects in the system.</p>
<a href="{{ url_for('classrooms.index') }}" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Add New Subject
<a href="{{ url_for('classrooms.list_classrooms') }}" class="btn btn-primary">
<i class="bi bi-door-open"></i> View Classrooms
</a>
</div>
</div>
+17 -19
View File
@@ -8,20 +8,19 @@
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3">
<i class="bi bi-calendar-today text-info"></i> Today's Schedule
<i class="bi bi-calendar-today text-info"></i> {% trans %}Today's Schedule{% endtrans %}
<small class="text-muted ms-2">{{ today.strftime('%B %d, %Y') }}</small>
</h1>
<div>
<div>
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-success">
<i class="bi bi-plus-circle"></i> New Reservation
<i class="bi bi-plus-circle"></i> {% trans %}New Reservation{% endtrans %}
</a>
<a href="{{ url_for('schedule.list_reservations') }}" class="btn btn-outline-primary ms-2">
<i class="bi bi-list"></i> All Reservations
<i class="bi bi-list"></i> {% trans %}All Reservations{% endtrans %}
</a>
{# Access to Genetic Algorithm Optimizer - Only for admin/teacher roles #}
{% if current_user and current_user.role in ['admin', 'teacher'] %}
<a href="{{ url_for('main.genetic_optimizer') }}" class="btn btn-info text-white ms-2">
<i class="bi bi-cpu"></i> AI Optimizer
<a href="{{ url_for('genetic_algorithm_web.genetic_optimizer') }}" class="btn btn-info text-white ms-2">
<i class="bi bi-cpu"></i> {% trans %}AI Room Optimizer{% endtrans %}
</a>
{% endif %}
</div>
@@ -36,7 +35,7 @@
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-2">Total Reservations</h6>
<h6 class="text-muted mb-2">{% trans %}Total Reservations{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.total_reservations }}</h2>
</div>
<div class="bg-info bg-opacity-10 rounded-circle p-3">
@@ -52,7 +51,7 @@
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-2">Confirmed</h6>
<h6 class="text-muted mb-2">{% trans %}Confirmed{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.confirmed }}</h2>
</div>
<div class="bg-success bg-opacity-10 rounded-circle p-3">
@@ -68,7 +67,7 @@
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-2">Pending</h6>
<h6 class="text-muted mb-2">{% trans %}Pending{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.pending }}</h2>
</div>
<div class="bg-warning bg-opacity-10 rounded-circle p-3">
@@ -84,7 +83,7 @@
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="text-muted mb-2">Active Classrooms</h6>
<h6 class="text-muted mb-2">{% trans %}Active Classrooms{% endtrans %}</h6>
<h2 class="mb-0">{{ stats.occupied_classrooms }}/{{ stats.total_classrooms }}</h2>
</div>
<div class="bg-primary bg-opacity-10 rounded-circle p-3">
@@ -340,21 +339,20 @@
<div class="col-12">
<div class="text-center py-5">
<i class="bi bi-calendar-x text-muted" style="font-size: 4rem;"></i>
<h4 class="mt-3 text-muted">No reservations scheduled for today</h4>
<h4 class="mt-3 text-muted">{% trans %}No reservations scheduled for today{% endtrans %}</h4>
<p class="text-muted">
There are no classroom reservations for {{ today.strftime('%B %d, %Y') }}.
{% trans %}There are no classroom reservations for today.{% endtrans %}
</p>
<div class="mt-3">
<div class="mt-3">
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-success">
<i class="bi bi-plus-circle"></i> Schedule First Class
<i class="bi bi-plus-circle"></i> {% trans %}Schedule First Class{% endtrans %}
</a>
<a href="{{ url_for('schedule.list_reservations') }}" class="btn btn-outline-primary ms-2">
<i class="bi bi-calendar-week"></i>View Week Schedule
<i class="bi bi-calendar-week"></i> {% trans %}View Week Schedule{% endtrans %}
</a>
{# Access to Genetic Algorithm Optimizer - Only for admin/teacher roles #}
{% if current_user and current_user.role in ['admin', 'teacher'] %}
<a href="{{ url_for('main.genetic_optimizer') }}" class="btn btn-info text-white ms-2">
<i class="bi bi-cpu"></i> AI Room Optimizer
<a href="{{ url_for('genetic_algorithm_web.genetic_optimizer') }}" class="btn btn-info text-white ms-2">
<i class="bi bi-cpu"></i> {% trans %}AI Room Optimizer{% endtrans %}
</a>
{% endif %}
</div>