From 29ed4d7847ab55bea60a951cb5b1cd1701bbce35 Mon Sep 17 00:00:00 2001 From: "alexis.palestina" Date: Thu, 19 Jun 2025 15:48:13 -0600 Subject: [PATCH] =?UTF-8?q?funcionalidad=20de=20b=C3=BAsqueda=20en=20alumn?= =?UTF-8?q?os=20completa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/alumnos.php | 145 +++-- api/cursos.php | 78 ++- api/importar_csv.php | 138 +++-- assets/css/styles.css | 33 + assets/js/main copy.js | 1333 ---------------------------------------- assets/js/main.js | 274 +++++---- 6 files changed, 430 insertions(+), 1571 deletions(-) delete mode 100644 assets/js/main copy.js diff --git a/api/alumnos.php b/api/alumnos.php index f34a914..e897bac 100644 --- a/api/alumnos.php +++ b/api/alumnos.php @@ -36,6 +36,26 @@ try { exit; } + // Verificar email duplicado + $stmt = $pdo->prepare("SELECT id FROM alumnos WHERE email = ?"); + $stmt->execute([$data['email']]); + if ($stmt->fetch()) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'El correo ya está registrado']); + exit; + } + + // Verificar teléfono duplicado + if (!empty($data['telefono'])) { + $stmt = $pdo->prepare("SELECT id FROM alumnos WHERE telefono = ?"); + $stmt->execute([$data['telefono']]); + if ($stmt->fetch()) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'El teléfono ya está registrado']); + exit; + } + } + $pdo->beginTransaction(); try { // Insertar alumno @@ -71,60 +91,81 @@ try { http_response_code(500); echo json_encode(['success' => false, 'error' => 'Error al registrar: ' . $e->getMessage()]); } - break; + break; + - case 'PUT': - $data = json_decode(file_get_contents('php://input'), true); - - if (empty($data['id']) || empty($data['nombre']) || empty($data['email'])) { - http_response_code(400); - echo json_encode(['success' => false, 'error' => 'Datos incompletos']); - exit; - } - - $stmt = $pdo->prepare(" - UPDATE alumnos SET - nombre = ?, - email = ?, - telefono = ? - WHERE id = ? - "); - $stmt->execute([ - $data['nombre'], - $data['email'], - $data['telefono'] ?? null, - $data['id'] - ]); - - // Si curso_id viene en la actualización, actualizar también el curso - if (!empty($data['curso_id'])) { - // Verifica si ya tiene una relación previa - $check = $pdo->prepare("SELECT id FROM alumnos_cursos WHERE alumno_id = ?"); - $check->execute([$data['id']]); - - if ($check->fetch()) { - // Si ya tiene relación, actualizamos el curso asignado - $updateCurso = $pdo->prepare(" - UPDATE alumnos_cursos SET curso_id = ?, estado = 'cursando' - WHERE alumno_id = ? + case 'PUT': + $data = json_decode(file_get_contents('php://input'), true); + + if (empty($data['id']) || empty($data['nombre']) || empty($data['email'])) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'Datos incompletos']); + exit; + } + + // Verificar email duplicado (excluyendo el mismo alumno) + $stmt = $pdo->prepare("SELECT id FROM alumnos WHERE email = ? AND id != ?"); + $stmt->execute([$data['email'], $data['id']]); + if ($stmt->fetch()) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'El correo ya está registrado por otro alumno']); + exit; + } + + // Verificar teléfono duplicado (excluyendo el mismo alumno) + if (!empty($data['telefono'])) { + $stmt = $pdo->prepare("SELECT id FROM alumnos WHERE telefono = ? AND id != ?"); + $stmt->execute([$data['telefono'], $data['id']]); + if ($stmt->fetch()) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'El teléfono ya está registrado por otro alumno']); + exit; + } + } + + $stmt = $pdo->prepare(" + UPDATE alumnos SET + nombre = ?, + email = ?, + telefono = ? + WHERE id = ? "); - $updateCurso->execute([$data['curso_id'], $data['id']]); - } else { - // Si no tiene relación previa, la insertamos - $insertCurso = $pdo->prepare(" - INSERT INTO alumnos_cursos (alumno_id, curso_id, estado) - VALUES (?, ?, 'cursando') - "); - $insertCurso->execute([$data['id'], $data['curso_id']]); - } - } - - - echo json_encode([ - 'success' => true, - 'message' => 'Alumno actualizado exitosamente' - ]); - break; + $stmt->execute([ + $data['nombre'], + $data['email'], + $data['telefono'] ?? null, + $data['id'] + ]); + + // Si curso_id viene en la actualización, actualizar también el curso + if (!empty($data['curso_id'])) { + // Verifica si ya tiene una relación previa + $check = $pdo->prepare("SELECT id FROM alumnos_cursos WHERE alumno_id = ?"); + $check->execute([$data['id']]); + + if ($check->fetch()) { + // Si ya tiene relación, actualizamos el curso asignado + $updateCurso = $pdo->prepare(" + UPDATE alumnos_cursos SET curso_id = ?, estado = 'cursando' + WHERE alumno_id = ? + "); + $updateCurso->execute([$data['curso_id'], $data['id']]); + } else { + // Si no tiene relación previa, la insertamos + $insertCurso = $pdo->prepare(" + INSERT INTO alumnos_cursos (alumno_id, curso_id, estado) + VALUES (?, ?, 'cursando') + "); + $insertCurso->execute([$data['id'], $data['curso_id']]); + } + } + + echo json_encode([ + 'success' => true, + 'message' => 'Alumno actualizado exitosamente' + ]); + break; + case 'DELETE': $id = $_GET['id'] ?? null; diff --git a/api/cursos.php b/api/cursos.php index 3ada92b..3dada2d 100644 --- a/api/cursos.php +++ b/api/cursos.php @@ -29,18 +29,29 @@ switch ($method) { http_response_code(500); echo json_encode(['error' => 'Error al cargar cursos']); } - break; - + break; + case 'POST': $data = json_decode(file_get_contents('php://input'), true); - + if (empty($data['nombre']) || empty($data['tipo'])) { http_response_code(400); echo json_encode(['error' => 'Nombre y tipo son requeridos']); exit; } - + try { + // Validar que no exista otro curso con el mismo nombre y tipo para ese profesor + $stmt = $pdo->prepare("SELECT id FROM cursos WHERE nombre = ? AND tipo = ? AND profesor_id = ?"); + $stmt->execute([$data['nombre'], $data['tipo'], $profesorId]); + if ($stmt->fetch()) { + echo json_encode([ + 'success' => false, + 'error' => "Ya existe un curso llamado '{$data['nombre']}' de tipo '{$data['tipo']}'." + ]); + exit; + } + $stmt = $pdo->prepare(" INSERT INTO cursos (nombre, descripcion, tipo, competencias, estado, profesor_id) VALUES (?, ?, ?, ?, ?, ?) @@ -53,42 +64,53 @@ switch ($method) { $data['estado'], $profesorId ]); - + echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => $e->getMessage()]); } - + break; - + case 'PUT': $data = json_decode(file_get_contents('php://input'), true); - - if (empty($data['id']) || empty($data['nombre']) || empty($data['tipo'])) { - http_response_code(400); - echo json_encode(['error' => 'Datos incompletos']); - exit; + + if (empty($data['id']) || empty($data['nombre']) || empty($data['tipo'])) { + http_response_code(400); + echo json_encode(['error' => 'Datos incompletos']); + exit; } - + try { // Verificar que el curso pertenece al profesor $stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?"); $stmt->execute([$data['id'], $profesorId]); - + if (!$stmt->fetch()) { http_response_code(403); echo json_encode(['error' => 'No autorizado']); exit; } - + + // Validar que no haya otro curso con el mismo nombre y tipo (excluyendo el actual) + $stmt = $pdo->prepare("SELECT id FROM cursos WHERE nombre = ? AND tipo = ? AND profesor_id = ? AND id != ?"); + $stmt->execute([$data['nombre'], $data['tipo'], $profesorId, $data['id']]); + if ($stmt->fetch()) { + echo json_encode([ + 'success' => false, + 'error' => "Ya existe otro curso llamado '{$data['nombre']}' de tipo '{$data['tipo']}'." + ]); + exit; + } + $stmt = $pdo->prepare(" UPDATE cursos SET - nombre = ?, - descripcion = ?, - tipo = ?, - competencias = ?, - estado = ? + nombre = ?, + descripcion = ?, + tipo = ?, + competencias = ?, + estado = ? WHERE id = ? "); $stmt->execute([ @@ -99,14 +121,14 @@ switch ($method) { $data['estado'], $data['id'] ]); - + echo json_encode(['success' => true]); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Error al actualizar curso']); } break; - + case 'DELETE': $id = $_GET['id'] ?? null; if (!$id) { @@ -114,30 +136,30 @@ switch ($method) { echo json_encode(['error' => 'ID de curso no proporcionado']); exit; } - + try { // Verificar que el curso pertenece al profesor $stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?"); $stmt->execute([$id, $profesorId]); - + if (!$stmt->fetch()) { http_response_code(403); echo json_encode(['error' => 'No autorizado']); exit; } - + $stmt = $pdo->prepare("DELETE FROM cursos WHERE id = ?"); $stmt->execute([$id]); - + echo json_encode(['success' => true]); } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Error al eliminar curso']); } break; - + default: http_response_code(405); echo json_encode(['error' => 'Método no permitido']); } -?> \ No newline at end of file +?> diff --git a/api/importar_csv.php b/api/importar_csv.php index 0ce5be5..e8f8999 100644 --- a/api/importar_csv.php +++ b/api/importar_csv.php @@ -1,67 +1,109 @@ false, 'error' => 'No autenticado']); - exit; + http_response_code(401); + echo json_encode(['success' => false, 'error' => 'No autenticado']); + exit; } -$data = json_decode(file_get_contents('php://input'), true); -$alumnos = $data['alumnos'] ?? []; +$input = json_decode(file_get_contents('php://input'), true); +$alumnos = $input['alumnos'] ?? []; if (empty($alumnos)) { - http_response_code(400); - echo json_encode(['success' => false, 'error' => 'No se enviaron alumnos']); - exit; + echo json_encode(['success' => false, 'error' => 'No se recibieron alumnos']); + exit; +} + +function limpiar($s) { + return trim($s, "\"' "); } -// Paso 1: Validar todos los cursos $errores = []; -$profesorId = $_SESSION['profesor']['id'] ?? null; +$importados = 0; foreach ($alumnos as $index => $alumno) { - $nombreCurso = trim($alumno['nombreCurso']); - $tipoCurso = trim($alumno['tipoCurso']); + $fila = $index + 2; - $stmt = $pdo->prepare("SELECT id FROM cursos WHERE nombre = ? AND tipo = ? AND profesor_id = ?"); - $stmt->execute([$nombreCurso, $tipoCurso, $profesorId]); - $curso = $stmt->fetch(); + $nombre = limpiar($alumno['nombre'] ?? ''); + $email = limpiar($alumno['email'] ?? ''); + $telefono = limpiar($alumno['telefono'] ?? ''); + $tipoCurso = strtolower(limpiar($alumno['tipoCurso'] ?? '')); + $nombreCurso = limpiar($alumno['nombreCurso'] ?? ''); - if (!$curso) { - $errores[] = "El curso \"{$nombreCurso}\" de tipo \"{$tipoCurso}\" en {$alumno['nombre']} ({$alumno['email']}) es inválido."; - } else { - $alumnos[$index]['curso_id'] = $curso['id']; // Guardamos para usar después - } + if (!$nombre || !$email || !$telefono || !$tipoCurso || !$nombreCurso) { + $errores[] = "Fila $fila: faltan datos obligatorios."; + continue; + } + + // Verificar curso+tipo + $stmtCurso = $pdo->prepare(" + SELECT id + FROM cursos + WHERE nombre = ? AND tipo = ? + "); + $stmtCurso->execute([$nombreCurso, $tipoCurso]); + $curso = $stmtCurso->fetch(PDO::FETCH_ASSOC); + + if (!$curso) { + $errores[] = "Fila $fila: el curso '$nombreCurso' con tipo '$tipoCurso' no existe."; + continue; + } + + // Verificar duplicados por separado + $emailDuplicado = false; + $telefonoDuplicado = false; + + $stmtEmail = $pdo->prepare("SELECT id FROM alumnos WHERE email = ?"); + $stmtEmail->execute([$email]); + if ($stmtEmail->fetch()) { + $emailDuplicado = true; + } + + $stmtTel = $pdo->prepare("SELECT id FROM alumnos WHERE telefono = ?"); + $stmtTel->execute([$telefono]); + if ($stmtTel->fetch()) { + $telefonoDuplicado = true; + } + + if ($emailDuplicado || $telefonoDuplicado) { + if ($emailDuplicado && $telefonoDuplicado) { + $errores[] = "Fila $fila: el email '$email' y el teléfono '$telefono' ya están registrados."; + } elseif ($emailDuplicado) { + $errores[] = "Fila $fila: el email '$email' ya está registrado."; + } elseif ($telefonoDuplicado) { + $errores[] = "Fila $fila: el teléfono '$telefono' ya está registrado."; + } + continue; + } + + try { + $pdo->beginTransaction(); + + $stmtAlumno = $pdo->prepare("INSERT INTO alumnos (nombre, email, telefono) VALUES (?, ?, ?)"); + $stmtAlumno->execute([$nombre, $email, $telefono]); + $alumnoId = $pdo->lastInsertId(); + + $stmtAsignar = $pdo->prepare("INSERT INTO alumnos_cursos (alumno_id, curso_id, estado) VALUES (?, ?, 'cursando')"); + $stmtAsignar->execute([$alumnoId, $curso['id']]); + + $pdo->commit(); + $importados++; + } catch (PDOException $e) { + $pdo->rollBack(); + $errores[] = "Fila $fila: error al guardar en base de datos."; + } } if (!empty($errores)) { - echo json_encode([ - 'success' => false, - 'error' => "Error en cursos: " . count($errores) . " conflictos encontrados", - 'conflicts' => $errores - ]); - exit; -} - -// Paso 2: Insertar alumnos -try { - $pdo->beginTransaction(); - - foreach ($alumnos as $a) { - $stmt = $pdo->prepare("INSERT INTO alumnos (nombre, email, telefono) VALUES (?, ?, ?)"); - $stmt->execute([$a['nombre'], $a['email'], $a['telefono'] ?? null]); - $alumnoId = $pdo->lastInsertId(); - - $stmt = $pdo->prepare("INSERT INTO alumnos_cursos (alumno_id, curso_id, estado) VALUES (?, ?, 'cursando')"); - $stmt->execute([$alumnoId, $a['curso_id']]); - } - - $pdo->commit(); - echo json_encode(['success' => true, 'message' => 'Todos los alumnos fueron importados correctamente.']); -} catch (PDOException $e) { - $pdo->rollBack(); - http_response_code(500); - echo json_encode(['success' => false, 'error' => 'Error al guardar en la base de datos: ' . $e->getMessage()]); + echo json_encode([ + 'success' => false, + 'conflicts' => $errores + ]); +} else { + echo json_encode([ + 'success' => true, + 'message' => "$importados alumnos importados exitosamente." + ]); } diff --git a/assets/css/styles.css b/assets/css/styles.css index e9e13db..5e69df1 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -965,3 +965,36 @@ textarea { font-size: 0.85rem; font-weight: 500; } + +.description-cell { + max-width: 250px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.truncated-text { + display: inline-block; + max-width: 200px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + vertical-align: middle; +} + +.modal-description { + max-height: 300px; + overflow-y: auto; + white-space: pre-wrap; + word-wrap: break-word; + line-height: 1.4; +} + +.read-more { + color: #2563eb; + font-size: 0.85rem; + text-decoration: underline; + margin-left: 6px; + cursor: pointer; +} + diff --git a/assets/js/main copy.js b/assets/js/main copy.js deleted file mode 100644 index f6dd55c..0000000 --- a/assets/js/main copy.js +++ /dev/null @@ -1,1333 +0,0 @@ -document.addEventListener("DOMContentLoaded", function () { - // Manejo del formulario de login - const loginForm = document.getElementById("loginForm"); - if (loginForm) { - loginForm.addEventListener("submit", handleLogin); - } - - // Configuración inicial del dashboard - if (document.body.classList.contains("admin")) { - initializeDashboard(); - } -}); - -// Función para manejar el login -function handleLogin(e) { - e.preventDefault(); - const formData = new FormData(this); - - fetch(this.action, { - method: "POST", - body: formData, - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - window.location.href = data.redirect; - } else { - alert(data.message); - } - }) - .catch((error) => console.error("Error:", error)); -} - -// Inicialización del dashboard -function initializeDashboard() { - setupSidebarNavigation(); - loadInitialData(); - - const activeSection = document.querySelector(".sidebar-menu li.active"); - if (activeSection) { - const sectionId = activeSection.getAttribute("data-section"); - showSection(sectionId, true); - } -} - -// Configuración del menú lateral -function setupSidebarNavigation() { - document.querySelectorAll(".sidebar-menu li").forEach((item) => { - if (item.getAttribute("data-section")) { - item.addEventListener("click", function () { - const section = this.getAttribute("data-section"); - showSection(section); - }); - } - }); -} - -// Carga de datos iniciales -function loadInitialData() { - const profesorId = getProfesorId(); - - if (!profesorId) { - console.error("No se pudo obtener el ID del profesor"); - return; - } - - fetch(`api/cursos.php?profesor_id=${profesorId}`) - .then((response) => response.json()) - .then((courses) => { - // Cargar estudiantes y diplomas - Promise.all([ - fetch(`api/alumnos.php?profesor_id=${profesorId}`).then((res) => - res.json() - ), - fetch(`api/diplomas.php?profesor_id=${profesorId}`).then((res) => - res.json() - ), - ]).then(([students, diplomas]) => { - updateProfessorStats(courses, students, diplomas); - }); - }) - .catch((error) => console.error("Error:", error)); -} - -// Actualización de estadísticas -function updateProfessorStats(courses, students, diplomas) { - document.getElementById("active-courses-count").textContent = courses.length; - document.getElementById("students-count").textContent = students.length; - document.getElementById("diplomas-count").textContent = diplomas.length || 0; -} - -// Obtención del ID del profesor -function getProfesorId() { - try { - // 1. Verificar en el elemento del DOM (primera opción) - const profesorElement = document.getElementById("current-profesor"); - if (profesorElement && profesorElement.dataset.id) { - return profesorElement.dataset.id; - } - - // 2. Verificar en sessionStorage/localStorage - const storedUser = - sessionStorage.getItem("currentUser") || - localStorage.getItem("currentUser"); - if (storedUser) { - const user = JSON.parse(storedUser); - if (user && user.profesor_id) { - return user.profesor_id; - } - } - - console.log( - "No se encontró el ID del profesor en el DOM o en el almacenamiento" - ); - return null; - } catch (error) { - console.error("Error en getProfesorId:", error); - return null; - } -} - -function showModal(title, content, buttons = []) { - const modalHtml = ` - - `; - - const modalContainer = document.createElement("div"); - modalContainer.innerHTML = modalHtml; - document.body.appendChild(modalContainer); - document.body.style.overflow = "hidden"; -} - -function closeModal() { - const modal = document.getElementById("modal-overlay"); - if (modal) { - modal.remove(); - document.body.style.overflow = ""; - } -} - -function showToast(type, message) { - const toast = document.createElement("div"); - toast.className = `toast toast-${type}`; - toast.textContent = message; - document.body.appendChild(toast); - - setTimeout(() => { - toast.classList.add("fade-out"); - setTimeout(() => toast.remove(), 300); - }, 3000); -} - -// Manejo de secciones -function showSection(sectionId, isInitialLoad = false) { - updateActiveMenu(sectionId); - - const sectionElement = document.getElementById(`${sectionId}-content`); - if (!sectionElement) return; - - document.querySelectorAll(".content-section").forEach((s) => { - s.classList.remove("active"); - }); - sectionElement.classList.add("active"); - - // Cargar siempre los datos del dashboard al acceder - if (sectionId === "dashboard") { - loadInitialData(); - } else { - loadDynamicContent(sectionId, sectionElement); - } -} - -function updateActiveMenu(sectionId) { - document.querySelectorAll(".sidebar-menu li").forEach((li) => { - li.classList.remove("active"); - }); - - const activeItem = document.querySelector( - `.sidebar-menu li[data-section="${sectionId}"]` - ); - if (activeItem) activeItem.classList.add("active"); -} - -// Carga de contenido dinámico -function loadDynamicContent(sectionId, container) { - container.innerHTML = '
Cargando...
'; - - switch (sectionId) { - case "dashboard": - loadDashboardContent(container); - break; - case "courses": - loadProfessorCourses(container); - break; - case "students": - loadStudentsManagement(container); - break; - case "diplomas": - loadDiplomasSection(container); - break; - default: - container.innerHTML = - '

