Compare commits
11 Commits
76b0584667
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b000e7f665 | ||
|
|
fefd3a8d78 | ||
|
|
a02e77d09f | ||
|
|
959b5e3596 | ||
|
|
9b219c7856 | ||
|
|
67c4d8173f | ||
|
|
8bd34c8ddb | ||
|
|
acc0033e63 | ||
|
|
6a5a1d48ef | ||
|
|
ffda892859 | ||
|
|
6823a5d6d3 |
0
000-default.conf
Normal file → Executable file
0
000-default.conf
Normal file → Executable file
0
Dockerfile
Normal file → Executable file
0
Dockerfile
Normal file → Executable file
0
apache2.conf
Normal file → Executable file
0
apache2.conf
Normal file → Executable file
@@ -20,14 +20,16 @@ class TelegramBot {
|
|||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
// --- MEJORAS ---
|
// --- MEJORAS ---
|
||||||
// 1. Forzar el uso de IPv4. Un problema común en entornos Docker
|
// 1. Forzar el uso de IPv4.
|
||||||
// es un timeout al intentar resolver AAAA (IPv6) antes de usar A (IPv4).
|
|
||||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||||
|
|
||||||
// 2. Añadir timeouts para evitar que el script se cuelgue indefinidamente.
|
// 2. Añadir timeouts de seguridad.
|
||||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); // 5 segundos para conectar
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); // 5 segundos para conectar
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 10 segundos para la transferencia total
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 10 segundos para la transferencia total
|
||||||
|
|
||||||
|
// 3. (ÚLTIMO INTENTO) Forzar versión de TLS.
|
||||||
|
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||||
|
|
||||||
if ($httpMethod === 'POST') {
|
if ($httpMethod === 'POST') {
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||||
@@ -96,7 +98,7 @@ class TelegramBot {
|
|||||||
['text' => 'Semana Actual', 'callback_data' => 'semana_actual']
|
['text' => 'Semana Actual', 'callback_data' => 'semana_actual']
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
['text' => '📄 Mi PDF', 'callback_data' => 'mi_pdf']
|
['text' => '📄 Horarios PDF', 'callback_data' => 'mi_pdf']
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
['text' => '🔍 Buscar por Nombre', 'callback_data' => 'buscar_nombre']
|
['text' => '🔍 Buscar por Nombre', 'callback_data' => 'buscar_nombre']
|
||||||
@@ -210,7 +212,7 @@ class TelegramBot {
|
|||||||
return "Error de conexion.";
|
return "Error de conexion.";
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE nombre LIKE ? AND rol = 'ayudante' AND activo = 1 LIMIT 1");
|
$stmt = $pdo->prepare("SELECT * FROM users WHERE nombre LIKE ? AND (rol = 'ayudante' OR rol = 'coordinador') AND activo = 1 LIMIT 1");
|
||||||
$stmt->execute(["%$nombre%"]);
|
$stmt->execute(["%$nombre%"]);
|
||||||
$user = $stmt->fetch();
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
@@ -269,7 +271,7 @@ class TelegramBot {
|
|||||||
$db = \Database::getInstance()->getConnection();
|
$db = \Database::getInstance()->getConnection();
|
||||||
|
|
||||||
$horarios = $horariosModel->getActivos();
|
$horarios = $horariosModel->getActivos();
|
||||||
$ayudantes = $userModel->getAyudantesActivos();
|
$ayudantes = $asignacionModel->getAyudantesPorOrden();
|
||||||
|
|
||||||
$semanasFuturas = [];
|
$semanasFuturas = [];
|
||||||
$hoy = new DateTime();
|
$hoy = new DateTime();
|
||||||
@@ -398,7 +400,7 @@ class TelegramBot {
|
|||||||
$db = \Database::getInstance()->getConnection();
|
$db = \Database::getInstance()->getConnection();
|
||||||
|
|
||||||
$horarios = $horariosModel->getActivos();
|
$horarios = $horariosModel->getActivos();
|
||||||
$ayudantes = $userModel->getAyudantesActivos();
|
$ayudantes = $asignacionModel->getAyudantesPorOrden();
|
||||||
|
|
||||||
$semanasFuturas = [];
|
$semanasFuturas = [];
|
||||||
$hoy = new DateTime();
|
$hoy = new DateTime();
|
||||||
|
|||||||
@@ -116,16 +116,14 @@ class TurnoBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_timing("handleUpdate: DB query start");
|
log_timing("handleUpdate: DB query start");
|
||||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE (nombre LIKE ? OR username LIKE ?) AND rol = 'ayudante' AND activo = 1 LIMIT 1");
|
$stmt = $pdo->prepare("SELECT * FROM users WHERE (nombre LIKE ? OR username LIKE ?) AND (rol = 'ayudante' OR rol = 'coordinador') AND activo = 1 LIMIT 1");
|
||||||
$stmt->execute(["%$text%", "%$text%"]);
|
$stmt->execute(["%$text%", "%$text%"]);
|
||||||
$user = $stmt->fetch();
|
$user = $stmt->fetch();
|
||||||
log_timing("handleUpdate: DB query end");
|
log_timing("handleUpdate: DB query end");
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
log_timing("handleUpdate: user found, sending PDF");
|
log_timing("handleUpdate: user found, sending text turnos");
|
||||||
$this->bot->sendMessage($chatId, "Generando PDF de turnos...");
|
$this->bot->sendMessage($chatId, $this->bot->getTurnosAyudante($user['nombre']));
|
||||||
$this->bot->sendPDF($chatId, $user['id']);
|
|
||||||
log_timing("handleUpdate: PDF sent");
|
|
||||||
} else {
|
} else {
|
||||||
log_timing("handleUpdate: user not found, getting plain text turnos");
|
log_timing("handleUpdate: user not found, getting plain text turnos");
|
||||||
$this->bot->sendMessage($chatId, $this->bot->getTurnosAyudante($text));
|
$this->bot->sendMessage($chatId, $this->bot->getTurnosAyudante($text));
|
||||||
@@ -165,6 +163,7 @@ class TurnoBot {
|
|||||||
|
|
||||||
case 'mi_pdf':
|
case 'mi_pdf':
|
||||||
$this->bot->answerCallback($callbackId, 'Generando PDF...');
|
$this->bot->answerCallback($callbackId, 'Generando PDF...');
|
||||||
|
$this->bot->deleteMessage($chatId, $messageId);
|
||||||
$this->bot->sendPDFGeneral($chatId);
|
$this->bot->sendPDFGeneral($chatId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -196,7 +195,7 @@ class TurnoBot {
|
|||||||
$mensaje .= "Selecciona una opcion del menu:\n\n";
|
$mensaje .= "Selecciona una opcion del menu:\n\n";
|
||||||
$mensaje .= "Ver Turnos - Tabla completa de asignaciones\n";
|
$mensaje .= "Ver Turnos - Tabla completa de asignaciones\n";
|
||||||
$mensaje .= "Semana Actual - Quien tiene turno esta semana\n";
|
$mensaje .= "Semana Actual - Quien tiene turno esta semana\n";
|
||||||
$mensaje .= "Mi PDF - Descargar horarios en PDF\n";
|
$mensaje .= "Horarios PDF - Descargar horarios en PDF\n";
|
||||||
$mensaje .= "Buscar por Nombre - Consultar un ayudante especifico\n";
|
$mensaje .= "Buscar por Nombre - Consultar un ayudante especifico\n";
|
||||||
$mensaje .= "Mi Turno - Ver tu proximo turno";
|
$mensaje .= "Mi Turno - Ver tu proximo turno";
|
||||||
|
|
||||||
|
|||||||
0
config.yaml
Normal file → Executable file
0
config.yaml
Normal file → Executable file
0
docker-compose.yml
Normal file → Executable file
0
docker-compose.yml
Normal file → Executable file
0
docker/contenedor-ibiza.conf
Normal file → Executable file
0
docker/contenedor-ibiza.conf
Normal file → Executable file
601
public/admin/asignaciones.php
Executable file → Normal file
601
public/admin/asignaciones.php
Executable file → Normal file
@@ -10,7 +10,7 @@ require_once BASE_PATH . '/src/Asignacion.php';
|
|||||||
require_once BASE_PATH . '/src/CSRF.php';
|
require_once BASE_PATH . '/src/CSRF.php';
|
||||||
|
|
||||||
$auth = new Auth();
|
$auth = new Auth();
|
||||||
$auth->requireAdmin();
|
$auth->requireCoordinador();
|
||||||
|
|
||||||
$userModel = new User();
|
$userModel = new User();
|
||||||
$horariosModel = new DiasHorarios();
|
$horariosModel = new DiasHorarios();
|
||||||
@@ -24,6 +24,11 @@ if (isset($_GET['success']) && $_GET['success'] === 'orden_actualizado') {
|
|||||||
$messageType = 'success';
|
$messageType = 'success';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['success']) && $_GET['success'] === 'automatica') {
|
||||||
|
$message = 'Turnos generados automáticamente correctamente.';
|
||||||
|
$messageType = 'success';
|
||||||
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
if (!CSRF::isValidRequest()) {
|
if (!CSRF::isValidRequest()) {
|
||||||
$message = 'Error de validación del formulario';
|
$message = 'Error de validación del formulario';
|
||||||
@@ -31,527 +36,247 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
} else {
|
} else {
|
||||||
$action = $_POST['action'] ?? '';
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
if ($action === 'asignar') {
|
if ($action === 'asignar_actual') {
|
||||||
$userId = $_POST['user_id'] ?? 0;
|
$userId = $_POST['user_id'] ?? 0;
|
||||||
$semana = $_POST['semana'] ?? '';
|
|
||||||
|
|
||||||
if ($userId && $semana) {
|
// Encontrar el domingo actual
|
||||||
$asignacionModel->asignar($userId, $semana);
|
$hoy = new DateTime();
|
||||||
$message = 'Turno asignado correctamente';
|
$diaSemana = (int)$hoy->format('w');
|
||||||
$messageType = 'success';
|
$domingoActual = clone $hoy;
|
||||||
}
|
$domingoActual->modify('-' . $diaSemana . ' days');
|
||||||
} elseif ($action === 'rotar') {
|
$currentWeekStart = $domingoActual->format('Y-m-d');
|
||||||
$semana = $_POST['semana'] ?? '';
|
|
||||||
$asignacionActual = $asignacionModel->getAsignacionPorSemana($semana);
|
|
||||||
|
|
||||||
if ($asignacionActual) {
|
if ($userId) {
|
||||||
$proximaPersona = $asignacionModel->getProximaPersona($asignacionActual['user_id']);
|
$asignacionModel->asignar($userId, $currentWeekStart);
|
||||||
if ($proximaPersona) {
|
$message = 'Turno actual asignado correctamente';
|
||||||
$asignacionModel->asignar($proximaPersona['id'], $semana);
|
|
||||||
$message = 'Turno rotado a: ' . htmlspecialchars($proximaPersona['nombre']);
|
|
||||||
$messageType = 'success';
|
$messageType = 'success';
|
||||||
|
header("Location: " . $_SERVER['PHP_SELF'] . "?success=actual");
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
}
|
} elseif ($action === 'rotar_automatica') {
|
||||||
} elseif ($action === 'rotacion_automatica') {
|
|
||||||
$resultado = $asignacionModel->asignarSemanasFuturasAutomaticas(12);
|
$resultado = $asignacionModel->asignarSemanasFuturasAutomaticas(12);
|
||||||
|
|
||||||
if ($resultado['success'] > 0) {
|
if ($resultado['success'] > 0) {
|
||||||
$message = "Se generaron {$resultado['success']} semanas futuras correctamente";
|
header("Location: " . $_SERVER['PHP_SELF'] . "?success=automatica");
|
||||||
if (!empty($resultado['errors'])) {
|
exit;
|
||||||
$message .= ". Errores: " . implode(', ', $resultado['errors']);
|
|
||||||
$messageType = 'warning';
|
|
||||||
} else {
|
} else {
|
||||||
$messageType = 'success';
|
$message = 'No se pudieron generar turnos: ' . implode(', ', $resultado['errors']);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$message = 'No se pudieron generar asignaciones: ' . implode(', ', $resultado['errors']);
|
|
||||||
$messageType = 'danger';
|
|
||||||
}
|
|
||||||
} elseif ($action === 'asignar_masivo') {
|
|
||||||
$userIds = $_POST['user_ids'] ?? [];
|
|
||||||
$semanaInicio = $_POST['semana_inicio'] ?? '';
|
|
||||||
$rotacionAutomatica = isset($_POST['rotacion_automatica']) ? true : false;
|
|
||||||
|
|
||||||
if (!empty($userIds) && $semanaInicio) {
|
|
||||||
$resultado = $asignacionModel->asignarMasivo($userIds, $semanaInicio, $rotacionAutomatica);
|
|
||||||
|
|
||||||
if ($resultado['success'] > 0) {
|
|
||||||
$message = "Se asignaron {$resultado['success']} turnos correctamente";
|
|
||||||
if ($rotacionAutomatica) {
|
|
||||||
$message .= " con rotación automática para la siguiente semana";
|
|
||||||
}
|
|
||||||
$messageType = 'success';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($resultado['errors'])) {
|
|
||||||
$message .= "<br>Errores: " . implode('<br>', $resultado['errors']);
|
|
||||||
$messageType = 'warning';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$message = 'Debes seleccionar al menos un ayudante y una semana';
|
|
||||||
$messageType = 'danger';
|
$messageType = 'danger';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ayudantes = $userModel->getAyudantesActivos();
|
// Obtener datos principales
|
||||||
$horarios = $horariosModel->getActivos();
|
|
||||||
|
|
||||||
// Encontrar el domingo actual
|
|
||||||
$hoy = new DateTime();
|
$hoy = new DateTime();
|
||||||
$diaSemana = (int)$hoy->format('w'); // 0 = domingo, 6 = sábado
|
$diaSemana = (int)$hoy->format('w');
|
||||||
$domingoActual = clone $hoy;
|
$domingoActual = clone $hoy;
|
||||||
$domingoActual->modify('-' . $diaSemana . ' days'); // Restar días para llegar al domingo
|
$domingoActual->modify('-' . $diaSemana . ' days');
|
||||||
|
|
||||||
$currentWeekStart = $domingoActual->format('Y-m-d');
|
$currentWeekStart = $domingoActual->format('Y-m-d');
|
||||||
|
|
||||||
$asignacionActual = $asignacionModel->getAsignacionPorSemana($currentWeekStart);
|
$asignacionActual = $asignacionModel->getAsignacionPorSemana($currentWeekStart);
|
||||||
|
// Para gestión de turnos, incluir ayudantes y coordinadores
|
||||||
|
$ayudantes = $userModel->getUsuariosGestion();
|
||||||
|
|
||||||
// Calcular posición en el ciclo (semana X de 4)
|
// Obtener próximos turnos (siguientes 3 semanas)
|
||||||
function calcularPosicionCiclo($semanaInicio) {
|
$proximosTurnos = [];
|
||||||
// Empezamos desde el inicio del ciclo: 28 Dic 2025
|
for ($i = 1; $i <= 3; $i++) {
|
||||||
$fechaInicioCiclo = new DateTime('2025-12-28');
|
|
||||||
$semanaActual = new DateTime($semanaInicio);
|
|
||||||
|
|
||||||
$diasDiferencia = $fechaInicioCiclo->diff($semanaActual)->days;
|
|
||||||
$semanasDesdeInicio = floor($diasDiferencia / 7);
|
|
||||||
|
|
||||||
// Posición en ciclo de 4 semanas (1-4)
|
|
||||||
$posicion = ($semanasDesdeInicio % 4) + 1;
|
|
||||||
|
|
||||||
return $posicion;
|
|
||||||
}
|
|
||||||
|
|
||||||
$posicionCicloActual = calcularPosicionCiclo($currentWeekStart);
|
|
||||||
|
|
||||||
// Generar semanas agrupadas por mes
|
|
||||||
$mesesEspanol = [
|
|
||||||
'January' => 'Enero', 'February' => 'Febrero', 'March' => 'Marzo',
|
|
||||||
'April' => 'Abril', 'May' => 'Mayo', 'June' => 'Junio',
|
|
||||||
'July' => 'Julio', 'August' => 'Agosto', 'September' => 'Septiembre',
|
|
||||||
'October' => 'Octubre', 'November' => 'Noviembre', 'December' => 'Diciembre'
|
|
||||||
];
|
|
||||||
|
|
||||||
$semanasAgrupadas = [];
|
|
||||||
for ($i = -4; $i <= 12; $i++) {
|
|
||||||
$semanaDomingo = clone $domingoActual;
|
$semanaDomingo = clone $domingoActual;
|
||||||
$semanaDomingo->modify("+{$i} weeks");
|
$semanaDomingo->modify("+{$i} weeks");
|
||||||
|
$semanaInicio = $semanaDomingo->format('Y-m-d');
|
||||||
|
$asignacion = $asignacionModel->getAsignacionPorSemana($semanaInicio);
|
||||||
|
|
||||||
$key = $semanaDomingo->format('Y-m');
|
$proximosTurnos[] = [
|
||||||
$mesIngles = $semanaDomingo->format('F');
|
'semana' => $semanaInicio,
|
||||||
$mesEspanol = $mesesEspanol[$mesIngles] ?? $mesIngles;
|
'fin' => date('Y-m-d', strtotime('+5 days', strtotime($semanaInicio))),
|
||||||
$anio = $semanaDomingo->format('Y');
|
'asignacion' => $asignacion
|
||||||
|
|
||||||
if (!isset($semanasAgrupadas[$key])) {
|
|
||||||
$semanasAgrupadas[$key] = [
|
|
||||||
'nombre' => "$mesEspanol $anio",
|
|
||||||
'semanas' => []
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$semanasAgrupadas[$key]['semanas'][] = [
|
|
||||||
'fecha' => $semanaDomingo->format('Y-m-d'),
|
|
||||||
'posicion' => calcularPosicionCiclo($semanaDomingo->format('Y-m-d'))
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$currentPage = 'asignaciones';
|
$currentPage = 'asignaciones';
|
||||||
$pageTitle = 'Asignación de Turnos';
|
$pageTitle = 'Gestión de Turnos';
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="es">
|
<html lang="es">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Asignaciones - Contenedor Ibiza</title>
|
<title>Turnos - Contenedor Ibiza</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
|
<style>
|
||||||
|
.turno-card {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.turno-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
.btn-accion {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
.turno-actual {
|
||||||
|
border-left: 5px solid #198754;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<?php include BASE_PATH . '/public/partials/navbar.php'; ?>
|
<?php include BASE_PATH . '/public/partials/navbar.php'; ?>
|
||||||
|
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<h2 class="mb-4">Asignación de Turnos</h2>
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h2 class="mb-0">📅 Gestión de Turnos</h2>
|
||||||
|
<a href="/admin/asignaciones_completo.php" class="btn btn-outline-secondary btn-sm">
|
||||||
|
⚙️ Vista Avanzada
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php if ($message): ?>
|
<?php if ($message): ?>
|
||||||
<div class="alert alert-<?= $messageType ?>"><?= htmlspecialchars($message) ?></div>
|
<div class="alert alert-<?= $messageType ?> alert-dismissible fade show">
|
||||||
|
<?= htmlspecialchars($message) ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- TURNO ACTUAL -->
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-md-6">
|
<div class="col-12">
|
||||||
<div class="card shadow-sm">
|
<div class="card turno-actual shadow-sm">
|
||||||
<div class="card-header bg-primary text-white">
|
<div class="card-header bg-success text-white">
|
||||||
<h5 class="mb-0">Asignación Actual (Semana <?= $posicionCicloActual ?> de 4)</h5>
|
<h5 class="mb-0">📍 Turno Actual</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="mb-3">
|
<div class="row align-items-center">
|
||||||
<strong>Fecha:</strong> <?= date('d/m/y', strtotime($currentWeekStart)) ?> (Dom) - <?= date('d/m/y', strtotime('+5 days', strtotime($currentWeekStart))) ?> (Vie)
|
<div class="col-md-6">
|
||||||
|
<p class="mb-2">
|
||||||
|
<strong>📆 Semana:</strong>
|
||||||
|
<?= date('d/m/Y', strtotime($currentWeekStart)) ?>
|
||||||
|
al <?= date('d/m/Y', strtotime('+5 days', strtotime($currentWeekStart))) ?>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<?php if ($asignacionActual): ?>
|
<?php if ($asignacionActual): ?>
|
||||||
<div class="alert alert-success">
|
<div class="alert alert-success mb-3">
|
||||||
<strong>Asignado a:</strong> <?= htmlspecialchars($asignacionActual['nombre']) ?>
|
<h6 class="mb-1">✅ Asignado a:</h6>
|
||||||
|
<strong class="fs-5"><?= htmlspecialchars($asignacionActual['nombre']) ?></strong>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="POST" class="d-flex gap-2">
|
|
||||||
<?= CSRF::getTokenField() ?>
|
|
||||||
<input type="hidden" name="action" value="rotar">
|
|
||||||
<input type="hidden" name="semana" value="<?= $currentWeekStart ?>">
|
|
||||||
<button type="submit" class="btn btn-outline-primary">
|
|
||||||
↻ Rotar al siguiente
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="alert alert-warning">No hay asignación para esta semana</div>
|
<div class="alert alert-warning mb-3">
|
||||||
|
<h6 class="mb-1">⚠️ Sin asignar</h6>
|
||||||
<form method="POST">
|
<span class="fs-5">No hay ayudante asignado esta semana</span>
|
||||||
<input type="hidden" name="action" value="asignar">
|
|
||||||
<?= CSRF::getTokenField() ?>
|
|
||||||
<input type="hidden" name="semana" value="<?= $currentWeekStart ?>">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Asignar a:</label>
|
|
||||||
<select class="form-select" name="user_id" required>
|
|
||||||
<option value="">Seleccionar persona...</option>
|
|
||||||
<?php foreach ($ayudantes as $a): ?>
|
|
||||||
<option value="<?= $a['id'] ?>"><?= htmlspecialchars($a['nombre']) ?></option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Asignar</button>
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6 text-center">
|
||||||
<div class="card shadow-sm">
|
<form method="POST" class="d-flex justify-content-center gap-2">
|
||||||
<div class="card-header bg-secondary text-white">
|
|
||||||
<h5 class="mb-0">Historial de Asignaciones</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<form method="GET" class="mb-3">
|
|
||||||
<label class="form-label">Seleccionar semana:</label>
|
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<select class="form-select" name="semana" style="max-width: 320px;">
|
|
||||||
<?php foreach ($semanasAgrupadas as $grupo): ?>
|
|
||||||
<optgroup label="<?= $grupo['nombre'] ?>">
|
|
||||||
<?php foreach ($grupo['semanas'] as $s): ?>
|
|
||||||
<option value="<?= $s['fecha'] ?>" <?= $s['fecha'] === ($_GET['semana'] ?? $currentWeekStart) ? 'selected' : '' ?>>
|
|
||||||
Semana <?= $s['posicion'] ?> de 4 - <?= date('d/m', strtotime($s['fecha'])) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</optgroup>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
<button type="submit" class="btn btn-outline-primary">Ver</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
$semanaVer = $_GET['semana'] ?? $currentWeekStart;
|
|
||||||
$asignacionVer = $asignacionModel->getAsignacionPorSemana($semanaVer);
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if ($asignacionVer): ?>
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<?php $posicionSemanaVer = calcularPosicionCiclo($semanaVer); ?>
|
|
||||||
<strong>Semana <?= $posicionSemanaVer ?> de 4 (<?= date('d/m/y', strtotime($semanaVer)) ?>):</strong>
|
|
||||||
<?= htmlspecialchars($asignacionVer['nombre']) ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="POST" class="d-flex gap-2">
|
|
||||||
<?= CSRF::getTokenField() ?>
|
<?= CSRF::getTokenField() ?>
|
||||||
<input type="hidden" name="action" value="asignar">
|
<input type="hidden" name="action" value="asignar_actual">
|
||||||
<input type="hidden" name="semana" value="<?= $semanaVer ?>">
|
|
||||||
<select class="form-select" name="user_id" style="max-width: 250px;">
|
<select class="form-select" name="user_id" style="max-width: 200px;" required>
|
||||||
<option value="">Cambiar persona...</option>
|
<option value="">Seleccionar...</option>
|
||||||
<?php foreach ($ayudantes as $a): ?>
|
<?php foreach ($ayudantes as $a): ?>
|
||||||
<option value="<?= $a['id'] ?>" <?= isset($asignacionVer['user_id']) && $a['id'] == $asignacionVer['user_id'] ? 'selected' : '' ?>>
|
<option value="<?= $a['id'] ?>"
|
||||||
|
<?= $asignacionActual && $a['id'] == $asignacionActual['id'] ? 'selected' : '' ?>>
|
||||||
<?= htmlspecialchars($a['nombre']) ?>
|
<?= htmlspecialchars($a['nombre']) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
<button type="submit" class="btn btn-outline-secondary">Actualizar</button>
|
|
||||||
</form>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="alert alert-secondary">
|
|
||||||
<?php $posicionSinAsignar = calcularPosicionCiclo($semanaVer); ?>
|
|
||||||
No hay asignación para la semana <?= $posicionSinAsignar ?> de 4 (<?= date('d/m/y', strtotime($semanaVer)) ?>)
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="POST">
|
<button type="submit" class="btn btn-success btn-accion">
|
||||||
<input type="hidden" name="action" value="asignar">
|
💾 Asignar
|
||||||
<input type="hidden" name="semana" value="<?= $semanaVer ?>">
|
</button>
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<select class="form-select" name="user_id" style="max-width: 300px;" required>
|
|
||||||
<option value="">Seleccionar persona...</option>
|
|
||||||
<?php foreach ($ayudantes as $a): ?>
|
|
||||||
<option value="<?= $a['id'] ?>"><?= htmlspecialchars($a['nombre']) ?></option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
<button type="submit" class="btn btn-primary">Asignar</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
<?php endif; ?>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sección de Asignación Masiva -->
|
<!-- ACCIONES RÁPIDAS -->
|
||||||
<div class="card shadow-sm mt-4">
|
<div class="row mb-4">
|
||||||
<div class="card-header bg-success text-white">
|
<div class="col-12">
|
||||||
<h5 class="mb-0">Asignación Masiva</h5>
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h5 class="mb-0">⚡ Acciones Rápidas</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body text-center">
|
||||||
<form method="POST" id="asignacionMasivaForm">
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<div class="p-3">
|
||||||
|
<h6 class="mb-3">🔄 Generar Turnos Automáticos</h6>
|
||||||
|
<p class="text-muted mb-3">Crea automáticamente las próximas 12 semanas siguiendo el orden actual</p>
|
||||||
|
<form method="POST" style="display: inline;">
|
||||||
<?= CSRF::getTokenField() ?>
|
<?= CSRF::getTokenField() ?>
|
||||||
<input type="hidden" name="action" value="asignar_masivo">
|
<input type="hidden" name="action" value="rotar_automatica">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg btn-accion">
|
||||||
<div class="row mb-3">
|
🚀 Generar Automático
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">Semana de inicio:</label>
|
|
||||||
<input type="date" class="form-control" name="semana_inicio" required>
|
|
||||||
<small class="text-muted">Debe ser un domingo</small>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">Opciones:</label>
|
|
||||||
<div class="form-check mt-2">
|
|
||||||
<input class="form-check-input" type="checkbox" name="rotacion_automatica" id="rotacion_automatica">
|
|
||||||
<label class="form-check-label" for="rotacion_automatica">
|
|
||||||
Activar rotación automática para la siguiente semana
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Seleccionar ayudantes:</label>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-sm">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="50">
|
|
||||||
<input type="checkbox" class="form-check-input" id="selectAll">
|
|
||||||
</th>
|
|
||||||
<th>Nombre</th>
|
|
||||||
<th>Email</th>
|
|
||||||
<th>Username</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($ayudantes as $a): ?>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<input type="checkbox" class="form-check-input user-checkbox"
|
|
||||||
name="user_ids[]" value="<?= $a['id'] ?>">
|
|
||||||
</td>
|
|
||||||
<td><?= htmlspecialchars($a['nombre']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($a['email']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($a['username']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<button type="submit" class="btn btn-success">
|
|
||||||
<i class="fas fa-users"></i> Asignar a seleccionados
|
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary" onclick="selectAllUsers()">
|
|
||||||
<i class="fas fa-check-square"></i> Seleccionar todos
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-outline-secondary" onclick="deselectAllUsers()">
|
|
||||||
<i class="fas fa-square"></i> Deseleccionar todos
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sección de Rotación Automática -->
|
<div class="col-md-6 mb-3">
|
||||||
<div class="card shadow-sm mt-4">
|
<div class="p-3">
|
||||||
<div class="card-header bg-warning text-dark">
|
<h6 class="mb-3">👥 Gestionar Orden</h6>
|
||||||
<h5 class="mb-0">Rotación Automática</h5>
|
<p class="text-muted mb-3">Modifica el orden de rotación de los ayudantes</p>
|
||||||
|
<a href="/admin/asignaciones_completo.php#reordenar" class="btn btn-outline-primary btn-lg btn-accion">
|
||||||
|
⚙️ Orden de Rotación
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PRÓXIMOS TURNOS -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h5 class="mb-0">📋 Próximos Turnos</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<?php foreach ($proximosTurnos as $index => $turno): ?>
|
||||||
<h6>Orden de Rotación Actual:</h6>
|
<div class="col-md-4 mb-3">
|
||||||
<div class="d-flex flex-wrap gap-2 mb-3">
|
<div class="card turno-card h-100">
|
||||||
<?php
|
<div class="card-body text-center">
|
||||||
$ayudantesOrdenados = $asignacionModel->getAyudantesPorOrden();
|
<h6 class="card-title">
|
||||||
foreach ($ayudantesOrdenados as $index => $ayudante):
|
📅 <?= date('d/m', strtotime($turno['semana'])) ?> -
|
||||||
?>
|
<?= date('d/m', strtotime($turno['fin'])) ?>
|
||||||
<span class="badge bg-primary fs-6">
|
</h6>
|
||||||
<?= ($index + 1) ?>. <?= htmlspecialchars($ayudante['nombre']) ?>
|
|
||||||
</span>
|
<?php if ($turno['asignacion']): ?>
|
||||||
<?php endforeach; ?>
|
<div class="alert alert-success mb-2">
|
||||||
|
<strong>✅ <?= htmlspecialchars($turno['asignacion']['nombre']) ?></strong>
|
||||||
</div>
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="alert alert-warning mb-2">
|
||||||
|
<strong>⚠️ Sin asignar</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
|
||||||
<form method="POST" class="h-100 d-flex flex-column justify-content-center">
|
|
||||||
<?= CSRF::getTokenField() ?>
|
|
||||||
<input type="hidden" name="action" value="rotacion_automatica">
|
|
||||||
<button type="submit" class="btn btn-warning w-100">
|
|
||||||
<i class="fas fa-sync"></i> Generar Rotación Automática
|
|
||||||
</button>
|
|
||||||
<small class="text-muted mt-2">
|
|
||||||
Asigna automáticamente los próximos 12 semanas siguiendo el orden de rotación
|
|
||||||
</small>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert alert-info mt-3 mb-0">
|
|
||||||
<strong>ℹ️ ¿Cómo funciona?</strong><br>
|
|
||||||
• El sistema mantiene un orden cíclico de ayudantes<br>
|
|
||||||
• Cada semana (Dom→Vie) asigna automáticamente al siguiente en la lista<br>
|
|
||||||
• Al agregar nuevos ayudantes, se integran automáticamente en el ciclo<br>
|
|
||||||
• Usa el botón para generar las próximas 12 semanas
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Sección de Reordenar Rotación -->
|
|
||||||
<div class="card shadow-sm mt-4">
|
|
||||||
<div class="card-header bg-dark text-white">
|
|
||||||
<h5 class="mb-0">Reordenar Rotación</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<form method="POST" id="reordenarForm">
|
|
||||||
<?= CSRF::getTokenField() ?>
|
|
||||||
<input type="hidden" name="action" value="reordenar">
|
|
||||||
|
|
||||||
<p class="text-muted">
|
|
||||||
Arrastra los elementos para cambiar el orden de rotación.
|
|
||||||
Los cambios afectarán las asignaciones futuras.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul id="sortableList" class="list-group">
|
|
||||||
<?php
|
|
||||||
$ayudantesOrdenados = $asignacionModel->getAyudantesPorOrden();
|
|
||||||
foreach ($ayudantesOrdenados as $index => $ayudante):
|
|
||||||
?>
|
|
||||||
<li class="list-group-item d-flex align-items-center" data-id="<?= $ayudante['id'] ?>">
|
|
||||||
<input type="hidden" name="ordenes[<?= $index ?>]" value="<?= $ayudante['id'] ?>">
|
|
||||||
<span class="badge bg-primary me-2" style="cursor: grab;">☰ <?= ($index + 1) ?></span>
|
|
||||||
<span><?= htmlspecialchars($ayudante['nombre']) ?></span>
|
|
||||||
</li>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="mt-3 d-flex gap-2">
|
|
||||||
<button type="submit" class="btn btn-dark">
|
|
||||||
<i class="fas fa-save"></i> Guardar Nuevo Orden
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-outline-secondary" onclick="invertirOrden()">
|
|
||||||
<i class="fas fa-exchange-alt"></i> Invertir Orden
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'reordenar'): ?>
|
|
||||||
<?php
|
|
||||||
$nuevosOrdenes = $_POST['ordenes'] ?? [];
|
|
||||||
$errores = [];
|
|
||||||
|
|
||||||
if (!empty($nuevosOrdenes)) {
|
|
||||||
foreach ($nuevosOrdenes as $index => $userId) {
|
|
||||||
$stmt = $asignacionModel->getDb()->prepare("
|
|
||||||
UPDATE rotacion_orden
|
|
||||||
SET orden = ?
|
|
||||||
WHERE user_id = ? AND activo = 1
|
|
||||||
");
|
|
||||||
$stmt->execute([$index + 1, $userId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recalcular asignaciones futuras
|
|
||||||
$resultado = $asignacionModel->recalcularAsignaciones(20);
|
|
||||||
|
|
||||||
if ($resultado['success'] > 0) {
|
|
||||||
// Redireccionar para recargar los datos actualizados
|
|
||||||
header("Location: " . $_SERVER['PHP_SELF'] . "?success=orden_actualizado");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<a href="/admin/asignaciones_completo.php" class="btn btn-sm btn-outline-primary">
|
||||||
|
Modificar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<a href="/admin/asignaciones_completo.php" class="btn btn-outline-info">
|
||||||
|
📊 Ver todos los turnos
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
||||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script>
|
|
||||||
// Inicializar sortable
|
|
||||||
$(function() {
|
|
||||||
$("#sortableList").sortable({
|
|
||||||
placeholder: "ui-state-highlight",
|
|
||||||
update: function(event, ui) {
|
|
||||||
actualizarNumeros();
|
|
||||||
}
|
|
||||||
}).disableSelection();
|
|
||||||
});
|
|
||||||
|
|
||||||
function actualizarNumeros() {
|
|
||||||
$("#sortableList li").each(function(index) {
|
|
||||||
$(this).find('.badge').text('☰ ' + (index + 1));
|
|
||||||
// Actualizar el valor del input oculto con el nuevo orden
|
|
||||||
var userId = $(this).data('id');
|
|
||||||
$(this).find('input[type="hidden"]').attr('name', 'ordenes[' + index + ']').val(userId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function invertirOrden() {
|
|
||||||
var items = $("#sortableList li").get().reverse();
|
|
||||||
$("#sortableList").append(items);
|
|
||||||
actualizarNumeros();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seleccionar/deseleccionar todos
|
|
||||||
document.getElementById('selectAll').addEventListener('change', function() {
|
|
||||||
const checkboxes = document.querySelectorAll('.user-checkbox');
|
|
||||||
checkboxes.forEach(checkbox => checkbox.checked = this.checked);
|
|
||||||
});
|
|
||||||
|
|
||||||
function selectAllUsers() {
|
|
||||||
document.querySelectorAll('.user-checkbox').forEach(checkbox => checkbox.checked = true);
|
|
||||||
document.getElementById('selectAll').checked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deselectAllUsers() {
|
|
||||||
document.querySelectorAll('.user-checkbox').forEach(checkbox => checkbox.checked = false);
|
|
||||||
document.getElementById('selectAll').checked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validar que al menos un usuario esté seleccionado
|
|
||||||
document.getElementById('asignacionMasivaForm').addEventListener('submit', function(e) {
|
|
||||||
const selectedUsers = document.querySelectorAll('.user-checkbox:checked');
|
|
||||||
if (selectedUsers.length === 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
alert('Debes seleccionar al menos un ayudante');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Establecer fecha por defecto al domingo actual
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const hoy = new Date();
|
|
||||||
const diaSemana = hoy.getDay(); // 0 = domingo, 6 = sábado
|
|
||||||
const diasParaDomingo = diaSemana === 0 ? 0 : (7 - diaSemana);
|
|
||||||
const domingoActual = new Date(hoy);
|
|
||||||
domingoActual.setDate(hoy.getDate() - diasParaDomingo);
|
|
||||||
|
|
||||||
const fechaInput = document.querySelector('input[name="semana_inicio"]');
|
|
||||||
if (fechaInput) {
|
|
||||||
fechaInput.value = domingoActual.toISOString().split('T')[0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
568
public/admin/asignaciones_completo.php
Executable file
568
public/admin/asignaciones_completo.php
Executable file
@@ -0,0 +1,568 @@
|
|||||||
|
<?php
|
||||||
|
if (!defined('BASE_PATH')) {
|
||||||
|
define('BASE_PATH', dirname(__DIR__, 2));
|
||||||
|
}
|
||||||
|
require_once BASE_PATH . '/config/config.php';
|
||||||
|
require_once BASE_PATH . '/src/Auth.php';
|
||||||
|
require_once BASE_PATH . '/src/User.php';
|
||||||
|
require_once BASE_PATH . '/src/DiasHorarios.php';
|
||||||
|
require_once BASE_PATH . '/src/Asignacion.php';
|
||||||
|
require_once BASE_PATH . '/src/CSRF.php';
|
||||||
|
|
||||||
|
$auth = new Auth();
|
||||||
|
$auth->requireCoordinador();
|
||||||
|
|
||||||
|
$userModel = new User();
|
||||||
|
$horariosModel = new DiasHorarios();
|
||||||
|
$asignacionModel = new Asignacion();
|
||||||
|
|
||||||
|
$message = '';
|
||||||
|
$messageType = '';
|
||||||
|
|
||||||
|
if (isset($_GET['success']) && $_GET['success'] === 'orden_actualizado') {
|
||||||
|
$message = 'Orden actualizado correctamente. Se recalcularon las asignaciones futuras.';
|
||||||
|
$messageType = 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (!CSRF::isValidRequest()) {
|
||||||
|
$message = 'Error de validación del formulario';
|
||||||
|
$messageType = 'danger';
|
||||||
|
} else {
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
|
if ($auth->isAdmin()) {
|
||||||
|
if ($action === 'asignar') {
|
||||||
|
$userId = $_POST['user_id'] ?? 0;
|
||||||
|
$semana = $_POST['semana'] ?? '';
|
||||||
|
|
||||||
|
if ($userId && $semana) {
|
||||||
|
$asignacionModel->asignar($userId, $semana);
|
||||||
|
$message = 'Turno asignado correctamente';
|
||||||
|
$messageType = 'success';
|
||||||
|
}
|
||||||
|
} elseif ($action === 'rotar') {
|
||||||
|
$semana = $_POST['semana'] ?? '';
|
||||||
|
$asignacionActual = $asignacionModel->getAsignacionPorSemana($semana);
|
||||||
|
|
||||||
|
if ($asignacionActual) {
|
||||||
|
$proximaPersona = $asignacionModel->getProximaPersona($asignacionActual['user_id']);
|
||||||
|
if ($proximaPersona) {
|
||||||
|
$asignacionModel->asignar($proximaPersona['id'], $semana);
|
||||||
|
$message = 'Turno rotado a: ' . htmlspecialchars($proximaPersona['nombre']);
|
||||||
|
$messageType = 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($action === 'asignar_masivo') {
|
||||||
|
$userIds = $_POST['user_ids'] ?? [];
|
||||||
|
$semanaInicio = $_POST['semana_inicio'] ?? '';
|
||||||
|
$rotacionAutomatica = isset($_POST['rotacion_automatica']) ? true : false;
|
||||||
|
|
||||||
|
if (!empty($userIds) && $semanaInicio) {
|
||||||
|
$resultado = $asignacionModel->asignarMasivo($userIds, $semanaInicio, $rotacionAutomatica);
|
||||||
|
|
||||||
|
if ($resultado['success'] > 0) {
|
||||||
|
$message = "Se asignaron {$resultado['success']} turnos correctamente";
|
||||||
|
if ($rotacionAutomatica) {
|
||||||
|
$message .= " con rotación automática para la siguiente semana";
|
||||||
|
}
|
||||||
|
$messageType = 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($resultado['errors'])) {
|
||||||
|
$message .= "<br>Errores: " . implode('<br>', $resultado['errors']);
|
||||||
|
$messageType = 'warning';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message = 'Debes seleccionar al menos un ayudante y una semana';
|
||||||
|
$messageType = 'danger';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acciones permitidas tanto para Admin como para Coordinador
|
||||||
|
if ($action === 'rotacion_automatica') {
|
||||||
|
$resultado = $asignacionModel->asignarSemanasFuturasAutomaticas(12);
|
||||||
|
|
||||||
|
if ($resultado['success'] > 0) {
|
||||||
|
$message = "Se generaron {$resultado['success']} semanas futuras correctamente";
|
||||||
|
if (!empty($resultado['errors'])) {
|
||||||
|
$message .= ". Errores: " . implode(', ', $resultado['errors']);
|
||||||
|
$messageType = 'warning';
|
||||||
|
} else {
|
||||||
|
$messageType = 'success';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message = 'No se pudieron generar asignaciones: ' . implode(', ', $resultado['errors']);
|
||||||
|
$messageType = 'danger';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Para asignaciones masiva, mostrar todos los ayudantes y coordinadores
|
||||||
|
$ayudantes = $userModel->getUsuariosGestion();
|
||||||
|
$horarios = $horariosModel->getActivos();
|
||||||
|
|
||||||
|
// Encontrar el domingo actual
|
||||||
|
$hoy = new DateTime();
|
||||||
|
$diaSemana = (int)$hoy->format('w'); // 0 = domingo, 6 = sábado
|
||||||
|
$domingoActual = clone $hoy;
|
||||||
|
$domingoActual->modify('-' . $diaSemana . ' days'); // Restar días para llegar al domingo
|
||||||
|
|
||||||
|
$currentWeekStart = $domingoActual->format('Y-m-d');
|
||||||
|
$asignacionActual = $asignacionModel->getAsignacionPorSemana($currentWeekStart);
|
||||||
|
|
||||||
|
// Calcular posición en el ciclo (semana X de 4)
|
||||||
|
function calcularPosicionCiclo($semanaInicio) {
|
||||||
|
// Empezamos desde el inicio del ciclo: 28 Dic 2025
|
||||||
|
$fechaInicioCiclo = new DateTime('2025-12-28');
|
||||||
|
$semanaActual = new DateTime($semanaInicio);
|
||||||
|
|
||||||
|
$diasDiferencia = $fechaInicioCiclo->diff($semanaActual)->days;
|
||||||
|
$semanasDesdeInicio = floor($diasDiferencia / 7);
|
||||||
|
|
||||||
|
// Posición en ciclo de 4 semanas (1-4)
|
||||||
|
$posicion = ($semanasDesdeInicio % 4) + 1;
|
||||||
|
|
||||||
|
return $posicion;
|
||||||
|
}
|
||||||
|
|
||||||
|
$posicionCicloActual = calcularPosicionCiclo($currentWeekStart);
|
||||||
|
|
||||||
|
// Generar semanas agrupadas por mes
|
||||||
|
$mesesEspanol = [
|
||||||
|
'January' => 'Enero', 'February' => 'Febrero', 'March' => 'Marzo',
|
||||||
|
'April' => 'Abril', 'May' => 'Mayo', 'June' => 'Junio',
|
||||||
|
'July' => 'Julio', 'August' => 'Agosto', 'September' => 'Septiembre',
|
||||||
|
'October' => 'Octubre', 'November' => 'Noviembre', 'December' => 'Diciembre'
|
||||||
|
];
|
||||||
|
|
||||||
|
$semanasAgrupadas = [];
|
||||||
|
for ($i = -4; $i <= 12; $i++) {
|
||||||
|
$semanaDomingo = clone $domingoActual;
|
||||||
|
$semanaDomingo->modify("+{$i} weeks");
|
||||||
|
|
||||||
|
$key = $semanaDomingo->format('Y-m');
|
||||||
|
$mesIngles = $semanaDomingo->format('F');
|
||||||
|
$mesEspanol = $mesesEspanol[$mesIngles] ?? $mesIngles;
|
||||||
|
$anio = $semanaDomingo->format('Y');
|
||||||
|
|
||||||
|
if (!isset($semanasAgrupadas[$key])) {
|
||||||
|
$semanasAgrupadas[$key] = [
|
||||||
|
'nombre' => "$mesEspanol $anio",
|
||||||
|
'semanas' => []
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$semanasAgrupadas[$key]['semanas'][] = [
|
||||||
|
'fecha' => $semanaDomingo->format('Y-m-d'),
|
||||||
|
'posicion' => calcularPosicionCiclo($semanaDomingo->format('Y-m-d'))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentPage = 'asignaciones';
|
||||||
|
$pageTitle = 'Asignación de Turnos';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Asignaciones - Contenedor Ibiza</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include BASE_PATH . '/public/partials/navbar.php'; ?>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h2 class="mb-4">Asignación de Turnos</h2>
|
||||||
|
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="alert alert-<?= $messageType ?>"><?= htmlspecialchars($message) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h5 class="mb-0">Asignación Actual (Semana <?= $posicionCicloActual ?> de 4)</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="mb-3">
|
||||||
|
<strong>Fecha:</strong> <?= date('d/m/y', strtotime($currentWeekStart)) ?> (Dom) - <?= date('d/m/y', strtotime('+5 days', strtotime($currentWeekStart))) ?> (Vie)
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php if ($asignacionActual): ?>
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>Asignado a:</strong> <?= htmlspecialchars($asignacionActual['nombre']) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" class="d-flex gap-2">
|
||||||
|
<?= CSRF::getTokenField() ?>
|
||||||
|
<input type="hidden" name="action" value="rotar">
|
||||||
|
<input type="hidden" name="semana" value="<?= $currentWeekStart ?>">
|
||||||
|
<button type="submit" class="btn btn-outline-primary">
|
||||||
|
↻ Rotar al siguiente
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="alert alert-warning">No hay asignación para esta semana</div>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="asignar">
|
||||||
|
<?= CSRF::getTokenField() ?>
|
||||||
|
<input type="hidden" name="semana" value="<?= $currentWeekStart ?>">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Asignar a:</label>
|
||||||
|
<select class="form-select" name="user_id" required>
|
||||||
|
<option value="">Seleccionar persona...</option>
|
||||||
|
<?php foreach ($ayudantes as $a): ?>
|
||||||
|
<option value="<?= $a['id'] ?>"><?= htmlspecialchars($a['nombre']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Asignar</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-secondary text-white">
|
||||||
|
<h5 class="mb-0">Historial de Asignaciones</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="GET" class="mb-3">
|
||||||
|
<label class="form-label">Seleccionar semana:</label>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<select class="form-select" name="semana" style="max-width: 320px;">
|
||||||
|
<?php foreach ($semanasAgrupadas as $grupo): ?>
|
||||||
|
<optgroup label="<?= $grupo['nombre'] ?>">
|
||||||
|
<?php foreach ($grupo['semanas'] as $s): ?>
|
||||||
|
<option value="<?= $s['fecha'] ?>" <?= $s['fecha'] === ($_GET['semana'] ?? $currentWeekStart) ? 'selected' : '' ?>>
|
||||||
|
Semana <?= $s['posicion'] ?> de 4 - <?= date('d/m', strtotime($s['fecha'])) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</optgroup>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<button type="submit" class="btn btn-outline-primary">Ver</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$semanaVer = $_GET['semana'] ?? $currentWeekStart;
|
||||||
|
$asignacionVer = $asignacionModel->getAsignacionPorSemana($semanaVer);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ($asignacionVer): ?>
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<?php $posicionSemanaVer = calcularPosicionCiclo($semanaVer); ?>
|
||||||
|
<strong>Semana <?= $posicionSemanaVer ?> de 4 (<?= date('d/m/y', strtotime($semanaVer)) ?>):</strong>
|
||||||
|
<?= htmlspecialchars($asignacionVer['nombre']) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST" class="d-flex gap-2">
|
||||||
|
<?= CSRF::getTokenField() ?>
|
||||||
|
<input type="hidden" name="action" value="asignar">
|
||||||
|
<input type="hidden" name="semana" value="<?= $semanaVer ?>">
|
||||||
|
<select class="form-select" name="user_id" style="max-width: 250px;">
|
||||||
|
<option value="">Cambiar persona...</option>
|
||||||
|
<?php foreach ($ayudantes as $a): ?>
|
||||||
|
<option value="<?= $a['id'] ?>" <?= isset($asignacionVer['user_id']) && $a['id'] == $asignacionVer['user_id'] ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($a['nombre']) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<button type="submit" class="btn btn-outline-secondary">Actualizar</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="alert alert-secondary">
|
||||||
|
<?php $posicionSinAsignar = calcularPosicionCiclo($semanaVer); ?>
|
||||||
|
No hay asignación para la semana <?= $posicionSinAsignar ?> de 4 (<?= date('d/m/y', strtotime($semanaVer)) ?>)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="asignar">
|
||||||
|
<input type="hidden" name="semana" value="<?= $semanaVer ?>">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<select class="form-select" name="user_id" style="max-width: 300px;" required>
|
||||||
|
<option value="">Seleccionar persona...</option>
|
||||||
|
<?php foreach ($ayudantes as $a): ?>
|
||||||
|
<option value="<?= $a['id'] ?>"><?= htmlspecialchars($a['nombre']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<button type="submit" class="btn btn-primary">Asignar</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Sección de Asignación Masiva -->
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
|
<div class="card shadow-sm mt-4">
|
||||||
|
<div class="card-header bg-success text-white">
|
||||||
|
<h5 class="mb-0">Asignación Masiva</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" id="asignacionMasivaForm">
|
||||||
|
<?= CSRF::getTokenField() ?>
|
||||||
|
<input type="hidden" name="action" value="asignar_masivo">
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">Semana de inicio:</label>
|
||||||
|
<input type="date" class="form-control" name="semana_inicio" required>
|
||||||
|
<small class="text-muted">Debe ser un domingo</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">Opciones:</label>
|
||||||
|
<div class="form-check mt-2">
|
||||||
|
<input class="form-check-input" type="checkbox" name="rotacion_automatica" id="rotacion_automatica">
|
||||||
|
<label class="form-check-label" for="rotacion_automatica">
|
||||||
|
Activar rotación automática para la siguiente semana
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Seleccionar ayudantes:</label>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="50">
|
||||||
|
<input type="checkbox" class="form-check-input" id="selectAll">
|
||||||
|
</th>
|
||||||
|
<th>Nombre</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Username</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($ayudantes as $a): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" class="form-check-input user-checkbox"
|
||||||
|
name="user_ids[]" value="<?= $a['id'] ?>">
|
||||||
|
</td>
|
||||||
|
<td><?= htmlspecialchars($a['nombre']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($a['email']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($a['username']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-success">
|
||||||
|
<i class="fas fa-users"></i> Asignar a seleccionados
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary" onclick="selectAllUsers()">
|
||||||
|
<i class="fas fa-check-square"></i> Seleccionar todos
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary" onclick="deselectAllUsers()">
|
||||||
|
<i class="fas fa-square"></i> Deseleccionar todos
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Sección de Rotación Automática -->
|
||||||
|
<div class="card shadow-sm mt-4">
|
||||||
|
<div class="card-header bg-warning text-dark">
|
||||||
|
<h5 class="mb-0">Rotación Automática</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h6>Orden de Rotación Actual:</h6>
|
||||||
|
<div class="d-flex flex-wrap gap-2 mb-3">
|
||||||
|
<?php
|
||||||
|
$ayudantesOrdenados = $asignacionModel->getAyudantesPorOrden();
|
||||||
|
foreach ($ayudantesOrdenados as $index => $ayudante):
|
||||||
|
?>
|
||||||
|
<span class="badge bg-primary fs-6">
|
||||||
|
<?= ($index + 1) ?>. <?= htmlspecialchars($ayudante['nombre']) ?>
|
||||||
|
</span>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<form method="POST" class="h-100 d-flex flex-column justify-content-center">
|
||||||
|
<?= CSRF::getTokenField() ?>
|
||||||
|
<input type="hidden" name="action" value="rotacion_automatica">
|
||||||
|
<button type="submit" class="btn btn-warning w-100">
|
||||||
|
<i class="fas fa-sync"></i> Generar Rotación Automática
|
||||||
|
</button>
|
||||||
|
<small class="text-muted mt-2">
|
||||||
|
Asigna automáticamente los próximos 12 semanas siguiendo el orden de rotación
|
||||||
|
</small>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-info mt-3 mb-0">
|
||||||
|
<strong>ℹ️ ¿Cómo funciona?</strong><br>
|
||||||
|
• El sistema mantiene un orden cíclico de ayudantes<br>
|
||||||
|
• Cada semana (Dom→Vie) asigna automáticamente al siguiente en la lista<br>
|
||||||
|
• Al agregar nuevos ayudantes, se integran automáticamente en el ciclo<br>
|
||||||
|
• Usa el botón para generar las próximas 12 semanas
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sección de Reordenar Rotación -->
|
||||||
|
<div class="card shadow-sm mt-4">
|
||||||
|
<div class="card-header bg-dark text-white">
|
||||||
|
<h5 class="mb-0">Reordenar Rotación</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" id="reordenarForm">
|
||||||
|
<?= CSRF::getTokenField() ?>
|
||||||
|
<input type="hidden" name="action" value="reordenar">
|
||||||
|
|
||||||
|
<p class="text-muted">
|
||||||
|
Arrastra los elementos para cambiar el orden de rotación.
|
||||||
|
Los cambios afectarán las asignaciones futuras.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul id="sortableList" class="list-group">
|
||||||
|
<?php
|
||||||
|
$ayudantesOrdenados = $asignacionModel->getAyudantesPorOrden();
|
||||||
|
foreach ($ayudantesOrdenados as $index => $ayudante):
|
||||||
|
?>
|
||||||
|
<li class="list-group-item d-flex align-items-center" data-id="<?= $ayudante['id'] ?>">
|
||||||
|
<input type="hidden" name="ordenes[<?= $index ?>]" value="<?= $ayudante['id'] ?>">
|
||||||
|
<span class="badge bg-primary me-2" style="cursor: grab;">☰ <?= ($index + 1) ?></span>
|
||||||
|
<span><?= htmlspecialchars($ayudante['nombre']) ?></span>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="mt-3 d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-dark">
|
||||||
|
<i class="fas fa-save"></i> Guardar Nuevo Orden
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary" onclick="invertirOrden()">
|
||||||
|
<i class="fas fa-exchange-alt"></i> Invertir Orden
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'reordenar'): ?>
|
||||||
|
<?php
|
||||||
|
$nuevosOrdenes = $_POST['ordenes'] ?? [];
|
||||||
|
$errores = [];
|
||||||
|
|
||||||
|
if (!empty($nuevosOrdenes)) {
|
||||||
|
foreach ($nuevosOrdenes as $index => $userId) {
|
||||||
|
$stmt = $asignacionModel->getDb()->prepare("
|
||||||
|
UPDATE rotacion_orden
|
||||||
|
SET orden = ?
|
||||||
|
WHERE user_id = ? AND activo = 1
|
||||||
|
");
|
||||||
|
$stmt->execute([$index + 1, $userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalcular asignaciones futuras
|
||||||
|
$resultado = $asignacionModel->recalcularAsignaciones(20);
|
||||||
|
|
||||||
|
if ($resultado['success'] > 0) {
|
||||||
|
// Redireccionar para recargar los datos actualizados
|
||||||
|
header("Location: " . $_SERVER['PHP_SELF'] . "?success=orden_actualizado");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||||
|
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Inicializar sortable
|
||||||
|
$(function() {
|
||||||
|
$("#sortableList").sortable({
|
||||||
|
placeholder: "ui-state-highlight",
|
||||||
|
update: function(event, ui) {
|
||||||
|
actualizarNumeros();
|
||||||
|
}
|
||||||
|
}).disableSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
function actualizarNumeros() {
|
||||||
|
$("#sortableList li").each(function(index) {
|
||||||
|
$(this).find('.badge').text('☰ ' + (index + 1));
|
||||||
|
// Actualizar el valor del input oculto con el nuevo orden
|
||||||
|
var userId = $(this).data('id');
|
||||||
|
$(this).find('input[type="hidden"]').attr('name', 'ordenes[' + index + ']').val(userId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function invertirOrden() {
|
||||||
|
var items = $("#sortableList li").get().reverse();
|
||||||
|
$("#sortableList").append(items);
|
||||||
|
actualizarNumeros();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seleccionar/deseleccionar todos
|
||||||
|
document.getElementById('selectAll').addEventListener('change', function() {
|
||||||
|
const checkboxes = document.querySelectorAll('.user-checkbox');
|
||||||
|
checkboxes.forEach(checkbox => checkbox.checked = this.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectAllUsers() {
|
||||||
|
document.querySelectorAll('.user-checkbox').forEach(checkbox => checkbox.checked = true);
|
||||||
|
document.getElementById('selectAll').checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function deselectAllUsers() {
|
||||||
|
document.querySelectorAll('.user-checkbox').forEach(checkbox => checkbox.checked = false);
|
||||||
|
document.getElementById('selectAll').checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validar que al menos un usuario esté seleccionado
|
||||||
|
document.getElementById('asignacionMasivaForm').addEventListener('submit', function(e) {
|
||||||
|
const selectedUsers = document.querySelectorAll('.user-checkbox:checked');
|
||||||
|
if (selectedUsers.length === 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
alert('Debes seleccionar al menos un ayudante');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Establecer fecha por defecto al domingo actual
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const hoy = new Date();
|
||||||
|
const diaSemana = hoy.getDay(); // 0 = domingo, 6 = sábado
|
||||||
|
const diasParaDomingo = diaSemana === 0 ? 0 : (7 - diaSemana);
|
||||||
|
const domingoActual = new Date(hoy);
|
||||||
|
domingoActual.setDate(hoy.getDate() - diasParaDomingo);
|
||||||
|
|
||||||
|
const fechaInput = document.querySelector('input[name="semana_inicio"]');
|
||||||
|
if (fechaInput) {
|
||||||
|
fechaInput.value = domingoActual.toISOString().split('T')[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
203
public/admin/coordinador.php
Normal file
203
public/admin/coordinador.php
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
<?php
|
||||||
|
if (!defined('BASE_PATH')) {
|
||||||
|
define('BASE_PATH', dirname(__DIR__, 2));
|
||||||
|
}
|
||||||
|
require_once BASE_PATH . '/config/config.php';
|
||||||
|
require_once BASE_PATH . '/src/Auth.php';
|
||||||
|
require_once BASE_PATH . '/src/User.php';
|
||||||
|
require_once BASE_PATH . '/src/DiasHorarios.php';
|
||||||
|
require_once BASE_PATH . '/src/Asignacion.php';
|
||||||
|
|
||||||
|
$auth = new Auth();
|
||||||
|
$auth->requireCoordinador();
|
||||||
|
|
||||||
|
$userModel = new User();
|
||||||
|
$horariosModel = new DiasHorarios();
|
||||||
|
$asignacionModel = new Asignacion();
|
||||||
|
|
||||||
|
// Estadísticas básicas
|
||||||
|
$totalUsuarios = count($userModel->getUsuariosGestion());
|
||||||
|
$totalHorarios = count($horariosModel->getActivos());
|
||||||
|
$asignacionActual = $asignacionModel->getAsignacionActual();
|
||||||
|
|
||||||
|
// Obtener orden actual de rotación
|
||||||
|
$ayudantesOrdenados = $asignacionModel->getAyudantesPorOrden();
|
||||||
|
|
||||||
|
$currentPage = 'dashboard';
|
||||||
|
$pageTitle = 'Panel Coordinador';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Panel Coordinador - Contenedor Ibiza</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.stat-card {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
.rol-badge {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include BASE_PATH . '/public/partials/navbar.php'; ?>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h2 class="mb-1">🎯 Panel Coordinador</h2>
|
||||||
|
<p class="text-muted mb-0">Gestión de turnos y ayudantes</p>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-info rol-badge">Coordinador</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<div class="card stat-card shadow-sm border-primary">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h3 class="text-primary"><?= $totalUsuarios ?></h3>
|
||||||
|
<p class="mb-0">Ayudantes Activos</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<div class="card stat-card shadow-sm border-success">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h3 class="text-success"><?= $totalHorarios ?></h3>
|
||||||
|
<p class="mb-0">Días Configurados</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<div class="card stat-card shadow-sm border-warning">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h3 class="text-warning"><?= count($ayudantesOrdenados) ?></h3>
|
||||||
|
<p class="mb-0">Orden Rotación</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h5 class="mb-0">📋 Turno Actual</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if ($asignacionActual): ?>
|
||||||
|
<div class="alert alert-success mb-3">
|
||||||
|
<h6 class="mb-1">✅ Esta semana:</h6>
|
||||||
|
<strong><?= htmlspecialchars($asignacionActual['nombre']) ?></strong>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="alert alert-warning mb-3">
|
||||||
|
<h6 class="mb-1">⚠️ Sin asignar</h6>
|
||||||
|
<span>No hay ayudante asignado esta semana</span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<a href="/admin/asignaciones.php" class="btn btn-primary">
|
||||||
|
🔄 Gestionar Turnos
|
||||||
|
</a>
|
||||||
|
<a href="/ayudante.php" class="btn btn-outline-primary">
|
||||||
|
👥 Vista Ayudante
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-success text-white">
|
||||||
|
<h5 class="mb-0">👥 Ayudantes</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="mb-3">Orden de Rotación:</h6>
|
||||||
|
<div class="d-flex flex-wrap gap-2 mb-3">
|
||||||
|
<?php foreach ($ayudantesOrdenados as $index => $ayudante): ?>
|
||||||
|
<span class="badge bg-secondary">
|
||||||
|
<?= ($index + 1) ?>. <?= htmlspecialchars($ayudante['nombre']) ?>
|
||||||
|
</span>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<a href="/admin/usuarios.php" class="btn btn-success">
|
||||||
|
➕ Agregar Ayudante
|
||||||
|
</a>
|
||||||
|
<a href="/admin/asignaciones_completo.php#reordenar" class="btn btn-outline-success">
|
||||||
|
🔄 Modificar Orden
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h5 class="mb-0">🎯 Acciones Rápidas</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<div class="text-center p-3">
|
||||||
|
<div class="mb-3">🔄</div>
|
||||||
|
<h6>Generar Turnos</h6>
|
||||||
|
<p class="text-muted small">Crear próximas semanas automáticamente</p>
|
||||||
|
<a href="/admin/asignaciones_completo.php" class="btn btn-sm btn-primary">
|
||||||
|
Generar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<div class="text-center p-3">
|
||||||
|
<div class="mb-3">📄</div>
|
||||||
|
<h6>Exportar PDF</h6>
|
||||||
|
<p class="text-muted small">Descargar turnos en formato PDF</p>
|
||||||
|
<a href="/export-pdf.php" target="_blank" class="btn btn-sm btn-danger">
|
||||||
|
Exportar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<div class="text-center p-3">
|
||||||
|
<div class="mb-3">👥</div>
|
||||||
|
<h6>Ver Turnos</h6>
|
||||||
|
<p class="text-muted small">Ver asignaciones actuales</p>
|
||||||
|
<a href="/ayudante.php" class="btn btn-sm btn-info">
|
||||||
|
Ver Turnos
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<div class="text-center p-3">
|
||||||
|
<div class="mb-3">🔧</div>
|
||||||
|
<h6>Gestionar</h6>
|
||||||
|
<p class="text-muted small">Administrar todo</p>
|
||||||
|
<a href="/admin/asignaciones_completo.php" class="btn btn-sm btn-secondary">
|
||||||
|
Gestionar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -18,11 +18,11 @@ $asignacionModel = new Asignacion();
|
|||||||
$db = \Database::getInstance()->getConnection();
|
$db = \Database::getInstance()->getConnection();
|
||||||
|
|
||||||
$totalUsuarios = count($userModel->getAll());
|
$totalUsuarios = count($userModel->getAll());
|
||||||
$totalAyudantes = count($userModel->getAyudantesActivos());
|
$totalAyudantes = count($userModel->getUsuariosGestion());
|
||||||
$totalHorarios = count($horariosModel->getAll());
|
$totalHorarios = count($horariosModel->getAll());
|
||||||
$asignacionActual = $asignacionModel->getAsignacionActual();
|
$asignacionActual = $asignacionModel->getAsignacionActual();
|
||||||
|
|
||||||
$ayudantes = $userModel->getAyudantesActivos();
|
$ayudantes = $asignacionModel->getAyudantesPorOrden();
|
||||||
$horarios = $horariosModel->getAll();
|
$horarios = $horariosModel->getAll();
|
||||||
$asignaciones = $asignacionModel->getTodasAsignaciones();
|
$asignaciones = $asignacionModel->getTodasAsignaciones();
|
||||||
|
|
||||||
@@ -89,6 +89,40 @@ foreach ($asignacionesLimit as $a) {
|
|||||||
|
|
||||||
$html .= '</tbody></table>';
|
$html .= '</tbody></table>';
|
||||||
|
|
||||||
|
$html .= '<h2>Turnos de Ayudantes</h2>';
|
||||||
|
$html .= '<table>';
|
||||||
|
$html .= '<thead><tr><th>Ayudante</th><th class="text-center">Fecha 1</th><th class="text-center">Fecha 2</th><th class="text-center">Fecha 3</th><th class="text-center">Fecha 4</th></tr></thead>';
|
||||||
|
$html .= '<tbody>';
|
||||||
|
|
||||||
|
foreach ($ayudantes as $ayudante) {
|
||||||
|
$stmt = $db->prepare("
|
||||||
|
SELECT semana_inicio, semana_fin
|
||||||
|
FROM asignaciones_turnos
|
||||||
|
WHERE user_id = ? AND semana_inicio >= CURDATE()
|
||||||
|
ORDER BY semana_inicio
|
||||||
|
LIMIT 4
|
||||||
|
");
|
||||||
|
$stmt->execute([$ayudante['id']]);
|
||||||
|
$turnos = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$html .= '<tr>';
|
||||||
|
$html .= '<td>' . htmlspecialchars($ayudante['nombre']) . '</td>';
|
||||||
|
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
$html .= '<td class="text-center">';
|
||||||
|
if (isset($turnos[$i])) {
|
||||||
|
$html .= date('d/m/Y', strtotime($turnos[$i]['semana_inicio'])) . ' - ';
|
||||||
|
$html .= date('d/m/Y', strtotime($turnos[$i]['semana_fin']));
|
||||||
|
} else {
|
||||||
|
$html .= '-';
|
||||||
|
}
|
||||||
|
$html .= '</td>';
|
||||||
|
}
|
||||||
|
$html .= '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= '</tbody></table>';
|
||||||
|
|
||||||
$html .= PDFGenerator::getFooter();
|
$html .= PDFGenerator::getFooter();
|
||||||
|
|
||||||
$pdf = new PDFGenerator();
|
$pdf = new PDFGenerator();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ $horariosModel = new DiasHorarios();
|
|||||||
$asignacionModel = new Asignacion();
|
$asignacionModel = new Asignacion();
|
||||||
|
|
||||||
$totalUsuarios = count($userModel->getAll());
|
$totalUsuarios = count($userModel->getAll());
|
||||||
$totalAyudantes = count($userModel->getAyudantesActivos());
|
$totalAyudantes = count($userModel->getUsuariosGestion());
|
||||||
$totalHorarios = count($horariosModel->getAll());
|
$totalHorarios = count($horariosModel->getAll());
|
||||||
$asignacionActual = $asignacionModel->getAsignacionActual();
|
$asignacionActual = $asignacionModel->getAsignacionActual();
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ require_once BASE_PATH . '/src/CSRF.php';
|
|||||||
require_once BASE_PATH . '/src/Session.php';
|
require_once BASE_PATH . '/src/Session.php';
|
||||||
|
|
||||||
$auth = new Auth();
|
$auth = new Auth();
|
||||||
$auth->requireAdmin();
|
$auth->requireCoordinador();
|
||||||
|
|
||||||
$userModel = new User();
|
$userModel = new User();
|
||||||
$message = '';
|
$message = '';
|
||||||
@@ -51,13 +51,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$password = $_POST['password'] ?? '';
|
$password = $_POST['password'] ?? '';
|
||||||
$rol = $_POST['rol'] ?? 'ayudante';
|
$rol = $_POST['rol'] ?? 'ayudante';
|
||||||
|
|
||||||
if (empty($nombre) || empty($email)) {
|
// Obtener usuario actual para verificar su rol
|
||||||
|
$usuarioActual = $userModel->getById($id);
|
||||||
|
|
||||||
|
// Prevenir que coordinadores editen administradores
|
||||||
|
if ($usuarioActual && $usuarioActual['rol'] === 'admin' && !$auth->isAdmin()) {
|
||||||
|
$message = 'No tienes permisos para editar administradores';
|
||||||
|
$messageType = 'danger';
|
||||||
|
}
|
||||||
|
// Solo los administradores pueden asignar rol de administrador
|
||||||
|
elseif ($rol === 'admin' && !$auth->isAdmin()) {
|
||||||
|
$rol = 'coordinador';
|
||||||
|
}
|
||||||
|
elseif (empty($nombre) || empty($email)) {
|
||||||
$message = 'Nombre y email son obligatorios';
|
$message = 'Nombre y email son obligatorios';
|
||||||
$messageType = 'danger';
|
$messageType = 'danger';
|
||||||
} elseif ($userModel->usernameExists($username, $id)) {
|
}
|
||||||
|
elseif ($userModel->usernameExists($username, $id)) {
|
||||||
$message = 'El username ya está en uso';
|
$message = 'El username ya está en uso';
|
||||||
$messageType = 'danger';
|
$messageType = 'danger';
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
$userModel->update($id, compact('nombre', 'email', 'username', 'password', 'rol'));
|
$userModel->update($id, compact('nombre', 'email', 'username', 'password', 'rol'));
|
||||||
$message = 'Usuario actualizado exitosamente';
|
$message = 'Usuario actualizado exitosamente';
|
||||||
$messageType = 'success';
|
$messageType = 'success';
|
||||||
@@ -65,6 +79,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
} elseif ($action === 'toggle') {
|
} elseif ($action === 'toggle') {
|
||||||
$id = $_POST['id'] ?? 0;
|
$id = $_POST['id'] ?? 0;
|
||||||
$user = $userModel->getById($id);
|
$user = $userModel->getById($id);
|
||||||
|
|
||||||
|
// Prevenir que coordinadores desactiven administradores
|
||||||
|
if ($user && $user['rol'] === 'admin' && !$auth->isAdmin()) {
|
||||||
|
$message = 'No tienes permisos para modificar administradores';
|
||||||
|
$messageType = 'danger';
|
||||||
|
}
|
||||||
|
elseif ($user && $user['id'] == Session::get('user_id')) {
|
||||||
|
$message = 'No puedes desactivar tu propio usuario';
|
||||||
|
$messageType = 'danger';
|
||||||
|
}
|
||||||
|
else {
|
||||||
if ($user) {
|
if ($user) {
|
||||||
if ($user['activo']) {
|
if ($user['activo']) {
|
||||||
$userModel->deactivate($id);
|
$userModel->deactivate($id);
|
||||||
@@ -76,9 +101,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$users = $userModel->getAll(true);
|
$users = $userModel->getAll(true);
|
||||||
|
|
||||||
|
// Filtrar administradores para coordinadores
|
||||||
|
if ($auth->isCoordinador() && !$auth->isAdmin()) {
|
||||||
|
$users = array_filter($users, function($user) {
|
||||||
|
return $user['rol'] !== 'admin';
|
||||||
|
});
|
||||||
|
}
|
||||||
$currentPage = 'usuarios';
|
$currentPage = 'usuarios';
|
||||||
$pageTitle = 'Gestión de Usuarios';
|
$pageTitle = 'Gestión de Usuarios';
|
||||||
?>
|
?>
|
||||||
@@ -126,8 +159,19 @@ $pageTitle = 'Gestión de Usuarios';
|
|||||||
<td><?= htmlspecialchars($u['nombre']) ?></td>
|
<td><?= htmlspecialchars($u['nombre']) ?></td>
|
||||||
<td><?= htmlspecialchars($u['email']) ?></td>
|
<td><?= htmlspecialchars($u['email']) ?></td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge bg-<?= $u['rol'] === 'admin' ? 'danger' : 'primary' ?>">
|
<?php
|
||||||
<?= ucfirst($u['rol']) ?>
|
$badgeClass = 'primary';
|
||||||
|
if ($u['rol'] === 'admin') $badgeClass = 'danger';
|
||||||
|
elseif ($u['rol'] === 'coordinador') $badgeClass = 'success';
|
||||||
|
?>
|
||||||
|
<span class="badge bg-<?= $badgeClass ?>">
|
||||||
|
<?php
|
||||||
|
if ($u['rol'] === 'coordinador') {
|
||||||
|
echo '🎯 Coordinador';
|
||||||
|
} else {
|
||||||
|
echo ucfirst($u['rol']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -140,7 +184,12 @@ $pageTitle = 'Gestión de Usuarios';
|
|||||||
onclick="editUser(<?= $u['id'] ?>, '<?= htmlspecialchars($u['nombre']) ?>', '<?= htmlspecialchars($u['email']) ?>', '<?= htmlspecialchars($u['username'] ?? '') ?>', '<?= $u['rol'] ?>')">
|
onclick="editUser(<?= $u['id'] ?>, '<?= htmlspecialchars($u['nombre']) ?>', '<?= htmlspecialchars($u['email']) ?>', '<?= htmlspecialchars($u['username'] ?? '') ?>', '<?= $u['rol'] ?>')">
|
||||||
Editar
|
Editar
|
||||||
</button>
|
</button>
|
||||||
<?php if ($u['id'] != Session::get('user_id')): ?>
|
<?php
|
||||||
|
// Determinar si puede desactivar este usuario
|
||||||
|
$puedeDesactivar = ($u['id'] != Session::get('user_id')) && $u['rol'] !== 'admin';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ($puedeDesactivar): ?>
|
||||||
<form method="POST" class="d-inline">
|
<form method="POST" class="d-inline">
|
||||||
<?= CSRF::getTokenField() ?>
|
<?= CSRF::getTokenField() ?>
|
||||||
<input type="hidden" name="action" value="toggle">
|
<input type="hidden" name="action" value="toggle">
|
||||||
@@ -194,7 +243,10 @@ $pageTitle = 'Gestión de Usuarios';
|
|||||||
<label for="rol" class="form-label">Rol</label>
|
<label for="rol" class="form-label">Rol</label>
|
||||||
<select class="form-select" id="rol" name="rol">
|
<select class="form-select" id="rol" name="rol">
|
||||||
<option value="ayudante">Ayudante</option>
|
<option value="ayudante">Ayudante</option>
|
||||||
|
<option value="coordinador">🎯 Coordinador</option>
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
<option value="admin">Administrador</option>
|
<option value="admin">Administrador</option>
|
||||||
|
<?php endif; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../src/Auth.php';
|
if (!defined('BASE_PATH')) {
|
||||||
require_once __DIR__ . '/../src/User.php';
|
define('BASE_PATH', dirname(__DIR__));
|
||||||
require_once __DIR__ . '/../src/DiasHorarios.php';
|
}
|
||||||
require_once __DIR__ . '/../src/Asignacion.php';
|
require_once BASE_PATH . '/src/Auth.php';
|
||||||
require_once __DIR__ . '/../src/Database.php';
|
require_once BASE_PATH . '/src/User.php';
|
||||||
|
require_once BASE_PATH . '/src/DiasHorarios.php';
|
||||||
|
require_once BASE_PATH . '/src/Asignacion.php';
|
||||||
|
require_once BASE_PATH . '/src/Database.php';
|
||||||
|
|
||||||
$auth = new Auth();
|
$auth = new Auth();
|
||||||
$auth->requireAuth();
|
$auth->requireAuth();
|
||||||
|
|
||||||
if ($auth->isAdmin()) {
|
// Permitir acceso a administradores para que puedan ver la vista de ayudante
|
||||||
header('Location: /admin/index.php');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $auth->getCurrentUser();
|
$user = $auth->getCurrentUser();
|
||||||
$horariosModel = new DiasHorarios();
|
$horariosModel = new DiasHorarios();
|
||||||
$asignacionModel = new Asignacion();
|
$asignacionModel = new Asignacion();
|
||||||
$db = Database::getInstance()->getConnection();
|
$db = Database::getInstance()->getConnection();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$horarios = $horariosModel->getActivos();
|
$horarios = $horariosModel->getActivos();
|
||||||
$asignacionActual = $asignacionModel->getAsignacionActual();
|
$asignacionActual = $asignacionModel->getAsignacionActual();
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ foreach ($semanasFuturas as $semana) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$userModel = new User();
|
$userModel = new User();
|
||||||
$ayudantes = $userModel->getAyudantesActivos();
|
$ayudantes = $asignacionModel->getAyudantesPorOrden();
|
||||||
|
|
||||||
$domingo = new DateTime();
|
$domingo = new DateTime();
|
||||||
$domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
$domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
||||||
@@ -72,6 +74,13 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<?php
|
||||||
|
// Si es administrador o coordinador, usar el navbar del admin
|
||||||
|
if ($auth->isAdmin() || $auth->isCoordinador()) {
|
||||||
|
include BASE_PATH . '/public/partials/navbar.php';
|
||||||
|
} else {
|
||||||
|
// Si es ayudante, usar navbar simple
|
||||||
|
?>
|
||||||
<nav class="navbar navbar-dark bg-primary">
|
<nav class="navbar navbar-dark bg-primary">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" href="/ayudante.php">Contenedor Ibiza</a>
|
<a class="navbar-brand" href="/ayudante.php">Contenedor Ibiza</a>
|
||||||
@@ -82,14 +91,78 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
<a href="/logout.php" class="btn btn-outline-light btn-sm">Cerrar Sesión</a>
|
<a href="/logout.php" class="btn btn-outline-light btn-sm">Cerrar Sesión</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<?php if ($auth->isAdmin() || $auth->isCoordinador()): ?>
|
||||||
|
<h2 class="mb-1">👥 Vista de Ayudante</h2>
|
||||||
|
<p class="text-muted mb-0">Viendo la interfaz que ven los ayudantes</p>
|
||||||
|
<?php else: ?>
|
||||||
<h2 class="mb-0">Mis Turnos</h2>
|
<h2 class="mb-0">Mis Turnos</h2>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<?php if ($auth->isAdmin() || $auth->isCoordinador()): ?>
|
||||||
|
<a href="/admin/asignaciones.php" class="btn btn-outline-secondary btn-sm me-2">
|
||||||
|
⚙️ Gestionar Turnos
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
<a href="/export-pdf.php" target="_blank" class="btn btn-danger btn-sm">
|
<a href="/export-pdf.php" target="_blank" class="btn btn-danger btn-sm">
|
||||||
📄 Exportar PDF
|
📄 Exportar PDF
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($auth->isCoordinador()): ?>
|
||||||
|
<!-- SECCIÓN ESPECIAL PARA COORDINADORES -->
|
||||||
|
<div class="card shadow-sm mb-4 border-success">
|
||||||
|
<div class="card-header bg-success text-white">
|
||||||
|
<h5 class="mb-0">🎯 Panel de Coordinador</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<h6 class="mb-2">⚡ Acciones Rápidas</h6>
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<a href="/admin/asignaciones.php" class="btn btn-primary btn-sm">
|
||||||
|
🔄 Gestionar Turnos
|
||||||
|
</a>
|
||||||
|
<a href="/admin/usuarios.php" class="btn btn-success btn-sm">
|
||||||
|
👥 Agregar Ayudante
|
||||||
|
</a>
|
||||||
|
<a href="/admin/asignaciones_completo.php#reordenar" class="btn btn-outline-primary btn-sm">
|
||||||
|
📋 Modificar Orden
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<h6 class="mb-2">📈 Estadísticas</h6>
|
||||||
|
<div class="row text-center">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="border rounded p-2 mb-2">
|
||||||
|
<div class="h4 mb-0 text-primary"><?= count($ayudantes) ?></div>
|
||||||
|
<small class="text-muted">Ayudantes</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="border rounded p-2 mb-2">
|
||||||
|
<div class="h4 mb-0 text-success"><?= count($misAsignacionesFuturas) ?></div>
|
||||||
|
<small class="text-muted">Tus turnos</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="/export-pdf.php" target="_blank" class="btn btn-danger btn-sm">
|
||||||
|
📄 Exportar PDF
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$hoy = new DateTime();
|
$hoy = new DateTime();
|
||||||
@@ -109,6 +182,8 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
</div>
|
</div>
|
||||||
<?php elseif ($asignacionEstaSemana): ?>
|
<?php elseif ($asignacionEstaSemana): ?>
|
||||||
<div class="alert alert-secondary mb-4">
|
<div class="alert alert-secondary mb-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
|
<div>
|
||||||
<strong>Turno esta semana:</strong> <?= htmlspecialchars($asignacionEstaSemana['nombre']) ?><br>
|
<strong>Turno esta semana:</strong> <?= htmlspecialchars($asignacionEstaSemana['nombre']) ?><br>
|
||||||
<?php if (!empty($misAsignacionesFuturas)): ?>
|
<?php if (!empty($misAsignacionesFuturas)): ?>
|
||||||
Tu proximo turno: <?= date('d/m/y', strtotime($misAsignacionesFuturas[0]['semana']['inicio'])) ?>
|
Tu proximo turno: <?= date('d/m/y', strtotime($misAsignacionesFuturas[0]['semana']['inicio'])) ?>
|
||||||
@@ -117,16 +192,47 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
Tu proximo turno sera en las proximas semanas.
|
Tu proximo turno sera en las proximas semanas.
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if ($auth->isCoordinador()): ?>
|
||||||
|
<div class="text-end">
|
||||||
|
<a href="/admin/asignaciones.php" class="btn btn-sm btn-outline-primary">
|
||||||
|
🔄 Cambiar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<?php elseif (!empty($misAsignacionesFuturas)): ?>
|
<?php elseif (!empty($misAsignacionesFuturas)): ?>
|
||||||
<div class="alert alert-info mb-4">
|
<div class="alert alert-info mb-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
|
<div>
|
||||||
<strong>Proximo turno:</strong><br>
|
<strong>Proximo turno:</strong><br>
|
||||||
Del <?= date('d/m/y', strtotime($misAsignacionesFuturas[0]['semana']['inicio'])) ?>
|
Del <?= date('d/m/y', strtotime($misAsignacionesFuturas[0]['semana']['inicio'])) ?>
|
||||||
al <?= date('d/m/y', strtotime($misAsignacionesFuturas[0]['semana']['fin'])) ?>
|
al <?= date('d/m/y', strtotime($misAsignacionesFuturas[0]['semana']['fin'])) ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if ($auth->isCoordinador()): ?>
|
||||||
|
<div class="text-end">
|
||||||
|
<a href="/admin/asignaciones_completo.php" class="btn btn-sm btn-outline-primary">
|
||||||
|
📋 Gestionar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="alert alert-warning mb-4">
|
<div class="alert alert-warning mb-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
|
<div>
|
||||||
No hay turnos asignados para las proximas semanas.
|
No hay turnos asignados para las proximas semanas.
|
||||||
</div>
|
</div>
|
||||||
|
<?php if ($auth->isCoordinador()): ?>
|
||||||
|
<div class="text-end">
|
||||||
|
<a href="/admin/asignaciones.php" class="btn btn-sm btn-success">
|
||||||
|
🚀 Generar Turnos
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
@@ -151,6 +257,7 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
<thead class="table-light">
|
<thead class="table-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center" style="min-width: 120px;">Semana</th>
|
<th class="text-center" style="min-width: 120px;">Semana</th>
|
||||||
|
<th class="text-center" style="min-width: 120px;">Ayudante</th>
|
||||||
<?php foreach ($diasOrden as $dia): ?>
|
<?php foreach ($diasOrden as $dia): ?>
|
||||||
<th class="text-center"><?= $diasNombres[$dia] ?></th>
|
<th class="text-center"><?= $diasNombres[$dia] ?></th>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
@@ -172,6 +279,21 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
<span class="badge bg-success ms-1">Tu turno</span>
|
<span class="badge bg-success ms-1">Tu turno</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center align-middle">
|
||||||
|
<?php if (!empty($semana['asignaciones'])): ?>
|
||||||
|
<?php foreach ($semana['asignaciones'] as $asignacion): ?>
|
||||||
|
<span class="badge <?= $asignacion['id'] == $user['id'] ? 'bg-success' : 'bg-secondary' ?> mb-1">
|
||||||
|
<?= htmlspecialchars($asignacion['nombre']) ?>
|
||||||
|
</span>
|
||||||
|
<?php if ($asignacion['id'] == $user['id']): ?>
|
||||||
|
<span class="badge bg-warning text-dark">Tú</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<br>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="text-muted">Sin asignación</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
<?php foreach ($diasOrden as $dia): ?>
|
<?php foreach ($diasOrden as $dia): ?>
|
||||||
<?php
|
<?php
|
||||||
$horarioDia = null;
|
$horarioDia = null;
|
||||||
@@ -227,6 +349,9 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
<th class="text-center">Fecha 2</th>
|
<th class="text-center">Fecha 2</th>
|
||||||
<th class="text-center">Fecha 3</th>
|
<th class="text-center">Fecha 3</th>
|
||||||
<th class="text-center">Fecha 4</th>
|
<th class="text-center">Fecha 4</th>
|
||||||
|
<?php if ($auth->isCoordinador()): ?>
|
||||||
|
<th class="text-center">Acciones</th>
|
||||||
|
<?php endif; ?>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -261,6 +386,13 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<?php endfor; ?>
|
<?php endfor; ?>
|
||||||
|
<?php if ($auth->isCoordinador()): ?>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="/admin/asignaciones_completo.php" class="btn btn-sm btn-outline-primary">
|
||||||
|
🔄 Gestionar
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<?php endif; ?>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -268,6 +400,39 @@ $domingo->modify('-' . (int)$domingo->format('w') . ' days');
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php if ($auth->isCoordinador()): ?>
|
||||||
|
<!-- BARRA DE ACCIONES PARA COORDINADORES -->
|
||||||
|
<div class="card mt-4 shadow-sm border-success">
|
||||||
|
<div class="card-header bg-success text-white">
|
||||||
|
<h5 class="mb-0">🎯 Acciones de Coordinador</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row text-center">
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<a href="/admin/coordinador.php" class="btn btn-success w-100">
|
||||||
|
📊 Mi Panel
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<a href="/admin/asignaciones.php" class="btn btn-primary w-100">
|
||||||
|
🔄 Gestionar Turnos
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<a href="/admin/usuarios.php" class="btn btn-outline-success w-100">
|
||||||
|
👥 Agregar Ayudante
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<a href="/export-pdf.php" target="_blank" class="btn btn-danger w-100">
|
||||||
|
📄 Exportar PDF
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ $db = Database::getInstance()->getConnection();
|
|||||||
|
|
||||||
$horarios = $horariosModel->getActivos();
|
$horarios = $horariosModel->getActivos();
|
||||||
$userModel = new User();
|
$userModel = new User();
|
||||||
$ayudantes = $userModel->getAyudantesActivos();
|
$ayudantes = $asignacionModel->getAyudantesPorOrden();
|
||||||
|
|
||||||
$semanasFuturas = [];
|
$semanasFuturas = [];
|
||||||
$hoy = new DateTime();
|
$hoy = new DateTime();
|
||||||
@@ -127,6 +127,44 @@ foreach ($semanasFuturas as $index => $semana) {
|
|||||||
$html .= '</tr>';
|
$html .= '</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$html .= '</tbody></table>';
|
||||||
|
|
||||||
|
$html .= '<h2>Turnos de Ayudantes</h2>';
|
||||||
|
$html .= '<table>';
|
||||||
|
$html .= '<thead><tr><th>Ayudante</th><th class="text-center">Fecha 1</th><th class="text-center">Fecha 2</th><th class="text-center">Fecha 3</th><th class="text-center">Fecha 4</th></tr></thead>';
|
||||||
|
$html .= '<tbody>';
|
||||||
|
|
||||||
|
foreach ($ayudantes as $ayudante) {
|
||||||
|
$stmt = $db->prepare("
|
||||||
|
SELECT semana_inicio, semana_fin
|
||||||
|
FROM asignaciones_turnos
|
||||||
|
WHERE user_id = ? AND semana_inicio >= CURDATE()
|
||||||
|
ORDER BY semana_inicio
|
||||||
|
LIMIT 4
|
||||||
|
");
|
||||||
|
$stmt->execute([$ayudante['id']]);
|
||||||
|
$turnos = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$html .= '<tr class="' . ($ayudante['id'] == $user['id'] ? 'table-success' : '') . '">';
|
||||||
|
$html .= '<td>' . htmlspecialchars($ayudante['nombre']);
|
||||||
|
if ($ayudante['id'] == $user['id']) {
|
||||||
|
$html .= ' <span class="badge badge-success">Tu</span>';
|
||||||
|
}
|
||||||
|
$html .= '</td>';
|
||||||
|
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
$html .= '<td class="text-center">';
|
||||||
|
if (isset($turnos[$i])) {
|
||||||
|
$html .= date('d/m/Y', strtotime($turnos[$i]['semana_inicio'])) . ' - ';
|
||||||
|
$html .= date('d/m/Y', strtotime($turnos[$i]['semana_fin']));
|
||||||
|
} else {
|
||||||
|
$html .= '-';
|
||||||
|
}
|
||||||
|
$html .= '</td>';
|
||||||
|
}
|
||||||
|
$html .= '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
$html .= '</tbody></table>';
|
$html .= '</tbody></table>';
|
||||||
$html .= PDFGenerator::getFooter();
|
$html .= PDFGenerator::getFooter();
|
||||||
|
|
||||||
|
|||||||
@@ -15,35 +15,70 @@ $dbName = getenv('DB_NAME') ?: 'No configurado';
|
|||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav me-auto">
|
<ul class="navbar-nav me-auto">
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= $currentPage === 'dashboard' ? 'active' : '' ?>" href="/admin/index.php">Dashboard</a>
|
<a class="nav-link <?= $currentPage === 'dashboard' ? 'active' : '' ?>" href="/admin/index.php">Dashboard</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= $currentPage === 'coordinador' ? 'active' : '' ?>" href="/admin/coordinador.php">🎯 Panel</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= $currentPage === 'usuarios' ? 'active' : '' ?>" href="/admin/usuarios.php">Usuarios</a>
|
<a class="nav-link <?= $currentPage === 'usuarios' ? 'active' : '' ?>" href="/admin/usuarios.php">Usuarios</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= $currentPage === 'horarios' ? 'active' : '' ?>" href="/admin/horarios.php">Horarios</a>
|
<a class="nav-link <?= $currentPage === 'horarios' ? 'active' : '' ?>" href="/admin/horarios.php">Horarios</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= $currentPage === 'asignaciones' ? 'active' : '' ?>" href="/admin/asignaciones.php">Asignaciones</a>
|
<a class="nav-link <?= $currentPage === 'asignaciones' ? 'active' : '' ?>" href="/admin/asignaciones.php">Asignaciones</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= $currentPage === 'vista-ayudante' ? 'active' : '' ?>" href="/ayudante.php">👥 Vista Ayudante</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= $currentPage === 'webhook' ? 'active' : '' ?>" href="/admin/webhook.php">🤖 Bot</a>
|
<a class="nav-link <?= $currentPage === 'webhook' ? 'active' : '' ?>" href="/admin/webhook.php">🤖 Bot</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link <?= $currentPage === 'logs' ? 'active' : '' ?>" href="/admin/logs.php">Logs</a>
|
<a class="nav-link <?= $currentPage === 'logs' ? 'active' : '' ?>" href="/admin/logs.php">Logs</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= $currentPage === 'logs' ? 'active' : '' ?>" href="/admin/logs.php">Logs</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
|
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
|
||||||
<?= htmlspecialchars($user['nombre'] ?? 'Usuario') ?>
|
<?= htmlspecialchars($user['nombre'] ?? 'Usuario') ?>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><span class="dropdown-item-text d-block"><strong>Usuario:</strong> <?= htmlspecialchars($user['nombre'] ?? 'Usuario') ?></span></li>
|
<li><span class="dropdown-item-text d-block"><strong>Usuario:</strong> <?= htmlspecialchars($user['nombre'] ?? 'Usuario') ?></span></li>
|
||||||
<li><span class="dropdown-item-text d-block"><strong>Rol:</strong> <?= htmlspecialchars(ucfirst($user['rol'] ?? '')) ?></span></li>
|
<li><span class="dropdown-item-text d-block"><strong>Rol:</strong>
|
||||||
|
<?php
|
||||||
|
$rol = $user['rol'] ?? '';
|
||||||
|
if ($rol === 'admin') {
|
||||||
|
echo 'Administrador';
|
||||||
|
} elseif ($rol === 'coordinador') {
|
||||||
|
echo '🎯 Coordinador';
|
||||||
|
} else {
|
||||||
|
echo htmlspecialchars(ucfirst($rol));
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</span></li>
|
||||||
|
<?php if ($auth->isAdmin()): ?>
|
||||||
<li><span class="dropdown-item-text d-block small text-muted"><strong>DB Host:</strong> <?= htmlspecialchars($dbHost) ?></span></li>
|
<li><span class="dropdown-item-text d-block small text-muted"><strong>DB Host:</strong> <?= htmlspecialchars($dbHost) ?></span></li>
|
||||||
<li><span class="dropdown-item-text d-block small text-muted"><strong>DB Name:</strong> <?= htmlspecialchars($dbName) ?></span></li>
|
<li><span class="dropdown-item-text d-block small text-muted"><strong>DB Name:</strong> <?= htmlspecialchars($dbName) ?></span></li>
|
||||||
|
<?php endif; ?>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<?php if ($user['rol'] ?? '' === 'ayudante'): ?>
|
<?php if ($user['rol'] ?? '' === 'ayudante'): ?>
|
||||||
<li><a class="dropdown-item" href="/cambiar-password.php">Cambiar Contraseña</a></li>
|
<li><a class="dropdown-item" href="/cambiar-password.php">Cambiar Contraseña</a></li>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ fi
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${GREEN}Building image...${NC}"
|
echo -e "${GREEN}Building image...${NC}"
|
||||||
docker build -t "$FULL_IMAGE" .
|
docker build --no-cache -t "$FULL_IMAGE" .
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
read -p "¿Subir imagen al registry? (s/n): " push_confirm
|
read -p "¿Subir imagen al registry? (s/n): " push_confirm
|
||||||
|
|||||||
0
scripts/generate-env.php
Normal file → Executable file
0
scripts/generate-env.php
Normal file → Executable file
@@ -221,7 +221,7 @@ public function asignar($userId, $semanaInicio) {
|
|||||||
SELECT u.*, ro.orden
|
SELECT u.*, ro.orden
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN rotacion_orden ro ON u.id = ro.user_id AND ro.activo = 1
|
LEFT JOIN rotacion_orden ro ON u.id = ro.user_id AND ro.activo = 1
|
||||||
WHERE u.rol = 'ayudante' AND u.activo = 1
|
WHERE (u.rol = 'ayudante' OR u.rol = 'coordinador') AND u.activo = 1
|
||||||
ORDER BY COALESCE(ro.orden, 999), u.nombre
|
ORDER BY COALESCE(ro.orden, 999), u.nombre
|
||||||
");
|
");
|
||||||
return $stmt->fetchAll();
|
return $stmt->fetchAll();
|
||||||
|
|||||||
42
src/Auth.php
42
src/Auth.php
@@ -10,6 +10,31 @@ class Auth {
|
|||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->userModel = new User();
|
$this->userModel = new User();
|
||||||
Session::init();
|
Session::init();
|
||||||
|
$this->refreshUserSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresca los datos del usuario en la sesión directamente desde la base de datos
|
||||||
|
* para que los cambios de rol sean instantáneos.
|
||||||
|
*/
|
||||||
|
private function refreshUserSession() {
|
||||||
|
if ($this->isLoggedIn()) {
|
||||||
|
$userId = Session::get('user_id');
|
||||||
|
$user = $this->userModel->getById($userId);
|
||||||
|
|
||||||
|
if (!$user || (isset($user['activo']) && !$user['activo'])) {
|
||||||
|
$this->logout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizar solo si hay cambios para evitar escrituras innecesarias en la sesión
|
||||||
|
if (Session::get('user_rol') !== $user['rol']) {
|
||||||
|
Session::set('user_rol', $user['rol']);
|
||||||
|
}
|
||||||
|
if (Session::get('user_name') !== $user['nombre']) {
|
||||||
|
Session::set('user_name', $user['nombre']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login($login, $password) {
|
public function login($login, $password) {
|
||||||
@@ -43,10 +68,19 @@ class Auth {
|
|||||||
return Session::get('user_rol') === 'ayudante';
|
return Session::get('user_rol') === 'ayudante';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isCoordinador() {
|
||||||
|
return Session::get('user_rol') === 'coordinador';
|
||||||
|
}
|
||||||
|
|
||||||
public function hasRole($role) {
|
public function hasRole($role) {
|
||||||
return Session::get('user_rol') === $role;
|
return Session::get('user_rol') === $role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function hasAnyRole($roles) {
|
||||||
|
$userRole = Session::get('user_rol');
|
||||||
|
return in_array($userRole, (array)$roles);
|
||||||
|
}
|
||||||
|
|
||||||
public function getCurrentUser() {
|
public function getCurrentUser() {
|
||||||
if (!$this->isLoggedIn()) {
|
if (!$this->isLoggedIn()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -73,6 +107,14 @@ class Auth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requireCoordinador($redirectUrl = '/ayudante.php') {
|
||||||
|
$this->requireAuth($redirectUrl);
|
||||||
|
if (!$this->isCoordinador() && !$this->isAdmin()) {
|
||||||
|
header('Location: ' . $redirectUrl);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function requireRole($roles, $redirectUrl = '/') {
|
public function requireRole($roles, $redirectUrl = '/') {
|
||||||
$this->requireAuth($redirectUrl);
|
$this->requireAuth($redirectUrl);
|
||||||
$userRole = Session::get('user_rol');
|
$userRole = Session::get('user_rol');
|
||||||
|
|||||||
@@ -132,8 +132,14 @@ class User {
|
|||||||
return $stmt->fetchAll();
|
return $stmt->fetchAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUsuariosGestion() {
|
||||||
|
// Incluye ayudantes y coordinadores para gestión de turnos
|
||||||
|
$stmt = $this->db->query("SELECT * FROM users WHERE (rol = 'ayudante' OR rol = 'coordinador') AND activo = 1 ORDER BY nombre");
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
public function isValidRole($role) {
|
public function isValidRole($role) {
|
||||||
return in_array($role, ['admin', 'ayudante']);
|
return in_array($role, ['admin', 'ayudante', 'coordinador']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasRole($userId, $role) {
|
public function hasRole($userId, $role) {
|
||||||
|
|||||||
Reference in New Issue
Block a user