This commit is contained in:
2023-10-12 16:29:39 +00:00
parent 6390bbb766
commit 2e00fbec20
13 changed files with 633 additions and 941 deletions

View File

@@ -36,8 +36,8 @@ try {
horario_fecha_fin, horario_fecha_fin,
horario_grupo, horario_grupo,
horario_hora, horario_hora,
periodo_fecha_inicio, PERIODO.periodo_fecha_inicio,
periodo_fecha_fin, PERIODO.periodo_fecha_fin,
salon, salon,
materia_nombre as materia, materia_nombre as materia,
carrera_nombre as carrera, carrera_nombre as carrera,
@@ -49,10 +49,9 @@ try {
JOIN carrera USING (carrera_id) JOIN carrera USING (carrera_id)
JOIN nivel USING (nivel_id) JOIN nivel USING (nivel_id)
JOIN facultad ON facultad.facultad_id = carrera.facultad_id JOIN facultad ON facultad.facultad_id = carrera.facultad_id
JOIN PERIODO_CARRERA USING (carrera_id)
JOIN PERIODO USING (periodo_id) JOIN PERIODO USING (periodo_id)
JOIN SALON USING (salon_id) JOIN SALON USING (salon_id)
WHERE (periodo_id, facultad.facultad_id) = (:periodo_id, COALESCE(:facultad_id, facultad.facultad_id)) WHERE (PERIODO.periodo_id, facultad.facultad_id) = (:periodo_id, COALESCE(:facultad_id, facultad.facultad_id))
), ),
fechas AS ( fechas AS (
SELECT fechas_clase(h.horario_id, true) as registro_fecha_ideal, h.horario_id SELECT fechas_clase(h.horario_id, true) as registro_fecha_ideal, h.horario_id

View File

@@ -30,6 +30,7 @@ try {
SELECT facultad_nombre, facultad_id, clave_dependencia SELECT facultad_nombre, facultad_id, clave_dependencia
FROM facultad FROM facultad
WHERE facultad_id = :facultad_id OR :facultad_id IS NULL WHERE facultad_id = :facultad_id OR :facultad_id IS NULL
ORDER BY facultad_nombre ASC
SQL SQL
, ,
[':facultad_id' => $user->facultad['facultad_id']] [':facultad_id' => $user->facultad['facultad_id']]

View File

@@ -1,5 +1,4 @@
<?php <?php
require_once "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php"; require_once "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php";
header('Content-Type: application/json'); header('Content-Type: application/json');
@@ -15,13 +14,44 @@ try {
case 'GET': case 'GET':
// Fetch all puestos // Fetch all puestos
$facultad_id = $user->facultad['facultad_id']; $facultad_id = $user->facultad['facultad_id'];
$periodo_nivel_id = $db->where('periodo_id', $user->periodo_id)->getOne('periodo', 'nivel_id')['nivel_id'];
$carreras = $db->query(<<<SQL $carreras = $db->query(<<<SQL
SELECT carrera_id, carrera_nombre, clave_carrera, facultad_id SELECT carrera_id, carrera_nombre, clave_carrera,
facultad_id, facultad_nombre,
nivel_id, nivel_nombre
FROM carrera FROM carrera
WHERE facultad_id = :facultad_id OR :facultad_id IS NULL join nivel using (nivel_id)
SQL, ['facultad_id' => $facultad_id]); join facultad using (facultad_id)
WHERE facultad_id = :facultad_id OR :facultad_id IS NULL AND
nivel.nivel_id = :periodo_nivel_id
ORDER BY facultad_nombre, carrera_nombre
SQL, [
'facultad_id' => $facultad_id,
'periodo_nivel_id' => $periodo_nivel_id
]);
echo json_encode($carreras); echo json_encode($carreras);
break; break;
case 'PUT':
// Update carrera {nivel_id}
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (!isset($data['carrera_id'], $data['nivel_id'])) {
header('HTTP/1.1 400 Bad Request');
echo json_encode(['error' => 'Falta el id de la carrera o el nivel']);
exit();
}
$carrera_id = $data['carrera_id'];
$nivel_id = $data['nivel_id'];
$db->where('carrera_id', $carrera_id)->update('carrera', ['nivel_id' => $nivel_id]);
$carrera_nombre = $db->where('carrera_id', $carrera_id)->getOne('carrera', 'carrera_nombre')['carrera_nombre'];
$nivel_nombre = $db->where('nivel_id', $nivel_id)->getOne('nivel', 'nivel_nombre')['nivel_nombre'];
echo json_encode(['success' => "$carrera_nombre actualizada a $nivel_nombre"]);
break;
default: default:
header('HTTP/1.1 405 Method Not Allowed'); header('HTTP/1.1 405 Method Not Allowed');
echo json_encode(['error' => 'Método no permitido']); echo json_encode(['error' => 'Método no permitido']);

31
action/nivel.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
require_once "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php";
header('Content-Type: application/json');
if (!Login::is_logged()) {
header('HTTP/1.1 401 Unauthorized');
echo json_encode(['error' => 'No se ha iniciado sesión']);
exit();
}
$user = Login::get_user();
try {
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
// Fetch all puestos
$nivel = $db->get('nivel');
echo json_encode($nivel);
break;
default:
header('HTTP/1.1 405 Method Not Allowed');
echo json_encode(['error' => 'Método no permitido']);
break;
}
} catch (PDOException $e) {
echo json_encode([
'error' => $e->getMessage(),
'query' => $db->getLastQuery(),
'exception' => $e->getTraceAsString()
]);
}

View File

@@ -1,809 +1,124 @@
<?php
require_once 'class/c_login.php';
require_once 'include/bd_pdo.php';
$user = Login::get_user();
$user->access('facultades');
if($user->acceso == null){
header('Location: main.php?error=1');
}else{
$user->print_to_log('Carreras');
}
if($user->facultad['facultad_id']!=$_GET['facultad']){
header('Location: carreras.php?facultad='.$user->facultad['facultad_id']);
$mal=true;
}
$mal=false;
?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<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>Carreras</title> <title>Auditoría asistencial</title>
<link rel="stylesheet" href="css/jquery-ui.css">
<link rel="stylesheet" href="css/calendar.css">
<link rel="stylesheet" href="css/toggle.css" type="text/css">
<?php <?php
include 'import/html_css_files.php'; include 'import/html_css_files.php';
?> ?>
</head> <link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.0/dist/trix.css">
<style>
<body> [v-cloak] {
<?php display: none;
if(isset($_GET['facultad'])){ }
$facultad=query("SELECT facultad_nombre FROM facultad WHERE facultad_id = :facultad", array(":facultad" => $_GET['facultad']), true); </style>
$fs_carreras = query(
"SELECT * FROM fs_carreras(:idfacultad, null, null)",
array(':idfacultad' => $_GET['facultad']),
single:false
);
}
include "import/html_header.php";
html_header(
"CARRERAS | " . $facultad['facultad_nombre'],
"Gestión de Checador "
);
$user->access('facultades');
$fs_niveles = query(
"SELECT * FROM nivel", null, false
);
$fs_periodos = query(
"SELECT * FROM fs_periodos(:idfacultad) WHERE estado = 'Activo' ",
array(':idfacultad' => $_GET['facultad']),
false
);
$fs_tiempoLic = query(
"SELECT * FROM fs_tiempo_checado(:idfacultad, 1)",
array(':idfacultad' => $_GET['facultad']),
true
);
$fs_tiempoPos = query(
"SELECT * FROM fs_tiempo_checado(:idfacultad, 2)",
array(':idfacultad' => $_GET['facultad']),
true
);
?>
<main class="content marco">
<?php #if($mal==true){ ?>
<div class="row">
<div class="col-12 text-left">
<a href="facultades.php" title="Volver">
<button type="button" class="btn btn-outline-secondary"><span class="ing-regresar ing-fw"></span>Volver</button>
</a>
</div>
</div>
<?php #} ?>
<div id="message"></div>
<ul class="nav nav-tabs mt-3" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="periodo-tab" data-toggle="tab" data-target="#periodo" type="button" role="tab" aria-controls="periodo" aria-selected="true">Periodo</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="carrera-tab" data-toggle="tab" data-target="#carrera" type="button" role="tab" aria-controls="carrera" aria-selected="false">Carrera</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="carrera-tab" data-toggle="tab" data-target="#tiempos" type="button" role="tab" aria-controls="tiempos" aria-selected="false">Tiempos</button>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<!-- PERIODOS -->
<div class="tab-pane fade show active" id="periodo" role="tabpanel" aria-labelledby="periodo-tab">
<div class="row mt-3">
<?php if($user->acceso == 'w') {?>
<div class="col-12 text-right">
<button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#modal_periodo" data-tipo="1"><span class="ing-mas ing-fw"></span>Agregar periodo</button>
</div>
<?php }?>
</div>
<!-- Tabla -->
<div class="row mt-3">
<div class="col-12 table-responsive">
<table class="table table-sm table-striped table-white">
<thead class="thead-dark">
<tr>
<th>Estado</th>
<th>Nivel</th>
<th>Periodo</th>
<th>Inicio</th>
<th>Fin</th>
<?php if($user->acceso == 'w') {?>
<th>Acciones</th>
<?php }?>
</tr>
</thead>
<tbody>
<?php foreach($fs_periodos as $periodo){
$title=$periodo['estado'];
if($title=='Activo')
$color="success";
else
$color="danger";
?>
<tr data-id="<?= $periodo['id']?>" id="<?= $periodo['id']?>" >
<td class="text-<?= $color ?> text-center" title="<?= $title?>">
<span class="ing-bullet"></span>
</td>
<td class="text-primary">
<?= $periodo['nivel']?>
</td>
<td class="text-primary">
<?= $periodo['periodo']?>
</td>
<td class="text-primary">
<?= $periodo['inicio']?>
</td>
<td class="text-primary">
<?= $periodo['fin']?>
</td>
<?php if($user->acceso == 'w') {?>
<td class="text-center icono-acciones">
<a href="#" data-toggle="modal" data-target="#modal_periodo" data-tipo="2" title="Editar"><span class="ing-editar ing-fw"></span></a>
</td>
<?php }?>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- CARRERAS -->
<div class="tab-pane fade" id="carrera" role="tabpanel" aria-labelledby="carrera-tab">
<div class="row mt-3">
<?php if($user->acceso == 'w') {?>
<div class="col-12 text-right">
<button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#modal" data-tipo="1"><span class="ing-mas ing-fw"></span>Crear carrera</button>
</div>
<?php }?>
</div>
<!-- Tabla -->
<div class="row mt-3">
<div class="col-12 table-responsive">
<table class="table table-sm table-striped table-white">
<thead class="thead-dark">
<tr>
<th>Estado</th>
<th>Nivel</th>
<th>Carrera</th>
<?php if($user->acceso == 'w') {?>
<th>Acciones</th>
<?php }?>
</tr>
</thead>
<tbody>
<?php
foreach($fs_carreras as $carrera){
$color = "danger";
$title = "Inactiva";
if($carrera["carrera_activa"]==1){
$color ="success";
$title="Activa";
}
$nivel='Licenciatura';
if($carrera['nivel_id']==2)
$nivel='Posgrado';
?>
<tr data-id="<?php echo $carrera['carrera_id'];?>" id="<?php echo $carrera['carrera_id'];?>">
<td class="text-<?php echo $color;?> text-center" title="<?php echo $title;?>">
<span class="ing-bullet"></span>
</td>
<td class="text-primary"><?php echo $nivel;?></td>
<td class="text-primary"><?php echo $carrera["carrera_nombre"];?></td>
<?php if($user->acceso == 'w') {?>
<td class="text-center icono-acciones">
<a href="#" data-toggle="modal" data-target="#modal" data-tipo="2" title="Editar"><span class="ing-editar ing-fw"></span></a>
</td>
<?php }?>
</tr>
<?php }?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Tiempos -->
<div class="tab-pane fade" id="tiempos" role="tabpanel" aria-labelledby="tiempos-tab">
<p class="mt-4">Asigna los minutos de tolerancia antes y después del horario de clase</p>
<form action="" method="post" id="formaModalTiempos" onsubmit="return valida_camposT()">
<input type="hidden" name="facultadT" id="facultadT">
<h3 class="text-center">Licenciatura</h3>
<div class="row mt-3" style="border:solid 2px; border-radius: 8px;">
<div class="offset-1 col-2 text-center">
Antes
<input id="antesL" name="antesL" type="number" class="form-control text-center" maxlenth="10">
<div class="invalid-feedback">
Debe ser un numero mayor que 0
</div>
min
</div>
<div class="col-2 text-center bg-light mt-4 mb-4">
<h5 class="mt-2">Hora de clase</h5>
</div>
<div class="col-2 text-center">
Despues
<input id="despuesL" name="despuesL" type="number" class="form-control text-center" maxlenth="10">
<div class="invalid-feedback">
Debe ser un numero mayor que 0
</div>
min
</div>
<div class="col-2 text-center retardoLic">
Retardos
<input id="retardoL" name="retardoL" type="number" class="form-control text-center" maxlenth="10">
<div class="invalid-feedback">
Debe ser un numero mayor que 0
</div>
min
</div>
<div class="col-3 text-center">
¿Tiene retardos?<br>
<div class="custom-control custom-switch mt-2">
<input type="checkbox" class="custom-control-input tipo-switch" name="retardoLic" id="retardoLic" value="1">
<label class="custom-control-label" for="retardoLic">Si</label>
</div>
</div>
</div>
<br><br>
<h3 class="text-center">Posgrado</h3>
<div class="row mt-3" style="border:solid 2px; border-radius: 8px;">
<div class="offset-1 col-2 text-center">
Antes
<input id="antesP" name="antesP" type="number" class="form-control text-center" maxlenth="10">
<div class="invalid-feedback">
Debe ser un numero mayor que 0
</div>
min
</div>
<div class="col-2 text-center bg-light mt-4 mb-4">
<h5 class="mt-2">Hora de clase</h5>
</div>
<div class="col-2 text-center">
Despues
<input id="despuesP" name="despuesP" type="number" class="form-control text-center" maxlenth="10">
<div class="invalid-feedback">
Debe ser un numero mayor que 0
</div>
min
</div>
<div class="col-2 text-center retardoPos">
Retardo
<input id="retardoP" name="retardoP" type="number" class="form-control text-center" maxlenth="10">
<div class="invalid-feedback">
Debe ser un numero mayor que 0
</div>
min
</div>
<div class="col-3 text-center">
¿Tiene retardos?<br>
<div class="custom-control custom-switch mt-2">
<input type="checkbox" class="custom-control-input tipo-switch" name="retardoPos" id="retardoPos" value="1">
<label class="custom-control-label" for="retardoPos"></label>
</div>
</div>
</div>
<br>
</form>
<div class="form-group row mt-3">
<div class="offset-4 col-8">
<button class="btn btn-outline-primary" id="submitBtnT">
<span class="ing-aceptar ing-fw"></span> Guardar
</button>
<button type="reset" id="reset" class="btn btn-outline-danger" data-dismiss="modal">
<span class="ing-cancelar ing-fw"></span> Limpiar
</button>
</div>
</div>
</div>
</div>
</main>
<!-- Footer -->
<?php
include "import/html_footer.php";
?>
<!-- Modal -->
<div class="modal fade" id="modal_periodo" tabindex="-1" role="dialog" arialabelledby="modal" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="col-12 modal-title text-center">
<span id="modalLabelP">
Editar periodo
</span>
<button type="button" class="close text-white" data-dismiss="modal" aria-label="close">
<span aria-hidden="true">&times;</span>
</button>
</h4>
</div>
<div class="modal-body">
<form action="" method="post" id="formaModalP" onsubmit="return valida_camposP()">
<input type="hidden" name="idP" id="idP">
<input type="hidden" name="facultadP" id="facultadP" value="<?php echo $_GET['facultad']; ?>">
<div class="form-box">
<div class="form-group row">
<label for="nombreP" class="col-4 col-form-label">Nombre *</label>
<div class="col-8">
<input id="nombreP" name="nombreP" type="text" class="form-control" maxlength="100">
<div class="invalid-feedback" id="nombreP-error">Campo obligatorio</div>
</div>
</div>
<div class="form-group row">
<label for="inicio" class="col-4 col-form-label">Fecha de inicio *</label>
<div class="col-8">
<input id="fecha_inicial" name="fecha_inicial" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="">
<div class="invalid-feedback">Debe seleccionar una fecha</div>
</div>
</div>
<div class="form-group row">
<label for="fin" class="col-4 col-form-label">Fecha de fin *</label>
<div class="col-8">
<input id="fecha_final" name="fecha_final" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="">
<div class="invalid-feedback">Debe seleccionar una fecha</div>
</div>
</div>
<div class="form-group row">
<label for="nivelP" class="col-4 col-form-label">Nivel *</label>
<div class="col-8">
<div class="datalist datalist-select mb-1 w-100">
<div class="datalist-input">Mostrar todos</div>
<span class="ing-buscar icono"></span>
<ul style="display:none">
<?php foreach($fs_niveles as $pnivel){?>
<li data-id="<?php echo $pnivel['nivel_id']?>" class="pl-4"><?php echo $pnivel['nivel_nombre'] ?></li>
<?php }?>
</ul>
<input type="hidden" id="nivelP" name="nivelP" value="">
</div>
<div class="invalid-feedback">Debe seleccionar un nivel</div>
</div>
</div>
<div class="form-group row">
<label for="estadoP" class="col-4 col-form-label">Estado *</label>
<div class="col-4">
<div class="form-check form-check-inline">
<input class="form-check-input radio-lg" type="radio" id="estado_activoP" name="estadoP" value="1" checked="checked">
<label for="estado_activoP" class="col-form-label">Activo</label>
</div>
</div>
<div class="col-4">
<div class="form-check form-check-inline">
<input class="form-check-input radio-lg" type="radio" id="estado_inactivoP" name="estadoP" value="2">
<label for="estado_inactivoP" class="col-form-label">Inactivo</label>
</div>
</div>
</div>
<div class="from-group row">
<div class="offset-4 col-8">
<button type="submit" class="btn btn-outline-primary" id="submitBtnP" data-tipo="1">
<span class="ing-aceptar ing-fw"></span> Guardar
</button>
<button type="reset" class="btn btn-outline-danger" data-dismiss="modal">
<span class="ing-cancelar ing-fw"></span> Cancelar
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modal" tabindex="-1" role="dialog" arialabelledby="modal" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="col-12 modal-title text-center">
<span id="modalLabel">
Editar nombre de Carrera
</span>
<button type="button" class="close text-white" data-dismiss="modal" aria-label="close">
<span aria-hidden="true">&times;</span>
</button>
</h4>
</div>
<div class="modal-body">
<form action="" method="post" id="formaModal" onsubmit="return valida_campos()">
<input type="hidden" name="id" id="id">
<input type="hidden" name="facultad" id="facultad" value="<?php echo $_GET['facultad']; ?>">
<div class="form-box">
<div class="form-group row">
<label for="nivel" class="col-4 col-form-label">Nivel *</label>
<div class="col-8">
<div class="datalist datalist-select mb-1 w-100">
<div class="datalist-input">Mostrar todos</div>
<span class="ing-buscar icono"></span>
<ul style="display:none">
<?php foreach($fs_niveles as $pnivel){?>
<li data-id="<?php echo $pnivel['nivel_id']?>" class="pl-4"><?php echo $pnivel['nivel_nombre'] ?></li>
<?php }?>
</ul>
<input type="hidden" id="nivel" name="nivel" value="">
</div>
</div>
</div>
<div class="form-group row">
<label for="nombre" class="col-4 col-form-label">Nombre *</label>
<div class="col-8">
<input id="nombre" name="nombre" type="text" class="form-control" maxlength="100">
<div class="invalid-feedback" id="nombre-error">Campo obligatorio</div>
</div>
</div>
<div class="form-group row">
<label for="estado" class="col-4 col-form-label">Estado *</label>
<div class="col-4">
<div class="form-check form-check-inline">
<input class="form-check-input radio-lg" type="radio" id="estado_activo" name="estado" value="1" checked="checked">
<label for="estado_activo" class="col-form-label">Activo</label>
</div>
</div>
<div class="col-4">
<div class="form-check form-check-inline">
<input class="form-check-input radio-lg" type="radio" id="estado_inactivo" name="estado" value="0">
<label for="estado_inactivo" class="col-form-label">Inactivo</label>
</div>
</div>
</div>
<div class="from-group row">
<div class="offset-4 col-8">
<button type="submit" class="btn btn-outline-primary" id="submitBtn" data-tipo="1">
<span class="ing-aceptar ing-fw"></span> Guardar
</button>
<button type="reset" class="btn btn-outline-danger" id="reset" data-dismiss="modal">
<span class="ing-cancelar ing-fw"></span> Cancelar
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script src="js/jquery.min.js"></script> <script src="js/jquery.min.js"></script>
<script src="js/jquery-ui.js"></script> <script src="js/jquery-ui.js"></script>
<script src="js/bootstrap/bootstrap.min.js"></script> <script src="js/bootstrap/bootstrap.min.js"></script>
<script src="js/datalist.js"></script> </head>
<script src="js/datepicker-es.js"></script>
<script src="js/toggle.js"></script> <body>
<?php <?
require_once 'js/messages.php'; $redirect = $_SERVER['PHP_SELF'];
include "import/html_header.php";
global $user;
html_header(
"Carreras",
"Sistema de gestión de checador",
);
?> ?>
<script> <main class="container-fluid px-4 my-4" id="app" v-cloak @vue:mounted="mounted" style="min-height: 70vh;">
$('#retardoLic').change(function(){ <section class="row mt-4">
if($(this).is(':checked')){ <div class="col-12 position-relative">
$('.retardoLic').show(); <!-- Loop for messages -->
} <div class="toast show shadow-sm mb-3 bg-white"
else{ style="position: fixed; top: 15%; right: 1%; max-width: 300px;" role="alert" aria-live="assertive"
$('.retardoLic').hide(); aria-atomic="true"
$('#retardoL').val("0"); @vue:mounted="$('.toast').toast({delay: 5000}).toast('show').on('hidden.bs.toast', () => { message.text = '' })"
} v-if="message.text">
}); <div class="toast-header">
$('#retardoPos').change(function(){ <strong class="mr-auto text-primary text-uppercase text-center w-100 px-4">
if($(this).is(':checked')){ {{ message.title }}
$('.retardoPos').show(); </strong>
} <small class="text-muted">{{ message.timestamp }}</small>
else{ <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"
$('.retardoPos').hide(); @click="message.text = ''">
$('#retardoP').val("0"); <span aria-hidden="true">
} <i class="fas fa-times"></i>
}); </span>
$('#reset').on('click', function(){ </button>
$('#antesL').val("<?= -1*($fs_tiempoLic['desde_asistencia'] ?? 0) ?>"); </div>
$('#despuesL').val("<?= ($fs_tiempoLic['hasta_asistencia'] ?? 1)-1 ?>"); <div class="toast-body">
$('#retardoL').val("<?= ($fs_tiempoLic['hasta_retardo'] ?? 0) - ($fs_tiempoLic['hasta_asistencia'] ?? 0) ?>"); <div :class="message.type == 'success' ? 'text-success' : 'text-danger'"><i
$('#antesP').val("<?= -1*($fs_tiempoPos['desde_asistencia'] ?? 0) ?>"); :class="message.type == 'success' ? 'fas fa-check-circle' : 'fas fa-times-circle'"></i>
$('#despuesP').val("<?= ($fs_tiempoPos['hasta_asistencia'] ?? 1)-1 ?>"); {{ message.text }}
$('#retardoP').val("<?= ($fs_tiempoPos['hasta_retardo'] ?? 0) - ($fs_tiempoPos['hasta_asistencia'] ?? 0) ?>"); </div>
<?php </div>
if(($fs_tiempoLic['hasta_asistencia'] ?? 0) == ($fs_tiempoLic['hasta_retardo'] ?? 0)){ ?> </div>
$('.retardoLic').hide(); </div>
$('#retardoL').val("0"); </section>
$('#retardoLic').prop("checked", false).change(); <div class="container">
<?php } <div class="row" v-for="facultad in carreras">
else{ ?> <!-- Facultad Card -->
$('#retardoLic').prop("checked", true).change(); <div class="card col-12 mb-4 shadow-lg">
<?php } <div class="card-header bg-primary text-white">
if(($fs_tiempoPos['hasta_asistencia'] ?? 0) == ($fs_tiempoPos['hasta_retardo'] ?? 0)){ ?> <h3 class="mb-1">{{ facultad.facultad_nombre }}</h3>
$('.retardoPos').hide(); </div>
$('#retardoP').val("0"); <div class="card-body bg-white">
$('#retardoPos').prop("checked", false).change(); <div class="row justify-content-center">
<?php } <!-- Loop for Carreras -->
else{ ?> <div class="col-md-6 mb-3" v-for="carrera in facultad.carreras">
$('#retardoPos').prop("checked", true).change(); <div class="card border-secondary mb-3 shadow-sm">
<?php } <div class="card-body">
?> <h5 class="card-title text-primary text-uppercase text-center w-100 px-4 mb-3 text-truncate text-break border-bottom border-secondary pb-2"
$('#antesL').removeClass("is-invalid"); :title="carrera.carrera_nombre">
$('#despuesL').removeClass("is-invalid"); {{ carrera.carrera_nombre }}
$('#retardoL').removeClass("is-invalid"); </h5>
$('#antesP').removeClass("is-invalid");
$('#despuesP').removeClass("is-invalid");
$('#retardoP').removeClass("is-invalid");
});
$(".date-picker").datepicker($.datepicker.regional["es"]);
$(".date-picker").datepicker({
dateFormat: "dd/mm/yyyy",
changeMonth: true,
});
$('#submitBtnT').on('click', function(){ <!-- Dropdown for Niveles -->
//$('#antesL').addClass("is-invalid"); <div class="dropdown">
$('#formaModalTiempos').submit(); <button class="btn btn-outline-secondary dropdown-toggle" type="button"
}); data-toggle="dropdown" aria-expanded="false"
@vue:mounted="$('.dropdown-toggle').dropdown()">
var today = new Date(); {{ carrera.nivel_nombre }}
</button>
function valida_camposT(){ <div class="dropdown-menu shadow">
var error=false; <a class="dropdown-item" v-for="nivel in niveles" key="nivel.nivel_id"
if($('#antesL').val()==""){ style="cursor: pointer; user-select: none;"
$('#antesL').addClass("is-invalid"); @click="setNivel(carrera, nivel)"
error=true; :class="nivel.nivel_id == carrera.nivel_id ? 'active' : ''"
} :disabled="nivel.nivel_id == carrera.nivel_id">
if($('#antesL').val()<0){ {{ nivel.nivel_nombre }}
$('#antesL').addClass("is-invalid"); </a>
error=true; </div>
} </div>
if(isNaN($('#antesL').val())){ </div>
$('#antesL').addClass("is-invalid"); </div>
error=true; </div> <!-- End of Carreras loop -->
} </div>
if($('#despuesL').val()==""){ </div>
$('#despuesL').addClass("is-invalid"); </div>
error=true; <!-- End of Facultad Card -->
} </div>
if($('#despuesL').val()<0){ </div>
$('#despuesL').addClass("is-invalid"); </main>
error=true; <?
} include "import/html_footer.php"; ?>
if(isNaN($('#despuesL').val())){ <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
$('#despuesL').addClass("is-invalid"); integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
error=true; crossorigin="anonymous"></script>
} <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
if($('#retardoL').val()==""){ integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
$('#retardoL').addClass("is-invalid"); crossorigin="anonymous"></script>
error=true; <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js"
} integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+"
if($('#retardoL').val()<0){ crossorigin="anonymous"></script>
$('#retardoL').addClass("is-invalid"); <script src="js/datalist.js"></script>
error=true; <script src="js/carreras.js?<?= rand(0, 2) ?>" type="module"></script>
} <script src="js/scrollables.js"></script>
if(isNaN($('#retardoL').val())){
$('#retardoL').addClass("is-invalid");
error=true;
}
if($('#antesP').val()==""){
$('#antesP').addClass("is-invalid");
error=true;
}
if(isNaN($('#antesP').val())){
$('#antesP').addClass("is-invalid");
error=true;
}
if($('#despuesP').val()==""){
$('#despuesP').addClass("is-invalid");
error=true;
}
if(isNaN($('#despuesP').val())){
$('#despuesP').addClass("is-invalid");
error=true;
}
if($('#retardoP').val()==""){
$('#retardoP').addClass("is-invalid");
error=true;
}
if(isNaN($('#retardoP').val())){
$('#retardoP').addClass("is-invalid");
error=true;
}
if(!error){
$('#formaModalTiempos').prop("action", "./action/action_tiempos_update.php");
}else{
return false;
}
}
function valida_camposP(){
var error=false;
if($("#fecha_inicial").val()==""){
$("#fecha_inicial").addClass("is-invalid");
error=true;
}
if($("#fecha_final").val()==""){
$("#fecha_final").addClass("is-invalid");
error=true;
}
if($("#nombreP").val()==""){
$("#nombreP").addClass("is-invalid");
$("#nombreP-error").html("Campo obligatorio");
error=true;
}
if($("#nombreP").val()[0]==" "){
$("#nombreP").addClass("is-invalid");
$("#nombreP-error").html("No puede haber espacios al inicio");
error=true;
}
if($("#nivelP").val()==""){
error=true;
$("#nivelP").addClass("is-invalid");
}
if(error){
return false;
}else{
var btn = $("#submitBtnP");
if(btn.data("tipo")==2)//update
$("#formaModalP").prop("action", "./action/action_periodos_update.php");
else{//insert
$("#formaModalP").prop("action", "./action/action_periodos_insert.php");
}
}
}
<?php if(!$fs_carreras && !$fs_periodos){ ?>
triggerMessage("No se encontraron carreras ni periodos en esta facultad", "Error");
<?php } else if(!$fs_carreras){?>
triggerMessage("No se encontraron carreras en esta facultad", "Error");
<?php } else if(!$fs_periodos){?>
triggerMessage("No se encontraron periodos en esta facultad", "Error");
<?php }?>
function valida_campos(){
var error=false;
if($("#nombre").val()==""){
$("#nombre").addClass("is-invalid");
$("#nombre-error").html("Campo obligatorio");
error=true;
}
if($("#nombre").val()[0]==" "){
$("#nombre").addClass("is-invalid");
$("#nombre-error").html("No puede haber espacios al inicio");
error=true;
}
if($("#nivel").val()==""){
error=true;
}
if($('#estado_activo').prop('checked') == false && $('#estado_inactivo').prop('checked') == false){
error=true;
}
if(error){
return false;
}else{
var btn = $('#submitBtn');
if(btn.data("tipo")==2)//update
$("#formaModal").prop("action", "./action/action_carreras_update.php");
else//insert
$("#formaModal").prop("action", "./action/action_carreras_insert.php");
}
}
$('#modal_periodo').on('show.bs.modal', function(event){//datos periodo
var button = $(event.relatedTarget);
var tipo = button.data('tipo');
$("#nombreP").removeClass("is-invalid");
if(tipo==1){//crear
$('#modalLabelP').html("Agregar periodo");
$("#submitBtnP").data("tipo", 1);
$("#fecha_inicial").datepicker("setDate", today);
$("#fecha_final").datepicker("setDate", today);
$("#nombreP").val("");
$("#estado_activoP").prop("checked", true);
setDatalist("#nivelP",1);
$("li").removeClass("selected");
var fi = $("#fecha_inicial").datepicker("getDate");
//$("#fecha_final").datepicker("option", "minDate", fi);
}else{//editar
$('#modalLabelP').html("Editar periodo");
$("#submitBtnP").data("tipo", 2);
var id = $(event.relatedTarget).parents("tr").data("id");
var fac = $("#facultadP").val();
$.ajax({
url:"action/action_periodos_select.php",
type:"post",
dataType:"json",
data:{idfacultad: fac, idperiodo: id},
success:function(result){
//console.log(result);
$("#idP").val(result["id"]);
$("#facultadP").val(result["facultad_id"]);
$("#nombreP").val(result["periodo"]);
var date = new Date(result["inicio"])
date.setDate(date.getDate() + 1);
$("#fecha_inicial").datepicker("setDate", date);
date = new Date(result["fin"])
date.setDate(date.getDate() + 1);
$("#fecha_final").datepicker("setDate", date);
//$(".datalist-input").html(result["nivel"]);
setDatalist("#nivelP",result["nivel_id"]);
var fi = $("#fecha_inicial").datepicker("getDate");
//$("#fecha_final").datepicker("option", "minDate", fi);
var ff = $("#fecha_final").datepicker("getDate");
//$("#fecha_inicial").datepicker("option", "maxDate", ff);
if(result['estado']=="Activo"){
$('#estado_activoP').prop('checked', true);
}else{
$('#estado_inactivoP').prop('checked', true);
}
},
error: function(){console.log("Error")}
});
}
})
$('#modal').on('show.bs.modal', function(event){
var button = $(event.relatedTarget);
var tipo = button.data('tipo');
var modal = $(this);
$("#nombre").removeClass("is-invalid");
if(tipo == 1){//crear
$("#submitBtn").data('tipo', 1);
$("#modalLabel").html("Crear Carrera");
$("#nombre").val("");
$('#estado_activo').prop('checked', true);
$('li').removeClass('selected');
$(".datalist-input").html("Mostrar todas");
$("#nivel").val("");
}else{//editar
$("#submitBtn").data('tipo', 2);
$("#modalLabel").html("Editar Carrera");
$("#nombre").val("");
$('#estado_activo').prop('checked', true);
var id = $(event.relatedTarget).parents("tr").data("id");
var fac = $("#facultad").val();
$.ajax({
url:"action/action_carreras_select.php",
type:"post",
dataType:"json",
data:{idfacultad: fac, idcarrera: id},
success:function(result){
//console.log(result);
$("#id").val(result["carrera_id"]);
$("#nombre").val(result["carrera_nombre"])
if(result['carrera_activa']==1){
$('#estado_activo').prop('checked', true);
}else{
$('#estado_inactivo').prop('checked', true);
}
setDatalist("#nivel", result["nivel_id"]);
},
error: function(){console.log("Error")}
});
}
});
$(document).ready(function(){
$('#antesL').val("<?= -1*($fs_tiempoLic['desde_asistencia'] ?? 0) ?>");
$('#despuesL').val("<?= ($fs_tiempoLic['hasta_asistencia'] ?? 1)-1 ?>");
$('#retardoL').val("<?= ($fs_tiempoLic['hasta_retardo'] ?? 1) - ($fs_tiempoLic['hasta_asistencia'] ?? 0) ?>");
$('#antesP').val("<?= -1*($fs_tiempoPos['desde_asistencia'] ?? 0) ?>");
$('#despuesP').val("<?= ($fs_tiempoPos['hasta_asistencia'] ?? 1) -1 ?>");
$('#retardoP').val("<?= ($fs_tiempoPos['hasta_retardo'] ?? 1) - ($fs_tiempoPos['hasta_asistencia'] ?? 0) ?>");
$('#facultadT').val("<?= $_GET['facultad'] ?>");
<?php
if(($fs_tiempoLic['hasta_asistencia'] ?? 0) == ($fs_tiempoLic['hasta_retardo'] ?? 0)){ ?>
$('.retardoLic').hide();
$('#retardoL').val("0");
$('#retardoLic').prop("checked", false).change();
<?php }
else{ ?>
$('#retardoLic').prop("checked", true).change();
<?php }
if(($fs_tiempoPos['hasta_asistencia'] ?? 0) == ($fs_tiempoPos['hasta_retardo'] ?? 0)){ ?>
$('.retardoPos').hide();
$('#retardoP').val("0");
$('#retardoPos').prop("checked", false).change();
<?php }
else{ ?>
$('#retardoPos').prop("checked", true).change();
<?php }
?>
});
</script>
</body> </body>
</html> </html>

View File

@@ -863,7 +863,7 @@ footer ul {
} }
footer .footerTop .menuFooter ul>li { footer .footerTop .menuFooter ul>li {
*zoom: 1; zoom: 1;
float: left; float: left;
clear: none; clear: none;
text-align: inherit; text-align: inherit;
@@ -1058,11 +1058,18 @@ footer .tab-pane p {
.movie { .movie {
transition: all 0.1s; transition: all 0.1s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
height: 8rem;
/* align all inside content to the middle */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #f7f7f7;
} }
.movie:hover { .movie:hover {
transform: scale(1.05); transform: scale(1.05);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
font-size: 1.1em; font-size: 1.1em;
} }

View File

@@ -1,133 +0,0 @@
<?php
require_once 'class/c_login.php';
$user = Login::get_user();
$user->access();
if (in_array($user->acceso, ['n']))
die(header('Location: main.php?error=1'));
$user->print_to_log('Consultar horario');
$write = $user->admin || in_array($user->acceso, ['w']);
// var_dump($user);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Consultar horario | <?= $user->facultad['facultad'] ?? 'General' ?></title>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/plain; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<?php include_once "import/html_css_files.php"; ?>
<script src="js/scrollables.js" defer></script>
<script src="js/jquery.min.js" defer></script>
<script src="js/bootstrap/bootstrap.min.js" defer></script>
<script src="js/messages.js" defer></script>
<script>
const write = <?= $write ? 'true' : 'false' ?>;
</script>
<script src="js/moment.js" defer></script>
<script src="js/horario_profesor.js" defer></script>
</head>
<!-- -->
<body style="display: block;">
<?php
include('include/constantes.php');
include("import/html_header.php");
html_header("Consultar horario", "Sistema de gestión de checador");
?>
<?= "<!-- $user -->" ?>
<main class="container content marco content-margin" id="local-app">
<section id="message"></section>
<?php require('import/periodo.php') ?>
<form id="form" class="form-horizontal">
<div class="form-group">
<div class="form-box">
<input type="hidden" name="periodo" value="<?= $user->periodo_id ?>" />
<div class="form-group row">
<label for="clave_profesor" class="col-4 col-form-label">Profesor</label>
<div class="col-6">
<input list="lista_profesores" name="clave_profesor" id="clave_profesor" class="form-control" placeholder="Profesor" required="required">
<div class="valid-feedback">
Profesor encontrado
</div>
<div class="invalid-feedback">
Profesor no encontrado
</div>
<datalist id="lista_profesores">
<?php
$profesores = $db->where('facultad_id', $user->facultad['facultad_id'])->get("fs_profesor");
foreach ($profesores as $profesor) {
extract($profesor);
?>
<option data-grado="<?= $grado ?>" data-clave="<?= $clave ?>" data-profesor="<?= $profesor ?>" data-id="<?= $id; ?>" value="<?= "$clave | $grado $profesor" ?>"></option>
<?php
}
?>
</datalist>
<ul class="list-group" id="profesores"></ul>
<input type="hidden" id="periodo_id" name="periodo_id" value="<?= $user->periodo_id ?>">
<input type="hidden" id="profesor_id" name="profesor_id" value="">
</div>
</div>
<!-- ICO-BUSCAR FILTRAR & ICO-BORRAR LIMPIAR -->
<div class="form-group row justify-content-center">
<button class="btn btn-outline-primary mr-2">
<span class="ing-buscar icono"></span>
Buscar horario
</button>
<button type="button" class="btn btn-outline-danger" onclick="location.reload()">
<span class="ing-borrar icono"></span>
Limpiar
</button>
</div>
</div>
</div>
</form>
<div class="form-group mt-4 row justify-content-center">
<?php if ($write) { ?>
<button type="button" id="nuevo" class="btn btn-outline-primary ml-4 d-none" title="Nuevo horario" data-toggle="modal" data-target="#modal-editar">
<span class="ing-mas ing-fw"></span> Nuevo
</button>
<?php } ?>
</div>
</div>
</div>
</form>
<!-- Horario is a (table with one a cell) within a table
7:15 - 8:45, 8:45 - 10:15, 10:30 - 12:00, 12:00 - 13:30
de lunes a viernes, a excepción de que tenga sábado
-->
<div id="btn-excel-horario" class="mb-2 float-right hidden">
<button class="btn btn-outline-secondary " title="Exportar a Excel">
<span class="ing-descarga ing-fw"></span> Exportar a Excel
</button>
</div>
<!-- Table responsive -->
<div class="table-responsive">
<table class="table table-bordered table-sm table-responsive-md" id="table-horario">
<thead class="thead-dark">
<tr id="headers">
<th scope="col" class="text-center">Hora</th>
<th scope="col" class="text-center">Lunes</th>
<th scope="col" class="text-center">Martes</th>
<th scope="col" class="text-center">Miércoles</th>
<th scope="col" class="text-center">Jueves</th>
<th scope="col" class="text-center">Viernes</th>
<th scope="col" class="text-center">Sábado</th>
</tr>
</thead>
<tbody id="horario"></tbody>
</table>
</div>
</main>
</body>
</html>

44
js/carreras.js Normal file
View File

@@ -0,0 +1,44 @@
import { createApp } from 'https://unpkg.com/petite-vue?module';
const app = createApp({
carreras: [],
niveles: [],
message: {},
async setNivel(carrera, nivel) {
if (carrera.nivel_id === nivel.nivel_id) {
return;
}
carrera.nivel_id = nivel.nivel_id;
carrera.nivel_nombre = nivel.nivel_nombre;
await fetch('action/carrera.php', {
method: 'PUT',
body: JSON.stringify({
carrera_id: carrera.carrera_id,
nivel_id: nivel.nivel_id
})
})
.then(res => res.json())
.then(res => {
this.message.title = "Actualización";
this.message.text = res.error ?? res.success;
this.message.type = res.error ? 'danger' : 'success';
this.message.timestamp = new Date().toLocaleTimeString();
});
},
async mounted() {
this.carreras = await fetch('action/carrera.php').then(res => res.json());
this.niveles = await fetch('action/nivel.php').then(res => res.json());
// group by facultad_id
const carreras = this.carreras.reduce((acc, cur) => {
const { facultad_nombre } = cur;
if (!acc[facultad_nombre]) {
acc[facultad_nombre] = [];
}
acc[facultad_nombre].push(cur);
return acc;
}, {});
this.carreras = Object.entries(carreras).map(([facultad_nombre, carreras]) => ({
facultad_nombre: facultad_nombre,
carreras
}));
}
}).mount('#app');

44
js/periodos.js Normal file
View File

@@ -0,0 +1,44 @@
import { createApp } from 'https://unpkg.com/petite-vue?module';
const app = createApp({
carreras: [],
niveles: [],
message: {},
async setNivel(carrera, nivel) {
if (carrera.nivel_id === nivel.nivel_id) {
return;
}
carrera.nivel_id = nivel.nivel_id;
carrera.nivel_nombre = nivel.nivel_nombre;
await fetch('action/carrera.php', {
method: 'PUT',
body: JSON.stringify({
carrera_id: carrera.carrera_id,
nivel_id: nivel.nivel_id
})
})
.then(res => res.json())
.then(res => {
this.message.title = "Actualización";
this.message.text = res.error ?? res.success;
this.message.type = res.error ? 'danger' : 'success';
this.message.timestamp = new Date().toLocaleTimeString();
});
},
async mounted() {
this.carreras = await fetch('action/carrera.php').then(res => res.json());
this.niveles = await fetch('action/nivel.php').then(res => res.json());
// group by facultad_id
const carreras = this.carreras.reduce((acc, cur) => {
const { facultad_nombre } = cur;
if (!acc[facultad_nombre]) {
acc[facultad_nombre] = [];
}
acc[facultad_nombre].push(cur);
return acc;
}, {});
this.carreras = Object.entries(carreras).map(([facultad_nombre, carreras]) => ({
facultad_nombre: facultad_nombre,
carreras
}));
}
}).mount('#app');

220
periodos.php Normal file
View File

@@ -0,0 +1,220 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Auditoría asistencial</title>
<?php
include 'import/html_css_files.php';
?>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.0/dist/trix.css">
<style>
[v-cloak] {
display: none;
}
</style>
<script src="js/jquery.min.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/bootstrap/bootstrap.min.js"></script>
</head>
<body>
<?
$redirect = $_SERVER['PHP_SELF'];
include "import/html_header.php";
global $user;
html_header("Periodos", "Sistema de gestión de checador");
?>
<main class="container-fluid px-4 my-4" id="app" v-cloak @vue:mounted="mounted" style="min-height: 70vh;">
<section class="row mt-4">
<div class="col-12 position-relative">
<!-- Loop for messages -->
<div class="toast show shadow-sm mb-3 bg-white"
style="position: fixed; top: 15%; right: 1%; max-width: 300px;" role="alert" aria-live="assertive"
aria-atomic="true"
@vue:mounted="$('.toast').toast({delay: 5000}).toast('show').on('hidden.bs.toast', () => { message.text = '' })"
v-if="message.text">
<div class="toast-header">
<strong class="mr-auto text-primary text-uppercase text-center w-100 px-4">
{{ message.title }}
</strong>
<small class="text-muted">{{ message.timestamp }}</small>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"
@click="message.text = ''">
<span aria-hidden="true">
<i class="fas fa-times"></i>
</span>
</button>
</div>
<div class="toast-body">
<div :class="message.type == 'success' ? 'text-success' : 'text-danger'"><i
:class="message.type == 'success' ? 'fas fa-check-circle' : 'fas fa-times-circle'"></i>
{{ message.text }}
</div>
</div>
</div>
</div>
</section>
<div class="container">
<div class="d-flex flex-wrap justify-content-around">
<div class="accordion col-8 mb-4" id="puestos"
v-scope="{selected_carrera_id: -1, current_materia: null, current_encargado: null}"
v-if="puestos.length">
<div class="card" v-for="(puesto, index) in puestos" :key="puesto.puesto_id">
<div class="card-header bg-primary" :id="`puesto-${puesto.nombre}`">
<h2 class="mb-0">
<button class="btn btn-link btn-block text-left text-light" type="button"
data-toggle="collapse" :data-target="`#puesto-${puesto.puesto_id}`" aria-expanded="true"
:aria-controls="`puesto-${puesto.puesto_id}`">
{{puesto.nombre}}
<button type="button" class="btn btn-outline-danger float-right"
data-target="#eliminar-puesto" data-toggle="modal" @click="to_delete = puesto">
<span class="icono ing-basura"></span>
</button>
</button>
</h2>
</div>
<div :id="`puesto-${puesto.puesto_id}`" class="collapse" :class="{'show': index == 0}"
:aria-labelledby="`puesto-${puesto.nombre}`" data-parent="#puestos">
<div class="card-body">
<!-- Encargado -->
<div class="form-row justify-content-around align-items-center mb-2">
<label :for="`encargado-${puesto.puesto_id}`" class="col-3">
Encargado del área
</label>
<div id="encargados" class="datalist datalist-select mb-1 col-9">
<div class="datalist-input" v-if="puesto.encargado">
({{puesto.encargado.usuario_clave}}) {{ puesto.encargado.usuario_nombre }}
</div>
<div class="datalist-input" v-else>
Selecciona un encargado
</div>
<span class="icono ing-buscar"></span>
<ul style="display:none">
<li class="datalist-option" v-for="usuario in usuarios"
:key="usuario.usuario_id" :data-id="usuario.usuario_id"
style=" white-space: nowrap;" @click="puesto.encargado = usuario"
:class="{'selected': puesto.encargado?.usuario_id == usuario.usuario_id}">
(<small> {{usuario.usuario_clave}} </small>) {{ usuario.usuario_nombre }}
</li>
</ul>
<input type="hidden" id="encargado_id" name="id">
</div>
</div>
<hr>
<div class="form-row justify-content-around align-items-center mb-2"
v-show="carreras.length">
<label :for="`carrera-${puesto.puesto_id}`" class="col-3">
Carrera
</label>
<div id="dlCarreras" class="datalist datalist-select mb-1 col-9">
<div class="datalist-input">
Selecciona una carrera
</div>
<span class="icono ing-buscar"></span>
<ul style="display:none">
<li class="datalist-option" data-id="0" @click="selected_carrera_id = 0">
Todas las carreras
</li>
<li class="datalist-option" v-for="carrera in carreras"
:key="carrera.carrera_id" :data-id="carrera.carrera_id"
style=" white-space: nowrap;"
@click="selected_carrera_id = carrera.carrera_id">
(<small> {{carrera.clave_carrera}} </small>) {{ carrera.carrera_nombre }}
</li>
</ul>
<input type="hidden" id="carrera_id" name="id">
</div>
</div>
<div class="form-row justify-content-around align-items-center"
v-scope="{to_add_materia: null}">
<label :for="`materias-${puesto.puesto_id}`" class="col-3">
Materias
</label>
<input name="materia" placeholder="Seleccione una materia" list="datalist-materias"
class="form-control col-9 " v-model="current_materia" @input="to_add_materia = materias.find(m => current_materia == `${m.clave_materia} - ${m.materia_nombre}`);
if (to_add_materia) {
if (puesto.materias.find(p => p.materia_id == to_add_materia.materia_id)) {
console.log('La materia ya está asignada');
current_materia = null;
return;
}
puesto.materias.push(to_add_materia);
materias.splice(materias.indexOf(to_add_materia), 1);
current_materia = null;
}" :disabled="selected_carrera_id == -1" v-model="current_materia"
:id="`materias-${puesto.puesto_id}`" autocomplete="off">
</div>
<datalist id="datalist-materias">
<option
v-for="materia in materias.filter(m => selected_carrera_id == 0 || m.carrera_id == selected_carrera_id).filter(m => !puesto.materias.find(p => p.materia_id == m.materia_id))"
:value="`${materia.clave_materia} - ${materia.materia_nombre}`">
</datalist>
<hr>
<fieldset class="container d-flex flex-column justify-content-center align-items-center">
<legend>Materias Asignadas <span
class="badge badge-secondary">{{puesto.materias.length}}</span></legend>
<ul class="list-group overflow-auto col-10" v-if="puesto.materias.length"
style="max-height: 200px; overflow-y: auto;">
<li class="list-group-item list-group-item-action"
v-for="materia in puesto.materias" :key="materia.materia_id"
@click="puesto.materias.splice(puesto.materias.indexOf(materia), 1); materias.push(materia)"
style="cursor: pointer;">
<div class="d-flex justify-content-center">
<div class="col-2 text-center">
<span class="icono ing-borrar text-danger"></span>
</div>
<div class="col-10 text-left">
{{materia.clave_materia}} - {{materia.materia_nombre}}
</div>
</div>
</li>
</ul>
<div class="alert alert-light" role="alert" v-else>
No hay materias asignadas
</div>
</fieldset>
</div>
<div class="card-footer text-muted">
<!-- scroll to top -->
<button type="button" class="btn btn-outline-primary btn-lg btn-block"
@click="actualizarPuesto(puesto.puesto_id, puesto.materias, puesto.encargado?.usuario_id)"
onclick="window.scrollTo(0, 0);">
{{ puesto.encargado ? 'Guardar cambios' : 'Guardar sin encargado' }}
</button>
</div>
</div>
</div>
</div>
<div v-else>
<div class="alert alert-dark" role="alert">
No hay puestos registrados
</div>
</div>
</div>
</div>
</main>
<?
include "import/html_footer.php"; ?>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js"
integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+"
crossorigin="anonymous"></script>
<script src="js/datalist.js"></script>
<script src="js/periodos.js?<?= rand(0, 2) ?>" type="module"></script>
<script src="js/scrollables.js"></script>
</body>
</html>

View File

@@ -38,12 +38,20 @@
<div class="alert alert-success" role="alert" v-if="message"> <div class="alert alert-success" role="alert" v-if="message">
{{message}} {{message}}
</div> </div>
<div class="justify-content-between align-items-center d-flex mb-4">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#nuevo-puesto"> <?
<span class="ing-mas"></span> if ($user->acceso == 'w') {
Agregar puesto ?>
</button> <div class="justify-content-between align-items-center d-flex mb-4">
</div> <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#nuevo-puesto">
<span class="ing-mas"></span>
Agregar puesto
</button>
</div>
<?
}
?>
<div class="d-flex flex-wrap justify-content-around"> <div class="d-flex flex-wrap justify-content-around">
<div class="accordion col-8 mb-4" id="puestos" <div class="accordion col-8 mb-4" id="puestos"

63
ts/carreras.ts Normal file
View File

@@ -0,0 +1,63 @@
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'
type Carrera = {
carrera_id: number;
carrera_nombre: string;
clave_carrera: string;
facultad_id: number;
facultad_nombre: string;
nivel_id: number;
nivel_nombre: string;
}
type Nivel = {
nivel_id: number;
nivel_nombre: string;
}
const app = createApp({
carreras: [] as Carrera[],
niveles: [] as Nivel[],
message: {} as Record<string, string>,
async setNivel(carrera: Carrera, nivel: Nivel) {
if (carrera.nivel_id === nivel.nivel_id) {
return
}
carrera.nivel_id = nivel.nivel_id
carrera.nivel_nombre = nivel.nivel_nombre
await fetch('action/carrera.php', {
method: 'PUT',
body: JSON.stringify({
carrera_id: carrera.carrera_id,
nivel_id: nivel.nivel_id
})
})
.then(res => res.json())
.then(res => {
this.message.title = "Actualización"
this.message.text = res.error ?? res.success
this.message.type = res.error ? 'danger' : 'success'
this.message.timestamp = new Date().toLocaleTimeString()
})
},
async mounted() {
this.carreras = await fetch('action/carrera.php').then(res => res.json())
this.niveles = await fetch('action/nivel.php').then(res => res.json())
// group by facultad_id
const carreras = this.carreras.reduce((acc, cur) => {
const { facultad_nombre } = cur
if (!acc[facultad_nombre]) {
acc[facultad_nombre] = []
}
acc[facultad_nombre].push(cur)
return acc
}, {} as Record<number, Carrera[]>)
this.carreras = Object.entries(carreras).map(([facultad_nombre, carreras]) => ({
facultad_nombre: facultad_nombre,
carreras
}))
}
}).mount('#app')

63
ts/periodos.ts Normal file
View File

@@ -0,0 +1,63 @@
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'
type Carrera = {
carrera_id: number;
carrera_nombre: string;
clave_carrera: string;
facultad_id: number;
facultad_nombre: string;
nivel_id: number;
nivel_nombre: string;
}
type Nivel = {
nivel_id: number;
nivel_nombre: string;
}
const app = createApp({
carreras: [] as Carrera[],
niveles: [] as Nivel[],
message: {} as Record<string, string>,
async setNivel(carrera: Carrera, nivel: Nivel) {
if (carrera.nivel_id === nivel.nivel_id) {
return
}
carrera.nivel_id = nivel.nivel_id
carrera.nivel_nombre = nivel.nivel_nombre
await fetch('action/carrera.php', {
method: 'PUT',
body: JSON.stringify({
carrera_id: carrera.carrera_id,
nivel_id: nivel.nivel_id
})
})
.then(res => res.json())
.then(res => {
this.message.title = "Actualización"
this.message.text = res.error ?? res.success
this.message.type = res.error ? 'danger' : 'success'
this.message.timestamp = new Date().toLocaleTimeString()
})
},
async mounted() {
this.carreras = await fetch('action/carrera.php').then(res => res.json())
this.niveles = await fetch('action/nivel.php').then(res => res.json())
// group by facultad_id
const carreras = this.carreras.reduce((acc, cur) => {
const { facultad_nombre } = cur
if (!acc[facultad_nombre]) {
acc[facultad_nombre] = []
}
acc[facultad_nombre].push(cur)
return acc
}, {} as Record<number, Carrera[]>)
this.carreras = Object.entries(carreras).map(([facultad_nombre, carreras]) => ({
facultad_nombre: facultad_nombre,
carreras
}))
}
}).mount('#app')