Sección no implementada

'; - } -} - -// Carga del dashboard -async function loadDashboardContent(container, forceReload = false) { - try { - // Mostrar loader - container.innerHTML = '
Cargando datos...
'; - - // Obtener siempre los datos frescos del servidor - const profesorId = getProfesorId(); - if (!profesorId) { - throw new Error("Debes iniciar sesión nuevamente"); - } - - // Usar caché solo si no es un forceReload - const cacheKey = `dashboardData-${profesorId}`; - if (!forceReload && sessionStorage.getItem(cacheKey)) { - const cachedData = JSON.parse(sessionStorage.getItem(cacheKey)); - renderDashboard(container, cachedData); - return; - } - - // Obtener datos frescos - const [coursesRes, studentsRes, diplomasRes] = await Promise.all([ - fetch(`api/cursos.php?profesor_id=${profesorId}&t=${Date.now()}`), - fetch(`api/alumnos.php?t=${Date.now()}`), - fetch(`api/diplomas.php?profesor_id=${profesorId}&t=${Date.now()}`), - ]); - - const [coursesData, studentsData, diplomasData] = await Promise.all([ - coursesRes.json(), - studentsRes.json(), - diplomasRes.json(), - ]); - - if ( - !coursesData.success || - !studentsData.success || - !diplomasData.success - ) { - throw new Error("Error en los datos recibidos"); - } - - const dashboardData = { - cursos: coursesData.data, - alumnos: studentsData.data, - diplomas: diplomasData.data, - lastUpdated: new Date().toLocaleTimeString(), - }; - - // Guardar en caché y renderizar - sessionStorage.setItem(cacheKey, JSON.stringify(dashboardData)); - renderDashboard(container, dashboardData); - } catch (error) { - console.error("Error:", error); - container.innerHTML = ` -
-

