feat: Crear interfaz simple de gestión y unificar vista de ayudante
- Crear versión ultra simple de admin/asignaciones.php con solo funcionalidades esenciales
- Mover asignaciones complejas a asignaciones_completo.php para acceso avanzado
- Modificar ayudante.php para permitir acceso a administradores sin redirigir
- Añadir detección automática: usuarios ven navbar simple, admins ven navbar completo
- Eliminar archivo duplicado vista-ayudante.php para evitar mantener código duplicado
- Corregir rutas BASE_PATH para compatibilidad entre archivos en /admin/ y /public/
- Añadir enlace 👥 Vista Ayudante en navbar de admin
- Mejorar experiencia de usuario con diseño limpio y minimalista
This commit is contained in:
628
public/admin/asignaciones.php
Executable file → Normal file
628
public/admin/asignaciones.php
Executable file → Normal file
@@ -24,6 +24,11 @@ if (isset($_GET['success']) && $_GET['success'] === 'orden_actualizado') {
|
||||
$messageType = 'success';
|
||||
}
|
||||
|
||||
if (isset($_GET['success']) && $_GET['success'] === 'automatica') {
|
||||
$message = 'Turnos generados automáticamente correctamente.';
|
||||
$messageType = 'success';
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (!CSRF::isValidRequest()) {
|
||||
$message = 'Error de validación del formulario';
|
||||
@@ -31,527 +36,246 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'asignar') {
|
||||
if ($action === 'asignar_actual') {
|
||||
$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';
|
||||
}
|
||||
// Encontrar el domingo actual
|
||||
$hoy = new DateTime();
|
||||
$diaSemana = (int)$hoy->format('w');
|
||||
$domingoActual = clone $hoy;
|
||||
$domingoActual->modify('-' . $diaSemana . ' days');
|
||||
$currentWeekStart = $domingoActual->format('Y-m-d');
|
||||
|
||||
if ($userId) {
|
||||
$asignacionModel->asignar($userId, $currentWeekStart);
|
||||
$message = 'Turno actual asignado correctamente';
|
||||
$messageType = 'success';
|
||||
header("Location: " . $_SERVER['PHP_SELF'] . "?success=actual");
|
||||
exit;
|
||||
}
|
||||
} elseif ($action === 'rotacion_automatica') {
|
||||
} elseif ($action === 'rotar_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';
|
||||
}
|
||||
header("Location: " . $_SERVER['PHP_SELF'] . "?success=automatica");
|
||||
exit;
|
||||
} 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';
|
||||
$message = 'No se pudieron generar turnos: ' . implode(', ', $resultado['errors']);
|
||||
$messageType = 'danger';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ayudantes = $userModel->getAyudantesActivos();
|
||||
$horarios = $horariosModel->getActivos();
|
||||
|
||||
// Encontrar el domingo actual
|
||||
// Obtener datos principales
|
||||
$hoy = new DateTime();
|
||||
$diaSemana = (int)$hoy->format('w'); // 0 = domingo, 6 = sábado
|
||||
$diaSemana = (int)$hoy->format('w');
|
||||
$domingoActual = clone $hoy;
|
||||
$domingoActual->modify('-' . $diaSemana . ' days'); // Restar días para llegar al domingo
|
||||
|
||||
$domingoActual->modify('-' . $diaSemana . ' days');
|
||||
$currentWeekStart = $domingoActual->format('Y-m-d');
|
||||
|
||||
$asignacionActual = $asignacionModel->getAsignacionPorSemana($currentWeekStart);
|
||||
$ayudantes = $userModel->getAyudantesActivos();
|
||||
|
||||
// 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++) {
|
||||
// Obtener próximos turnos (siguientes 3 semanas)
|
||||
$proximosTurnos = [];
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
$semanaDomingo = clone $domingoActual;
|
||||
$semanaDomingo->modify("+{$i} weeks");
|
||||
$semanaInicio = $semanaDomingo->format('Y-m-d');
|
||||
$asignacion = $asignacionModel->getAsignacionPorSemana($semanaInicio);
|
||||
|
||||
$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'))
|
||||
$proximosTurnos[] = [
|
||||
'semana' => $semanaInicio,
|
||||
'fin' => date('Y-m-d', strtotime('+5 days', strtotime($semanaInicio))),
|
||||
'asignacion' => $asignacion
|
||||
];
|
||||
}
|
||||
|
||||
$currentPage = 'asignaciones';
|
||||
$pageTitle = 'Asignación de Turnos';
|
||||
$pageTitle = 'Gestió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>
|
||||
<title>Turnos - 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">
|
||||
<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>
|
||||
<body>
|
||||
<?php include BASE_PATH . '/public/partials/navbar.php'; ?>
|
||||
|
||||
<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): ?>
|
||||
<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; ?>
|
||||
|
||||
<!-- TURNO ACTUAL -->
|
||||
<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 class="col-12">
|
||||
<div class="card turno-actual shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0">📍 Turno Actual</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 class="row align-items-center">
|
||||
<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>
|
||||
|
||||
<?php if ($asignacionActual): ?>
|
||||
<div class="alert alert-success mb-3">
|
||||
<h6 class="mb-1">✅ Asignado a:</h6>
|
||||
<strong class="fs-5"><?= htmlspecialchars($asignacionActual['nombre']) ?></strong>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning mb-3">
|
||||
<h6 class="mb-1">⚠️ Sin asignar</h6>
|
||||
<span class="fs-5">No hay ayudante asignado esta semana</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</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>
|
||||
<div class="col-md-6 text-center">
|
||||
<form method="POST" class="d-flex justify-content-center gap-2">
|
||||
<?= CSRF::getTokenField() ?>
|
||||
<input type="hidden" name="action" value="asignar_actual">
|
||||
|
||||
<select class="form-select" name="user_id" style="max-width: 200px;" required>
|
||||
<option value="">Seleccionar...</option>
|
||||
<?php foreach ($ayudantes as $a): ?>
|
||||
<option value="<?= $a['id'] ?>"><?= htmlspecialchars($a['nombre']) ?></option>
|
||||
<option value="<?= $a['id'] ?>"
|
||||
<?= $asignacionActual && $a['id'] == $asignacionActual['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($a['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Asignar</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<button type="submit" class="btn btn-success btn-accion">
|
||||
💾 Asignar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<?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']) ?>
|
||||
<!-- ACCIONES RÁPIDAS -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">⚡ Acciones Rápidas</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<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() ?>
|
||||
<input type="hidden" name="action" value="rotar_automatica">
|
||||
<button type="submit" class="btn btn-primary btn-lg btn-accion">
|
||||
🚀 Generar Automático
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<!-- Sección de Asignación Masiva -->
|
||||
<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 class="col-md-6 mb-3">
|
||||
<div class="p-3">
|
||||
<h6 class="mb-3">👥 Gestionar Orden</h6>
|
||||
<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 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>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<!-- 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 class="card-body">
|
||||
<div class="row">
|
||||
<?php foreach ($proximosTurnos as $index => $turno): ?>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card turno-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h6 class="card-title">
|
||||
📅 <?= date('d/m', strtotime($turno['semana'])) ?> -
|
||||
<?= date('d/m', strtotime($turno['fin'])) ?>
|
||||
</h6>
|
||||
|
||||
<?php if ($turno['asignacion']): ?>
|
||||
<div class="alert alert-success mb-2">
|
||||
<strong>✅ <?= htmlspecialchars($turno['asignacion']['nombre']) ?></strong>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning mb-2">
|
||||
<strong>⚠️ Sin asignar</strong>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<a href="/admin/asignaciones_completo.php" class="btn btn-sm btn-outline-primary">
|
||||
Modificar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?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 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 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>
|
||||
</html>
|
||||
557
public/admin/asignaciones_completo.php
Executable file
557
public/admin/asignaciones_completo.php
Executable file
@@ -0,0 +1,557 @@
|
||||
<?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->requireAdmin();
|
||||
|
||||
$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 ($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 === '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';
|
||||
}
|
||||
} 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ayudantes = $userModel->getAyudantesActivos();
|
||||
$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; ?>
|
||||
|
||||
<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>
|
||||
|
||||
<!-- Sección de Asignación Masiva -->
|
||||
<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>
|
||||
|
||||
<!-- 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>
|
||||
@@ -1,17 +1,17 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../src/Auth.php';
|
||||
require_once __DIR__ . '/../src/User.php';
|
||||
require_once __DIR__ . '/../src/DiasHorarios.php';
|
||||
require_once __DIR__ . '/../src/Asignacion.php';
|
||||
require_once __DIR__ . '/../src/Database.php';
|
||||
if (!defined('BASE_PATH')) {
|
||||
define('BASE_PATH', dirname(__DIR__));
|
||||
}
|
||||
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/Database.php';
|
||||
|
||||
$auth = new Auth();
|
||||
$auth->requireAuth();
|
||||
|
||||
if ($auth->isAdmin()) {
|
||||
header('Location: /admin/index.php');
|
||||
exit;
|
||||
}
|
||||
// Permitir acceso a administradores para que puedan ver la vista de ayudante
|
||||
|
||||
$user = $auth->getCurrentUser();
|
||||
$horariosModel = new DiasHorarios();
|
||||
@@ -72,23 +72,45 @@ $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">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/ayudante.php">Contenedor Ibiza</a>
|
||||
<span class="navbar-text me-3">
|
||||
Hola, <?= htmlspecialchars($user['nombre']) ?>
|
||||
</span>
|
||||
<a href="/cambiar-password.php" class="btn btn-outline-light btn-sm me-3">Cambiar Contraseña</a>
|
||||
<a href="/logout.php" class="btn btn-outline-light btn-sm">Cerrar Sesión</a>
|
||||
</div>
|
||||
</nav>
|
||||
<?php
|
||||
// Si es administrador, usar el navbar del admin
|
||||
if ($auth->isAdmin()) {
|
||||
include BASE_PATH . '/public/partials/navbar.php';
|
||||
} else {
|
||||
// Si es ayudante, usar navbar simple
|
||||
?>
|
||||
<nav class="navbar navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/ayudante.php">Contenedor Ibiza</a>
|
||||
<span class="navbar-text me-3">
|
||||
Hola, <?= htmlspecialchars($user['nombre']) ?>
|
||||
</span>
|
||||
<a href="/cambiar-password.php" class="btn btn-outline-light btn-sm me-3">Cambiar Contraseña</a>
|
||||
<a href="/logout.php" class="btn btn-outline-light btn-sm">Cerrar Sesión</a>
|
||||
</div>
|
||||
</nav>
|
||||
<?php } ?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="mb-0">Mis Turnos</h2>
|
||||
<a href="/export-pdf.php" target="_blank" class="btn btn-danger btn-sm">
|
||||
📄 Exportar PDF
|
||||
</a>
|
||||
<div>
|
||||
<?php if ($auth->isAdmin()): ?>
|
||||
<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>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div>
|
||||
<?php if ($auth->isAdmin()): ?>
|
||||
<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">
|
||||
📄 Exportar PDF
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
@@ -27,6 +27,9 @@ $dbName = getenv('DB_NAME') ?: 'No configurado';
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $currentPage === 'asignaciones' ? 'active' : '' ?>" href="/admin/asignaciones.php">Asignaciones</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $currentPage === 'vista-ayudante' ? 'active' : '' ?>" href="/ayudante.php">👥 Vista Ayudante</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $currentPage === 'webhook' ? 'active' : '' ?>" href="/admin/webhook.php">🤖 Bot</a>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user