This commit is contained in:
luis.aguilar 2025-05-14 15:01:09 -06:00
parent 6b1d644adc
commit 2b862b74c2
10 changed files with 955 additions and 552 deletions

64
api/alumnos.php Normal file
View File

@ -0,0 +1,64 @@
<?php
header('Content-Type: application/json');
require '../includes/config.php';
if (!is_logged_in()) {
http_response_code(401);
echo json_encode(['error' => 'No autenticado']);
exit;
}
$method = $_SERVER['REQUEST_METHOD'];
switch ($method) {
case 'GET':
try {
$stmt = $pdo->query("
SELECT a.*,
GROUP_CONCAT(ac.curso_id) AS cursos
FROM alumnos a
LEFT JOIN alumnos_cursos ac ON a.id = ac.alumno_id
GROUP BY a.id
");
$alumnos = $stmt->fetchAll();
// Convertir cursos a array
foreach ($alumnos as &$alumno) {
$alumno['cursos'] = $alumno['cursos'] ? explode(',', $alumno['cursos']) : [];
}
echo json_encode($alumnos);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Error al cargar alumnos']);
}
break;
case 'POST':
$data = json_decode(file_get_contents('php://input'), true);
try {
$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()]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Error al crear alumno: ' . $e->getMessage()]);
}
break;
default:
http_response_code(405);
echo json_encode(['error' => 'Método no permitido']);
}
?>

View File

@ -9,44 +9,91 @@ if (!is_logged_in()) {
}
$method = $_SERVER['REQUEST_METHOD'];
$user = $_SESSION['user'];
$profesorId = $_SESSION['profesor']['id'] ?? null;
switch ($method) {
case 'GET':
if ($user['rol'] === 'admin') {
$stmt = $pdo->query("SELECT * FROM cursos");
} else {
$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']]);
try {
$query = "
SELECT
c.*,
COUNT(ac.id) AS alumnos_count,
u.nombre AS profesor_nombre
FROM cursos c
LEFT JOIN alumnos_cursos ac ON c.id = ac.curso_id
LEFT JOIN usuarios u ON c.profesor_id = u.id
";
$params = [];
if (isset($_GET['profesor_id'])) {
$query .= " WHERE c.profesor_id = ?";
$params[] = $_GET['profesor_id'];
}
$query .= " GROUP BY c.id ORDER BY c.estado, c.nombre";
$stmt = $pdo->prepare($query);
$stmt->execute($params);
echo json_encode($stmt->fetchAll());
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Error al cargar cursos: ' . $e->getMessage()]);
}
break;
case 'POST':
if ($user['rol'] !== 'admin') {
http_response_code(403);
echo json_encode(['error' => 'Acceso no autorizado']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
try {
$stmt = $pdo->prepare("
INSERT INTO cursos (nombre, tipo, competencias)
VALUES (?, ?, ?)
INSERT INTO cursos (nombre, descripcion, tipo, estado, profesor_id)
VALUES (?, ?, ?, 'activo', ?)
");
$stmt->execute([
$data['nombre'],
$data['descripcion'] ?? null,
$data['tipo'],
$data['competencias'] ?? null
$profesorId
]);
echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Error al crear curso: ' . $e->getMessage()]);
}
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
if ($profesorId) {
$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: ' . $e->getMessage()]);
}
break;
default:

78
api/diploma.php Normal file
View File

@ -0,0 +1,78 @@
<?php
header('Content-Type: application/json');
require '../includes/config.php';
if (!is_logged_in()) {
http_response_code(401);
echo json_encode(['error' => 'No autenticado']);
exit;
}
try {
$profesorId = $_SESSION['profesor']['id'] ?? null;
$action = $_GET['action'] ?? null;
if ($action === 'resend') {
// Lógica para reenviar diploma
$codigo = $_GET['codigo'] ?? null;
if (!$codigo) {
throw new Exception('Código de diploma no proporcionado');
}
// Aquí iría la lógica para reenviar el diploma por email
echo json_encode(['success' => true, 'message' => 'Diploma reenviado']);
exit;
}
$query = "
SELECT
d.id,
d.codigo_unico,
d.fecha_emision,
a.nombre AS alumno_nombre,
a.email AS alumno_email,
c.nombre AS curso_nombre,
c.tipo AS curso_tipo,
c.id AS curso_id,
DATE_FORMAT(d.fecha_emision, '%d/%m/%Y') AS fecha_formateada
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();
// Asegurar que todos los diplomas tengan código único
foreach ($diplomas as &$diploma) {
if (empty($diploma['codigo_unico'])) {
$diploma['codigo_unico'] = 'DIPL-' . str_pad($diploma['id'], 6, '0', STR_PAD_LEFT);
}
}
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()
]);
}
?>

View File

@ -2,80 +2,57 @@
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']
'profesor' => [
'id' => $profesor['id'],
'nombre' => $profesor['nombre']
]
]);
} 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.'

View File

@ -566,3 +566,36 @@ header h1 {
background-color: #a0aec0;
cursor: not-allowed;
}
/* Badges para tipos de curso */
.badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8em;
font-weight: 600;
text-transform: capitalize;
}
.badge.active {
background-color: #4CAF50;
color: white;
}
.badge.inactive {
background-color: #f44336;
color: white;
}
.badge.type-inyeccion {
background-color: #2196F3;
color: white;
}
.badge.type-pildora {
background-color: #FF9800;
color: white;
}
.badge.type-tratamiento {
background-color: #9C27B0;
color: white;
}