Error al cargar datos

-

${error.message}

- -
`; - } -} -function renderDashboard(container, data) { - const activeCourses = data.cursos.filter((c) => c.estado === "activo"); - const totalStudents = data.alumnos.length; - const totalDiplomas = data.diplomas.length; - - container.innerHTML = ` -
-
-

Resumen General

- Actualizado: ${ - data.lastUpdated - } - -
-
-

Resumen:

-

${ - activeCourses.length - } cursos activos

-

${totalStudents} alumnos registrados

-

${totalDiplomas} diplomas emitidos

-
-
-
-

Mis Cursos Activos

- ${renderCoursesPreview(activeCourses)} -
`; -} - -// Gestión de cursos -function loadProfessorCourses(container) { - fetch(`api/cursos.php?profesor_id=${getProfesorId()}`) - .then((response) => response.json()) - .then((res) => { - if (!res.success) throw new Error("No se pudieron cargar los cursos"); - const courses = res.data; - - container.innerHTML = ` -
-

Mis Cursos

-
- - - - - - - - - -
- - -
- - - - -
- - -
-
-
-
-

Lista de Cursos

-
- - - - - - - - - - - - ${courses - .map( - (course) => ` - - - - - - - - ` - ) - .join("")} - -
NombreDescripciónTipoEstadoAcciones
${course.nombre}${course.descripcion || "-"}${formatCourseType(course.tipo)} - ${course.estado} - -
- - -
-
-
-
`; - - setupCourseForm(); - }) - .catch((err) => { - console.error("Error al cargar cursos:", err); - container.innerHTML = `
No se pudieron cargar los cursos
`; - }); -} - -function formatCourseType(type) { - const types = { - pildora: "Píldora", - inyeccion: "Inyección", - tratamiento: "Tratamiento" - }; - return types[type] || type; -} - -function setupCourseForm() { - const courseTypeSelect = document.getElementById('courseType'); - const competencesField = document.getElementById('competencesField'); - const competenciasInput = competencesField - ? competencesField.querySelector('input[name="competencias"]') - : null; - - if (courseTypeSelect && competenciasInput) { - courseTypeSelect.addEventListener('change', function () { - const isTratamiento = this.value === 'tratamiento'; - competencesField.classList.toggle('oculto', !isTratamiento); - competenciasInput.required = isTratamiento; - if (!isTratamiento) { - competenciasInput.value = ''; - } - }); - - const isTratamiento = courseTypeSelect.value === 'tratamiento'; - competencesField.classList.toggle('oculto', !isTratamiento); - competenciasInput.required = isTratamiento; - } - - const form = document.getElementById("courseForm"); - if (!form) return; - - const cancelBtn = document.getElementById("cancelCourseBtn"); - if (cancelBtn) { - cancelBtn.addEventListener("click", function () { - form.reset(); - competencesField.classList.add("oculto"); - form.dataset.editing = "false"; - delete form.dataset.courseId; - form.querySelector("button[type='submit']").textContent = "Guardar"; - cancelBtn.style.display = "none"; - }); - } - - form.addEventListener("submit", function (e) { - e.preventDefault(); - const formData = new FormData(this); - const jsonData = { - nombre: formData.get("nombre"), - descripcion: formData.get("descripcion"), - competencias: formData.get("competencias"), - tipo: formData.get("tipo"), - estado: formData.get("estado"), - profesor_id: getProfesorId(), - }; - - const isEdit = form.dataset.editing === "true"; - const courseId = form.dataset.courseId; - - if (isEdit) { - updateCourse(courseId, jsonData); - } else { - createCourse(jsonData); - } - }); -} - -window.createCourse = function (data) { - fetch("api/cursos.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - showSection("courses"); - } - }) - .catch((error) => console.error("Error:", error)); -}; - -window.updateCourse = function (id, data) { - data.id = id; - - fetch("api/cursos.php", { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - showSection("courses"); - } - }) - .catch((error) => console.error("Error:", error)); -}; - -window.editCourse = function (id) { - fetch(`api/cursos.php?profesor_id=${getProfesorId()}`) - .then((response) => response.json()) - .then((res) => { - const course = res.data.find((c) => c.id == id); - if (!course) return; - - const form = document.getElementById("courseForm"); - form.nombre.value = course.nombre; - form.descripcion.value = course.descripcion || ""; - form.tipo.value = course.tipo; - form.estado.value = course.estado || "activo"; - - const competencesField = document.getElementById("competencesField"); - const competenciasInput = competencesField.querySelector('input[name="competencias"]'); - if (course.tipo === "tratamiento") { - competencesField.classList.remove("oculto"); - competenciasInput.value = course.competencias || ""; - competenciasInput.required = true; - } else { - competencesField.classList.add("oculto"); - competenciasInput.value = ""; - competenciasInput.required = false; - } - - form.dataset.editing = "true"; - form.dataset.courseId = id; - form.querySelector("button[type='submit']").textContent = "Actualizar Curso"; - document.getElementById("cancelCourseBtn").style.display = "inline-block"; - form.scrollIntoView({ behavior: "smooth" }); - }); -}; - - -window.deleteCourse = function (id) { - if (!confirm("¿Estás seguro de eliminar este curso?")) return; - - fetch(`api/cursos.php?id=${id}`, { - method: "DELETE", - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - showSection("courses"); - } - }) - .catch((error) => console.error("Error:", error)); -}; - -// Gestión de alumnos -function loadStudentsManagement(container) { - container.innerHTML = '
Cargando alumnos...
'; - - fetch("api/alumnos.php") - .then((response) => { - if (!response.ok) throw new Error("Error en la respuesta del servidor"); - return response.json(); - }) - .then((data) => { - if (!data.success) - throw new Error(data.error || "Error al obtener alumnos"); - - container.innerHTML = ` -
- ${renderStudentForm()} - ${renderStudentsTable(data.data || [])} -
- `; - - setupStudentForm(); - (data.data || []).forEach((alumno) => loadStudentCourses(alumno.id)); - }) - .catch((error) => { - console.error("Error:", error); - container.innerHTML = ` -
-

