fix(ui): resolver superposicion en menu colapsado, reparar edicion de ciclo lectivo, aplicar paleta UniCABA y agregar control de modo demo en config global

This commit is contained in:
2026-09-23 16:26:42 -03:00
parent 122caca9ce
commit dd4a4c042a
9 changed files with 619 additions and 287 deletions
+73 -69
View File
@@ -106,72 +106,6 @@
<i class="bi bi-pencil"></i>
</button>
</div>
<!-- Modal Editar Ciclo Lectivo -->
<div class="modal fade text-start" id="modalEditTerm{{ term.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST" action="/admin/academic_term_edit">
<input type="hidden" name="id" value="{{ term.id }}">
<div class="modal-header">
<h5 class="modal-title h6 fw-bold">
<i class="bi bi-pencil-square text-primary me-2"></i>Editar Ciclo Lectivo
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-2 mb-3">
<div class="col-md-5">
<label class="form-label small fw-semibold">Código *</label>
<input type="text" name="code" class="form-control form-control-sm font-monospace" value="{{ term.code }}" required>
</div>
<div class="col-md-7">
<label class="form-label small fw-semibold">Año Calendario *</label>
<input type="number" name="year" class="form-control form-control-sm" value="{{ term.year }}" min="2020" max="2050" required>
</div>
</div>
<div class="mb-3">
<label class="form-label small fw-semibold">Nombre Descriptivo *</label>
<input type="text" name="name" class="form-control form-control-sm" value="{{ term.name }}" required>
</div>
<div class="mb-3">
<label class="form-label small fw-semibold">Tipo de Período</label>
<select name="period_type" class="form-select form-select-sm">
<option value="Primer Cuatrimestre" {% if term.period_type == 'Primer Cuatrimestre' %}selected{% endif %}>Primer Cuatrimestre</option>
<option value="Segundo Cuatrimestre" {% if term.period_type == 'Segundo Cuatrimestre' %}selected{% endif %}>Segundo Cuatrimestre</option>
<option value="Anual" {% if term.period_type == 'Anual' %}selected{% endif %}>Anual</option>
<option value="Curso de Verano" {% if term.period_type == 'Curso de Verano' %}selected{% endif %}>Curso de Verano</option>
</select>
</div>
<div class="row g-2 mb-3">
<div class="col-md-6">
<label class="form-label small fw-semibold">Fecha Inicio</label>
<input type="date" name="start_date" class="form-control form-control-sm" value="{{ term.start_date if term.start_date else '' }}">
</div>
<div class="col-md-6">
<label class="form-label small fw-semibold">Fecha Fin</label>
<input type="date" name="end_date" class="form-control form-control-sm" value="{{ term.end_date if term.end_date else '' }}">
</div>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="is_current" id="isCurr{{ term.id }}" {% if term.is_current %}checked{% endif %}>
<label class="form-check-label small fw-semibold text-primary" for="isCurr{{ term.id }}">
Establecer como Ciclo Lectivo Activo Actual
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_active" id="isAct{{ term.id }}" {% if term.is_active %}checked{% endif %}>
<label class="form-check-label small" for="isAct{{ term.id }}">Habilitado en el Sistema</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-sm btn-primary">Guardar Cambios</button>
</div>
</form>
</div>
</div>
</div>
</td>
{% endif %}
</tr>
@@ -189,11 +123,81 @@
</div>
</div>
<!-- Modales Editar Ciclo Lectivo (Fuera de la tabla para evitar conflictos de stacking context y cierre prematuro) -->
{% if can_edit %}
{% for term in terms %}
<div class="modal fade text-start" id="modalEditTerm{{ term.id }}" tabindex="-1" aria-labelledby="modalEditTermTitle{{ term.id }}" aria-hidden="true" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content shadow-lg border-0">
<form method="POST" action="{{ url_for('admin.academic_term_edit', id=term.id) }}">
<input type="hidden" name="id" value="{{ term.id }}">
<div class="modal-header border-bottom">
<h5 class="modal-title h6 fw-bold" id="modalEditTermTitle{{ term.id }}">
<i class="bi bi-pencil-square text-primary me-2"></i>Editar Ciclo Lectivo: {{ term.name }}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<div class="row g-2 mb-3">
<div class="col-md-5">
<label class="form-label small fw-semibold">Código *</label>
<input type="text" name="code" class="form-control form-control-sm font-monospace" value="{{ term.code }}" required>
</div>
<div class="col-md-7">
<label class="form-label small fw-semibold">Año Calendario *</label>
<input type="number" name="year" class="form-control form-control-sm" value="{{ term.year }}" min="2020" max="2050" required>
</div>
</div>
<div class="mb-3">
<label class="form-label small fw-semibold">Nombre Descriptivo *</label>
<input type="text" name="name" class="form-control form-control-sm" value="{{ term.name }}" required>
</div>
<div class="mb-3">
<label class="form-label small fw-semibold">Tipo de Período</label>
<select name="period_type" class="form-select form-select-sm">
<option value="Primer Cuatrimestre" {% if term.period_type == 'Primer Cuatrimestre' %}selected{% endif %}>Primer Cuatrimestre</option>
<option value="Segundo Cuatrimestre" {% if term.period_type == 'Segundo Cuatrimestre' %}selected{% endif %}>Segundo Cuatrimestre</option>
<option value="Anual" {% if term.period_type == 'Anual' %}selected{% endif %}>Anual</option>
<option value="Curso de Verano" {% if term.period_type == 'Curso de Verano' %}selected{% endif %}>Curso de Verano</option>
</select>
</div>
<div class="row g-2 mb-3">
<div class="col-md-6">
<label class="form-label small fw-semibold">Fecha Inicio</label>
<input type="date" name="start_date" class="form-control form-control-sm" value="{{ term.start_date if term.start_date else '' }}">
</div>
<div class="col-md-6">
<label class="form-label small fw-semibold">Fecha Fin</label>
<input type="date" name="end_date" class="form-control form-control-sm" value="{{ term.end_date if term.end_date else '' }}">
</div>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="is_current" id="isCurr{{ term.id }}" {% if term.is_current %}checked{% endif %}>
<label class="form-check-label small fw-semibold text-primary" for="isCurr{{ term.id }}">
Establecer como Ciclo Lectivo Activo Actual
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_active" id="isAct{{ term.id }}" {% if term.is_active %}checked{% endif %}>
<label class="form-check-label small" for="isAct{{ term.id }}">Habilitado en el Sistema</label>
</div>
</div>
<div class="modal-footer border-top">
<button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-sm btn-primary">Guardar Cambios</button>
</div>
</form>
</div>
</div>
</div>
{% endfor %}
{% endif %}
<!-- Modal Nuevo Ciclo Lectivo -->
{% if can_edit %}
<div class="modal fade" id="modalAddTerm" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal fade" id="modalAddTerm" tabindex="-1" aria-labelledby="modalAddTermTitle" aria-hidden="true" data-bs-backdrop="static">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content shadow-lg border-0">
<form method="POST" action="/admin/academic_term_add">
<div class="modal-header">
<h5 class="modal-title h6 fw-bold">
@@ -226,6 +226,54 @@
</button>
</form>
</div>
<!-- Control de Modo Demo y Cuentas de Prueba Iniciales -->
<div class="card border border-warning-subtle shadow-sm p-3 rounded-3 mb-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="fw-bold mb-0 d-flex align-items-center text-body">
<i class="bi bi-shield-lock-fill text-warning me-2"></i>Acceso Rápido Demo & Cuentas de Prueba
</h6>
<span id="demoModeBadge" class="badge {% if demo_access_enabled %}bg-warning-subtle text-warning border border-warning-subtle{% else %}bg-secondary-subtle text-secondary border border-secondary-subtle{% endif %}">
{% if demo_access_enabled %}Activo en Login{% else %}Deshabilitado{% endif %}
</span>
</div>
<p class="small text-muted mb-2" style="font-size: 0.82rem;">
Permite ocultar el bloque de <em>"Acceso Rápido Demo (1 Clic)"</em> de la pantalla de inicio de sesión y deshabilitar las 3 cuentas de usuario de prueba iniciales para pasar a producción.
</p>
<div class="p-2 rounded bg-body-tertiary border mb-3 small">
<div class="d-flex justify-content-between align-items-center mb-1">
<span><i class="bi bi-building-gear me-1 text-warning"></i><strong>Bedelía:</strong> <code>bedelia@edu-space.com</code></span>
<span id="badgeBedelia" class="badge {% if demo_accounts_active %}bg-success-subtle text-success{% else %}bg-danger-subtle text-danger{% endif %}">
{% if demo_accounts_active %}Habilitado{% else %}Desactivado{% endif %}
</span>
</div>
<div class="d-flex justify-content-between align-items-center mb-1">
<span><i class="bi bi-person-video3 me-1 text-info"></i><strong>Docente:</strong> <code>docente@edu-space.com</code></span>
<span id="badgeDocente" class="badge {% if demo_accounts_active %}bg-success-subtle text-success{% else %}bg-danger-subtle text-danger{% endif %}">
{% if demo_accounts_active %}Habilitado{% else %}Desactivado{% endif %}
</span>
</div>
<div class="d-flex justify-content-between align-items-center">
<span><i class="bi bi-mortarboard me-1 text-success"></i><strong>Alumno:</strong> <code>alumno@edu-space.com</code></span>
<span id="badgeAlumno" class="badge {% if demo_accounts_active %}bg-success-subtle text-success{% else %}bg-danger-subtle text-danger{% endif %}">
{% if demo_accounts_active %}Habilitado{% else %}Desactivado{% endif %}
</span>
</div>
</div>
<div>
<button type="button" class="btn btn-sm {% if demo_access_enabled or demo_accounts_active %}btn-outline-danger{% else %}btn-outline-success{% endif %}" id="btnToggleDemoMode" onclick="toggleDemoMode()">
<i class="bi {% if demo_access_enabled or demo_accounts_active %}bi-shield-slash{% else %}bi-shield-check{% endif %} me-1"></i>
<span id="btnToggleDemoText">
{% if demo_access_enabled or demo_accounts_active %}
Deshabilitar Acceso Demo y Cuentas Default
{% else %}
Reactivar Acceso Demo y Cuentas Default
{% endif %}
</span>
</button>
</div>
</div>
</div>
<div class="col-lg-6">
@@ -556,6 +604,60 @@ async function saveAuthProviders(e) {
}
}
// -------------------------------------------------------------
// Alternar Modo Demo y Cuentas de Prueba Default
// -------------------------------------------------------------
async function toggleDemoMode() {
const btn = document.getElementById('btnToggleDemoMode');
const btnText = document.getElementById('btnToggleDemoText');
const badge = document.getElementById('demoModeBadge');
btn.disabled = true;
const isCurrentlyActive = btn.classList.contains('btn-outline-danger');
const targetEnable = !isCurrentlyActive;
try {
const res = await fetch('/admin/settings/demo-mode', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ enabled: targetEnable })
});
const data = await res.json();
if (res.ok) {
showAlert(data.message, targetEnable ? 'info' : 'success');
if (targetEnable) {
btn.className = 'btn btn-sm btn-outline-danger';
btn.querySelector('i').className = 'bi bi-shield-slash me-1';
btnText.textContent = 'Deshabilitar Acceso Demo y Cuentas Default';
badge.className = 'badge bg-warning-subtle text-warning border border-warning-subtle';
badge.textContent = 'Activo en Login';
['badgeBedelia', 'badgeDocente', 'badgeAlumno'].forEach(id => {
const el = document.getElementById(id);
if (el) { el.className = 'badge bg-success-subtle text-success'; el.textContent = 'Habilitado'; }
});
} else {
btn.className = 'btn btn-sm btn-outline-success';
btn.querySelector('i').className = 'bi bi-shield-check me-1';
btnText.textContent = 'Reactivar Acceso Demo y Cuentas Default';
badge.className = 'badge bg-secondary-subtle text-secondary border border-secondary-subtle';
badge.textContent = 'Deshabilitado';
['badgeBedelia', 'badgeDocente', 'badgeAlumno'].forEach(id => {
const el = document.getElementById(id);
if (el) { el.className = 'badge bg-danger-subtle text-danger'; el.textContent = 'Desactivado'; }
});
}
} else {
showAlert(data.message || 'Error al cambiar modo demo', 'danger');
}
} catch (err) {
showAlert('Error de conexión: ' + err.message, 'danger');
} finally {
btn.disabled = false;
}
}
// -------------------------------------------------------------
// Guardar Google OAuth 2.0
// -------------------------------------------------------------