View File

@ -6,7 +6,7 @@ document.addEventListener('DOMContentLoaded', function() {
}
// Configuración inicial del dashboard
if (document.body.classList.contains('admin') || document.body.classList.contains('user')) {
if (document.body.classList.contains('admin')) {
initializeDashboard();
}
});
@ -47,126 +47,126 @@ function initializeDashboard() {
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);
});
}
});
}
function loadInitialData() {
fetch('api/cursos.php')
.then(response => response.json())
.then(data => {
if (document.body.classList.contains('admin')) {
updateAdminStats(data);
// Verificar si es admin o profesor
const isAdmin = document.body.classList.contains('admin');
// Hacer todas las peticiones en paralelo
Promise.all([
fetch('api/cursos.php').then(res => res.json()),
fetch('api/alumnos.php').then(res => res.json()),
fetch('api/diplomas.php').then(res => res.json())
])
.then(([courses, students, diplomas]) => {
if (isAdmin) {
updateAdminStats(courses, students, diplomas);
} else {
updateUserStats(data);
updateProfessorStats(courses, students, diplomas);
}
})
.catch(error => console.error('Error:', error));
.catch(error => {
console.error('Error loading initial data:', error);
showErrorNotification('Error al cargar datos iniciales');
});
}
function updateAdminStats(courses) {
document.getElementById('active-courses-count').textContent = courses.length;
// Aquí puedes agregar más llamadas para estudiantes y diplomas
// fetch('api/estudiantes.php')...
// fetch('api/diplomas.php')...
function updateAdminStats(courses, students, diplomas) {
// Filtrar cursos activos
const activeCourses = courses.filter(c => c.estado === 'activo');
// Actualizar estadísticas
document.getElementById('active-courses-count').textContent = activeCourses.length;
document.getElementById('students-count').textContent = students.length;
document.getElementById('diplomas-count').textContent = diplomas.length;
// Actualizar gráficos o tablas adicionales si existen
updateCoursesTable(activeCourses.slice(0, 5));
updateRecentDiplomas(diplomas.slice(0, 5));
}
function updateUserStats(courses) {
document.getElementById('user-courses-count').textContent = courses.length;
const approvedCourses = courses.filter(c => c.estado === 'Aprobado');
document.getElementById('user-diplomas-count').textContent = approvedCourses.length;
window.userCourses = courses;
function updateProfessorStats(courses, students, diplomas) {
const profesorId = getProfesorId();
// Mostrar los primeros 5 cursos en el dashboard
renderDashboardCourses(courses.slice(0, 5));
setupDashboardPagination(courses);
setupDashboardSearch();
// Filtrar datos del profesor actual
const profesorCourses = courses.filter(c => c.profesor_id == profesorId);
const activeProfesorCourses = profesorCourses.filter(c => c.estado === 'activo');
// Obtener alumnos del profesor
const profesorStudents = students.filter(s =>
profesorCourses.some(c => s.cursos.includes(c.id))
);
// Obtener diplomas del profesor
const profesorDiplomas = diplomas.filter(d =>
profesorCourses.some(c => d.curso_id === c.id)
);
// Actualizar estadísticas
document.getElementById('active-courses-count').textContent = activeProfesorCourses.length;
document.getElementById('students-count').textContent = profesorStudents.length;
document.getElementById('diplomas-count').textContent = profesorDiplomas.length;
// Actualizar tablas específicas del profesor
updateProfessorCoursesTable(activeProfesorCourses);
updateProfessorStudentsTable(profesorStudents.slice(0, 5));
}
function renderDashboardCourses(courses) {
const tbody = document.getElementById('dashboard-courses-body');
if (!tbody) return;
tbody.innerHTML = courses.map(course => `
function updateRecentDiplomas(diplomas) {
const tableBody = document.getElementById('diplomas-table-body');
if (!tableBody) return;
tableBody.innerHTML = diplomas.map(diploma => `
<tr>
<td>${course.nombre}</td>
<td>${course.fecha_inicio || '-'}</td>
<td>${course.fecha_fin || '-'}</td>
<td>${diploma.alumno_nombre}</td>
<td>${diploma.curso_nombre}</td>
<td>${new Date(diploma.fecha_emision).toLocaleDateString()}</td>
<td>
${course.estado === 'Aprobado' ?
`<button class="btn download-btn" data-course-id="${course.id}">Descargar</button>` :
'No disponible'}
<button class="btn btn-sm" onclick="downloadDiploma('${diploma.codigo_unico}')">
Descargar
</button>
</td>
</tr>
`).join('');
}
function updateCoursesTable(courses) {
const tableBody = document.getElementById('courses-table-body');
if (!tableBody) return;
// Configurar eventos de descarga
document.querySelectorAll('.download-btn').forEach(btn => {
btn.addEventListener('click', function() {
const courseId = this.getAttribute('data-course-id');
window.open(`certificado.php?course_id=${courseId}`, '_blank');
});
});
tableBody.innerHTML = courses.map(course => `
<tr>
<td>${course.nombre}</td>
<td><span class="badge ${getCourseTypeClass(course.tipo)}">${course.tipo}</span></td>
<td>${course.alumnos_count || 0}</td>
<td><span class="badge ${course.estado === 'activo' ? 'active' : 'inactive'}">${course.estado}</span></td>
</tr>
`).join('');
}
function setupDashboardPagination(allCourses) {
let currentPage = 1;
const perPage = 5;
const totalPages = Math.ceil(allCourses.length / perPage);
function updateDashboardStats(courses, students, diplomas) {
const activeCourses = courses.filter(c => c.estado === 'activo');
function updatePagination() {
const start = (currentPage - 1) * perPage;
const end = start + perPage;
renderDashboardCourses(allCourses.slice(start, end));
document.getElementById('page-info').textContent = `Página ${currentPage} de ${totalPages}`;
document.getElementById('prev-page').disabled = currentPage === 1;
document.getElementById('next-page').disabled = currentPage === totalPages || totalPages === 0;
if (document.getElementById('active-courses-count')) {
document.getElementById('active-courses-count').textContent = activeCourses.length;
}
document.getElementById('prev-page')?.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
updatePagination();
if (document.getElementById('students-count')) {
document.getElementById('students-count').textContent = students.length;
}
});
document.getElementById('next-page')?.addEventListener('click', () => {
if (currentPage < totalPages) {
currentPage++;
updatePagination();
}
});
updatePagination();
}
function setupDashboardSearch() {
const searchBtn = document.getElementById('dashboard-search-btn');
const searchInput = document.getElementById('dashboard-course-search');
if (searchBtn && searchInput) {
searchBtn.addEventListener('click', () => {
const term = searchInput.value.toLowerCase();
const filtered = window.userCourses.filter(course =>
course.nombre.toLowerCase().includes(term) ||
course.tipo.toLowerCase().includes(term) ||
course.estado.toLowerCase().includes(term)
);
setupDashboardPagination(filtered);
});
// Permitir búsqueda al presionar Enter
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
searchBtn.click();
}
});
if (document.getElementById('diplomas-count')) {
document.getElementById('diplomas-count').textContent = diplomas.length;
}
}
function showSection(sectionId, isInitialLoad = false) {
// Actualizar menú activo
updateActiveMenu(sectionId);
@ -185,20 +185,21 @@ function showSection(sectionId, isInitialLoad = false) {
loadDynamicContent(sectionId, sectionElement);
}
}
}
}
}
function updateActiveMenu(sectionId) {
document.querySelectorAll('.sidebar-menu li').forEach(li => {
li.classList.remove('active');
});
document.querySelector(`.sidebar-menu li[data-section="${sectionId}"]`).classList.add('active');
const activeItem = document.querySelector(`.sidebar-menu li[data-section="${sectionId}"]`);
if (activeItem) {
activeItem.classList.add('active');
}
}
function getSectionElement(sectionId) {
if (sectionId === 'dashboard') {
return document.getElementById('dashboard-content');
}
return document.getElementById(`${sectionId}-content`);
}
@ -210,66 +211,133 @@ function toggleSections(activeSection) {
}
function loadDynamicContent(sectionId, container) {
// Mostrar loader mientras se carga
container.innerHTML = '<div class="loader">Cargando...</div>';
switch(sectionId) {
case 'dashboard':
loadDashboardContent(container);
break;
case 'courses':
loadAdminCourses(container);
loadProfessorCourses(container);
break;
case 'students':
loadAdminStudents(container);
break;
case 'my-courses':
loadUserCourses(container);
loadStudentsManagement(container);
break;
case 'diplomas':
loadUserDiplomas(container);
break;
case 'profile':
loadProfileSection(container);
loadDiplomasSection(container);
break;
default:
container.innerHTML = '<div class="card"><h2>Sección no implementada</h2></div>';
}
}
async function loadDashboardContent(container) {
try {
container.innerHTML = '<div class="loader">Cargando datos...</div>';
// Obtener datos del profesor
const profesorId = getProfesorId();
if (!profesorId) throw new Error('No se pudo obtener ID de profesor');
// Hacer peticiones en paralelo
const responses = await Promise.all([
fetch(`api/cursos.php?profesor_id=${profesorId}`),
fetch('api/alumnos.php'),
fetch(`api/diplomas.php?profesor_id=${profesorId}`)
]);
// Verificar respuestas
for (const response of responses) {
if (!response.ok) throw new Error(`Error HTTP: ${response.status}`);
}
// Parsear JSON
const [courses, students, diplomas] = await Promise.all(
responses.map(r => r.json())
);
// Filtrar cursos activos
const activeCourses = Array.isArray(courses) ?
courses.filter(c => c.estado === 'activo') : [];
// Generar HTML
container.innerHTML = `
<div class="card">
<h2>Resumen General</h2>
<div class="stats">
<p><strong>Resumen:</strong></p>
<p> <span class="stat-number">${activeCourses.length}</span> cursos activos</p>
<p> <span class="stat-number">${students.length || 0}</span> alumnos registrados</p>
<p> <span class="stat-number">${diplomas.length || 0}</span> diplomas emitidos</p>
</div>
</div>
<div class="card">
<h2>Mis Cursos Activos</h2>
${generateCoursesPreview(activeCourses.slice(0, 5))}
</div>`;
} catch (error) {
console.error('Error loading dashboard:', error);
container.innerHTML = `
<div class="card error-card">
<h2>Error al cargar datos</h2>
<p>${error.message}</p>
<button class="btn" onclick="showSection('dashboard')">Reintentar</button>
</div>`;
}
}
// Funciones para cargar contenido específico
function loadAdminCourses(container) {
fetch('api/cursos.php')
function loadProfessorCourses(container) {
fetch(`api/cursos.php?profesor_id=${getProfesorId()}`)
.then(response => response.json())
.then(courses => {
container.innerHTML = `
<div class="card">
<h2>Gestión de Cursos</h2>
<h2>Mis Cursos</h2>
<form id="courseForm">
<label>Nombre del Curso</label>
<input type="text" name="nombre" placeholder="Ej. Seguridad Informática" required>
<label>Descripción</label>
<textarea name="descripcion" placeholder="Descripción del curso"></textarea>
<label>Tipo de Curso</label>
<select id="courseType" name="tipo" required>
<select name="tipo" required>
<option value="">Seleccionar</option>
<option value="pildora">Píldora</option>
<option value="inyeccion">Inyección</option>
<option value="pildora">Píldora</option>
<option value="tratamiento">Tratamiento</option>
</select>
<div id="competencesField" class="oculto">
<label>Competencias Asociadas</label>
<input type="text" name="competencias" placeholder="Ej. Análisis de datos, Comunicación efectiva">
</div>
<button class="btn" type="submit">Guardar Curso</button>
<button class="btn" type="submit">Crear Curso</button>
</form>
</div>
<div class="card">
<h2>Lista de Cursos</h2>
<div class="course-list">
${generateCoursesList(courses)}
<div class="search-container">
<input type="text" id="courseSearch" placeholder="Buscar cursos..." class="search-input">
<button class="btn" id="searchCourseBtn">Buscar</button>
</div>
<div class="table-container">
<table class="courses-table">
<thead>
<tr>
<th>Nombre</th>
<th>Tipo</th>
<th>Estado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="coursesTableBody">
${generateCoursesTableRows(courses)}
</tbody>
</table>
</div>
</div>`;
setupCourseForm();
setupCourseSearch();
})
.catch(error => {
container.innerHTML = '<div class="card"><h2>Error al cargar los cursos</h2></div>';
@ -277,96 +345,241 @@ function loadAdminCourses(container) {
});
}
function loadUserCourses(container) {
const courses = window.userCourses || [];
function loadStudentsManagement(container) {
container.innerHTML = `
<div class="card">
<h2>Mis Cursos</h2>
<h2>Gestión de Alumnos</h2>
<form id="studentForm">
<label>Nombre del Alumno</label>
<input type="text" name="nombre" required>
<label>Email</label>
<input type="email" name="email" required>
<label>Teléfono</label>
<input type="tel" name="telefono">
<button class="btn" type="submit">Registrar Alumno</button>
</form>
</div>
<div class="card">
<h2>Lista de Alumnos</h2>
<div class="search-container">
<input type="text" id="courseSearch" placeholder="Buscar cursos..." class="search-input">
<button class="btn" id="searchButton">Buscar</button>
<input type="text" id="studentSearch" placeholder="Buscar alumnos..." class="search-input">
<button class="btn" id="searchStudentBtn">Buscar</button>
</div>
<div class="course-list">
${courses.length > 0 ?
courses.map(course => generateCourseCard(course)).join('') :
'<p>No tienes cursos registrados.</p>'}
<div class="table-container">
<table class="students-table">
<thead>
<tr>
<th>Nombre</th>
<th>Email</th>
<th>Teléfono</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="studentsTableBody">
<!-- Se llenará dinámicamente -->
</tbody>
</table>
</div>
</div>`;
setupCourseSearch();
fetch('api/alumnos.php')
.then(response => response.json())
.then(students => {
renderStudentsTable(students);
})
.catch(error => {
console.error('Error:', error);
});
setupStudentForm();
setupStudentSearch();
}
function loadUserDiplomas(container) {
const courses = (window.userCourses || []).filter(c => c.estado === 'Aprobado');
function loadDiplomasSection(container) {
container.innerHTML = '<div class="loader">Cargando diplomas...</div>';
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 = `
<div class="card">
<h2>Diplomas Emitidos</h2>
<p>No hay diplomas registrados aún</p>
<button class="btn" onclick="generateNewDiploma()">Generar Primer Diploma</button>
</div>`;
return;
}
container.innerHTML = `
<div class="card">
<h2>Mis Diplomas</h2>
${courses.length > 0 ?
courses.map(course => generateDiplomaCard(course)).join('') :
'<p>No tienes diplomas disponibles todavía.</p>'}
<h2>Diplomas Emitidos</h2>
<div class="controls">
<button class="btn" onclick="generateNewDiploma()">Generar Nuevo Diploma</button>
<div class="search-container">
<input type="text" id="diplomaSearch" placeholder="Buscar por alumno o curso...">
<button class="btn" onclick="searchDiplomas()">Buscar</button>
</div>
</div>
<div class="table-container">
<table class="diplomas-table">
<thead>
<tr>
<th>Alumno</th>
<th>Email</th>
<th>Curso</th>
<th>Tipo</th>
<th>Fecha</th>
<th>Código</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
${data.data.map(diploma => `
<tr>
<td>${diploma.alumno_nombre}</td>
<td>${diploma.alumno_email}</td>
<td>${diploma.curso_nombre}</td>
<td><span class="badge ${getCourseTypeClass(diploma.curso_tipo)}">${diploma.curso_tipo}</span></td>
<td>${diploma.fecha_formateada}</td>
<td class="code">${diploma.codigo_unico}</td>
<td>
<button class="btn" onclick="downloadDiploma('${diploma.codigo_unico}')">
<i class="icon-download"></i> Descargar
</button>
<button class="btn" onclick="resendDiploma('${diploma.codigo_unico}', '${diploma.alumno_email}')">
<i class="icon-send"></i> Reenviar
</button>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
<div class="pagination-info">
Mostrando ${data.data.length} diplomas
</div>
</div>`;
setupDiplomaDownloads();
})
.catch(error => {
console.error('Error:', error);
container.innerHTML = `
<div class="card error-card">
<h2>Error al cargar diplomas</h2>
<p>${error.message}</p>
<button class="btn" onclick="loadDiplomasSection(this.parentElement)">
Reintentar
</button>
</div>`;
});
}
// Funciones auxiliares
function generateCoursesList(courses) {
// Función para generar nuevo diploma
function generateNewDiploma() {
// Implementar lógica para seleccionar alumno y curso
alert('Implementar diálogo para seleccionar alumno y curso');
}
// Funciones auxiliares para generar contenido
function generateCoursesTableRows(courses) {
return courses.map(course => `
<div class="course-card">
<h3>${course.nombre}</h3>
<p>Tipo: ${course.tipo}</p>
<p>ID: ${course.id}</p>
</div>
<tr>
<td>${course.nombre}</td>
<td>${course.tipo}</td>
<td>${course.estado}</td>
<td>
<button class="btn" onclick="editCourse(${course.id})">Editar</button>
<button class="btn btn-danger" onclick="deleteCourse(${course.id})">Eliminar</button>
</td>
</tr>
`).join('');
}
function generateCourseCard(course) {
return `
<div class="course-card">
<h3>${course.nombre}</h3>
<p>Tipo: ${course.tipo}</p>
<p>Estado: ${course.estado}</p>
${course.competencias ? `<p>Competencias: ${course.competencias}</p>` : ''}
<p>Profesor: ${course.profesor || 'No asignado'}</p>
<p>Fecha: ${course.fecha_inicio} a ${course.fecha_fin}</p>
</div>`;
function generateStudentsTableRows(students) {
return students.map(student => `
<tr>
<td>${student.nombre}</td>
<td>${student.email}</td>
<td>${student.telefono || '-'}</td>
<td>
<button class="btn" onclick="editStudent(${student.id})">Editar</button>
<button class="btn btn-danger" onclick="deleteStudent(${student.id})">Eliminar</button>
</td>
</tr>
`).join('');
}
function generateDiplomaCard(course) {
function generateDiplomasTableRows(diplomas) {
return diplomas.map(diploma => `
<tr>
<td>${diploma.alumno_nombre}</td>
<td>${diploma.curso_nombre}</td>
<td>${new Date(diploma.fecha_emision).toLocaleDateString()}</td>
<td>
<button class="btn" onclick="resendDiploma(${diploma.id})">Reenviar</button>
<button class="btn" onclick="downloadDiploma(${diploma.id})">Descargar</button>
</td>
</tr>
`).join('');
}
function generateDashboardCoursesRows(courses) {
return courses.map(course => `
<tr>
<td>${course.nombre}</td>
<td><span class="badge ${getCourseTypeClass(course.tipo)}">${course.tipo}</span></td>
<td>${course.alumnos_count || 0}</td>
<td><span class="badge ${course.estado === 'activo' ? 'active' : 'inactive'}">${course.estado}</span></td>
</tr>
`).join('');
}
function generateCoursesPreview(courses) {
if (!courses.length) return '<p>No tienes cursos activos</p>';
return `
<div class="diploma-preview">
<h3>Diploma de ${course.nombre}</h3>
<p>Fecha: ${course.fecha_fin || 'Completado'}</p>
<p>Profesor: ${course.profesor || 'No especificado'}</p>
<button class="btn download-btn"
data-course-id="${course.id}"
data-course-name="${course.nombre}">
Descargar Diploma
</button>
<div class="table-container">
<table class="preview-table">
<thead>
<tr>
<th>Nombre</th>
<th>Tipo</th>
<th>Alumnos</th>
<th>Estado</th>
</tr>
</thead>
<tbody>
${courses.map(course => `
<tr>
<td>${course.nombre || 'Sin nombre'}</td>
<td><span class="badge ${getCourseTypeClass(course.tipo)}">${course.tipo || 'N/A'}</span></td>
<td>${course.alumnos_count || 0}</td>
<td><span class="badge ${course.estado === 'activo' ? 'active' : 'inactive'}">${course.estado || 'N/A'}</span></td>
</tr>
`).join('')}
</tbody>
</table>
</div>`;
}
// Configuración de eventos dinámicos
// Configuración de formularios y eventos
function setupCourseForm() {
const courseTypeSelect = document.getElementById('courseType');
if (courseTypeSelect) {
courseTypeSelect.addEventListener('change', function() {
const competencesField = document.getElementById('competencesField');
if (competencesField) {
competencesField.classList.toggle('oculto', this.value !== 'tratamiento');
}
});
}
const courseForm = document.getElementById('courseForm');
if (courseForm) {
courseForm.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const jsonData = {};
formData.forEach((value, key) => jsonData[key] = value);
const jsonData = {
nombre: formData.get('nombre'),
descripcion: formData.get('descripcion'),
tipo: formData.get('tipo'),
profesor_id: getProfesorId()
};
fetch('api/cursos.php', {
method: 'POST',
@ -377,7 +590,7 @@ function setupCourseForm() {
.then(data => {
if (data.success) {
alert('Curso creado exitosamente');
showSection('courses');
showSection('courses'); // Recargar la sección
} else {
alert('Error al crear el curso');
}
@ -387,54 +600,209 @@ function setupCourseForm() {
}
}
function setupStudentForm() {
const studentForm = document.getElementById('studentForm');
if (studentForm) {
studentForm.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const jsonData = {
nombre: formData.get('nombre'),
email: formData.get('email'),
telefono: formData.get('telefono')
};
fetch('api/alumnos.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(jsonData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Alumno registrado exitosamente');
showSection('students'); // Recargar la sección
} else {
alert('Error al registrar al alumno');
}
})
.catch(error => console.error('Error:', error));
});
}
}
function setupCourseSearch() {
const searchButton = document.getElementById('searchButton');
if (searchButton) {
searchButton.addEventListener('click', function() {
const searchTerm = document.getElementById('courseSearch').value.toLowerCase();
const courses = window.userCourses || [];
const filteredCourses = courses.filter(course =>
course.nombre.toLowerCase().includes(searchTerm) ||
(course.profesor && course.profesor.toLowerCase().includes(searchTerm))
const searchBtn = document.getElementById('searchCourseBtn');
const searchInput = document.getElementById('courseSearch');
if (searchBtn && searchInput) {
searchBtn.addEventListener('click', searchCourses);
searchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') searchCourses();
});
}
}
function setupStudentSearch() {
const searchBtn = document.getElementById('searchStudentBtn');
const searchInput = document.getElementById('studentSearch');
if (searchBtn && searchInput) {
searchBtn.addEventListener('click', searchStudents);
searchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') searchStudents();
});
}
}
function searchCourses() {
const term = document.getElementById('courseSearch').value.toLowerCase();
fetch(`api/cursos.php?profesor_id=${getProfesorId()}`)
.then(response => response.json())
.then(courses => {
const filtered = courses.filter(course =>
course.nombre.toLowerCase().includes(term) ||
course.tipo.toLowerCase().includes(term) ||
course.estado.toLowerCase().includes(term)
);
const courseList = document.querySelector('.course-list');
if (courseList) {
courseList.innerHTML = filteredCourses.length > 0 ?
filteredCourses.map(course => generateCourseCard(course)).join('') :
'<p>No se encontraron cursos que coincidan con la búsqueda.</p>';
}
});
document.getElementById('coursesTableBody').innerHTML = generateCoursesTableRows(filtered);
})
.catch(error => console.error('Error:', error));
}
function searchStudents() {
const term = document.getElementById('studentSearch').value.toLowerCase();
fetch('api/alumnos.php')
.then(response => response.json())
.then(students => {
const filtered = students.filter(student =>
student.nombre.toLowerCase().includes(term) ||
student.email.toLowerCase().includes(term) ||
(student.telefono && student.telefono.toLowerCase().includes(term))
);
document.getElementById('studentsTableBody').innerHTML = generateStudentsTableRows(filtered);
})
.catch(error => console.error('Error:', error));
}
function renderStudentsTable(students) {
const tbody = document.getElementById('studentsTableBody');
if (tbody) {
tbody.innerHTML = generateStudentsTableRows(students);
}
}
function setupDiplomaDownloads() {
document.querySelectorAll('.download-btn').forEach(btn => {
btn.addEventListener('click', function() {
function setupDiplomaActions() {
// Los eventos se manejan directamente con onclick en los botones
}
// Funciones de acciones
function editCourse(courseId) {
// Implementar lógica de edición
alert(`Editar curso con ID: ${courseId}`);
}
// Mostrar mensaje de carga
const originalText = this.innerHTML;
this.innerHTML = '<span class="loading-text">Generando diploma...</span>';
this.disabled = true;
function deleteCourse(courseId) {
if (confirm('¿Estás seguro de eliminar este curso?')) {
fetch(`api/cursos.php?id=${courseId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
showSection('courses'); // Recargar la sección
} else {
alert('Error al eliminar el curso');
}
})
.catch(error => console.error('Error:', error));
}
}
// Abrir en nueva pestaña en lugar de usar fetch
window.open(`certificado.php`, '_blank');
function editStudent(studentId) {
// Implementar lógica de edición
alert(`Editar alumno con ID: ${studentId}`);
}
// Restaurar botón después de un breve retraso
setTimeout(() => {
this.innerHTML = originalText;
this.disabled = false;
}, 2000);
});
function deleteStudent(studentId) {
if (confirm('¿Estás seguro de eliminar este alumno?')) {
fetch(`api/alumnos.php?id=${studentId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.success) {
showSection('students'); // Recargar la sección
} else {
alert('Error al eliminar el alumno');
}
})
.catch(error => console.error('Error:', error));
}
}
function resendDiploma(diplomaId) {
fetch(`api/diplomas.php?action=resend&id=${diplomaId}`)
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Diploma reenviado exitosamente');
} else {
alert('Error al reenviar el diploma');
}
})
.catch(error => console.error('Error:', error));
}
function downloadDiploma(diplomaId) {
window.open(`certificado.php?diploma_id=${diplomaId}`, '_blank');
}
// Función para obtener el ID del profesor desde la sesión
function getProfesorId() {
// En una implementación real, esto debería venir de la sesión
// Esto es solo un ejemplo temporal
return 1;
}
function getCourseTypeClass(type) {
const types = {
'inyeccion': 'type-inyeccion',
'pildora': 'type-pildora',
'tratamiento': 'type-tratamiento'
};
return types[type] || '';
}
// Funciones globales para usar en onclick
window.editCourse = editCourse;
window.deleteCourse = deleteCourse;
window.editStudent = editStudent;
window.deleteStudent = deleteStudent;
window.resendDiploma = resendDiploma;
window.downloadDiploma = downloadDiploma;
//Diplomas
function downloadDiploma(codigo) {
window.open(`certificado.php?codigo=${codigo}`, '_blank');
}
// Función para reenviar diploma
function resendDiploma(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');
});
}
// Funciones de secciones no implementadas (para completar)
function loadAdminStudents(container) {
container.innerHTML = '<div class="card"><h2>Gestión de Estudiantes</h2><p>Sección en desarrollo</p></div>';
}
function loadProfileSection(container) {
container.innerHTML = '<div class="card"><h2>Perfil de Usuario</h2><p>Sección en desarrollo</p></div>';
}

View File

@ -2,7 +2,7 @@
include 'includes/config.php';
redirect_if_not_logged_in();
$user = $_SESSION['user'];
$profesor = $_SESSION['profesor'];
?>
<!DOCTYPE html>
<html lang="es">
@ -13,43 +13,35 @@ $user = $_SESSION['user'];
<link rel="stylesheet" href="assets/css/styles.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
</head>
<body class="<?= $user['rol'] ?>">
<body class="admin">
<div id="app-content">
<header>
<h1>DiploMaster</h1>
<div id="user-info">
<span id="current-user"><?= htmlspecialchars($user['nombre']) ?></span>
<div id="profesor-info">
<span id="current-profesor"><?= htmlspecialchars($profesor['nombre']) ?></span>
</div>
</header>
<div class="main-container">
<div class="sidebar" id="sidebar">
<ul class="sidebar-menu">
<?php if ($user['rol'] === 'admin'): ?>
<li class="active" data-section="dashboard"><span>🏠 Inicio</span></li>
<li data-section="courses"><span>📚 Gestión de Cursos</span></li>
<li data-section="students"><span>👨‍🎓 Gestión de Estudiantes</span></li>
<?php else: ?>
<li class="active" data-section="dashboard"><span>🏠 Inicio</span></li>
<li data-section="my-courses"><span>📚 Mis Cursos</span></li>
<li data-section="diplomas"><span>🎓 Mis Diplomas</span></li>
<?php endif; ?>
<li data-section="profile"><span>👤 Perfil</span></li>
<li data-section="courses"><span>📚 Mis Cursos</span></li>
<li data-section="students"><span>👨‍🎓 Gestión de Alumnos</span></li>
<li data-section="diplomas"><span>🎓 Diplomas Emitidos</span></li>
<li><a href="api/logout.php" class="logout-link">Cerrar sesión</a></li>
</ul>
</div>
<div class="content" id="main-content">
<?php if ($user['rol'] === 'admin'): ?>
<!-- Contenido para Administrador -->
<div id="dashboard-content" class="content-section active">
<div class="card">
<h2>Panel de Administración</h2>
<p>Bienvenido al sistema de gestión de DiploMaster</p>
<h2>Bienvenido <?= htmlspecialchars($profesor['nombre']) ?></h2>
<p>Este es tu panel de gestión de DiploMaster</p>
<div class="stats">
<p><strong>Estadísticas:</strong></p>
<p><strong>Resumen:</strong></p>
<p> <span id="active-courses-count">0</span> cursos activos</p>
<p> <span id="students-count">0</span> estudiantes registrados</p>
<p> <span id="students-count">0</span> alumnos registrados</p>
<p> <span id="diplomas-count">0</span> diplomas emitidos</p>
</div>
</div>
@ -62,61 +54,10 @@ $user = $_SESSION['user'];
<div id="students-content" class="content-section">
<!-- Se cargará dinámicamente -->
</div>
<?php else: ?>
<!-- Contenido para Usuario Normal -->
<div id="dashboard-content" class="content-section active">
<div class="card">
<h2>Bienvenido <?= htmlspecialchars($user['nombre']) ?></h2>
<p>Este es tu panel de control en DiploMaster</p>
<div class="stats">
<p><strong>Resumen:</strong></p>
<p> <span id="user-courses-count">0</span> cursos registrados</p>
<p> <span id="user-diplomas-count">0</span> diplomas disponibles</p>
</div>
</div>
<div class="card">
<h2>Mis Cursos Recientes</h2>
<div class="search-container">
<input type="text" id="dashboard-course-search" placeholder="Buscar mis cursos..." class="search-input">
<button class="btn" id="dashboard-search-btn">Buscar</button>
</div>
<div class="table-container">
<table class="courses-table" id="dashboard-courses-table">
<thead>
<tr>
<th>Nombre</th>
<th>Fecha Inicio</th>
<th>Fecha Fin</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="dashboard-courses-body">
<!-- Se llenará dinámicamente -->
</tbody>
</table>
<div class="pagination" id="dashboard-pagination">
<button class="btn pagination-btn" id="prev-page">Anterior</button>
<span id="page-info">Página 1</span>
<button class="btn pagination-btn" id="next-page">Siguiente</button>
</div>
</div>
</div>
</div>
<div id="my-courses-content" class="content-section">
<!-- Se cargará dinámicamente -->
</div>
<div id="diplomas-content" class="content-section">
<!-- Se cargará dinámicamente -->
</div>
<?php endif; ?>
<!-- Sección común para todos los usuarios -->
<div id="profile-content" class="content-section">
<!-- Se cargará dinámicamente -->
</div>
</div>
</div>
</div>

View File

@ -21,7 +21,7 @@ try {
}
function is_logged_in() {
return isset($_SESSION['user']);
return isset($_SESSION['profesor']);
}
function redirect_if_not_logged_in() {

View File

@ -17,14 +17,14 @@
</svg>
<h1>DiploMaster</h1>
</div>
<p class="welcome-text">Bienvenido al sistema de diplomas</p>
<p class="welcome-text">Sistema de gestión de diplomas para profesores</p>
<form id="loginForm" action="api/login.php" method="POST">
<div class="input-group">
<svg class="input-icon" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z"/>
</svg>
<input type="text" id="username" name="username" placeholder="Usuario" required>
<input type="email" id="email" name="email" placeholder="Email" required>
</div>
<div class="input-group">
@ -40,11 +40,6 @@
<path fill="currentColor" d="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z"/>
</svg>
</button>
<div class="footer-links">
<a href="#">¿Olvidaste tu contraseña?</a>
<a href="#">Registrarse</a>
</div>
</form>
</div>
</div>

View File

@ -1,157 +1,57 @@
-- phpMyAdmin SQL Dump
-- version 5.2.1
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 05-05-2025 a las 04:15:28
-- Versión del servidor: 10.4.32-MariaDB
-- Versión de PHP: 8.2.12
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Base de datos: `diplomaster`
--
-- --------------------------------------------------------
--
-- Estructura de tabla para la tabla `cursos`
--
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;
--
-- Volcado de datos para la tabla `cursos`
--
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');
-- --------------------------------------------------------
--
-- Estructura de tabla para la tabla `usuarios`
--
-- Crear la base de datos
CREATE DATABASE IF NOT EXISTS diplomaster;
USE diplomaster;
-- 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()
`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 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Volcado de datos para la tabla `usuarios`
--
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');
-- --------------------------------------------------------
--
-- Estructura de tabla para la tabla `usuario_cursos`
--
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
-- Tabla de cursos (cada profesor crea sus 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,
FOREIGN KEY (`profesor_id`) REFERENCES `usuarios`(`id`) ON DELETE CASCADE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Volcado de datos para la tabla `usuario_cursos`
--
-- 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 COLLATE=utf8mb4_general_ci;
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');
-- 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',
`competencias` TEXT DEFAULT NULL, -- Solo para tratamiento
FOREIGN KEY (`alumno_id`) REFERENCES `alumnos`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`curso_id`) REFERENCES `cursos`(`id`) ON DELETE CASCADE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Índices para tablas volcadas
--
-- Tabla de diplomas
CREATE TABLE `diplomas` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`alumno_curso_id` INT(11) NOT NULL,
`fecha_emision` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`competencias` TEXT DEFAULT NULL,
FOREIGN KEY (`alumno_curso_id`) REFERENCES `alumnos_cursos`(`id`) ON DELETE CASCADE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
--
-- Indices de la tabla `cursos`
--
ALTER TABLE `cursos`
ADD PRIMARY KEY (`id`);
--
-- Indices de la tabla `usuarios`
--
ALTER TABLE `usuarios`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `username` (`username`),
ADD UNIQUE KEY `email` (`email`);
--
-- Indices de la tabla `usuario_cursos`
--
ALTER TABLE `usuario_cursos`
ADD PRIMARY KEY (`id`),
ADD KEY `usuario_id` (`usuario_id`),
ADD KEY `curso_id` (`curso_id`);
--
-- AUTO_INCREMENT de las tablas volcadas
--
--
-- AUTO_INCREMENT de la tabla `cursos`
--
ALTER TABLE `cursos`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- AUTO_INCREMENT de la tabla `usuarios`
--
ALTER TABLE `usuarios`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- AUTO_INCREMENT de la tabla `usuario_cursos`
--
ALTER TABLE `usuario_cursos`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
--
-- Restricciones para tablas volcadas
--
--
-- Filtros para la tabla `usuario_cursos`
--
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;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;