Error al cargar alumnos

-

${error.message}

- -
`; - }); -} - - -function renderStudentForm() { - return ` -
-

Gestión de Alumnos

-
-
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
- -
- - -
-
- -
- - -
-
-
`; -} - -function setupStudentForm() { - const form = document.getElementById("studentForm"); - if (!form) return; - - // Referencias a selects - const tipoCurso = document.getElementById("tipoCurso"); - const curso = document.getElementById("curso"); - - // Cargar cursos dinámicamente - tipoCurso.addEventListener("change", () => { - const tipo = tipoCurso.value; - curso.innerHTML = ''; - fetch(`api/cursos.php?tipo=${tipo}`) - .then((res) => res.json()) - .then((data) => { - if (data.success) { - curso.innerHTML = ''; - data.data.forEach((c) => { - const opt = document.createElement("option"); - opt.value = c.id; - opt.textContent = c.nombre; - curso.appendChild(opt); - }); - } else { - curso.innerHTML = ''; - } - }) - .catch(() => { - curso.innerHTML = ''; - }); - }); - - // Guardar alumno - form.addEventListener("submit", function (e) { - e.preventDefault(); - submitStudentForm(); // ✅ Usa función unificada que detecta si es edición o nuevo - }); - - const searchInput = document.getElementById("studentSearch"); - if (searchInput) { - searchInput.addEventListener("input", function () { - filterStudents(this.value.toLowerCase()); - }); - } -} - -function submitStudentForm() { - const form = document.getElementById("studentForm"); - const submitBtn = form.querySelector('button[type="submit"]'); - const submitText = document.getElementById("submitText"); - const spinner = document.getElementById("submitSpinner"); - - // Mostrar spinner - submitText.textContent = "Procesando..."; - spinner.style.display = "inline-block"; - submitBtn.disabled = true; - - const formData = new FormData(form); - const jsonData = { - nombre: formData.get("nombre"), - email: formData.get("email"), - telefono: formData.get("telefono"), - curso_id: document.getElementById("curso").value, // ✅ Cambio aquí - }; - - const isEdit = form.dataset.editing === "true"; - const studentId = form.dataset.studentId; - const url = "api/alumnos.php"; - const method = isEdit ? "PUT" : "POST"; - - if (isEdit) { - jsonData.id = studentId; - } - - fetch(url, { - method: method, - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(jsonData), - }) - .then((response) => response.json()) - .then((data) => { - if (!data.success) - throw new Error(data.error || "Error al guardar alumno"); - - showToast( - "success", - data.message || (isEdit ? "Alumno actualizado" : "Alumno creado") - ); - resetStudentForm(); - loadStudentsManagement(document.querySelector("#students-content")); - }) - .catch((error) => { - showToast("error", error.message || "Error en el servidor"); - }) - .finally(() => { - submitText.textContent = "Guardar"; - spinner.style.display = "none"; - submitBtn.disabled = false; - }); -} - -window.createStudent = function (data) { - fetch("api/alumnos.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - showSection("students"); - } - }) - .catch((error) => console.error("Error:", error)); -}; - -window.updateStudent = function (id, data) { - data.id = id; - - fetch("api/alumnos.php", { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - }) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - showSection("students"); - } - }) - .catch((error) => console.error("Error:", error)); -}; - -window.editStudent = function (id) { - fetch(`api/alumnos.php`) - .then((response) => response.json()) - .then((data) => { - if (!data.success) - throw new Error(data.error || "Error al obtener alumnos"); - - const alumno = data.data.find((a) => a.id == id); - if (!alumno) throw new Error("Alumno no encontrado"); - - const form = document.getElementById("studentForm"); - form.nombre.value = alumno.nombre; - form.email.value = alumno.email; - form.telefono.value = alumno.telefono || ""; - - form.dataset.editing = "true"; - form.dataset.studentId = id; - - document.getElementById("submitText").textContent = "Actualizar"; - document.getElementById("cancelBtn").style.display = "inline-block"; - - // 👇 Precargar tipo y curso si el alumno tiene asignación - fetch(`api/alumnos-cursos.php?alumno_id=${id}`) - .then((res) => res.json()) - .then((asignaciones) => { - if (asignaciones.success && asignaciones.data.length > 0) { - const curso = asignaciones.data[0]; - const tipoCursoSelect = document.getElementById("tipoCurso"); - const cursoSelect = document.getElementById("curso"); - - tipoCursoSelect.value = curso.tipo; - tipoCursoSelect.dispatchEvent(new Event("change")); - - setTimeout(() => { - cursoSelect.value = curso.id; - }, 300); - } - }); - - form.scrollIntoView({ behavior: "smooth" }); - }) - .catch((error) => { - showToast("error", error.message || "Error al cargar alumno"); - }); -}; - -window.deleteStudent = function (id) { - if (!id) { - showToast("error", "ID de alumno no proporcionado"); - return; - } - - fetch(`api/alumnos.php?id=${id}`, { - method: "DELETE", - }) - .then((response) => response.json()) - .then((data) => { - if (!data.success) - throw new Error(data.error || "Error al eliminar alumno"); - - showToast("success", data.message || "Alumno eliminado"); - closeModal(); - loadStudentsManagement(document.querySelector("#students-content")); - }) - .catch((error) => { - showToast("error", error.message || "Error al eliminar alumno"); - }); -}; - - -window.confirmDeleteStudent = function (id) { - showModal( - "Confirmar eliminación", - "¿Estás seguro de eliminar este alumno? Esta acción no se puede deshacer.", - [ - { - text: "Cancelar", - class: "btn-outline", - handler: "closeModal", - }, - { - text: "Eliminar", - class: "btn-danger", - handler: "deleteStudent(" + id + ")" - }, - ] - ); -}; - -window.resetStudentForm = function () { - const form = document.getElementById("studentForm"); - form.reset(); - form.dataset.editing = "false"; - delete form.dataset.studentId; - - document.getElementById("submitText").textContent = "Guardar"; - document.getElementById("cancelBtn").style.display = "none"; -}; -function filterStudents(searchTerm) { - const rows = document.querySelectorAll(".students-table tbody tr"); - - rows.forEach((row) => { - const nombre = row.cells[0].textContent.toLowerCase(); - const email = row.cells[1].textContent.toLowerCase(); - const telefono = row.cells[2].textContent.toLowerCase(); - - if ( - nombre.includes(searchTerm) || - email.includes(searchTerm) || - telefono.includes(searchTerm) - ) { - row.style.display = ""; - } else { - row.style.display = "none"; - } - }); -} - -//alumnos-cursos - -function renderStudentsTable(alumnos) { - if (alumnos.length === 0) { - return ` -
-
- - - - - - -

