/**
* Working English System - Módulo de Estudiantes JavaScript
*
* @package WorkingEnglishSystem
* @subpackage Modules/Students/Assets
* @version 1.0.0
*/
(function($) {
'use strict';
// Variables globales del módulo
let StudentsModule = {
currentPage: 1,
perPage: 20,
totalPages: 1,
isLoading: false,
currentFilters: {},
selectedStudents: [],
// Configuración
config: {
ajaxUrl: wesAjax.ajaxUrl,
nonce: wesAjax.nonce,
dateFormat: 'YYYY-MM-DD',
debounceDelay: 300
}
};
/**
* Inicializar el módulo cuando el DOM esté listo
*/
$(document).ready(function() {
StudentsModule.init();
});
/**
* Inicializar todas las funcionalidades del módulo
*/
StudentsModule.init = function() {
this.bindEvents();
this.initializeComponents();
this.loadStudents();
this.loadStatistics();
};
/**
* Vincular eventos a elementos del DOM
*/
StudentsModule.bindEvents = function() {
// Botones principales
$('#addStudentBtn, #addFirstStudentBtn').on('click', this.showAddStudentModal);
$('#exportStudentsBtn').on('click', this.exportStudents);
$('#clearFiltersBtn').on('click', this.clearFilters);
// Filtros y búsqueda
$('#searchInput').on('keyup', this.debounce(this.handleSearch, this.config.debounceDelay));
$('#statusFilter, #levelFilter').on('change', this.handleFilterChange);
$('#dateRangeFilter').on('change', this.handleDateRangeChange);
$('#startDate, #endDate').on('change', this.handleCustomDateRange);
// Tabla y paginación
$('#perPageSelect').on('change', this.handlePerPageChange);
$('#sortSelect').on('change', this.handleSortChange);
$('#selectAllStudents').on('change', this.handleSelectAll);
// Modal de estudiante
$('#studentForm').on('submit', this.handleStudentSubmit);
$('[data-dismiss="modal"]').on('click', this.hideModals);
// Tabs del modal
$('.wes-tab-btn').on('click', this.handleTabSwitch);
// Modal de eliminación
$('#confirmDeleteBtn').on('click', this.confirmDeleteStudent);
// Botón de editar desde vista
$('#editFromViewBtn').on('click', this.editFromView);
// Eventos delegados para elementos dinámicos
$(document).on('click', '.wes-btn-edit', this.showEditStudentModal);
$(document).on('click', '.wes-btn-delete', this.showDeleteStudentModal);
$(document).on('click', '.wes-btn-view', this.showViewStudentModal);
$(document).on('click', '.wes-pagination-btn', this.handlePaginationClick);
$(document).on('change', '.student-checkbox', this.handleStudentSelection);
// Cerrar modales con ESC
$(document).on('keydown', this.handleKeyboardEvents);
};
/**
* Inicializar componentes especiales
*/
StudentsModule.initializeComponents = function() {
// Configurar tooltips si están disponibles
if (typeof $.fn.tooltip === 'function') {
$('[data-toggle="tooltip"]').tooltip();
}
// Configurar validación en tiempo real
this.setupFormValidation();
};
/**
* Cargar lista de estudiantes
*/
StudentsModule.loadStudents = function(page = 1) {
if (this.isLoading) return;
this.isLoading = true;
this.currentPage = page;
// Mostrar estado de carga
$('#studentsTableLoading').show();
$('#studentsTableContainer, #studentsEmptyState').hide();
// Preparar datos para la petición
const requestData = {
action: 'wes_get_students',
nonce: this.config.nonce,
page: page,
per_page: this.perPage,
search: $('#searchInput').val(),
status: $('#statusFilter').val(),
level: $('#levelFilter').val(),
date_range: $('#dateRangeFilter').val(),
start_date: $('#startDate').val(),
end_date: $('#endDate').val(),
sort_by: $('#sortSelect').val().split('|')[0],
sort_order: $('#sortSelect').val().split('|')[1]
};
// Realizar petición AJAX
$.post(this.config.ajaxUrl, requestData)
.done(this.handleStudentsLoaded.bind(this))
.fail(this.handleLoadError.bind(this))
.always(() => {
this.isLoading = false;
$('#studentsTableLoading').hide();
});
};
/**
* Manejar respuesta de carga de estudiantes
*/
StudentsModule.handleStudentsLoaded = function(response) {
if (response.success) {
const { students, pagination } = response.data;
if (students.length > 0) {
this.renderStudentsTable(students);
this.renderPagination(pagination);
$('#studentsTableContainer').show();
$('#studentsEmptyState').hide();
} else {
$('#studentsTableContainer').hide();
$('#studentsEmptyState').show();
}
this.updatePaginationInfo(pagination);
} else {
this.showNotification('Error al cargar estudiantes: ' + response.data, 'error');
}
};
/**
* Renderizar tabla de estudiantes
*/
StudentsModule.renderStudentsTable = function(students) {
const tbody = $('#studentsTableBody');
tbody.empty();
students.forEach(student => {
const row = this.createStudentRow(student);
tbody.append(row);
});
// Actualizar estado de selección
this.updateSelectAllState();
};
/**
* Crear fila de estudiante para la tabla
*/
StudentsModule.createStudentRow = function(student) {
const statusClass = this.getStatusClass(student.status);
const levelBadge = this.getLevelBadge(student.current_level);
return `
<tr data-student-id="${student.id}">
<td>
<input type="checkbox" class="wes-checkbox student-checkbox"
value="${student.id}" ${this.selectedStudents.includes(student.id) ? 'checked' : ''}>
</td>
<td>
<span class="wes-student-id">${student.student_id}</span>
</td>
<td>
<div class="wes-student-info">
<div class="wes-student-name">${student.full_name}</div>
</div>
</td>
<td>${student.email}</td>
<td>${student.phone || '-'}</td>
<td>${levelBadge}</td>
<td>
<span class="wes-status-badge wes-status-${statusClass}">${student.status_label}</span>
</td>
<td>
<span class="wes-enrollments-count">${student.total_enrollments}</span>
</td>
<td>
<span class="wes-date">${this.formatDate(student.created_at)}</span>
</td>
<td>
<div class="wes-action-buttons">
<button type="button" class="wes-btn wes-btn-sm wes-btn-ghost wes-btn-view"
data-student-id="${student.id}" data-toggle="tooltip" title="Ver detalles">
<i class="fas fa-eye"></i>
</button>
<button type="button" class="wes-btn wes-btn-sm wes-btn-ghost wes-btn-edit"
data-student-id="${student.id}" data-toggle="tooltip" title="Editar">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="wes-btn wes-btn-sm wes-btn-ghost wes-btn-delete wes-btn-danger"
data-student-id="${student.id}" data-student-name="${student.full_name}"
data-toggle="tooltip" title="Eliminar">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
`;
};
/**
* Renderizar paginación
*/
StudentsModule.renderPagination = function(pagination) {
const { current_page, total_pages } = pagination;
const paginationContainer = $('#studentsPagination');
if (total_pages <= 1) {
paginationContainer.hide();
return;
}
paginationContainer.show();
paginationContainer.empty();
// Botón anterior
const prevDisabled = current_page === 1 ? 'disabled' : '';
paginationContainer.append(`
<button type="button" class="wes-pagination-btn wes-pagination-prev ${prevDisabled}"
data-page="${current_page - 1}" ${prevDisabled ? 'disabled' : ''}>
<i class="fas fa-chevron-left"></i>
Anterior
</button>
`);
// Números de página
const startPage = Math.max(1, current_page - 2);
const endPage = Math.min(total_pages, current_page + 2);
if (startPage > 1) {
paginationContainer.append(`
<button type="button" class="wes-pagination-btn" data-page="1">1</button>
`);
if (startPage > 2) {
paginationContainer.append('<span class="wes-pagination-dots">...</span>');
}
}
for (let i = startPage; i <= endPage; i++) {
const activeClass = i === current_page ? 'active' : '';
paginationContainer.append(`
<button type="button" class="wes-pagination-btn ${activeClass}" data-page="${i}">${i}</button>
`);
}
if (endPage < total_pages) {
if (endPage < total_pages - 1) {
paginationContainer.append('<span class="wes-pagination-dots">...</span>');
}
paginationContainer.append(`
<button type="button" class="wes-pagination-btn" data-page="${total_pages}">${total_pages}</button>
`);
}
// Botón siguiente
const nextDisabled = current_page === total_pages ? 'disabled' : '';
paginationContainer.append(`
<button type="button" class="wes-pagination-btn wes-pagination-next ${nextDisabled}"
data-page="${current_page + 1}" ${nextDisabled ? 'disabled' : ''}>
Siguiente
<i class="fas fa-chevron-right"></i>
</button>
`);
};
/**
* Mostrar modal para agregar estudiante
*/
StudentsModule.showAddStudentModal = function() {
$('#studentModalTitle').text('Nuevo Estudiante');
$('#studentId').val('0');
$('#studentForm')[0].reset();
StudentsModule.clearFormErrors();
StudentsModule.switchTab('personal');
$('#studentModal').addClass('wes-modal-active');
$('#firstName').focus();
};
/**
* Mostrar modal para editar estudiante
*/
StudentsModule.showEditStudentModal = function(e) {
const studentId = $(this).data('student-id');
StudentsModule.loadStudentForEdit(studentId);
};
/**
* Cargar datos del estudiante para edición
*/
StudentsModule.loadStudentForEdit = function(studentId) {
const requestData = {
action: 'wes_get_student',
nonce: StudentsModule.config.nonce,
student_id: studentId
};
$.post(StudentsModule.config.ajaxUrl, requestData)
.done(function(response) {
if (response.success) {
StudentsModule.populateStudentForm(response.data);
$('#studentModalTitle').text('Editar Estudiante');
$('#studentModal').addClass('wes-modal-active');
} else {
StudentsModule.showNotification('Error al cargar datos del estudiante', 'error');
}
})
.fail(function() {
StudentsModule.showNotification('Error de conexión al cargar estudiante', 'error');
});
};
/**
* Poblar formulario con datos del estudiante
*/
StudentsModule.populateStudentForm = function(student) {
$('#studentId').val(student.id);
$('#firstName').val(student.first_name);
$('#lastName').val(student.last_name);
$('#email').val(student.email);
$('#phone').val(student.phone);
$('#dateOfBirth').val(student.date_of_birth);
$('#gender').val(student.gender);
$('#address').val(student.address);
$('#emergencyContact').val(student.emergency_contact);
$('#emergencyPhone').val(student.emergency_phone);
$('#currentLevel').val(student.current_level);
$('#status').val(student.status);
$('#notes').val(student.notes);
StudentsModule.clearFormErrors();
StudentsModule.switchTab('personal');
};
/**
* Manejar envío del formulario de estudiante
*/
StudentsModule.handleStudentSubmit = function(e) {
e.preventDefault();
if (StudentsModule.isLoading) return;
StudentsModule.isLoading = true;
const $submitBtn = $('#saveStudentBtn');
const originalText = $submitBtn.html();
$submitBtn.html('<i class="fas fa-spinner fa-spin"></i> Guardando...').prop('disabled', true);
StudentsModule.clearFormErrors();
// Preparar datos del formulario
const formData = {
action: 'wes_save_student',
nonce: StudentsModule.config.nonce,
student_id: $('#studentId').val(),
first_name: $('#firstName').val(),
last_name: $('#lastName').val(),
email: $('#email').val(),
phone: $('#phone').val(),
date_of_birth: $('#dateOfBirth').val(),
gender: $('#gender').val(),
address: $('#address').val(),
emergency_contact: $('#emergencyContact').val(),
emergency_phone: $('#emergencyPhone').val(),
current_level: $('#currentLevel').val(),
status: $('#status').val(),
notes: $('#notes').val()
};
$.post(StudentsModule.config.ajaxUrl, formData)
.done(function(response) {
if (response.success) {
StudentsModule.showNotification('Estudiante guardado correctamente', 'success');
StudentsModule.hideModals();
StudentsModule.loadStudents(StudentsModule.currentPage);
StudentsModule.loadStatistics();
} else {
StudentsModule.handleFormErrors(response.data);
}
})
.fail(function() {
StudentsModule.showNotification('Error de conexión al guardar estudiante', 'error');
})
.always(function() {
StudentsModule.isLoading = false;
$submitBtn.html(originalText).prop('disabled', false);
});
};
/**
* Mostrar modal de confirmación para eliminar
*/
StudentsModule.showDeleteStudentModal = function(e) {
const studentId = $(this).data('student-id');
const studentName = $(this).data('student-name');
$('#deleteStudentName').text(studentName);
$('#confirmDeleteBtn').data('student-id', studentId);
$('#deleteStudentModal').addClass('wes-modal-active');
};
/**
* Confirmar eliminación de estudiante
*/
StudentsModule.confirmDeleteStudent = function() {
const studentId = $(this).data('student-id');
const $btn = $(this);
const originalText = $btn.html();
$btn.html('<i class="fas fa-spinner fa-spin"></i> Eliminando...').prop('disabled', true);
const requestData = {
action: 'wes_delete_student',
nonce: StudentsModule.config.nonce,
student_id: studentId
};
$.post(StudentsModule.config.ajaxUrl, requestData)
.done(function(response) {
if (response.success) {
StudentsModule.showNotification('Estudiante eliminado correctamente', 'success');
StudentsModule.hideModals();
StudentsModule.loadStudents(StudentsModule.currentPage);
StudentsModule.loadStatistics();
// Remover de seleccionados si estaba seleccionado
const index = StudentsModule.selectedStudents.indexOf(studentId);
if (index > -1) {
StudentsModule.selectedStudents.splice(index, 1);
}
} else {
StudentsModule.showNotification('Error: ' + response.data, 'error');
}
})
.fail(function() {
StudentsModule.showNotification('Error de conexión al eliminar estudiante', 'error');
})
.always(function() {
$btn.html(originalText).prop('disabled', false);
});
};
/**
* Mostrar modal de vista de estudiante
*/
StudentsModule.showViewStudentModal = function(e) {
const studentId = $(this).data('student-id');
StudentsModule.loadStudentDetails(studentId);
};
/**
* Cargar detalles del estudiante para vista
*/
StudentsModule.loadStudentDetails = function(studentId) {
const requestData = {
action: 'wes_get_student',
nonce: StudentsModule.config.nonce,
student_id: studentId
};
$('#studentDetailsContent').html('<div class="wes-loading-state"><div class="wes-spinner"></div><p>Cargando detalles...</p></div>');
$('#viewStudentModal').addClass('wes-modal-active');
$.post(StudentsModule.config.ajaxUrl, requestData)
.done(function(response) {
if (response.success) {
StudentsModule.renderStudentDetails(response.data);
$('#editFromViewBtn').data('student-id', studentId);
} else {
$('#studentDetailsContent').html('<div class="wes-error-state"><p>Error al cargar detalles del estudiante</p></div>');
}
})
.fail(function() {
$('#studentDetailsContent').html('<div class="wes-error-state"><p>Error de conexión</p></div>');
});
};
/**
* Renderizar detalles del estudiante
*/
StudentsModule.renderStudentDetails = function(student) {
const statusClass = StudentsModule.getStatusClass(student.status);
const levelBadge = StudentsModule.getLevelBadge(student.current_level);
const detailsHtml = `
<div class="wes-student-details">
<div class="wes-details-header">
<div class="wes-student-avatar">
<i class="fas fa-user-circle"></i>
</div>
<div class="wes-student-basic">
<h3>${student.first_name} ${student.last_name}</h3>
<p class="wes-student-id">ID: ${student.student_id}</p>
<div class="wes-badges">
<span class="wes-status-badge wes-status-${statusClass}">${StudentsModule.getStatusLabel(student.status)}</span>
${levelBadge}
</div>
</div>
</div>
<div class="wes-details-grid">
<div class="wes-detail-section">
<h4><i class="fas fa-user"></i> Información Personal</h4>
<div class="wes-detail-items">
<div class="wes-detail-item">
<label>Nombre Completo:</label>
<span>${student.first_name} ${student.last_name}</span>
</div>
${student.date_of_birth ? `
<div class="wes-detail-item">
<label>Fecha de Nacimiento:</label>
<span>${StudentsModule.formatDate(student.date_of_birth)}</span>
</div>` : ''}
${student.gender ? `
<div class="wes-detail-item">
<label>Género:</label>
<span>${StudentsModule.getGenderLabel(student.gender)}</span>
</div>` : ''}
${student.address ? `
<div class="wes-detail-item">
<label>Dirección:</label>
<span>${student.address}</span>
</div>` : ''}
</div>
</div>
<div class="wes-detail-section">
<h4><i class="fas fa-phone"></i> Información de Contacto</h4>
<div class="wes-detail-items">
<div class="wes-detail-item">
<label>Email:</label>
<span><a href="mailto:${student.email}">${student.email}</a></span>
</div>
${student.phone ? `
<div class="wes-detail-item">
<label>Teléfono:</label>
<span><a href="tel:${student.phone}">${student.phone}</a></span>
</div>` : ''}
</div>
</div>
${(student.emergency_contact || student.emergency_phone) ? `
<div class="wes-detail-section">
<h4><i class="fas fa-exclamation-triangle"></i> Contacto de Emergencia</h4>
<div class="wes-detail-items">
${student.emergency_contact ? `
<div class="wes-detail-item">
<label>Nombre:</label>
<span>${student.emergency_contact}</span>
</div>` : ''}
${student.emergency_phone ? `
<div class="wes-detail-item">
<label>Teléfono:</label>
<span><a href="tel:${student.emergency_phone}">${student.emergency_phone}</a></span>
</div>` : ''}
</div>
</div>` : ''}
<div class="wes-detail-section">
<h4><i class="fas fa-graduation-cap"></i> Información Académica</h4>
<div class="wes-detail-items">
<div class="wes-detail-item">
<label>Nivel Actual:</label>
<span>${StudentsModule.getLevelLabel(student.current_level)}</span>
</div>
<div class="wes-detail-item">
<label>Estado:</label>
<span class="wes-status-badge wes-status-${statusClass}">${StudentsModule.getStatusLabel(student.status)}</span>
</div>
<div class="wes-detail-item">
<label>Total de Inscripciones:</label>
<span>${student.total_enrollments || 0}</span>
</div>
<div class="wes-detail-item">
<label>Fecha de Registro:</label>
<span>${StudentsModule.formatDate(student.created_at)}</span>
</div>
${student.updated_at !== student.created_at ? `
<div class="wes-detail-item">
<label>Última Actualización:</label>
<span>${StudentsModule.formatDate(student.updated_at)}</span>
</div>` : ''}
</div>
</div>
${student.notes ? `
<div class="wes-detail-section wes-full-width">
<h4><i class="fas fa-sticky-note"></i> Notas Adicionales</h4>
<div class="wes-notes-content">
<p>${student.notes}</p>
</div>
</div>` : ''}
</div>
</div>
`;
$('#studentDetailsContent').html(detailsHtml);
$('#viewStudentTitle').text(`Detalles de ${student.first_name} ${student.last_name}`);
};
/**
* Editar desde vista
*/
StudentsModule.editFromView = function() {
const studentId = $(this).data('student-id');
StudentsModule.hideModals();
setTimeout(() => {
StudentsModule.loadStudentForEdit(studentId);
}, 300);
};
/**
* Manejar búsqueda
*/
StudentsModule.handleSearch = function() {
StudentsModule.loadStudents(1);
};
/**
* Manejar cambio de filtros
*/
StudentsModule.handleFilterChange = function() {
StudentsModule.loadStudents(1);
};
/**
* Manejar cambio de rango de fechas
*/
StudentsModule.handleDateRangeChange = function() {
const value = $(this).val();
if (value === 'custom') {
$('#customDateRange').show();
} else {
$('#customDateRange').hide();
$('#startDate, #endDate').val('');
StudentsModule.loadStudents(1);
}
};
/**
* Manejar rango de fechas personalizado
*/
StudentsModule.handleCustomDateRange = function() {
const startDate = $('#startDate').val();
const endDate = $('#endDate').val();
if (startDate && endDate) {
StudentsModule.loadStudents(1);
}
};
/**
* Manejar cambio de elementos por página
*/
StudentsModule.handlePerPageChange = function() {
StudentsModule.perPage = parseInt($(this).val());
StudentsModule.loadStudents(1);
};
/**
* Manejar cambio de ordenamiento
*/
StudentsModule.handleSortChange = function() {
StudentsModule.loadStudents(1);
};
/**
* Manejar clic en paginación
*/
StudentsModule.handlePaginationClick = function(e) {
e.preventDefault();
const page = parseInt($(this).data('page'));
if (page && page !== StudentsModule.currentPage) {
StudentsModule.loadStudents(page);
}
};
/**
* Manejar selección de todos los estudiantes
*/
StudentsModule.handleSelectAll = function() {
const isChecked = $(this).is(':checked');
$('.student-checkbox').prop('checked', isChecked);
if (isChecked) {
StudentsModule.selectedStudents = $('.student-checkbox').map(function() {
return parseInt($(this).val());
}).get();
} else {
StudentsModule.selectedStudents = [];
}
StudentsModule.updateBulkActions();
};
/**
* Manejar selección individual de estudiante
*/
StudentsModule.handleStudentSelection = function() {
const studentId = parseInt($(this).val());
const isChecked = $(this).is(':checked');
if (isChecked) {
if (!StudentsModule.selectedStudents.includes(studentId)) {
StudentsModule.selectedStudents.push(studentId);
}
} else {
const index = StudentsModule.selectedStudents.indexOf(studentId);
if (index > -1) {
StudentsModule.selectedStudents.splice(index, 1);
}
}
StudentsModule.updateSelectAllState();
StudentsModule.updateBulkActions();
};
/**
* Manejar cambio de tab
*/
StudentsModule.handleTabSwitch = function(e) {
e.preventDefault();
const tabId = $(this).data('tab');
StudentsModule.switchTab(tabId);
};
/**
* Cambiar tab activo
*/
StudentsModule.switchTab = function(tabId) {
$('.wes-tab-btn').removeClass('active');
$('.wes-tab-content').removeClass('active');
$(`.wes-tab-btn[data-tab="${tabId}"]`).addClass('active');
$(`#${tabId}Tab`).addClass('active');
};
/**
* Manejar eventos de teclado
*/
StudentsModule.handleKeyboardEvents = function(e) {
if (e.keyCode === 27) { // ESC
StudentsModule.hideModals();
}
};
/**
* Limpiar filtros
*/
StudentsModule.clearFilters = function() {
$('#searchInput').val('');
$('#statusFilter').val('');
$('#levelFilter').val('');
$('#dateRangeFilter').val('');
$('#startDate, #endDate').val('');
$('#customDateRange').hide();
StudentsModule.loadStudents(1);
};
/**
* Ocultar todos los modales
*/
StudentsModule.hideModals = function() {
$('.wes-modal').removeClass('wes-modal-active');
};
/**
* Cargar estadísticas
*/
StudentsModule.loadStatistics = function() {
const requestData = {
action: 'wes_get_students_stats',
nonce: StudentsModule.config.nonce
};
$.post(StudentsModule.config.ajaxUrl, requestData)
.done(function(response) {
if (response.success) {
const stats = response.data;
$('#totalStudents').text(stats.total || 0);
$('#activeStudents').text(stats.active || 0);
$('#graduatedStudents').text(stats.graduated || 0);
$('#newStudentsThisMonth').text(stats.new_this_month || 0);
}
});
};
/**
* Exportar estudiantes
*/
StudentsModule.exportStudents = function() {
const params = new URLSearchParams({
action: 'wes_export_students',
nonce: StudentsModule.config.nonce,
search: $('#searchInput').val(),
status: $('#statusFilter').val(),
level: $('#levelFilter').val(),
date_range: $('#dateRangeFilter').val(),
start_date: $('#startDate').val(),
end_date: $('#endDate').val()
});
window.open(`${StudentsModule.config.ajaxUrl}?${params.toString()}`, '_blank');
};
/**
* Funciones de utilidad
*/
/**
* Actualizar información de paginación
*/
StudentsModule.updatePaginationInfo = function(pagination) {
const { current_page, per_page, total_items } = pagination;
const start = (current_page - 1) * per_page + 1;
const end = Math.min(current_page * per_page, total_items);
$('#paginationInfo').text(`Mostrando ${start}-${end} de ${total_items} estudiantes`);
};
/**
* Actualizar estado del botón "Seleccionar todo"
*/
StudentsModule.updateSelectAllState = function() {
const totalCheckboxes = $('.student-checkbox').length;
const checkedCheckboxes = $('.student-checkbox:checked').length;
const selectAllCheckbox = $('#selectAllStudents');
if (checkedCheckboxes === 0) {
selectAllCheckbox.prop('indeterminate', false).prop('checked', false);
} else if (checkedCheckboxes === totalCheckboxes) {
selectAllCheckbox.prop('indeterminate', false).prop('checked', true);
} else {
selectAllCheckbox.prop('indeterminate', true).prop('checked', false);
}
};
/**
* Actualizar acciones en lote
*/
StudentsModule.updateBulkActions = function() {
const selectedCount = StudentsModule.selectedStudents.length;
// Aquí se pueden agregar acciones en lote como eliminar múltiples estudiantes
// Por ahora solo mostramos la cantidad seleccionada
if (selectedCount > 0) {
console.log(`${selectedCount} estudiante(s) seleccionado(s)`);
}
};
/**
* Obtener clase CSS para estado
*/
StudentsModule.getStatusClass = function(status) {
const statusMap = {
'active': 'success',
'inactive': 'secondary',
'graduated': 'info',
'suspended': 'warning'
};
return statusMap[status] || 'secondary';
};
/**
* Obtener etiqueta de estado
*/
StudentsModule.getStatusLabel = function(status) {
const statusMap = {
'active': 'Activo',
'inactive': 'Inactivo',
'graduated': 'Graduado',
'suspended': 'Suspendido'
};
return statusMap[status] || status;
};
/**
* Obtener badge de nivel
*/
StudentsModule.getLevelBadge = function(level) {
const levelMap = {
'beginner': { label: 'Principiante', class: 'secondary' },
'elementary': { label: 'Elemental', class: 'info' },
'pre_intermediate': { label: 'Pre-Intermedio', class: 'primary' },
'intermediate': { label: 'Intermedio', class: 'success' },
'upper_intermediate': { label: 'Intermedio Alto', class: 'warning' },
'advanced': { label: 'Avanzado', class: 'danger' },
'proficiency': { label: 'Competencia', class: 'dark' }
};
const levelInfo = levelMap[level] || { label: level, class: 'secondary' };
return `<span class="wes-level-badge wes-level-${levelInfo.class}">${levelInfo.label}</span>`;
};
/**
* Obtener etiqueta de nivel
*/
StudentsModule.getLevelLabel = function(level) {
const levelMap = {
'beginner': 'Principiante',
'elementary': 'Elemental',
'pre_intermediate': 'Pre-Intermedio',
'intermediate': 'Intermedio',
'upper_intermediate': 'Intermedio Alto',
'advanced': 'Avanzado',
'proficiency': 'Competencia'
};
return levelMap[level] || level;
};
/**
* Obtener etiqueta de género
*/
StudentsModule.getGenderLabel = function(gender) {
const genderMap = {
'male': 'Masculino',
'female': 'Femenino',
'other': 'Otro',
'prefer_not_to_say': 'Prefiero no decir'
};
return genderMap[gender] || gender;
};
/**
* Formatear fecha
*/
StudentsModule.formatDate = function(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
const options = {
year: 'numeric',
month: 'short',
day: 'numeric',
timeZone: 'UTC'
};
return date.toLocaleDateString('es-ES', options);
};
/**
* Configurar validación del formulario
*/
StudentsModule.setupFormValidation = function() {
// Validación en tiempo real del email
$('#email').on('blur', function() {
const email = $(this).val();
if (email && !StudentsModule.isValidEmail(email)) {
StudentsModule.showFieldError('email', 'Formato de email inválido');
} else {
StudentsModule.clearFieldError('email');
}
});
// Validación de campos requeridos
$('#firstName, #lastName').on('blur', function() {
const value = $(this).val().trim();
const fieldName = $(this).attr('id');
if (!value) {
const label = fieldName === 'firstName' ? 'Nombre' : 'Apellido';
StudentsModule.showFieldError(fieldName, `${label} es requerido`);
} else {
StudentsModule.clearFieldError(fieldName);
}
});
};
/**
* Validar formato de email
*/
StudentsModule.isValidEmail = function(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
/**
* Mostrar error en campo específico
*/
StudentsModule.showFieldError = function(fieldName, message) {
const errorElement = $(`#${fieldName}Error`);
const fieldElement = $(`#${fieldName}`);
errorElement.text(message).show();
fieldElement.addClass('wes-form-error-field');
};
/**
* Limpiar error de campo específico
*/
StudentsModule.clearFieldError = function(fieldName) {
const errorElement = $(`#${fieldName}Error`);
const fieldElement = $(`#${fieldName}`);
errorElement.hide();
fieldElement.removeClass('wes-form-error-field');
};
/**
* Limpiar todos los errores del formulario
*/
StudentsModule.clearFormErrors = function() {
$('.wes-form-error').hide();
$('.wes-form-error-field').removeClass('wes-form-error-field');
};
/**
* Manejar errores del formulario desde el servidor
*/
StudentsModule.handleFormErrors = function(errors) {
if (typeof errors === 'string') {
StudentsModule.showNotification(errors, 'error');
} else if (typeof errors === 'object') {
Object.keys(errors).forEach(field => {
StudentsModule.showFieldError(field, errors[field]);
});
}
};
/**
* Manejar error de carga
*/
StudentsModule.handleLoadError = function() {
$('#studentsTableContainer, #studentsEmptyState').hide();
StudentsModule.showNotification('Error al cargar los estudiantes', 'error');
};
/**
* Mostrar notificación
*/
StudentsModule.showNotification = function(message, type = 'info') {
// Usar el sistema de notificaciones global de WES
if (typeof WES !== 'undefined' && WES.showNotification) {
WES.showNotification(message, type);
} else {
// Fallback simple
alert(message);
}
};
/**
* Función debounce para optimizar búsquedas
*/
StudentsModule.debounce = function(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func.apply(this, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
})(jQuery);