funcionalidad de búsqueda en alumnos completa
This commit is contained in:
parent
b9fad9acb8
commit
29ed4d7847
145
api/alumnos.php
145
api/alumnos.php
|
@ -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;
|
||||||
|
|
|
@ -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']);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -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()]);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
@ -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()">×</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">×</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, """).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, """)
|
||||||
|
.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);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue