funcionalidad de búsqueda en alumnos completa

This commit is contained in:
alexis.palestina 2025-06-19 15:48:13 -06:00
parent b9fad9acb8
commit 29ed4d7847
6 changed files with 430 additions and 1571 deletions

View File

@ -36,6 +36,26 @@ try {
exit; 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(); $pdo->beginTransaction();
try { try {
// Insertar alumno // Insertar alumno
@ -71,60 +91,81 @@ try {
http_response_code(500); http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Error al registrar: ' . $e->getMessage()]); echo json_encode(['success' => false, 'error' => 'Error al registrar: ' . $e->getMessage()]);
} }
break; break;
case 'PUT': case 'PUT':
$data = json_decode(file_get_contents('php://input'), true); $data = json_decode(file_get_contents('php://input'), true);
if (empty($data['id']) || empty($data['nombre']) || empty($data['email'])) { if (empty($data['id']) || empty($data['nombre']) || empty($data['email'])) {
http_response_code(400); http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Datos incompletos']); echo json_encode(['success' => false, 'error' => 'Datos incompletos']);
exit; exit;
} }
$stmt = $pdo->prepare(" // Verificar email duplicado (excluyendo el mismo alumno)
UPDATE alumnos SET $stmt = $pdo->prepare("SELECT id FROM alumnos WHERE email = ? AND id != ?");
nombre = ?, $stmt->execute([$data['email'], $data['id']]);
email = ?, if ($stmt->fetch()) {
telefono = ? http_response_code(400);
WHERE id = ? echo json_encode(['success' => false, 'error' => 'El correo ya está registrado por otro alumno']);
"); exit;
$stmt->execute([ }
$data['nombre'],
$data['email'], // Verificar teléfono duplicado (excluyendo el mismo alumno)
$data['telefono'] ?? null, if (!empty($data['telefono'])) {
$data['id'] $stmt = $pdo->prepare("SELECT id FROM alumnos WHERE telefono = ? AND id != ?");
]); $stmt->execute([$data['telefono'], $data['id']]);
if ($stmt->fetch()) {
// Si curso_id viene en la actualización, actualizar también el curso http_response_code(400);
if (!empty($data['curso_id'])) { echo json_encode(['success' => false, 'error' => 'El teléfono ya está registrado por otro alumno']);
// Verifica si ya tiene una relación previa exit;
$check = $pdo->prepare("SELECT id FROM alumnos_cursos WHERE alumno_id = ?"); }
$check->execute([$data['id']]); }
if ($check->fetch()) { $stmt = $pdo->prepare("
// Si ya tiene relación, actualizamos el curso asignado UPDATE alumnos SET
$updateCurso = $pdo->prepare(" nombre = ?,
UPDATE alumnos_cursos SET curso_id = ?, estado = 'cursando' email = ?,
WHERE alumno_id = ? telefono = ?
WHERE id = ?
"); ");
$updateCurso->execute([$data['curso_id'], $data['id']]); $stmt->execute([
} else { $data['nombre'],
// Si no tiene relación previa, la insertamos $data['email'],
$insertCurso = $pdo->prepare(" $data['telefono'] ?? null,
INSERT INTO alumnos_cursos (alumno_id, curso_id, estado) $data['id']
VALUES (?, ?, 'cursando') ]);
");
$insertCurso->execute([$data['id'], $data['curso_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']]);
echo json_encode([
'success' => true, if ($check->fetch()) {
'message' => 'Alumno actualizado exitosamente' // Si ya tiene relación, actualizamos el curso asignado
]); $updateCurso = $pdo->prepare("
break; 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': case 'DELETE':
$id = $_GET['id'] ?? null; $id = $_GET['id'] ?? null;

View File

@ -29,18 +29,29 @@ switch ($method) {
http_response_code(500); http_response_code(500);
echo json_encode(['error' => 'Error al cargar cursos']); echo json_encode(['error' => 'Error al cargar cursos']);
} }
break; break;
case 'POST': case 'POST':
$data = json_decode(file_get_contents('php://input'), true); $data = json_decode(file_get_contents('php://input'), true);
if (empty($data['nombre']) || empty($data['tipo'])) { if (empty($data['nombre']) || empty($data['tipo'])) {
http_response_code(400); http_response_code(400);
echo json_encode(['error' => 'Nombre y tipo son requeridos']); echo json_encode(['error' => 'Nombre y tipo son requeridos']);
exit; exit;
} }
try { 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(" $stmt = $pdo->prepare("
INSERT INTO cursos (nombre, descripcion, tipo, competencias, estado, profesor_id) INSERT INTO cursos (nombre, descripcion, tipo, competencias, estado, profesor_id)
VALUES (?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?)
@ -53,42 +64,53 @@ switch ($method) {
$data['estado'], $data['estado'],
$profesorId $profesorId
]); ]);
echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]); echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
} catch (PDOException $e) { } catch (PDOException $e) {
http_response_code(500); http_response_code(500);
echo json_encode(['error' => $e->getMessage()]); echo json_encode(['error' => $e->getMessage()]);
} }
break; break;
case 'PUT': case 'PUT':
$data = json_decode(file_get_contents('php://input'), true); $data = json_decode(file_get_contents('php://input'), true);
if (empty($data['id']) || empty($data['nombre']) || empty($data['tipo'])) { if (empty($data['id']) || empty($data['nombre']) || empty($data['tipo'])) {
http_response_code(400); http_response_code(400);
echo json_encode(['error' => 'Datos incompletos']); echo json_encode(['error' => 'Datos incompletos']);
exit; exit;
} }
try { try {
// Verificar que el curso pertenece al profesor // Verificar que el curso pertenece al profesor
$stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?"); $stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?");
$stmt->execute([$data['id'], $profesorId]); $stmt->execute([$data['id'], $profesorId]);
if (!$stmt->fetch()) { if (!$stmt->fetch()) {
http_response_code(403); http_response_code(403);
echo json_encode(['error' => 'No autorizado']); echo json_encode(['error' => 'No autorizado']);
exit; 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(" $stmt = $pdo->prepare("
UPDATE cursos SET UPDATE cursos SET
nombre = ?, nombre = ?,
descripcion = ?, descripcion = ?,
tipo = ?, tipo = ?,
competencias = ?, competencias = ?,
estado = ? estado = ?
WHERE id = ? WHERE id = ?
"); ");
$stmt->execute([ $stmt->execute([
@ -99,14 +121,14 @@ switch ($method) {
$data['estado'], $data['estado'],
$data['id'] $data['id']
]); ]);
echo json_encode(['success' => true]); echo json_encode(['success' => true]);
} catch (PDOException $e) { } catch (PDOException $e) {
http_response_code(500); http_response_code(500);
echo json_encode(['error' => 'Error al actualizar curso']); echo json_encode(['error' => 'Error al actualizar curso']);
} }
break; break;
case 'DELETE': case 'DELETE':
$id = $_GET['id'] ?? null; $id = $_GET['id'] ?? null;
if (!$id) { if (!$id) {
@ -114,30 +136,30 @@ switch ($method) {
echo json_encode(['error' => 'ID de curso no proporcionado']); echo json_encode(['error' => 'ID de curso no proporcionado']);
exit; exit;
} }
try { try {
// Verificar que el curso pertenece al profesor // Verificar que el curso pertenece al profesor
$stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?"); $stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?");
$stmt->execute([$id, $profesorId]); $stmt->execute([$id, $profesorId]);
if (!$stmt->fetch()) { if (!$stmt->fetch()) {
http_response_code(403); http_response_code(403);
echo json_encode(['error' => 'No autorizado']); echo json_encode(['error' => 'No autorizado']);
exit; exit;
} }
$stmt = $pdo->prepare("DELETE FROM cursos WHERE id = ?"); $stmt = $pdo->prepare("DELETE FROM cursos WHERE id = ?");
$stmt->execute([$id]); $stmt->execute([$id]);
echo json_encode(['success' => true]); echo json_encode(['success' => true]);
} catch (PDOException $e) { } catch (PDOException $e) {
http_response_code(500); http_response_code(500);
echo json_encode(['error' => 'Error al eliminar curso']); echo json_encode(['error' => 'Error al eliminar curso']);
} }
break; break;
default: default:
http_response_code(405); http_response_code(405);
echo json_encode(['error' => 'Método no permitido']); echo json_encode(['error' => 'Método no permitido']);
} }
?> ?>

View File

@ -1,67 +1,109 @@
<?php <?php
header('Content-Type: application/json');
require '../includes/config.php'; require '../includes/config.php';
header('Content-Type: application/json');
if (!is_logged_in()) { if (!is_logged_in()) {
http_response_code(401); http_response_code(401);
echo json_encode(['success' => false, 'error' => 'No autenticado']); echo json_encode(['success' => false, 'error' => 'No autenticado']);
exit; exit;
} }
$data = json_decode(file_get_contents('php://input'), true); $input = json_decode(file_get_contents('php://input'), true);
$alumnos = $data['alumnos'] ?? []; $alumnos = $input['alumnos'] ?? [];
if (empty($alumnos)) { if (empty($alumnos)) {
http_response_code(400); echo json_encode(['success' => false, 'error' => 'No se recibieron alumnos']);
echo json_encode(['success' => false, 'error' => 'No se enviaron alumnos']); exit;
exit; }
function limpiar($s) {
return trim($s, "\"' ");
} }
// Paso 1: Validar todos los cursos
$errores = []; $errores = [];
$profesorId = $_SESSION['profesor']['id'] ?? null; $importados = 0;
foreach ($alumnos as $index => $alumno) { foreach ($alumnos as $index => $alumno) {
$nombreCurso = trim($alumno['nombreCurso']); $fila = $index + 2;
$tipoCurso = trim($alumno['tipoCurso']);
$stmt = $pdo->prepare("SELECT id FROM cursos WHERE nombre = ? AND tipo = ? AND profesor_id = ?"); $nombre = limpiar($alumno['nombre'] ?? '');
$stmt->execute([$nombreCurso, $tipoCurso, $profesorId]); $email = limpiar($alumno['email'] ?? '');
$curso = $stmt->fetch(); $telefono = limpiar($alumno['telefono'] ?? '');
$tipoCurso = strtolower(limpiar($alumno['tipoCurso'] ?? ''));
$nombreCurso = limpiar($alumno['nombreCurso'] ?? '');
if (!$curso) { if (!$nombre || !$email || !$telefono || !$tipoCurso || !$nombreCurso) {
$errores[] = "El curso \"{$nombreCurso}\" de tipo \"{$tipoCurso}\" en {$alumno['nombre']} ({$alumno['email']}) es inválido."; $errores[] = "Fila $fila: faltan datos obligatorios.";
} else { continue;
$alumnos[$index]['curso_id'] = $curso['id']; // Guardamos para usar después }
}
// 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)) { if (!empty($errores)) {
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'error' => "Error en cursos: " . count($errores) . " conflictos encontrados", 'conflicts' => $errores
'conflicts' => $errores ]);
]); } else {
exit; echo json_encode([
} 'success' => true,
'message' => "$importados alumnos importados exitosamente."
// 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()]);
} }

View File

@ -965,3 +965,36 @@ textarea {
font-size: 0.85rem; font-size: 0.85rem;
font-weight: 500; 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;
}

File diff suppressed because it is too large Load Diff

View File

@ -120,47 +120,68 @@ function getProfesorId() {
} }
function showModal(title, content, buttons = []) { function showModal(title, content, buttons = []) {
const modalHtml = ` // Remover modal existente si hay
<div class="modal-overlay" id="modal-overlay"> const existing = document.getElementById("modal-overlay");
<div class="modal"> if (existing) existing.remove();
<div class="modal-header">
<h3>${title}</h3> const modalOverlay = document.createElement("div");
<button class="close-btn" onclick="closeModal()">&times;</button> modalOverlay.className = "modal-overlay";
</div> modalOverlay.id = "modal-overlay";
<div class="modal-body">${content}</div>
<div class="modal-footer"> const modal = document.createElement("div");
${buttons modal.className = "modal";
.map(
(btn) => ` modal.innerHTML = `
<button class="btn ${btn.class}" <div class="modal-header">
onclick="${ <h3>${title}</h3>
typeof btn.handler === "function" <button class="close-btn" id="modal-close-btn">&times;</button>
? `(${btn.handler.toString()})()`
: btn.handler
}">
${btn.text}
</button>
`
)
.join("")}
</div>
</div>
</div> </div>
<div class="modal-body">${content}</div>
<div class="modal-footer" id="modal-footer"></div>
`; `;
const modalContainer = document.createElement("div"); modalOverlay.appendChild(modal);
modalContainer.innerHTML = modalHtml; document.body.appendChild(modalOverlay);
document.body.appendChild(modalContainer);
document.body.style.overflow = "hidden"; document.body.style.overflow = "hidden";
// Cerrar con el botón de la esquina
document.getElementById("modal-close-btn").addEventListener("click", closeModal);
// Agregar botones y handlers
const footer = document.getElementById("modal-footer");
buttons.forEach((btn) => {
const button = document.createElement("button");
button.className = "btn " + btn.class;
button.innerText = btn.text;
button.addEventListener("click", () => {
if (typeof btn.handler === "function") {
btn.handler();
} else if (typeof btn.handler === "string") {
if (btn.handler.endsWith(")")) {
eval(btn.handler); // deleteStudent(5)
} else if (typeof window[btn.handler] === "function") {
window[btn.handler](); // closeModal
}
}
});
footer.appendChild(button);
});
} }
function closeModal() { window.showFullDescription = function (descripcion) {
showModal("Descripción del Curso", `<div class="modal-description">${descripcion}</div>`);
};
window.closeModal = function () {
const modal = document.getElementById("modal-overlay"); const modal = document.getElementById("modal-overlay");
if (modal) { if (modal) {
modal.remove(); modal.remove();
document.body.style.overflow = ""; document.body.style.overflow = "";
} }
} };
function showToast(type, message) { function showToast(type, message) {
const toast = document.createElement("div"); const toast = document.createElement("div");
@ -343,7 +364,7 @@ function loadProfessorCourses(container) {
<input type="text" name="nombre" required> <input type="text" name="nombre" required>
<label>Descripción</label> <label>Descripción</label>
<textarea name="descripcion" maxlength="250" rows="4" style="width: 100%; padding: 0.75rem; margin: 0.5rem 0 1rem 0; border: 1px solid #e2e8f0; border-radius: 6px; font-family: 'Inter', sans-serif;"></textarea> <textarea name="descripcion" maxlength="250" rows="4" style="width: 100%; padding: 0.75rem; margin: 0.5rem 0 1rem 0; border: 1px solid #e2e8f0; border-radius: 6px; font-family: 'Inter', sans-serif;"></textarea>
<label>Tipo de Curso *</label> <label>Tipo de Curso *</label>
<select id="courseType" name="tipo" required> <select id="courseType" name="tipo" required>
@ -387,38 +408,64 @@ function loadProfessorCourses(container) {
</thead> </thead>
<tbody> <tbody>
${courses ${courses
.map( .map((course) => {
(course) => ` const desc = course.descripcion || "-";
<tr> const safeDesc = desc.replace(/"/g, "&quot;").replace(/'/g, "\\'");
<td>${course.nombre}</td> return `
<td class="description-cell">${course.descripcion || "-"}</td> <tr>
<td><span class="badge ${getCourseTypeClass(course.tipo)}">${formatCourseType(course.tipo)}</span></td> <td>${course.nombre}</td>
<td> <td class="description-cell">
<span class="badge ${ <span class="truncated-text" id="desc-${course.id}">
course.estado === "activo" ${desc}
? "active" </span>
: course.estado === "completado" <span class="read-more-container" id="readmore-${course.id}"></span>
? "completed" </td>
: course.estado === "archivado" <td><span class="badge ${getCourseTypeClass(course.tipo)}">${formatCourseType(course.tipo)}</span></td>
? "archived" <td>
: "inactive" <span class="badge ${
}">${course.estado}</span> course.estado === "activo"
</td> ? "active"
<td> : course.estado === "completado"
<div class="action-buttons"> ? "completed"
<button class="btn btn-sm" onclick="editCourse(${course.id})">Editar</button> : course.estado === "archivado"
<button class="btn btn-sm btn-danger" onclick="deleteCourse(${course.id})">Eliminar</button> ? "archived"
</div> : "inactive"
</td> }">${course.estado}</span>
</tr> </td>
` <td>
) <div class="action-buttons">
<button class="btn btn-sm" onclick="editCourse(${course.id})">Editar</button>
<button class="btn btn-sm btn-danger" onclick="deleteCourse(${course.id})">Eliminar</button>
</div>
</td>
</tr>`;
})
.join("")} .join("")}
</tbody> </tbody>
</table> </table>
</div> </div>
</div>`; </div>`;
// Evaluar si se debe mostrar "Leer más" en cada descripción
setTimeout(() => {
courses.forEach(course => {
const span = document.getElementById(`desc-${course.id}`);
const leerMas = document.getElementById(`readmore-${course.id}`);
if (span && leerMas && span.scrollWidth > span.clientWidth) {
const safeDesc = (course.descripcion || "")
.replace(/'/g, "\\'")
.replace(/"/g, "&quot;")
.replace(/\n/g, "\\n");
leerMas.innerHTML = `
<a href="#" class="read-more" onclick="showFullDescription('${safeDesc}'); return false;">
Leer más
</a>`;
}
});
}, 0);
setupCourseForm(); setupCourseForm();
}) })
.catch((err) => { .catch((err) => {
@ -954,12 +1001,6 @@ function renderStudentsTable(alumnos) {
<input type="text" id="studentSearch" placeholder="Buscar alumno..."> <input type="text" id="studentSearch" placeholder="Buscar alumno...">
</div> </div>
<input type="file" id="csvInput" accept=".csv" style="display:none;" onchange="handleCSVUpload(event)">
<button class="btn btn-sm btn-primary" onclick="document.getElementById('csvInput').click()">
Importar CSV
</button>
</div> </div>
</div> </div>
<div class="table-responsive"> <div class="table-responsive">
@ -1343,51 +1384,64 @@ window.resendDiploma = function (codigo) {
console.error("Error:", error); console.error("Error:", error);
alert("Error al reenviar el diploma"); 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 = `<ul>${data.conflicts.map(msg => `<li>${msg}</li>`).join("")}</ul>`;
showModal("Errores en Importación CSV", `<p>No se importaron alumnos por los siguientes errores:</p>${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 = `<div class="alert alert-danger">No se encontraron alumnos válidos en el archivo.</div>`;
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 = `<div class="alert alert-success">${data.message}</div>`;
loadStudentsManagement(document.querySelector("#students-content"));
} else {
if (data.conflicts && data.conflicts.length > 0) {
const list = `<ul>${data.conflicts.map(msg => `<li>${msg}</li>`).join("")}</ul>`;
showModal("Errores en Importación CSV", `<p>No se importaron alumnos por los siguientes errores:</p>${list}`);
if (resultadoDiv) resultadoDiv.innerHTML = `<div class="alert alert-danger">${list}</div>`;
} else {
showToast("error", data.error || "Error al importar alumnos");
if (resultadoDiv) resultadoDiv.innerHTML = `<div class="alert alert-danger">${data.error || "Error desconocido"}</div>`;
}
}
})
.catch(() => {
showToast("error", "Error al procesar el archivo");
if (resultadoDiv) resultadoDiv.innerHTML = `<div class="alert alert-danger">Error al procesar el archivo.</div>`;
});
};
reader.readAsText(file);
};