No hay alumnos registrados

-

Comienza agregando tu primer alumno

-
-
`; - } - - return ` -
-
-

Lista de Alumnos

-
- - -
-
-
- - - - - - - - - - - - ${alumnos - .map( - (alumno) => ` - - - - - - - - ` - ) - .join("")} - -
NombreEmailTeléfonoCursosAcciones
${alumno.nombre}${alumno.email}${alumno.telefono || "-"} -
- ${renderCourseBadges(alumno.id)} -
-
-
- - -
-
-
-
`; -} - -function renderCourseBadges(alumnoId) { - return '
'; // Se cargará dinámicamente -} - -async function loadStudentCourses(alumnoId) { - try { - const response = await fetch( - `api/alumnos-cursos.php?alumno_id=${alumnoId}` - ); - const data = await response.json(); - - if (data.success) { - const container = document.getElementById(`courses-${alumnoId}`); - if (container) { - container.innerHTML = - data.data - .map( - (curso) => ` - - ${curso.nombre} - - - ` - ) - .join("") || 'Sin cursos'; - } - } - } catch (error) { - console.error("Error loading student courses:", error); - } -} - -window.showAssignStudentModal = async function () { - try { - console.log("Mostrando modal de vinculación"); - - const profesorId = getProfesorId(); - if (!profesorId) { - console.error("No se pudo obtener el ID del profesor"); - showToast("error", "No se pudo identificar al profesor"); - return; - } - - // Mostrar loader mientras se cargan los datos - const tempModalContent = `
Cargando datos...
`; - showModal("Vincular Alumnos a Curso", tempModalContent); - - // Cargar cursos y alumnos en paralelo - const [cursosRes, alumnosRes] = await Promise.all([ - fetch(`api/cursos.php?profesor_id=${profesorId}`), - fetch("api/alumnos.php"), - ]); - - const [cursosData, alumnosData] = await Promise.all([ - cursosRes.json(), - alumnosRes.json(), - ]); - - if (!cursosData || !alumnosData || !alumnosData.success) { - closeModal(); - throw new Error("Error al cargar datos necesarios"); - } - - // Crear contenido del modal - const modalHtml = ` -
- - -
- -
- -
- ${ - alumnosData.data.length > 0 - ? alumnosData.data - .map( - (alumno) => ` - - ` - ) - .join("") - : "

No hay alumnos disponibles

" - } -
-
- `; - - // Actualizar el contenido del modal - const modalBody = document.querySelector(".modal-body"); - if (modalBody) { - modalBody.innerHTML = modalHtml; - } - - // Crear el footer si no existe - let modalFooter = document.querySelector(".modal-footer"); - if (!modalFooter) { - modalFooter = document.createElement("div"); - modalFooter.className = "modal-footer"; - document.querySelector(".modal").appendChild(modalFooter); - } - - // Actualizar el contenido del footer - modalFooter.innerHTML = ` - - - `; - - // Función para vincular alumnos seleccionados - window.assignStudentsToCourse = async function () { - const cursoId = document.getElementById("selectCurso").value; - const selectedAlumnos = Array.from( - document.querySelectorAll('input[name="alumnos"]:checked') - ).map((input) => input.value); - - if (selectedAlumnos.length === 0) { - showToast("warning", "Selecciona al menos un alumno"); - return; - } - - try { - const response = await fetch("api/alumnos-cursos.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - curso_id: cursoId, - alumnos: selectedAlumnos, - }), - }); - - const result = await response.json(); - if (result.success) { - showToast("success", "Alumnos vinculados exitosamente"); - closeModal(); - loadStudentsManagement(document.querySelector("#students-content")); - } else { - showToast("error", result.error || "Error al vincular"); - } - } catch (error) { - console.error("Error al vincular:", error); - showToast("error", "Error al realizar la vinculación"); - } - }; - } catch (error) { - console.error("Error en showAssignStudentModal:", error); - closeModal(); - showToast("error", "Error al cargar datos para vinculación"); - } -}; - -window.unassignStudent = async function (alumnoId, cursoId) { - if (confirm("¿Desvincular este alumno del curso?")) { - try { - const response = await fetch( - `api/alumnos-cursos.php?alumno_id=${alumnoId}&curso_id=${cursoId}`, - { - method: "DELETE", - } - ); - - const data = await response.json(); - - if (data.success) { - showToast("success", data.message); - loadStudentCourses(alumnoId); - } else { - showToast("error", data.error || "Error al desvincular"); - } - } catch (error) { - showToast("error", "Error en la conexión"); - } - } -}; - -// Gestión de diplomas -function loadDiplomasSection(container) { - container.innerHTML = '
Cargando diplomas...
'; - - fetch(`api/diplomas.php?profesor_id=${getProfesorId()}`) - .then((response) => { - if (!response.ok) throw new Error("Error en la respuesta del servidor"); - return response.json(); - }) - .then((data) => { - if (!data.success) - throw new Error(data.error || "Error al obtener diplomas"); - - if (data.data.length === 0) { - container.innerHTML = ` -
-

Diplomas Emitidos

-

No hay diplomas registrados aún

-
`; - return; - } - - container.innerHTML = ` -
-

Diplomas Emitidos

-
- - - - - - - - - - - - - - ${data.data - .map( - (diploma) => ` - - - - - - - - - - ` - ) - .join("")} - -
AlumnoEmailCursoTipoFechaCódigoAcciones
${diploma.alumno_nombre}${diploma.alumno_email}${diploma.curso_nombre}${diploma.curso_tipo}${diploma.fecha_formateada}${diploma.codigo_unico} - - -
-
-
`; - }) - .catch((error) => { - console.error("Error:", error); - container.innerHTML = ` -
-

Error al cargar diplomas

-

${error.message}

- -
`; - }); -} - -// Funciones auxiliares -function generateCoursesPreview(courses) { - if (!courses.length) return "

No tienes cursos activos

"; - - return ` -
- - - - - - - - - - ${courses - .map( - (course) => ` - - - - - - ` - ) - .join("")} - -
NombreTipoEstado
${course.nombre || "Sin nombre"}${ - course.tipo || "N/A" - }${course.estado || "N/A"}
-
`; -} - -function getCourseTypeClass(type) { - const types = { - inyeccion: "type-inyeccion", - pildora: "type-pildora", - tratamiento: "type-tratamiento", - }; - return types[type] || ""; -} - -// Funciones globales para diplomas -window.downloadDiploma = function (codigo) { - window.open(`certificado.php?codigo=${codigo}`, "_blank"); -}; - -window.resendDiploma = function (codigo) { - fetch(`api/diplomas.php?action=resend&codigo=${codigo}`) - .then((response) => response.json()) - .then((data) => { - if (data.success) { - alert("Diploma reenviado exitosamente"); - } else { - alert("Error: " + (data.error || "No se pudo reenviar el diploma")); - } - }) - .catch((error) => { - console.error("Error:", error); - alert("Error al reenviar el diploma"); - }); -}; diff --git a/assets/js/main.js b/assets/js/main.js index ddfb411..963ff90 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -120,47 +120,68 @@ function getProfesorId() { } function showModal(title, content, buttons = []) { - const modalHtml = ` - - - - -
@@ -1343,51 +1384,64 @@ window.resendDiploma = function (codigo) { console.error("Error:", error); alert("Error al reenviar el diploma"); }); - - window.handleCSVUpload = function (event) { - const file = event.target.files[0]; - if (!file) return; - - const reader = new FileReader(); - reader.onload = function (e) { - const lines = e.target.result.split("\n").map(line => line.trim()).filter(Boolean); - const alumnos = []; - - for (let i = 1; i < lines.length; i++) { - const [nombre, email, telefono, tipoCurso, nombreCurso] = lines[i].split(",").map(s => s.trim()); - - // Validación básica de columnas mínimas - if (!nombre || !email || !tipoCurso || !nombreCurso) continue; - - alumnos.push({ nombre, email, telefono, tipoCurso, nombreCurso }); - } - - fetch("api/importar_csv.php", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ alumnos }), - }) - .then(res => res.json()) - .then(data => { - if (data.success) { - showToast("success", data.message || "Alumnos importados correctamente"); - loadStudentsManagement(document.querySelector("#students-content")); - } else { - // Si vienen errores detallados, los mostramos - if (data.conflicts && data.conflicts.length > 0) { - const list = ``; - showModal("Errores en Importación CSV", `

No se importaron alumnos por los siguientes errores:

${list}`); - } else { - showToast("error", data.error || "Error al importar alumnos"); - } - } - }) - .catch(() => { - showToast("error", "Error al procesar el archivo"); - }); - }; - - reader.readAsText(file); - }; - }; + +window.handleCSVUpload = function (event) { + const file = event.target.files[0]; + if (!file) return; + + const resultadoDiv = document.getElementById("resultadoCSV"); + if (resultadoDiv) resultadoDiv.innerHTML = ""; + + const reader = new FileReader(); + reader.onload = function (e) { + const lines = e.target.result.split("\n").map(line => line.trim()).filter(Boolean); + const alumnos = []; + + for (let i = 1; i < lines.length; i++) { + const [nombre, email, telefono, tipoCurso, nombreCurso] = lines[i].split(",").map(s => s.trim()); + + if (!nombre || !email || !telefono || !tipoCurso || !nombreCurso) { + continue; + } + + alumnos.push({ nombre, email, telefono, tipoCurso, nombreCurso }); + } + + if (alumnos.length === 0) { + showToast("error", "El archivo CSV no contiene datos válidos"); + if (resultadoDiv) resultadoDiv.innerHTML = `
No se encontraron alumnos válidos en el archivo.
`; + return; + } + + fetch("api/importar_csv.php", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ alumnos }), + }) + .then(res => res.json()) + .then(data => { + if (data.success) { + showToast("success", data.message || "Alumnos importados correctamente"); + if (resultadoDiv) resultadoDiv.innerHTML = `
${data.message}
`; + loadStudentsManagement(document.querySelector("#students-content")); + } else { + if (data.conflicts && data.conflicts.length > 0) { + const list = ``; + showModal("Errores en Importación CSV", `

No se importaron alumnos por los siguientes errores:

${list}`); + if (resultadoDiv) resultadoDiv.innerHTML = `
${list}
`; + } else { + showToast("error", data.error || "Error al importar alumnos"); + if (resultadoDiv) resultadoDiv.innerHTML = `
${data.error || "Error desconocido"}
`; + } + } + }) + .catch(() => { + showToast("error", "Error al procesar el archivo"); + if (resultadoDiv) resultadoDiv.innerHTML = `
Error al procesar el archivo.
`; + }); + }; + + reader.readAsText(file); +}; +