diff --git a/api/alumnos-cursos.php b/api/alumnos-cursos.php
new file mode 100644
index 0000000..32165b3
--- /dev/null
+++ b/api/alumnos-cursos.php
@@ -0,0 +1,115 @@
+ false, 'error' => 'No autenticado']);
+ exit;
+}
+
+$method = $_SERVER['REQUEST_METHOD'];
+$profesorId = $_SESSION['profesor']['id'] ?? null;
+
+try {
+ switch ($method) {
+ case 'GET':
+ $alumnoId = $_GET['alumno_id'] ?? null;
+
+ if ($alumnoId) {
+ // Obtener cursos de un alumno específico
+ $query = "SELECT c.* FROM cursos c
+ JOIN alumnos_cursos ac ON c.id = ac.curso_id
+ WHERE ac.alumno_id = ? AND c.profesor_id = ?";
+ $stmt = $pdo->prepare($query);
+ $stmt->execute([$alumnoId, $profesorId]);
+ } else {
+ // Obtener todos los alumnos
+ $query = "SELECT * FROM alumnos";
+ $stmt = $pdo->prepare($query);
+ $stmt->execute();
+ }
+
+ echo json_encode([
+ 'success' => true,
+ 'data' => $stmt->fetchAll(PDO::FETCH_ASSOC)
+ ]);
+ break;
+
+ case 'POST':
+ $data = json_decode(file_get_contents('php://input'), true);
+ $cursoId = $data['curso_id'] ?? null;
+ $alumnos = $data['alumnos'] ?? [];
+
+ if (empty($cursoId) || empty($alumnos)) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'error' => 'Datos incompletos']);
+ exit;
+ }
+
+ // Verificar que el curso pertenece al profesor
+ $stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?");
+ $stmt->execute([$cursoId, $profesorId]);
+
+ if (!$stmt->fetch()) {
+ http_response_code(403);
+ echo json_encode(['success' => false, 'error' => 'No autorizado']);
+ exit;
+ }
+
+ foreach ($alumnos as $alumnoId) {
+ $stmt = $pdo->prepare("
+ INSERT INTO alumnos_cursos (alumno_id, curso_id, estado)
+ VALUES (?, ?, 'cursando')
+ ON DUPLICATE KEY UPDATE estado = 'cursando'
+ ");
+ $stmt->execute([$alumnoId, $cursoId]);
+ }
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Alumnos asignados correctamente'
+ ]);
+ break;
+
+
+
+ case 'DELETE':
+ $alumnoId = $_GET['alumno_id'] ?? null;
+ $cursoId = $_GET['curso_id'] ?? null;
+
+ if (!$alumnoId || !$cursoId) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'error' => 'IDs requeridos']);
+ exit;
+ }
+
+ // Verificar que el curso pertenece al profesor
+ $stmt = $pdo->prepare("SELECT id FROM cursos WHERE id = ? AND profesor_id = ?");
+ $stmt->execute([$cursoId, $profesorId]);
+
+ if (!$stmt->fetch()) {
+ http_response_code(403);
+ echo json_encode(['success' => false, 'error' => 'No autorizado']);
+ exit;
+ }
+
+ // Eliminar asignación
+ $stmt = $pdo->prepare("DELETE FROM alumnos_cursos WHERE alumno_id = ? AND curso_id = ?");
+ $stmt->execute([$alumnoId, $cursoId]);
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Alumno desvinculado del curso correctamente'
+ ]);
+ break;
+
+ default:
+ http_response_code(405);
+ echo json_encode(['success' => false, 'error' => 'Método no permitido']);
+ }
+} catch (PDOException $e) {
+ http_response_code(500);
+ echo json_encode(['success' => false, 'error' => 'Error en la base de datos: ' . $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/api/alumnos.php b/api/alumnos.php
new file mode 100644
index 0000000..27adf55
--- /dev/null
+++ b/api/alumnos.php
@@ -0,0 +1,121 @@
+ false, 'error' => 'No autenticado']);
+ exit;
+}
+
+$method = $_SERVER['REQUEST_METHOD'];
+
+try {
+ switch ($method) {
+ case 'GET':
+ // Obtener todos los alumnos
+ $query = "SELECT * FROM alumnos ORDER BY nombre ASC";
+ $stmt = $pdo->prepare($query);
+ $stmt->execute();
+
+ $alumnos = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ echo json_encode([
+ 'success' => true,
+ 'data' => $alumnos,
+ 'count' => count($alumnos)
+ ]);
+ break;
+
+ case 'POST':
+ $data = json_decode(file_get_contents('php://input'), true);
+
+ if (empty($data['nombre']) || empty($data['email'])) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'error' => 'Nombre y email son requeridos']);
+ exit;
+ }
+
+ $stmt = $pdo->prepare("
+ INSERT INTO alumnos (nombre, email, telefono)
+ VALUES (?, ?, ?)
+ ");
+ $stmt->execute([
+ $data['nombre'],
+ $data['email'],
+ $data['telefono'] ?? null
+ ]);
+
+ echo json_encode([
+ 'success' => true,
+ 'id' => $pdo->lastInsertId(),
+ 'message' => 'Alumno creado exitosamente'
+ ]);
+ 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']
+ ]);
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Alumno actualizado exitosamente'
+ ]);
+ break;
+
+ case 'DELETE':
+ $id = $_GET['id'] ?? null;
+ if (!$id) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'error' => 'ID de alumno no proporcionado']);
+ exit;
+ }
+
+ // Verificar si el alumno está asignado a algún curso primero
+ $stmt = $pdo->prepare("SELECT COUNT(*) FROM alumnos_cursos WHERE alumno_id = ?");
+ $stmt->execute([$id]);
+ $tieneCursos = $stmt->fetchColumn();
+
+ if ($tieneCursos > 0) {
+ http_response_code(400);
+ echo json_encode(['success' => false, 'error' => 'No se puede eliminar, el alumno está asignado a cursos']);
+ exit;
+ }
+
+ $stmt = $pdo->prepare("DELETE FROM alumnos WHERE id = ?");
+ $stmt->execute([$id]);
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'Alumno eliminado exitosamente'
+ ]);
+ break;
+
+ default:
+ http_response_code(405);
+ echo json_encode(['success' => false, 'error' => 'Método no permitido']);
+ }
+} catch (PDOException $e) {
+ http_response_code(500);
+ echo json_encode(['success' => false, 'error' => 'Error en la base de datos: ' . $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/api/cursos.php b/api/cursos.php
index 7f30f4f..a29f6ea 100644
--- a/api/cursos.php
+++ b/api/cursos.php
@@ -9,30 +9,123 @@ if (!is_logged_in()) {
}
$method = $_SERVER['REQUEST_METHOD'];
+$profesorId = $_SESSION['profesor']['id'];
switch ($method) {
case 'GET':
- $stmt = $pdo->query("SELECT * FROM cursos");
- echo json_encode($stmt->fetchAll());
+ try {
+ $query = "SELECT * FROM cursos WHERE profesor_id = ?";
+ $stmt = $pdo->prepare($query);
+ $stmt->execute([$profesorId]);
+ echo json_encode($stmt->fetchAll());
+ } catch (PDOException $e) {
+ http_response_code(500);
+ echo json_encode(['error' => 'Error al cargar cursos']);
+ }
break;
case 'POST':
$data = json_decode(file_get_contents('php://input'), true);
- $stmt = $pdo->prepare("
- INSERT INTO cursos (nombre, tipo, competencias)
- VALUES (?, ?, ?)
- ");
- $stmt->execute([
- $data['nombre'],
- $data['tipo'],
- $data['competencias'] ?? null
- ]);
+ if (empty($data['nombre']) || empty($data['tipo'])) {
+ http_response_code(400);
+ echo json_encode(['error' => 'Nombre y tipo son requeridos']);
+ exit;
+ }
- echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
+ try {
+ $stmt = $pdo->prepare("
+ INSERT INTO cursos (nombre, descripcion, tipo, estado, profesor_id)
+ VALUES (?, ?, ?, 'activo', ?)
+ ");
+ $stmt->execute([
+ $data['nombre'],
+ $data['descripcion'] ?? null,
+ $data['tipo'],
+ $profesorId
+ ]);
+
+ echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
+ } catch (PDOException $e) {
+ http_response_code(500);
+ echo json_encode(['error' => 'Error al crear curso']);
+ }
+ 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;
+ }
+
+ 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;
+ }
+
+ $stmt = $pdo->prepare("
+ UPDATE cursos SET
+ nombre = ?,
+ descripcion = ?,
+ tipo = ?,
+ estado = ?
+ WHERE id = ?
+ ");
+ $stmt->execute([
+ $data['nombre'],
+ $data['descripcion'] ?? null,
+ $data['tipo'],
+ $data['estado'] ?? 'activo',
+ $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) {
+ http_response_code(400);
+ 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
+}
+?>
\ No newline at end of file
diff --git a/api/diploma.php b/api/diploma.php
new file mode 100644
index 0000000..c2a0cc2
--- /dev/null
+++ b/api/diploma.php
@@ -0,0 +1,44 @@
+ 'No autenticado']);
+ exit;
+}
+try {
+ $profesorId = $_GET['profesor_id'] ?? null;
+
+ $query = "
+ SELECT d.*, a.nombre AS alumno_nombre, c.nombre AS curso_nombre
+ FROM diplomas d
+ JOIN alumnos_cursos ac ON d.alumno_curso_id = ac.id
+ JOIN alumnos a ON ac.alumno_id = a.id
+ JOIN cursos c ON ac.curso_id = c.id
+ ";
+
+ $params = [];
+ if ($profesorId) {
+ $query .= " WHERE c.profesor_id = ?";
+ $params[] = $profesorId;
+ }
+
+ $query .= " ORDER BY d.fecha_emision DESC";
+
+ $stmt = $pdo->prepare($query);
+ $stmt->execute($params);
+
+ $diplomas = $stmt->fetchAll();
+
+ echo json_encode([
+ 'success' => true,
+ 'data' => $diplomas,
+ 'count' => count($diplomas)
+ ]);
+
+} catch (Exception $e) {
+ http_response_code(500);
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+}
+?>
\ No newline at end of file
diff --git a/api/login.php b/api/login.php
index b62b8f4..aa72057 100644
--- a/api/login.php
+++ b/api/login.php
@@ -2,80 +2,55 @@
header('Content-Type: application/json');
require '../includes/config.php';
-// Habilitar reporte de errores para depuración (quitar en producción)
error_reporting(E_ALL);
ini_set('display_errors', 1);
-// Verificar si la solicitud es POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Método no permitido']);
exit;
}
-// Obtener datos del formulario
-$username = trim($_POST['username'] ?? '');
+$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
-// Validaciones básicas
-if (empty($username) || empty($password)) {
- echo json_encode(['success' => false, 'message' => 'Usuario y contraseña son requeridos']);
+if (empty($email) || empty($password)) {
+ echo json_encode(['success' => false, 'message' => 'Email y contraseña son requeridos']);
exit;
}
try {
- // Buscar usuario en la base de datos
- $stmt = $pdo->prepare("SELECT * FROM usuarios WHERE username = ?");
- $stmt->execute([$username]);
- $user = $stmt->fetch();
+ // Buscar profesor en la base de datos
+ $stmt = $pdo->prepare("SELECT * FROM usuarios WHERE email = ? AND aprobado = 1");
+ $stmt->execute([$email]);
+ $profesor = $stmt->fetch();
- if (!$user) {
- echo json_encode(['success' => false, 'message' => 'Usuario no encontrado']);
+ if (!$profesor) {
+ echo json_encode(['success' => false, 'message' => 'Profesor no encontrado o no aprobado']);
exit;
}
- // Verificar contraseña
- if (!password_verify($password, $user['password'])) {
+ if (!password_verify($password, $profesor['password'])) {
echo json_encode(['success' => false, 'message' => 'Contraseña incorrecta']);
exit;
}
- // Obtener cursos del usuario
- $stmt = $pdo->prepare("
- SELECT c.*, uc.estado, uc.fecha_inicio, uc.fecha_fin, uc.profesor
- FROM usuario_cursos uc
- JOIN cursos c ON uc.curso_id = c.id
- WHERE uc.usuario_id = ?
- ");
- $stmt->execute([$user['id']]);
- $cursos = $stmt->fetchAll();
-
- // Configurar sesión de usuario
- $_SESSION['user'] = [
- 'id' => $user['id'],
- 'username' => $user['username'],
- 'nombre' => $user['nombre'],
- 'email' => $user['email'],
- 'rol' => $user['rol'],
- 'cursos' => $cursos
+ // Configurar sesión de profesor
+ $_SESSION['profesor'] = [
+ 'id' => $profesor['id'],
+ 'nombre' => $profesor['nombre'],
+ 'email' => $profesor['email']
];
- // Respuesta exitosa
echo json_encode([
- 'success' => true,
- 'redirect' => 'dashboard.php',
- 'user' => [
- 'id' => $user['id'],
- 'nombre' => $user['nombre'],
- 'rol' => $user['rol']
- ]
- ]);
+ 'success' => true,
+ 'redirect' => 'dashboard.php',
+ 'profesor_id' => $profesor['id']
+]);
+
} catch (PDOException $e) {
- // Registrar error en archivo de log
error_log('Error en login.php: ' . $e->getMessage());
-
- // Respuesta de error genérico (no mostrar detalles internos al usuario)
echo json_encode([
'success' => false,
'message' => 'Error en el servidor. Por favor, intente más tarde.'
diff --git a/assets/css/students-management.css b/assets/css/students-management.css
new file mode 100644
index 0000000..ce66b30
--- /dev/null
+++ b/assets/css/students-management.css
@@ -0,0 +1,77 @@
+/* Badges de cursos */
+.course-badges {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+
+.course-badge {
+ display: inline-flex;
+ align-items: center;
+ background-color: #e0e7ff;
+ color: #2563eb;
+ padding: 0.25rem 0.5rem;
+ border-radius: 12px;
+ font-size: 0.85rem;
+}
+
+.badge-remove {
+ background: none;
+ border: none;
+ color: #2563eb;
+ margin-left: 0.25rem;
+ cursor: pointer;
+ font-size: 1rem;
+ line-height: 1;
+ padding: 0 0.25rem;
+}
+
+.badge-remove:hover {
+ color: #1e40af;
+}
+
+/* Loader pequeño */
+.loader-sm {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ border: 2px solid #e2e8f0;
+ border-radius: 50%;
+ border-top-color: #2563eb;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* Modal de asignación */
+.form-select {
+ width: 100%;
+ padding: 0.75rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ margin-bottom: 1rem;
+ font-family: "Inter", sans-serif;
+}
+
+/* Header acciones */
+.header-actions {
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+}
+
+@media (max-width: 768px) {
+ .header-actions {
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ }
+
+ .search-box {
+ width: 100%;
+ }
+}
diff --git a/assets/css/styles.css b/assets/css/styles.css
index a2ab14d..f5512f6 100644
--- a/assets/css/styles.css
+++ b/assets/css/styles.css
@@ -1,21 +1,4 @@
-/* --- Nueva paleta de colores --- */
-:root {
- --primary-dark: #002b5c;
- --primary-main: #003f7d;
- --primary-light: #0066cc;
- --primary-lighter: #e1f0ff;
- --accent-main: #4facfe;
- --accent-light: #00f2fe;
- --text-primary: #1a1a1a;
- --text-secondary: #4a5568;
- --background: #f8fafc;
- --surface: #ffffff;
- --border: #e2e8f0;
- --error: #e53e3e;
- --success: #38a169;
-}
-
-/* --- Reset y tipografía --- */
+/* Reset y estilos base */
* {
box-sizing: border-box;
margin: 0;
@@ -23,74 +6,68 @@
}
body {
- font-family: 'Inter', sans-serif;
- color: var(--text-primary);
- background-color: var(--background);
- line-height: 1.6;
+ font-family: "Inter", sans-serif;
height: 100vh;
overflow: hidden;
}
-/* --- Login - Versión elegante --- */
+/* Estilos para el contenedor del login */
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
- background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary-main) 100%);
+ background: linear-gradient(135deg, #003f7d 0%, #0066cc 100%);
padding: 20px;
}
.login-card {
- background: var(--surface);
- border-radius: 12px;
+ background: white;
+ border-radius: 16px;
padding: 2.5rem;
width: 100%;
- max-width: 420px;
- box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
+ max-width: 380px;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
position: relative;
overflow: hidden;
- border: 1px solid rgba(255, 255, 255, 0.1);
}
.login-card::before {
- content: '';
+ content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
- height: 6px;
- background: linear-gradient(90deg, var(--accent-main) 0%, var(--accent-light) 100%);
+ height: 5px;
+ background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
}
.logo-container {
- text-align: center;
- margin-bottom: 2rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 1rem;
}
.logo-icon {
- width: 48px;
- height: 48px;
- color: var(--primary-light);
- margin: 0 auto 1rem;
- display: block;
+ width: 40px;
+ height: 40px;
+ color: #2563eb;
+ margin-right: 10px;
}
.login-card h1 {
- color: var(--primary-dark);
+ color: #1e293b;
margin: 0;
- font-size: 2rem;
- font-weight: 600;
- letter-spacing: -0.5px;
+ font-size: 1.8rem;
}
.welcome-text {
- color: var(--text-secondary);
+ color: #64748b;
text-align: center;
margin-bottom: 2rem;
- font-size: 1rem;
- font-weight: 400;
+ font-size: 0.95rem;
}
.input-group {
@@ -100,58 +77,76 @@ body {
.input-icon {
position: absolute;
- left: 16px;
+ left: 12px;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
- color: var(--text-secondary);
+ color: #94a3b8;
z-index: 2;
}
.login-card input {
width: 100%;
- padding: 0.85rem 0.85rem 0.85rem 48px;
- border: 1px solid var(--border);
+ padding: 0.75rem 0.75rem 0.75rem 40px;
+ border: 1px solid #e2e8f0;
border-radius: 8px;
- font-size: 1rem;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- background-color: var(--surface);
- color: var(--text-primary);
+ font-size: 0.95rem;
+ transition: all 0.3s ease;
+ background-color: #f8fafc;
}
.login-card input:focus {
- border-color: var(--primary-light);
- box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.15);
- outline: none;
+ border-color: #2563eb;
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
+ background-color: white;
}
.login-btn {
width: 100%;
- padding: 1rem;
+ padding: 0.85rem;
font-size: 1rem;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
- gap: 10px;
- margin-top: 1.5rem;
- background: linear-gradient(90deg, var(--primary-main) 0%, var(--primary-light) 100%);
- color: white;
+ gap: 8px;
+ margin-top: 1rem;
+ background: linear-gradient(90deg, #2563eb 0%, #1d4ed8 100%);
border: none;
border-radius: 8px;
cursor: pointer;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- box-shadow: 0 4px 6px rgba(0, 63, 125, 0.1);
+ transition: all 0.3s ease;
}
.login-btn:hover {
- background: linear-gradient(90deg, var(--primary-dark) 0%, var(--primary-main) 100%);
- transform: translateY(-2px);
- box-shadow: 0 6px 12px rgba(0, 63, 125, 0.15);
+ background: linear-gradient(90deg, #1d4ed8 0%, #1e40af 100%);
+ transform: translateY(-1px);
}
-/* --- Dashboard - Versión elegante --- */
+.btn-icon {
+ width: 20px;
+ height: 20px;
+}
+
+.footer-links {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 1.5rem;
+ font-size: 0.85rem;
+}
+
+.footer-links a {
+ color: #64748b;
+ text-decoration: none;
+ transition: color 0.2s ease;
+}
+
+.footer-links a:hover {
+ color: #2563eb;
+}
+
+/* Estilos para la aplicación principal */
#app-content {
display: flex;
flex-direction: column;
@@ -163,39 +158,65 @@ header {
display: flex;
justify-content: space-between;
align-items: center;
- padding: 1rem 2.5rem;
- background-color: var(--primary-dark);
+ padding: 1rem 2rem;
+ background-color: #003f7d;
color: white;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
position: relative;
- z-index: 10;
}
header h1 {
position: absolute;
left: 50%;
transform: translateX(-50%);
- font-weight: 600;
- font-size: 1.5rem;
- letter-spacing: 0.5px;
+}
+
+header h1 {
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
}
#user-info {
- margin-left: auto;
+ order: -1;
display: flex;
align-items: center;
- gap: 1.5rem;
+ gap: 1rem;
+}
+
+/* Estilos para la tabla en el dashboard */
+.stats {
+ margin-bottom: 1.5rem;
+}
+
+.stats p {
+ margin: 0.5rem 0;
+}
+
+/* Paginación */
+.pagination {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+.pagination-btn {
+ padding: 0.5rem 1rem;
+ min-width: 100px;
+}
+
+#page-info {
+ font-size: 0.9rem;
+ color: #64748b;
}
#current-user {
- font-weight: 500;
+ font-weight: 600;
background: rgba(255, 255, 255, 0.1);
- padding: 0.5rem 1.25rem;
+ padding: 0.5rem 1rem;
border-radius: 20px;
- font-size: 0.95rem;
- display: flex;
- align-items: center;
- gap: 0.5rem;
}
.main-container {
@@ -205,12 +226,11 @@ header h1 {
}
.sidebar {
- width: 280px;
- background-color: var(--surface);
- padding: 1.5rem 0;
- border-right: 1px solid var(--border);
+ width: 250px;
+ background-color: #f8fafc;
+ padding: 1.5rem 1rem;
+ border-right: 1px solid #e2e8f0;
overflow-y: auto;
- box-shadow: 2px 0 10px rgba(0, 0, 0, 0.03);
}
.sidebar-menu {
@@ -220,279 +240,723 @@ header h1 {
}
.sidebar-menu li {
- padding: 0.85rem 1.5rem;
- margin: 0.25rem 1rem;
- border-radius: 8px;
+ padding: 0.75rem 1rem;
+ margin-bottom: 0.5rem;
+ border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
- gap: 0.85rem;
- font-weight: 500;
- color: var(--text-secondary);
+ gap: 0.75rem;
}
.sidebar-menu li:hover {
- background-color: var(--primary-lighter);
- color: var(--primary-main);
+ background-color: #e0e7ff;
}
.sidebar-menu li.active {
- background-color: var(--primary-main);
+ background-color: #2563eb;
color: white;
}
-.sidebar-menu li svg {
- width: 20px;
- height: 20px;
-}
-
.content {
flex: 1;
- padding: 2.5rem;
+ padding: 2rem;
overflow-y: auto;
- background-color: var(--background);
+ background-color: #f9fafb;
+}
+
+.content-section {
+ display: none;
+}
+
+.content-section.active {
+ display: block;
}
.card {
- background: var(--surface);
- border-radius: 12px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
- padding: 2rem;
- margin-bottom: 2rem;
- border-left: none;
- transition: transform 0.3s ease, box-shadow 0.3s ease;
-}
-
-.card:hover {
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
- transform: translateY(-2px);
+ background: white;
+ border-left: 4px solid #2563eb;
+ border-radius: 8px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+ padding: 1.5rem;
+ margin-bottom: 1.5rem;
}
.card h2 {
margin-top: 0;
- margin-bottom: 1.5rem;
- color: var(--primary-dark);
- font-size: 1.5rem;
- font-weight: 600;
- padding-bottom: 0.75rem;
- border-bottom: 1px solid var(--border);
+ color: #003f7d;
+ font-size: 1.25rem;
+}
+
+.btn {
+ background-color: #2563eb;
+ color: white;
+ padding: 0.6rem 1.25rem;
+ border: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-weight: 600;
+ font-family: "Inter", sans-serif;
+ transition: background-color 0.2s ease;
+}
+
+.btn:hover {
+ background-color: #1d4ed8;
+}
+
+input,
+select {
+ width: 100%;
+ padding: 0.75rem;
+ margin-top: 0.5rem;
+ margin-bottom: 1rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ font-family: "Inter", sans-serif;
+}
+
+input:focus,
+select:focus {
+ outline: none;
+ border-color: #2563eb;
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
+}
+
+.oculto {
+ display: none;
+}
+
+/* Estilos para el contenido específico */
+.course-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 1.5rem;
+}
+
+.course-card {
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ padding: 1.25rem;
+ transition: transform 0.2s ease;
+}
+
+.course-card:hover {
+ transform: translateY(-2px);
+}
+
+.diploma-preview {
+ max-width: 600px;
+ margin: 0 auto;
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ text-align: center;
+}
+
+.diploma-preview img {
+ max-width: 100%;
+ height: auto;
+ margin: 1rem 0;
+}
+
+/* Estilos para el buscador y tabla */
+.search-container {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 20px;
+}
+
+.search-input {
+ flex: 1;
+ padding: 0.75rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ font-family: "Inter", sans-serif;
}
-/* --- Tablas mejoradas --- */
.table-container {
overflow-x: auto;
- margin-top: 1.5rem;
- border-radius: 8px;
- border: 1px solid var(--border);
+ margin-top: 20px;
}
.courses-table {
width: 100%;
border-collapse: collapse;
+ margin-bottom: 20px;
}
-.courses-table th, .courses-table td {
- padding: 1rem 1.25rem;
+.courses-table th,
+.courses-table td {
+ padding: 12px 15px;
text-align: left;
- border-bottom: 1px solid var(--border);
+ border-bottom: 1px solid #e2e8f0;
}
.courses-table th {
- background-color: var(--primary-lighter);
+ background-color: #f8fafc;
font-weight: 600;
- color: var(--primary-dark);
- text-transform: uppercase;
- font-size: 0.8rem;
- letter-spacing: 0.5px;
-}
-
-.courses-table tr:last-child td {
- border-bottom: none;
+ color: #003f7d;
}
.courses-table tr:hover {
- background-color: var(--primary-lighter);
-}
-
-/* --- Botones mejorados --- */
-.btn {
- background-color: var(--primary-main);
- color: white;
- padding: 0.75rem 1.5rem;
- border: none;
- border-radius: 8px;
- cursor: pointer;
- font-weight: 500;
- font-family: 'Inter', sans-serif;
- transition: all 0.2s ease;
- font-size: 0.95rem;
- display: inline-flex;
- align-items: center;
- gap: 0.5rem;
-}
-
-.btn:hover {
- background-color: var(--primary-dark);
- transform: translateY(-1px);
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
-}
-
-.btn svg {
- width: 16px;
- height: 16px;
+ background-color: #f5f7fa;
}
.download-btn {
- background-color: var(--success);
+ padding: 0.4rem 0.8rem;
+ font-size: 0.9rem;
+}
+
+.pagination {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 15px;
+ margin-top: 20px;
+}
+
+.pagination-btn {
+ padding: 0.5rem 1rem;
+}
+/* Mantén todos tus estilos anteriores y añade estos */
+
+/* Estilos para el dashboard */
+.admin .sidebar-menu li[data-section="courses"],
+.admin .sidebar-menu li[data-section="students"],
+.user .sidebar-menu li[data-section="my-courses"],
+.user .sidebar-menu li[data-section="diplomas"] {
+ display: none;
+}
+
+.admin .sidebar-menu li[data-section="courses"],
+.admin .sidebar-menu li[data-section="students"] {
+ display: flex;
+}
+
+.user .sidebar-menu li[data-section="my-courses"],
+.user .sidebar-menu li[data-section="diplomas"] {
+ display: flex;
+}
+
+/* Mejoras de responsive */
+@media (max-width: 768px) {
+ .main-container {
+ flex-direction: column;
+ }
+
+ .sidebar {
+ width: 100%;
+ height: auto;
+ }
+
+ .login-card {
+ width: 90%;
+ padding: 1.5rem;
+ }
+}
+/* Estilos adicionales */
+.loader {
+ text-align: center;
+ padding: 2rem;
+ font-style: italic;
+ color: #64748b;
+}
+
+.course-card {
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ padding: 1.25rem;
+ margin-bottom: 1rem;
+ transition: transform 0.2s ease;
+}
+
+.course-card:hover {
+ transform: translateY(-2px);
+}
+
+.course-card h3 {
+ margin-top: 0;
+ color: #003f7d;
+}
+
+.diploma-preview {
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ padding: 1.5rem;
+ margin-bottom: 1.5rem;
+ text-align: center;
+}
+
+.diploma-preview h3 {
+ color: #003f7d;
+}
+
+.search-container {
+ display: flex;
+ gap: 0.5rem;
+ margin-bottom: 1rem;
+}
+
+.search-input {
+ flex: 1;
+ padding: 0.75rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+}
+
+/* Añadir al final del archivo */
+
+/* Estilos para el header */
+header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem 2rem;
+}
+
+header h1 {
+ position: static;
+ transform: none;
+ margin: 0;
+}
+
+#user-info {
+ order: 1;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+/* Estilos para el enlace de cerrar sesión */
+.logout-link {
+ color: #333;
+ text-decoration: none;
+ padding: 0.75rem 1rem;
+ display: block;
+ transition: all 0.2s ease;
+}
+
+.logout-link:hover {
+ color: #e53e3e;
+ background-color: #fee2e2;
+}
+
+/* Estilos para la columna de acciones */
+.courses-table td:last-child {
+ text-align: center;
+}
+
+.download-btn {
+ padding: 0.4rem 0.8rem;
+ font-size: 0.9rem;
+ background-color: #38a169;
}
.download-btn:hover {
background-color: #2f855a;
}
-/* --- Formularios mejorados --- */
-input, select, textarea {
- width: 100%;
- padding: 0.85rem;
- margin-top: 0.5rem;
- margin-bottom: 1.25rem;
- border: 1px solid var(--border);
- border-radius: 8px;
- font-family: 'Inter', sans-serif;
- font-size: 1rem;
- transition: all 0.2s ease;
- background-color: var(--surface);
+.download-btn:disabled {
+ background-color: #a0aec0;
+ cursor: not-allowed;
}
-
-input:focus, select:focus, textarea:focus {
- outline: none;
- border-color: var(--primary-light);
- box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
-}
-
-label {
- font-weight: 500;
- color: var(--text-secondary);
- font-size: 0.95rem;
-}
-
-/* --- Efectos de transición --- */
-.content-section {
- transition: opacity 0.3s ease;
-}
-
-/* --- Responsive --- */
-@media (max-width: 1024px) {
- .sidebar {
- width: 240px;
- }
-
- .content {
- padding: 1.5rem;
- }
-}
-
-@media (max-width: 768px) {
- .main-container {
- flex-direction: column;
- }
-
- .sidebar {
- width: 100%;
- height: auto;
- padding: 1rem 0;
- }
-
- .sidebar-menu {
- display: flex;
- overflow-x: auto;
- padding: 0 1rem;
- }
-
- .sidebar-menu li {
- white-space: nowrap;
- }
-
- header h1 {
- position: static;
- transform: none;
- margin-right: auto;
- }
-
- #user-info {
- margin-left: 0;
- }
-}
-
-@media (max-width: 480px) {
- .login-card {
- padding: 1.75rem;
- }
-
- .card {
- padding: 1.5rem;
- }
-
- .content {
- padding: 1.25rem;
- }
-}
-
-.error-card {
- border-left: 4px solid var(--error);
-}
-
-.error-card h2 {
- color: var(--error);
-}
-
-.hidden {
- display: none;
-}
-
-.loading-spinner {
- border: 2px solid rgba(255, 255, 255, 0.3);
- border-radius: 50%;
- border-top: 2px solid white;
- width: 16px;
- height: 16px;
- animation: spin 1s linear infinite;
- display: inline-block;
- vertical-align: middle;
-}
-
-.loading-spinner.small {
- width: 12px;
- height: 12px;
- border-width: 1.5px;
-}
-
-@keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
-}
-
+/* Badges para tipos de curso */
.badge {
- display: inline-block;
- padding: 0.25rem 0.5rem;
+ padding: 4px 8px;
border-radius: 12px;
- font-size: 0.75rem;
+ font-size: 0.8em;
font-weight: 600;
text-transform: capitalize;
}
-.badge.pildora {
- background-color: #DBEAFE;
- color: #1E40AF;
+.badge.active {
+ background-color: #4caf50;
+ color: white;
}
-.badge.inyeccion {
- background-color: #D1FAE5;
- color: #065F46;
+.badge.inactive {
+ background-color: #f44336;
+ color: white;
}
-.badge.tratamiento {
- background-color: #E0E7FF;
- color: #3730A3;
-}
\ No newline at end of file
+.badge.type-inyeccion {
+ background-color: #2196f3;
+ color: white;
+}
+
+.badge.type-pildora {
+ background-color: #ff9800;
+ color: white;
+}
+
+.badge.type-tratamiento {
+ background-color: #9c27b0;
+ color: white;
+}
+/* Estilos para celdas de descripción */
+.description-cell {
+ max-width: 300px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.description-cell:hover {
+ white-space: normal;
+ overflow: visible;
+ position: relative;
+ z-index: 1;
+ background: white;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+}
+
+/* Estilos para mensaje de no datos */
+.no-data {
+ text-align: center;
+ padding: 20px;
+ color: #64748b;
+ font-style: italic;
+}
+
+/* Mejoras para textarea */
+textarea {
+ min-height: 100px;
+ resize: vertical;
+ font-family: "Inter", sans-serif;
+}
+
+/* Estilos para tarjetas de error */
+.error-card {
+ border-left-color: #e53e3e !important;
+}
+
+.error-card h2 {
+ color: #e53e3e;
+}
+/* ------------------------- */
+/* ESTILOS GESTIÓN ALUMNOS */
+/* ------------------------- */
+
+.students-management {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+/* Formulario */
+#studentForm {
+ margin-top: 1rem;
+}
+
+.form-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 1rem;
+ margin-bottom: 1rem;
+}
+
+.form-group {
+ margin-bottom: 0;
+}
+
+.form-group label {
+ display: block;
+ margin-bottom: 0.5rem;
+ font-weight: 500;
+ color: #334155;
+}
+
+.form-group input {
+ width: 100%;
+ padding: 0.75rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ font-family: "Inter", sans-serif;
+ transition: all 0.2s ease;
+}
+
+.form-group input:focus {
+ border-color: #2563eb;
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);
+ outline: none;
+}
+
+.form-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 0.75rem;
+ margin-top: 1rem;
+}
+
+/* Tabla */
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1.5rem;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.search-box {
+ position: relative;
+ min-width: 250px;
+}
+
+.search-box input {
+ padding: 0.5rem 1rem 0.5rem 2.5rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 20px;
+ width: 100%;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: 1rem center;
+ background-size: 1rem;
+}
+
+.table-responsive {
+ overflow-x: auto;
+ border-radius: 8px;
+ border: 1px solid #e2e8f0;
+}
+
+.students-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.95rem;
+}
+
+.students-table th {
+ background-color: #f8fafc;
+ padding: 0.75rem 1rem;
+ text-align: left;
+ font-weight: 600;
+ color: #334155;
+ border-bottom: 1px solid #e2e8f0;
+}
+
+.students-table td {
+ padding: 0.75rem 1rem;
+ border-bottom: 1px solid #e2e8f0;
+ vertical-align: middle;
+}
+
+.students-table tr:last-child td {
+ border-bottom: none;
+}
+
+.students-table tr:hover td {
+ background-color: #f8fafc;
+}
+
+/* Botones de acción */
+.action-buttons {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.btn-sm {
+ padding: 0.35rem 0.75rem;
+ font-size: 0.85rem;
+}
+
+.btn-edit {
+ background-color: #f59e0b;
+ color: white;
+}
+
+.btn-edit:hover {
+ background-color: #d97706;
+}
+
+/* Mensaje sin datos */
+.no-data {
+ text-align: center;
+ padding: 2rem;
+ color: #64748b;
+}
+
+.no-data svg {
+ margin-bottom: 1rem;
+ stroke-width: 1.5;
+}
+
+.no-data h3 {
+ color: #334155;
+ margin-bottom: 0.5rem;
+}
+
+.no-data p {
+ margin-bottom: 1.5rem;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .form-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .card-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .search-box {
+ width: 100%;
+ }
+
+ .action-buttons {
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ .action-buttons button {
+ width: 100%;
+ }
+}
+/* Modal styles */
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.modal {
+ background: white;
+ border-radius: 8px;
+ width: 90%;
+ max-width: 500px;
+ padding: 20px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.modal-header h3 {
+ margin: 0;
+}
+
+.close-btn {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+}
+
+.modal-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding-top: 1rem;
+ border-top: 1px solid #e2e8f0;
+ background-color: #f9fafb;
+}
+
+/* Toast styles */
+.toast {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ padding: 12px 20px;
+ border-radius: 4px;
+ color: white;
+ z-index: 1001;
+ animation: slideIn 0.3s ease-out;
+}
+
+.toast-success {
+ background-color: #4caf50;
+}
+
+.toast-error {
+ background-color: #f44336;
+}
+
+.toast-warning {
+ background-color: #ff9800;
+}
+
+.fade-out {
+ animation: fadeOut 0.3s ease-in;
+}
+
+@keyframes slideIn {
+ from {
+ transform: translateY(100px);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+@keyframes fadeOut {
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+.checkbox-group {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
+ gap: 0.5rem;
+ padding: 0.5rem;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ background-color: #f9fafb;
+}
+
+.checkbox-group label {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.3rem;
+ background-color: #ffffff;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.checkbox-group label:hover {
+ background-color: #e2e8f0;
+}
+
+.checkbox-group input[type="checkbox"] {
+ accent-color: #2563eb;
+}
+
+.course-badges {
+ display: flex;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+}
+
+.course-badge {
+ background-color: #e0e7ff;
+ color: #2563eb;
+ padding: 0.25rem 0.5rem;
+ border-radius: 12px;
+ font-size: 0.85rem;
+ font-weight: 500;
+}
diff --git a/assets/js/main.js b/assets/js/main.js
index 9e4416d..94fff68 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1,587 +1,1188 @@
-document.addEventListener('DOMContentLoaded', function() {
- const loginForm = document.getElementById('loginForm');
+document.addEventListener("DOMContentLoaded", function () {
+ // Manejo del formulario de login
+ const loginForm = document.getElementById("loginForm");
if (loginForm) {
- loginForm.addEventListener('submit', handleLogin);
- } else {
- initDashboard(); // Nombre de función consistente
+ loginForm.addEventListener("submit", handleLogin);
+ }
+
+ // Configuración inicial del dashboard
+ if (document.body.classList.contains("admin")) {
+ initializeDashboard();
}
});
-function initDashboard() {
- // Configuración inicial
- setupSidebarNavigation();
- loadInitialData();
-
- // Mostrar sección activa
- const activeSection = document.querySelector('.sidebar-menu li.active');
- if (activeSection) {
- const sectionId = activeSection.dataset.section;
- showSection(sectionId, true);
- }
-
- // Configurar tooltips para botones
- initTooltips();
-
- // Configurar eventos globales
- document.addEventListener('click', handleGlobalEvents);
-}
-
+// Función para manejar el login
function handleLogin(e) {
e.preventDefault();
const formData = new FormData(this);
-
+
fetch(this.action, {
- method: 'POST',
- body: formData
+ 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));
+ .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();
-function setupSidebarNavigation() {
- const menuItems = document.querySelectorAll('.sidebar-menu li:not(.logout-link)');
-
- menuItems.forEach(item => {
- item.addEventListener('click', function() {
- // Agregar efecto visual al hacer clic
- this.classList.add('click-feedback');
- setTimeout(() => this.classList.remove('click-feedback'), 200);
-
- const section = this.dataset.section;
- showSection(section);
- });
- });
-}
-
-async function loadInitialData() {
- try {
- // Mostrar skeleton loading
- document.querySelectorAll('.stats p span').forEach(span => {
- span.innerHTML = '';
- });
-
- const [courses, users] = await Promise.all([
- fetchData('api/cursos.php'),
- fetchData('api/usuarios.php')
- ]);
-
- updateStats(courses, users);
- } catch (error) {
- console.error('Error loading initial data:', error);
- showToast('Error al cargar datos iniciales', 'error');
+ const activeSection = document.querySelector(".sidebar-menu li.active");
+ if (activeSection) {
+ const sectionId = activeSection.getAttribute("data-section");
+ showSection(sectionId, true);
}
}
-function updateStats(courses = [], users = []) {
- // Efecto de conteo animado
- animateValue('active-courses-count', 0, courses.length, 1000);
- animateValue('students-count', 0, users.length, 1000);
-
- const approvedCourses = courses.filter(c => c.estado === 'Aprobado');
- animateValue('diplomas-count', 0, approvedCourses.length, 1000);
+// 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);
+ });
+ }
+ });
}
-async function showSection(sectionId, isInitialLoad = false) {
- updateActiveMenu(sectionId);
- const sectionElement = getSectionElement(sectionId);
-
- if (sectionElement) {
- toggleSections(sectionElement);
-
- if (!(isInitialLoad && sectionId === 'dashboard')) {
- await loadDynamicContent(sectionId, sectionElement);
+// 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');
+ 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');
+
+ 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
';
}
}
-function getSectionElement(sectionId) {
- return document.getElementById(`${sectionId}-content`);
-}
+// Carga del dashboard
+async function loadDashboardContent(container, forceReload = false) {
+ try {
+ // Mostrar loader
+ container.innerHTML = 'Cargando datos...
';
-function toggleSections(activeSection) {
- document.querySelectorAll('.content-section').forEach(section => {
- section.classList.remove('active');
- });
- activeSection.classList.add('active');
-}
+ // 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;
-async function loadDynamicContent(sectionId, container) {
- // Mostrar skeleton loading
container.innerHTML = `
-
- `;
-
- try {
- switch(sectionId) {
- case 'courses':
- await loadCoursesSection(container);
- break;
- case 'students':
- await loadStudentsSection(container);
- break;
- case 'diplomas':
- await loadDiplomasSection(container);
- break;
- default:
- container.innerHTML = 'Sección no implementada
';
- }
- } catch (error) {
- console.error(`Error loading ${sectionId} section:`, error);
- container.innerHTML = `
-
-
Error al cargar contenido
-
No se pudo cargar la información solicitada.
-
-
- `;
- }
-}
-
-// Funciones para cargar contenido específico
-async function loadCoursesSection(container) {
- try {
- const courses = await fetchData('api/cursos.php');
-
- // Verificar si courses es un array
- if (!Array.isArray(courses)) {
- throw new Error('Formato de datos inválido');
- }
-
- container.innerHTML = `
-
-
-
Lista de Cursos
-
-
+
+
+
+
Resumen:
+
• ${
+ activeCourses.length
+ } cursos activos
+
• ${totalStudents} alumnos registrados
+
• ${totalDiplomas} diplomas emitidos
+
-
-
-
-
- Nombre |
- Tipo |
- Competencias |
- Acciones |
-
-
-
- ${generateCoursesTableRows(courses)}
-
-
-
-
- `;
-
- setupCourseForm();
- } catch (error) {
- console.error('Error loading courses:', error);
- container.innerHTML = `
-
-
Error al cargar los cursos
-
${error.message || 'No se pudieron cargar los cursos'}
-
-
- `;
- }
-}
-function formatCourseType(type) {
- const types = {
- 'pildora': 'Píldora',
- 'inyeccion': 'Inyección',
- 'tratamiento': 'Tratamiento'
- };
- return types[type] || type;
+
+
Mis Cursos Activos
+ ${renderCoursesPreview(activeCourses)}
+ `;
}
-function loadStudentsSection(container) {
- fetch('api/usuarios.php')
- .then(response => response.json())
- .then(users => {
+// Gestión de cursos
+function loadProfessorCourses(container) {
+ fetch(`api/cursos.php?profesor_id=${getProfesorId()}`)
+ .then((response) => response.json())
+ .then((courses) => {
container.innerHTML = `
-
Gestión de Estudiantes
-
-
-
-
+
Mis Cursos
+
+
+
+
Lista de Cursos
Nombre |
- Usuario |
- Email |
- Cursos Inscritos |
-
-
-
- ${generateStudentsTableRows(users)}
-
-
-
-
`;
-
- setupStudentSearch();
- })
- .catch(error => {
- container.innerHTML = '
Error al cargar los estudiantes
';
- console.error('Error:', error);
- });
-}
-
-function loadDiplomasSection(container) {
- fetch('api/cursos.php')
- .then(response => response.json())
- .then(courses => {
- const approvedCourses = courses.filter(c => c.estado === 'Aprobado');
-
- container.innerHTML = `
-
-
Diplomas Emitidos
-
-
-
-
-
-
-
-
- Curso |
- Estudiante |
- Fecha de Aprobación |
+ Descripción |
+ Tipo |
+ Estado |
Acciones |
-
- ${generateDiplomasTableRows(approvedCourses)}
+
+ ${courses
+ .map(
+ (course) => `
+
+ ${course.nombre} |
+ ${
+ course.descripcion || "-"
+ } |
+ ${course.tipo} |
+ ${course.estado} |
+
+
+
+
+
+ |
+
+ `
+ )
+ .join("")}
`;
-
- setupDiplomaSearch();
- })
- .catch(error => {
- container.innerHTML = '
Error al cargar los diplomas
';
- console.error('Error:', error);
+
+ setupCourseForm();
});
}
-// Funciones auxiliares para generar contenido
-function generateCoursesTableRows(courses = []) {
- return courses.map(course => `
-
- ${course.nombre} |
- ${formatCourseType(course.tipo)} |
- ${course.competencias || '-'} |
-
-
-
- |
-
- `).join('');
-}
-
-/**
- * Muestra un toast notification
- */
-function showToast(message, type = 'info') {
- const toast = document.createElement('div');
- toast.className = `toast-notification ${type}`;
- toast.textContent = message;
- document.body.appendChild(toast);
-
- setTimeout(() => {
- toast.classList.add('fade-out');
- setTimeout(() => toast.remove(), 300);
- }, 3000);
-}
-
-function generateStudentsTableRows(users) {
- return users.map(user => `
-
- ${user.nombre} |
- ${user.username} |
- ${user.email} |
- ${user.cursos ? user.cursos.length : 0} |
-
- `).join('');
-}
-
-function generateDiplomasTableRows(courses) {
- return courses.map(course => `
-
- ${course.nombre} |
- ${course.estudiante || 'N/A'} |
- ${course.fecha_fin || 'N/A'} |
-
-
- |
-
- `).join('');
-}
-function animateValue(id, start, end, duration) {
- const element = document.getElementById(id);
- if (!element) return;
-
- const range = end - start;
- const startTime = Date.now();
- const endTime = startTime + duration;
-
- const update = () => {
- const now = Date.now();
- const progress = Math.min((now - startTime) / duration, 1);
- const value = Math.floor(start + progress * range);
- element.textContent = value.toLocaleString();
-
- if (now < endTime) {
- requestAnimationFrame(update);
- }
- };
-
- update();
-}
-
-
-// Configuración de eventos dinámicos
function setupCourseForm() {
- const courseTypeSelect = document.getElementById('courseType');
- if (courseTypeSelect) {
- courseTypeSelect.addEventListener('change', function() {
- const competencesField = document.getElementById('competencesField');
- competencesField.classList.toggle('hidden', this.value !== 'tratamiento');
- });
- }
-
- const courseForm = document.getElementById('courseForm');
- if (courseForm) {
- courseForm.addEventListener('submit', async function(e) {
- e.preventDefault();
-
- const submitBtn = this.querySelector('button[type="submit"]');
- const originalText = submitBtn.innerHTML;
- submitBtn.innerHTML = '
';
- submitBtn.disabled = true;
-
- try {
- const formData = new FormData(this);
- const jsonData = Object.fromEntries(formData.entries());
-
- const response = await fetch('api/cursos.php', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(jsonData)
- });
-
- const data = await response.json();
-
- if (data.success) {
- showToast('Curso creado exitosamente', 'success');
- await loadCoursesSection(document.getElementById('courses-content'));
- } else {
- showToast('Error al crear el curso', 'error');
- }
- } catch (error) {
- console.error('Error:', error);
- showToast('Error en la conexión', 'error');
- } finally {
- submitBtn.innerHTML = originalText;
- submitBtn.disabled = false;
- }
- });
- }
-}
+ const form = document.getElementById("courseForm");
+ if (!form) return;
-function setupStudentSearch() {
- const searchButton = document.getElementById('searchStudentButton');
- if (searchButton) {
- searchButton.addEventListener('click', function() {
- const searchTerm = document.getElementById('studentSearch').value.toLowerCase();
- const tableBody = document.getElementById('students-list-body');
-
- fetch('api/usuarios.php')
- .then(response => response.json())
- .then(users => {
- const filteredUsers = users.filter(user =>
- user.nombre.toLowerCase().includes(searchTerm) ||
- user.username.toLowerCase().includes(searchTerm) ||
- user.email.toLowerCase().includes(searchTerm)
- );
-
- tableBody.innerHTML = generateStudentsTableRows(filteredUsers);
- });
- });
- }
-}
+ form.addEventListener("submit", function (e) {
+ e.preventDefault();
+ const formData = new FormData(this);
+ const jsonData = {
+ nombre: formData.get("nombre"),
+ descripcion: formData.get("descripcion"),
+ tipo: formData.get("tipo"),
+ profesor_id: getProfesorId(),
+ };
-function setupDiplomaSearch() {
- const searchButton = document.getElementById('searchDiplomaButton');
- if (searchButton) {
- searchButton.addEventListener('click', function() {
- const searchTerm = document.getElementById('diplomaSearch').value.toLowerCase();
- const tableBody = document.getElementById('diplomas-list-body');
-
- fetch('api/cursos.php')
- .then(response => response.json())
- .then(courses => {
- const approvedCourses = courses.filter(c => c.estado === 'Aprobado');
- const filteredCourses = approvedCourses.filter(course =>
- course.nombre.toLowerCase().includes(searchTerm) ||
- (course.estudiante && course.estudiante.toLowerCase().includes(searchTerm))
- );
-
- tableBody.innerHTML = generateDiplomasTableRows(filteredCourses);
- });
- });
- }
-
- // Configurar eventos de descarga de diplomas
- document.querySelectorAll('.download-btn').forEach(btn => {
- btn.addEventListener('click', function() {
- const courseId = this.getAttribute('data-course-id');
-
- // Mostrar mensaje de carga
- const originalText = this.innerHTML;
- this.innerHTML = '
Generando diploma...';
- this.disabled = true;
-
- // Abrir en nueva pestaña
- window.open(`certificado.php?course_id=${courseId}`, '_blank');
-
- // Restaurar botón después de un breve retraso
- setTimeout(() => {
- this.innerHTML = originalText;
- this.disabled = false;
- }, 2000);
- });
+ const isEdit = form.dataset.editing === "true";
+ const courseId = form.dataset.courseId;
+
+ if (isEdit) {
+ updateCourse(courseId, jsonData);
+ } else {
+ createCourse(jsonData);
+ }
});
}
-function handleGlobalEvents(e) {
- // Delegación de eventos para mejor performance
- if (e.target.closest('.edit-btn')) {
- const courseId = e.target.closest('.edit-btn').dataset.id;
- handleEditCourse(courseId);
- }
-
- if (e.target.closest('.delete-btn')) {
- const courseId = e.target.closest('.delete-btn').dataset.id;
- handleDeleteCourse(courseId);
- }
-
- if (e.target.closest('.download-btn')) {
- const courseId = e.target.closest('.download-btn').dataset.id;
- handleDownloadDiploma(courseId);
+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((courses) => {
+ const course = courses.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.dataset.editing = "true";
+ form.dataset.courseId = id;
+ form.querySelector("button").textContent = "Actualizar Curso";
+ 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;
+
+ form.addEventListener("submit", function (e) {
+ e.preventDefault();
+ submitStudentForm();
+ });
+
+ // Configurar búsqueda
+ const searchInput = document.getElementById("studentSearch");
+ if (searchInput) {
+ searchInput.addEventListener("input", function () {
+ filterStudents(this.value.toLowerCase());
+ });
}
}
-function handleDownloadDiploma(courseId) {
- const btn = document.querySelector(`.download-btn[data-course-id="${courseId}"]`);
- const originalText = btn.innerHTML;
-
- btn.innerHTML = '
';
- btn.disabled = true;
-
- // Simular generación de diploma
- setTimeout(() => {
- window.open(`certificado.php?course_id=${courseId}`, '_blank');
- btn.innerHTML = originalText;
- btn.disabled = false;
- showToast('Diploma generado con éxito', 'success');
- }, 1500);
+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"),
+ };
+
+ const isEdit = form.dataset.editing === "true";
+ const studentId = form.dataset.studentId;
+
+ const url = "api/alumnos.php" + (isEdit ? "" : "");
+ 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;
+ });
}
-async function fetchData(url, options = {}) {
+
+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";
+
+ form.scrollIntoView({ behavior: "smooth" });
+ })
+ .catch((error) => {
+ showToast("error", error.message || "Error al cargar alumno");
+ });
+};
+
+window.deleteStudent = function (id) {
+ 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 `
+
+
+
+
+
+
+ Nombre |
+ Email |
+ Teléfono |
+ Cursos |
+ Acciones |
+
+
+
+ ${alumnos
+ .map(
+ (alumno) => `
+
+ ${alumno.nombre} |
+ ${alumno.email} |
+ ${alumno.telefono || "-"} |
+
+
+ ${renderCourseBadges(alumno.id)}
+
+ |
+
+
+
+
+
+ |
+
+ `
+ )
+ .join("")}
+
+
+
+
`;
+}
+
+function renderCourseBadges(alumnoId) {
+ return '
'; // Se cargará dinámicamente
+}
+
+async function loadStudentCourses(alumnoId) {
try {
- const response = await fetch(url, options);
-
- if (!response.ok) {
- throw new Error(`Error HTTP: ${response.status}`);
- }
-
+ const response = await fetch(
+ `api/alumnos-cursos.php?alumno_id=${alumnoId}`
+ );
const data = await response.json();
-
- // Verificar si la respuesta es válida
- if (data === null || data === undefined) {
- throw new Error('Respuesta vacía del servidor');
+
+ if (data.success) {
+ const container = document.getElementById(`courses-${alumnoId}`);
+ if (container) {
+ container.innerHTML =
+ data.data
+ .map(
+ (curso) => `
+
+ ${curso.nombre}
+
+
+ `
+ )
+ .join("") || '
Sin cursos';
+ }
}
-
- return data;
} catch (error) {
- console.error(`Error fetching data from ${url}:`, error);
- throw error; // Re-lanzamos el error para manejarlo en el nivel superior
+ console.error("Error loading student courses:", error);
}
-}
\ No newline at end of file
+}
+
+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 = `
+
+
+
+
+
+
+ `;
+
+ // 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
+
+
+
+
+ Alumno |
+ Email |
+ Curso |
+ Tipo |
+ Fecha |
+ Código |
+ Acciones |
+
+
+
+ ${data.data
+ .map(
+ (diploma) => `
+
+ ${diploma.alumno_nombre} |
+ ${diploma.alumno_email} |
+ ${diploma.curso_nombre} |
+ ${diploma.curso_tipo} |
+ ${diploma.fecha_formateada} |
+ ${diploma.codigo_unico} |
+
+
+
+ |
+
+ `
+ )
+ .join("")}
+
+
+
+
`;
+ })
+ .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 `
+
+
+
+
+ Nombre |
+ Tipo |
+ Estado |
+
+
+
+ ${courses
+ .map(
+ (course) => `
+
+ ${course.nombre || "Sin nombre"} |
+ ${
+ course.tipo || "N/A"
+ } |
+ ${course.estado || "N/A"} |
+
+ `
+ )
+ .join("")}
+
+
+
`;
+}
+
+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/dashboard.php b/dashboard.php
index 06ff4ee..b8f00c8 100644
--- a/dashboard.php
+++ b/dashboard.php
@@ -2,7 +2,36 @@
include 'includes/config.php';
redirect_if_not_logged_in();
-$user = $_SESSION['user'];
+
+$profesor = $_SESSION['profesor'];
+
+// Obtener estadísticas directamente desde PHP
+$stmt = $pdo->prepare("
+ SELECT COUNT(*) as total FROM cursos
+ WHERE profesor_id = ? AND estado = 'activo'
+");
+$stmt->execute([$profesor['id']]);
+$cursos_activos = $stmt->fetch()['total'];
+
+$stmt = $pdo->prepare("
+ SELECT COUNT(DISTINCT a.id) as total
+ FROM alumnos a
+ JOIN alumnos_cursos ac ON a.id = ac.alumno_id
+ JOIN cursos c ON ac.curso_id = c.id
+ WHERE c.profesor_id = ?
+");
+$stmt->execute([$profesor['id']]);
+$alumnos_registrados = $stmt->fetch()['total'];
+
+$stmt = $pdo->prepare("
+ SELECT COUNT(*) as total
+ FROM diplomas d
+ JOIN alumnos_cursos ac ON d.alumno_curso_id = ac.id
+ JOIN cursos c ON ac.curso_id = c.id
+ WHERE c.profesor_id = ?
+");
+$stmt->execute([$profesor['id']]);
+$diplomas_emitidos = $stmt->fetch()['total'];
?>
@@ -11,24 +40,25 @@ $user = $_SESSION['user'];
DiploMaster - Panel
-
-
+
@@ -36,13 +66,13 @@ $user = $_SESSION['user'];
-
Panel de Administración
-
Bienvenido al sistema de gestión de DiploMaster
+
Bienvenido = htmlspecialchars($profesor['nombre']) ?>
+
Este es tu panel de gestión de DiploMaster
-
Estadísticas:
-
• 0 cursos activos
-
• 0 estudiantes registrados
-
• 0 diplomas emitidos
+
Resumen:
+
• = $cursos_activos ?> cursos activos
+
• = $alumnos_registrados ?> alumnos registrados
+
• = $diplomas_emitidos ?> diplomas emitidos
diff --git a/hash.php b/hash.php
new file mode 100644
index 0000000..9695f31
--- /dev/null
+++ b/hash.php
@@ -0,0 +1,14 @@
+";
+echo "Hash generado: " . $hash . "
";
+
+// Verificación
+if (password_verify($contraseña, $hash)) {
+ echo "✅ La contraseña coincide con el hash!";
+} else {
+ echo "❌ La contraseña NO coincide";
+}
+?>
\ No newline at end of file
diff --git a/includes/config.php b/includes/config.php
index 930bb62..acd4194 100644
--- a/includes/config.php
+++ b/includes/config.php
@@ -21,7 +21,7 @@ try {
}
function is_logged_in() {
- return isset($_SESSION['user']);
+ return isset($_SESSION['profesor']);
}
function redirect_if_not_logged_in() {
@@ -29,5 +29,4 @@ function redirect_if_not_logged_in() {
header('Location: index.php');
exit;
}
-}
-?>
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/index.php b/index.php
index 73ec632..cda4885 100644
--- a/index.php
+++ b/index.php
@@ -17,14 +17,14 @@
DiploMaster
-
Bienvenido al sistema de diplomas
+
Sistema de gestión de diplomas para profesores
diff --git a/sql/diplomaster (1).sql b/sql/diplomaster (1).sql
index fb22b48..daac7be 100644
--- a/sql/diplomaster (1).sql
+++ b/sql/diplomaster (1).sql
@@ -1,94 +1,75 @@
+-- Crear la base de datos
+CREATE DATABASE IF NOT EXISTS diplomaster;
+USE diplomaster;
-
-SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
-START TRANSACTION;
-SET time_zone = "+00:00";
-
-CREATE database diplomaster;
-use diplomaster;
-
-
-CREATE TABLE `cursos` (
- `id` int(11) NOT NULL,
- `nombre` varchar(100) NOT NULL,
- `tipo` enum('pildora','inyeccion','tratamiento') NOT NULL,
- `competencias` text DEFAULT NULL,
- `fecha_creacion` timestamp NOT NULL DEFAULT current_timestamp()
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
-
-
-INSERT INTO `cursos` (`id`, `nombre`, `tipo`, `competencias`, `fecha_creacion`) VALUES
-(1, 'Seguridad Informática', 'tratamiento', 'Análisis de datos, Comunicación efectiva', '2025-05-05 01:27:58'),
-(2, 'Introducción a Python', 'pildora', NULL, '2025-05-05 01:27:58'),
-(3, 'Machine Learning Avanzado', 'tratamiento', 'Modelado predictivo, Python', '2025-05-05 01:27:58');
-
-
-
+-- Tabla de usuarios (profesores)
CREATE TABLE `usuarios` (
- `id` int(11) NOT NULL,
- `username` varchar(50) NOT NULL,
- `password` varchar(255) NOT NULL,
- `nombre` varchar(100) NOT NULL,
- `email` varchar(100) NOT NULL,
- `rol` enum('admin','user') NOT NULL DEFAULT 'user',
- `fecha_registro` timestamp NOT NULL DEFAULT current_timestamp()
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `nombre` VARCHAR(100) NOT NULL,
+ `email` VARCHAR(100) UNIQUE NOT NULL,
+ `password` VARCHAR(255) NOT NULL,
+ `aprobado` BOOLEAN NOT NULL DEFAULT 1,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+-- Tabla de cursos
+CREATE TABLE `cursos` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `nombre` VARCHAR(100) NOT NULL,
+ `descripcion` VARCHAR(250) DEFAULT NULL,
+ `estado` ENUM('activo', 'completado', 'archivado') NOT NULL DEFAULT 'activo',
+ `tipo` ENUM('inyeccion', 'pildora', 'tratamiento') NOT NULL,
+ `profesor_id` INT(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`profesor_id`) REFERENCES `usuarios`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+-- Tabla de alumnos
+CREATE TABLE `alumnos` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `nombre` VARCHAR(100) NOT NULL,
+ `email` VARCHAR(100) UNIQUE NOT NULL,
+ `telefono` VARCHAR(15) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-INSERT INTO `usuarios` (`id`, `username`, `password`, `nombre`, `email`, `rol`, `fecha_registro`) VALUES
-(1, 'admin', '$2y$10$H3tCRUt444g0jo996uiKXenINy2d84FfuwQhoDBfa3tNblZLtNZpK', 'Administrador', 'admin@diplomaster.com', 'admin', '2025-05-05 01:27:58'),
-(2, 'usuario1', '$2y$10$SjyU29E200ax73/m0NTjqe0sMLmpsPBThlIUucGKyxvqh/znHiwh.', 'Juan Pérez', 'juan@example.com', 'user', '2025-05-05 01:27:58');
+-- Tabla para vincular alumnos con cursos
+CREATE TABLE `alumnos_cursos` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `alumno_id` INT(11) NOT NULL,
+ `curso_id` INT(11) NOT NULL,
+ `estado` ENUM('cursando', 'aprobado', 'reprobado') NOT NULL DEFAULT 'cursando',
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`alumno_id`) REFERENCES `alumnos`(`id`) ON DELETE CASCADE,
+ FOREIGN KEY (`curso_id`) REFERENCES `cursos`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+-- Tabla de diplomas
+CREATE TABLE `diplomas` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `alumno_curso_id` INT(11) NOT NULL,
+ `codigo_unico` VARCHAR(20) NOT NULL UNIQUE,
+ `fecha_emision` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`),
+ FOREIGN KEY (`alumno_curso_id`) REFERENCES `alumnos_cursos`(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+-- Datos iniciales
+INSERT INTO `usuarios` (`nombre`, `email`, `password`, `aprobado`) VALUES
+('Profesor Demo', 'profesor@demo.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 1);
-CREATE TABLE `usuario_cursos` (
- `id` int(11) NOT NULL,
- `usuario_id` int(11) NOT NULL,
- `curso_id` int(11) NOT NULL,
- `estado` enum('En progreso','Aprobado','Completado') NOT NULL DEFAULT 'En progreso',
- `fecha_inicio` date DEFAULT NULL,
- `fecha_fin` date DEFAULT NULL,
- `profesor` varchar(100) DEFAULT NULL
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+INSERT INTO `cursos` (`nombre`, `descripcion`, `estado`, `tipo`, `profesor_id`) VALUES
+('Curso Básico', 'Curso introductorio', 'activo', 'inyeccion', 1),
+('Curso Avanzado', 'Para alumnos avanzados', 'activo', 'tratamiento', 1);
+INSERT INTO `alumnos` (`nombre`, `email`, `telefono`) VALUES
+('Alumno Uno', 'alumno1@demo.com', '123456789'),
+('Alumno Dos', 'alumno2@demo.com', '987654321');
+INSERT INTO `alumnos_cursos` (`alumno_id`, `curso_id`, `estado`) VALUES
+(1, 1, 'aprobado'),
+(2, 1, 'cursando'),
+(1, 2, 'cursando');
-INSERT INTO `usuario_cursos` (`id`, `usuario_id`, `curso_id`, `estado`, `fecha_inicio`, `fecha_fin`, `profesor`) VALUES
-(1, 2, 1, 'Aprobado', '2023-01-15', '2023-04-20', 'Dra. Ana López'),
-(2, 2, 2, 'En progreso', '2023-05-10', NULL, 'Prof. Carlos Ruiz');
-
-
-
-ALTER TABLE `cursos`
- ADD PRIMARY KEY (`id`);
-
-
-ALTER TABLE `usuarios`
- ADD PRIMARY KEY (`id`),
- ADD UNIQUE KEY `username` (`username`),
- ADD UNIQUE KEY `email` (`email`);
-
-ALTER TABLE `usuario_cursos`
- ADD PRIMARY KEY (`id`),
- ADD KEY `usuario_id` (`usuario_id`),
- ADD KEY `curso_id` (`curso_id`);
-
-
-ALTER TABLE `cursos`
- MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
-
-
-ALTER TABLE `usuarios`
- MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
-
-
-ALTER TABLE `usuario_cursos`
- MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
-
-
-ALTER TABLE `usuario_cursos`
- ADD CONSTRAINT `usuario_cursos_ibfk_1` FOREIGN KEY (`usuario_id`) REFERENCES `usuarios` (`id`),
- ADD CONSTRAINT `usuario_cursos_ibfk_2` FOREIGN KEY (`curso_id`) REFERENCES `cursos` (`id`);
-COMMIT;
+INSERT INTO `diplomas` (`alumno_curso_id`, `codigo_unico`) VALUES
+(1, 'DIPL-000001');
\ No newline at end of file