gestion alumnos actualizado

This commit is contained in:
alexis.palestina 2025-06-02 23:30:18 -06:00
parent 45368a331f
commit 5cda47e50b
7 changed files with 1690 additions and 108 deletions

View File

@ -14,27 +14,27 @@ $profesorId = $_SESSION['profesor']['id'] ?? null;
try { try {
switch ($method) { switch ($method) {
case 'GET': case 'GET':
$alumnoId = $_GET['alumno_id'] ?? null; $alumnoId = $_GET['alumno_id'] ?? null;
if ($alumnoId) { if ($alumnoId) {
// Obtener cursos de un alumno específico // Obtener cursos de un alumno específico (con tipo)
$query = "SELECT c.* FROM cursos c $query = "SELECT c.id, c.nombre, c.tipo FROM cursos c
JOIN alumnos_cursos ac ON c.id = ac.curso_id JOIN alumnos_cursos ac ON c.id = ac.curso_id
WHERE ac.alumno_id = ? AND c.profesor_id = ?"; WHERE ac.alumno_id = ? AND c.profesor_id = ?";
$stmt = $pdo->prepare($query); $stmt = $pdo->prepare($query);
$stmt->execute([$alumnoId, $profesorId]); $stmt->execute([$alumnoId, $profesorId]);
} else { } else {
// Obtener todos los alumnos // Obtener todos los alumnos
$query = "SELECT * FROM alumnos"; $query = "SELECT * FROM alumnos";
$stmt = $pdo->prepare($query); $stmt = $pdo->prepare($query);
$stmt->execute(); $stmt->execute();
} }
echo json_encode([ echo json_encode([
'success' => true, 'success' => true,
'data' => $stmt->fetchAll(PDO::FETCH_ASSOC) 'data' => $stmt->fetchAll(PDO::FETCH_ASSOC)
]); ]);
break; break;
case 'POST': case 'POST':
$data = json_decode(file_get_contents('php://input'), true); $data = json_decode(file_get_contents('php://input'), true);

View File

@ -27,31 +27,51 @@ try {
]); ]);
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['email'])) { if (empty($data['nombre']) || empty($data['email'])) {
http_response_code(400); http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Nombre y email son requeridos']); echo json_encode(['success' => false, 'error' => 'Nombre y email son requeridos']);
exit; exit;
} }
$stmt = $pdo->prepare(" $pdo->beginTransaction();
INSERT INTO alumnos (nombre, email, telefono) try {
VALUES (?, ?, ?) // Insertar alumno
"); $stmt = $pdo->prepare("
$stmt->execute([ INSERT INTO alumnos (nombre, email, telefono)
$data['nombre'], VALUES (?, ?, ?)
$data['email'], ");
$data['telefono'] ?? null $stmt->execute([
]); $data['nombre'],
$data['email'],
$data['telefono'] ?? null
]);
echo json_encode([ $alumnoId = $pdo->lastInsertId();
'success' => true,
'id' => $pdo->lastInsertId(), // Si viene curso_id, asignar también a curso
'message' => 'Alumno creado exitosamente' if (!empty($data['curso_id'])) {
]); $stmt = $pdo->prepare("
break; INSERT INTO alumnos_cursos (alumno_id, curso_id, estado)
VALUES (?, ?, 'cursando')
");
$stmt->execute([$alumnoId, $data['curso_id']]);
}
$pdo->commit();
echo json_encode([
'success' => true,
'id' => $alumnoId,
'message' => 'Alumno creado y asignado exitosamente'
]);
} catch (PDOException $e) {
$pdo->rollBack();
http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Error al registrar: ' . $e->getMessage()]);
}
break;
case 'PUT': case 'PUT':
$data = json_decode(file_get_contents('php://input'), true); $data = json_decode(file_get_contents('php://input'), true);
@ -75,6 +95,30 @@ try {
$data['telefono'] ?? null, $data['telefono'] ?? null,
$data['id'] $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([ echo json_encode([
'success' => true, 'success' => true,

View File

@ -14,15 +14,22 @@ $profesorId = $_SESSION['profesor']['id'];
switch ($method) { switch ($method) {
case 'GET': case 'GET':
try { try {
$query = "SELECT * FROM cursos WHERE profesor_id = ?"; $tipo = $_GET['tipo'] ?? null;
$stmt = $pdo->prepare($query); if ($tipo) {
$stmt->execute([$profesorId]); $query = "SELECT * FROM cursos WHERE profesor_id = ? AND tipo = ?";
echo json_encode($stmt->fetchAll()); $stmt = $pdo->prepare($query);
$stmt->execute([$profesorId, $tipo]);
} else {
$query = "SELECT * FROM cursos WHERE profesor_id = ?";
$stmt = $pdo->prepare($query);
$stmt->execute([$profesorId]);
}
echo json_encode(['success' => true, 'data' => $stmt->fetchAll()]);
} catch (PDOException $e) { } catch (PDOException $e) {
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);
@ -50,8 +57,9 @@ switch ($method) {
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' => 'Error al crear curso']); echo json_encode(['error' => $e->getMessage()]);
} }
break; break;
case 'PUT': case 'PUT':

67
api/importar_csv.php Normal file
View File

@ -0,0 +1,67 @@
<?php
header('Content-Type: application/json');
require '../includes/config.php';
if (!is_logged_in()) {
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'] ?? [];
if (empty($alumnos)) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'No se enviaron alumnos']);
exit;
}
// Paso 1: Validar todos los cursos
$errores = [];
$profesorId = $_SESSION['profesor']['id'] ?? null;
foreach ($alumnos as $index => $alumno) {
$nombreCurso = trim($alumno['nombreCurso']);
$tipoCurso = trim($alumno['tipoCurso']);
$stmt = $pdo->prepare("SELECT id FROM cursos WHERE nombre = ? AND tipo = ? AND profesor_id = ?");
$stmt->execute([$nombreCurso, $tipoCurso, $profesorId]);
$curso = $stmt->fetch();
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 (!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()]);
}

1333
assets/js/main copy.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -331,7 +331,10 @@ function renderDashboard(container, data) {
function loadProfessorCourses(container) { function loadProfessorCourses(container) {
fetch(`api/cursos.php?profesor_id=${getProfesorId()}`) fetch(`api/cursos.php?profesor_id=${getProfesorId()}`)
.then((response) => response.json()) .then((response) => response.json())
.then((courses) => { .then((res) => {
if (!res.success) throw new Error("No se pudieron cargar los cursos");
const courses = res.data;
container.innerHTML = ` container.innerHTML = `
<div class="card"> <div class="card">
<h2>Mis Cursos</h2> <h2>Mis Cursos</h2>
@ -388,12 +391,8 @@ function loadProfessorCourses(container) {
(course) => ` (course) => `
<tr> <tr>
<td>${course.nombre}</td> <td>${course.nombre}</td>
<td class="description-cell">${ <td class="description-cell">${course.descripcion || "-"}</td>
course.descripcion || "-" <td><span class="badge ${getCourseTypeClass(course.tipo)}">${formatCourseType(course.tipo)}</span></td>
}</td>
<td><span class="badge ${getCourseTypeClass(
course.tipo
)}">${course.tipo}</span></td>
<td> <td>
<span class="badge ${ <span class="badge ${
course.estado === "activo" course.estado === "activo"
@ -407,12 +406,8 @@ function loadProfessorCourses(container) {
</td> </td>
<td> <td>
<div class="action-buttons"> <div class="action-buttons">
<button class="btn btn-sm" onclick="editCourse(${ <button class="btn btn-sm" onclick="editCourse(${course.id})">Editar</button>
course.id <button class="btn btn-sm btn-danger" onclick="deleteCourse(${course.id})">Eliminar</button>
})">Editar</button>
<button class="btn btn-sm btn-danger" onclick="deleteCourse(${
course.id
})">Eliminar</button>
</div> </div>
</td> </td>
</tr> </tr>
@ -425,14 +420,18 @@ function loadProfessorCourses(container) {
</div>`; </div>`;
setupCourseForm(); setupCourseForm();
})
.catch((err) => {
console.error("Error al cargar cursos:", err);
container.innerHTML = `<div class="card error-card">No se pudieron cargar los cursos</div>`;
}); });
} }
function formatCourseType(type) { function formatCourseType(type) {
const types = { const types = {
'pildora': 'Píldora', pildora: "Píldora",
'inyeccion': 'Inyección', inyeccion: "Inyección",
'tratamiento': 'Tratamiento' tratamiento: "Tratamiento"
}; };
return types[type] || type; return types[type] || type;
} }
@ -445,7 +444,7 @@ function setupCourseForm() {
: null; : null;
if (courseTypeSelect && competenciasInput) { if (courseTypeSelect && competenciasInput) {
courseTypeSelect.addEventListener('change', function() { courseTypeSelect.addEventListener('change', function () {
const isTratamiento = this.value === 'tratamiento'; const isTratamiento = this.value === 'tratamiento';
competencesField.classList.toggle('oculto', !isTratamiento); competencesField.classList.toggle('oculto', !isTratamiento);
competenciasInput.required = isTratamiento; competenciasInput.required = isTratamiento;
@ -458,7 +457,7 @@ function setupCourseForm() {
competencesField.classList.toggle('oculto', !isTratamiento); competencesField.classList.toggle('oculto', !isTratamiento);
competenciasInput.required = isTratamiento; competenciasInput.required = isTratamiento;
} }
const form = document.getElementById("courseForm"); const form = document.getElementById("courseForm");
if (!form) return; if (!form) return;
@ -482,7 +481,7 @@ function setupCourseForm() {
descripcion: formData.get("descripcion"), descripcion: formData.get("descripcion"),
competencias: formData.get("competencias"), competencias: formData.get("competencias"),
tipo: formData.get("tipo"), tipo: formData.get("tipo"),
estado: formData.get("estado"), estado: formData.get("estado"),
profesor_id: getProfesorId(), profesor_id: getProfesorId(),
}; };
@ -532,8 +531,8 @@ window.updateCourse = function (id, data) {
window.editCourse = function (id) { window.editCourse = function (id) {
fetch(`api/cursos.php?profesor_id=${getProfesorId()}`) fetch(`api/cursos.php?profesor_id=${getProfesorId()}`)
.then((response) => response.json()) .then((response) => response.json())
.then((courses) => { .then((res) => {
const course = courses.find((c) => c.id == id); const course = res.data.find((c) => c.id == id);
if (!course) return; if (!course) return;
const form = document.getElementById("courseForm"); const form = document.getElementById("courseForm");
@ -562,6 +561,7 @@ window.editCourse = function (id) {
}); });
}; };
window.deleteCourse = function (id) { window.deleteCourse = function (id) {
if (!confirm("¿Estás seguro de eliminar este curso?")) return; if (!confirm("¿Estás seguro de eliminar este curso?")) return;
@ -613,48 +613,104 @@ function loadStudentsManagement(container) {
}); });
} }
function renderStudentForm() { function renderStudentForm() {
return ` return `
<div class="card"> <div class="card">
<h2>Gestión de Alumnos</h2> <div class="card-header" style="display: flex; justify-content: space-between; align-items: center;">
<form id="studentForm"> <h2>Gestión de Alumnos</h2>
<div class="form-grid"> <label class="btn btn-primary" style="margin: 0;">
<div class="form-group"> Importar CSV
<label for="nombre">Nombre*</label> <input type="file" accept=".csv" onchange="handleCSVUpload(event)" style="display: none;">
<input type="text" id="nombre" name="nombre" required> </label>
</div> </div>
<div class="form-group"> <form id="studentForm">
<label for="email">Email*</label> <div class="form-grid">
<input type="email" id="email" name="email" required> <div class="form-group">
</div> <label for="nombre">Nombre*</label>
<div class="form-group"> <input type="text" id="nombre" name="nombre" required>
<label for="telefono">Teléfono</label> </div>
<input type="tel" id="telefono" name="telefono"> <div class="form-group">
</div> <label for="email">Email*</label>
</div> <input type="email" id="email" name="email" required>
<div class="form-actions"> </div>
<button type="button" class="btn btn-outline" onclick="resetStudentForm()" id="cancelBtn" style="display:none;"> <div class="form-group">
Cancelar <label for="telefono">Teléfono</label>
</button> <input type="tel" id="telefono" name="telefono">
<button type="submit" class="btn"> </div>
<span id="submitText">Guardar</span> </div>
<span class="spinner-border spinner-border-sm" id="submitSpinner" style="display:none;"></span>
</button> <div class="form-grid">
</div> <div class="form-group">
</form> <label for="tipoCurso">Tipo de Curso*</label>
</div>`; <select id="tipoCurso" required>
<option value="">Seleccionar tipo</option>
<option value="inyeccion">Inyección</option>
<option value="pildora">Píldora</option>
<option value="tratamiento">Tratamiento</option>
</select>
</div>
<div class="form-group">
<label for="curso">Curso*</label>
<select id="curso" required>
<option value="">Selecciona primero un tipo</option>
</select>
</div>
</div>
<div class="form-actions">
<button type="button" class="btn btn-outline" onclick="resetStudentForm()" id="cancelBtn" style="display:none;">
Cancelar
</button>
<button type="submit" class="btn">
<span id="submitText">Guardar</span>
<span class="spinner-border spinner-border-sm" id="submitSpinner" style="display:none;"></span>
</button>
</div>
</form>
</div>`;
} }
function setupStudentForm() { function setupStudentForm() {
const form = document.getElementById("studentForm"); const form = document.getElementById("studentForm");
if (!form) return; if (!form) return;
form.addEventListener("submit", function (e) { // Referencias a selects
e.preventDefault(); const tipoCurso = document.getElementById("tipoCurso");
submitStudentForm(); const curso = document.getElementById("curso");
// Cargar cursos dinámicamente
tipoCurso.addEventListener("change", () => {
const tipo = tipoCurso.value;
curso.innerHTML = '<option value="">Cargando...</option>';
fetch(`api/cursos.php?tipo=${tipo}`)
.then((res) => res.json())
.then((data) => {
if (data.success) {
curso.innerHTML = '<option value="">Seleccionar curso</option>';
data.data.forEach((c) => {
const opt = document.createElement("option");
opt.value = c.id;
opt.textContent = c.nombre;
curso.appendChild(opt);
});
} else {
curso.innerHTML = '<option value="">Sin resultados</option>';
}
})
.catch(() => {
curso.innerHTML = '<option value="">Error al cargar</option>';
});
}); });
// Configurar búsqueda // 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"); const searchInput = document.getElementById("studentSearch");
if (searchInput) { if (searchInput) {
searchInput.addEventListener("input", function () { searchInput.addEventListener("input", function () {
@ -662,6 +718,7 @@ function setupStudentForm() {
}); });
} }
} }
function submitStudentForm() { function submitStudentForm() {
const form = document.getElementById("studentForm"); const form = document.getElementById("studentForm");
const submitBtn = form.querySelector('button[type="submit"]'); const submitBtn = form.querySelector('button[type="submit"]');
@ -678,12 +735,12 @@ function submitStudentForm() {
nombre: formData.get("nombre"), nombre: formData.get("nombre"),
email: formData.get("email"), email: formData.get("email"),
telefono: formData.get("telefono"), telefono: formData.get("telefono"),
curso_id: document.getElementById("curso").value, // ✅ Cambio aquí
}; };
const isEdit = form.dataset.editing === "true"; const isEdit = form.dataset.editing === "true";
const studentId = form.dataset.studentId; const studentId = form.dataset.studentId;
const url = "api/alumnos.php";
const url = "api/alumnos.php" + (isEdit ? "" : "");
const method = isEdit ? "PUT" : "POST"; const method = isEdit ? "PUT" : "POST";
if (isEdit) { if (isEdit) {
@ -772,6 +829,24 @@ window.editStudent = function (id) {
document.getElementById("submitText").textContent = "Actualizar"; document.getElementById("submitText").textContent = "Actualizar";
document.getElementById("cancelBtn").style.display = "inline-block"; 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" }); form.scrollIntoView({ behavior: "smooth" });
}) })
.catch((error) => { .catch((error) => {
@ -780,6 +855,11 @@ window.editStudent = function (id) {
}; };
window.deleteStudent = function (id) { window.deleteStudent = function (id) {
if (!id) {
showToast("error", "ID de alumno no proporcionado");
return;
}
fetch(`api/alumnos.php?id=${id}`, { fetch(`api/alumnos.php?id=${id}`, {
method: "DELETE", method: "DELETE",
}) })
@ -796,6 +876,8 @@ window.deleteStudent = function (id) {
showToast("error", error.message || "Error al eliminar alumno"); showToast("error", error.message || "Error al eliminar alumno");
}); });
}; };
window.confirmDeleteStudent = function (id) { window.confirmDeleteStudent = function (id) {
showModal( showModal(
"Confirmar eliminación", "Confirmar eliminación",
@ -809,11 +891,12 @@ window.confirmDeleteStudent = function (id) {
{ {
text: "Eliminar", text: "Eliminar",
class: "btn-danger", class: "btn-danger",
handler: () => deleteStudent(id), handler: "deleteStudent(" + id + ")"
}, },
] ]
); );
}; };
window.resetStudentForm = function () { window.resetStudentForm = function () {
const form = document.getElementById("studentForm"); const form = document.getElementById("studentForm");
form.reset(); form.reset();
@ -870,13 +953,13 @@ function renderStudentsTable(alumnos) {
<div class="search-box"> <div class="search-box">
<input type="text" id="studentSearch" placeholder="Buscar alumno..."> <input type="text" id="studentSearch" placeholder="Buscar alumno...">
</div> </div>
<button class="btn btn-sm btn-primary" onclick="showAssignStudentModal()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"> <input type="file" id="csvInput" accept=".csv" style="display:none;" onchange="handleCSVUpload(event)">
<path d="M12 5v14"></path> <button class="btn btn-sm btn-primary" onclick="document.getElementById('csvInput').click()">
<path d="M5 12h14"></path> Importar CSV
</svg>
Vincular a Curso
</button> </button>
</div> </div>
</div> </div>
<div class="table-responsive"> <div class="table-responsive">
@ -1260,4 +1343,51 @@ 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);
};
}; };

View File

@ -4,7 +4,7 @@ session_start();
$host = 'localhost'; $host = 'localhost';
$db = 'diplomaster'; $db = 'diplomaster';
$user = 'root'; $user = 'root';
$pass = ''; $pass = 'root';
$charset = 'utf8mb4'; $charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset"; $dsn = "mysql:host=$host;dbname=$db;charset=$charset";