justificar_asistencias.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <?php
  2. error_reporting(E_ALL & ~E_NOTICE);
  3. ini_set("display_errors", 1);
  4. require_once 'class/c_login.php';
  5. if (!isset($_SESSION['user']))
  6. die(header('Location: index.php'));
  7. $user = unserialize($_SESSION['user']);
  8. $user->access();
  9. if (!$user->admin && in_array($user->acceso, ['r', 'n'])) {
  10. // die($access);
  11. header('Location: main.php?error=1');
  12. } else {
  13. $user->print_to_log('Consultar asistencia');
  14. }
  15. ?>
  16. <!DOCTYPE html>
  17. <html lang="en">
  18. <head>
  19. <title>Justificar asistencias | <?= $user->facultad['facultad'] ?? 'General' ?></title>
  20. <meta charset="utf-8">
  21. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  22. <?php include_once "import/html_css_files.php"; ?>
  23. </head>
  24. <body style="display: block;">
  25. <?php
  26. include("import/html_header.php");
  27. html_header("Justificar asistencias", "Sistema de gestión de checador");
  28. ?>
  29. <main class="container content marco content-margin" id="local-app">
  30. <section id="message"></section>
  31. <?php
  32. require_once "import/html_forms_justificacion.php";
  33. ?>
  34. <!-- #scroll-btn sticky scroll down btn with a sticky position on the bottom right -->
  35. <a href="#top" id="scroll-up" class="btn btn-primary btn-sm" role="button" aria-pressed="true" style="display: none; position: fixed; bottom: 3rem; right: 1rem;">
  36. <?= $ICO['arriba'] ?>
  37. </a>
  38. <a href="#top" id="scroll-down" class="btn btn-primary btn-sm" role="button" aria-pressed="true" style="display: sticky; position: fixed; bottom: 1rem; right: 1rem;">
  39. <?= $ICO['abajo'] ?>
  40. </a>
  41. <table class="table table-striped table-hover table-white table-sm main-table" id="main-table">
  42. <thead class="thead-dark">
  43. <tr>
  44. <th>
  45. <input class="radio-md" type="checkbox" id="check" name="check" value="0">
  46. </th>
  47. <th>Clave</th>
  48. <th>Profesor</th>
  49. <th>Hora</th>
  50. <th>Materia</th>
  51. <th>Grupo</th>
  52. <th>Salón</th>
  53. </tr>
  54. </thead>
  55. <tbody id="visible"></tbody>
  56. </table>
  57. <div class="row justify-content-center main-table">
  58. <div class="col-4 justify-content-center">
  59. <button class="btn btn-primary btn-block disabled" id="justificar-btn">
  60. <i class="ing-justificar"></i>
  61. Justificar
  62. </button>
  63. </div>
  64. </div>
  65. <!-- scroll up -->
  66. </main>
  67. <?php
  68. require_once("import/html_footer.php");
  69. require_once("js/messages.php");
  70. ?>
  71. <script src="js/bootstrap/popper.min.js"></script>
  72. <script src="js/bootstrap/bootstrap.min.js"></script>
  73. <script>
  74. $(".main-table").hide();
  75. $("#scroll-down").hide();
  76. $("#scroll-up").hide();
  77. <?php
  78. if (isset($user->periodo)) {
  79. $periodo = query("SELECT * FROM FS_PERIODO WHERE ID = :periodo", [":periodo" => $user->periodo]);
  80. echo "// Período: {$periodo["inicio"]} - {$periodo["fin"]}\n";
  81. ?>
  82. // $periodo format = Y-m-d
  83. var today = new Date();
  84. var fecha_inicial = new Date(<?= date("Y, m-1, d", strtotime($periodo['inicio'])) ?>);
  85. var fecha_final = new Date(<?= date("Y, m-1, d", strtotime($periodo['fin'])) ?>);
  86. var limit = new Date(Math.min(today, fecha_final));
  87. console.log(`today: ${today}, fecha_inicial: ${fecha_inicial}, fecha_final: ${fecha_final}`);
  88. $("#filter_fecha").datepicker("option", "minDate", fecha_inicial);
  89. $("#filter_fecha").datepicker("option", "maxDate", fecha_final);
  90. $("#filter_fecha").datepicker("setDate", today <= fecha_final ? today : fecha_final);
  91. <?php
  92. }
  93. ?>
  94. makeRequiredDatalist("#filter_fecha", true);
  95. // send fecha, hora_inicio, hora_fin, nombre
  96. $(document).on('click', '#main-button', async function() {
  97. // check required fields
  98. if (!validateDatalist("#filter_fecha") || $("[required]").filter(function() {
  99. return this.value == "";
  100. }).length > 0) {
  101. triggerMessage("Faltan campos por llenar", "Error de datos");
  102. return;
  103. }
  104. // validar horas de inicio y fin unless si la hora final no está vacía
  105. var hora_inicio = $("#filter_hora_inicio").val();
  106. var hora_fin = $("#filter_hora_fin").val();
  107. if (hora_fin != "") {
  108. if (hora_inicio > hora_fin) {
  109. triggerMessage("La hora de inicio debe ser menor a la hora de fin", "Error de datos");
  110. return;
  111. }
  112. }
  113. var periodo = $("#periodo").val();
  114. // send serialized data and periodo
  115. // button suspense
  116. let btn = $(this).html();
  117. $("#main-button").html('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Cargando...');
  118. $("#main-button").attr("disabled", true);
  119. await $.post(
  120. 'action/action_profesor_faltas.php', {
  121. fecha: $("#filter_fecha").val(),
  122. hora_inicio: hora_inicio,
  123. hora_fin: hora_fin,
  124. // remove leading zeros from clave after replace(/[a-zA-Z]{2}/, '') to remove the first two letters
  125. clave: $("#filter_clave").val().replace(/[a-zA-Z]{2}/, '').replace(/^0+/, ''),
  126. nombre: $("#filter_nombre").val(),
  127. periodo: periodo
  128. },
  129. function(data) {
  130. console.log(data);
  131. if (data.length > 0) {
  132. var table = $("#visible");
  133. table.empty();
  134. $('#check').prop('checked', false);
  135. $('#justificar-btn').addClass('disabled');
  136. data.forEach(function(row) {
  137. // hora is hh:mm
  138. hora = row.hora.split(":");
  139. hora = `${hora[0]}:${hora[1]}`;
  140. // nombre is scentence case
  141. nombre = row.profesor.split(" ");
  142. nombre = nombre.map(function(word) {
  143. return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
  144. }).join(" ");
  145. table.append(
  146. `<tr id="${row.id}">
  147. <td>
  148. <input class="radio-md prof" type="checkbox" name="check" value="${row.profesor_id}">
  149. </td>
  150. <td>${row.clave}</td>
  151. <td>${nombre}</td>
  152. <td>${hora}</td>
  153. <td>${row.materia}</td>
  154. <td>${row.grupo}</td>
  155. <td>${row.horario_salon}</td>
  156. </tr>`
  157. );
  158. });
  159. $(".main-table").show();
  160. var btn = $("#justificar-btn");
  161. var btnPos = btn.offset().top;
  162. var table = $("#main-table");
  163. var tablePos = table.offset().top;
  164. var windowHeight = $(window).height();
  165. if (btnPos > windowHeight) {
  166. $("#scroll-down").show();
  167. } else {
  168. $("#scroll-down").hide();
  169. }
  170. // make table responsive
  171. $("table").wrap("<div class='table-responsive'></div>");
  172. } else {
  173. triggerMessage("No se encontraron resultados", "Sin resultados", "warning");
  174. $(".main-table").hide();
  175. }
  176. }, 'json'
  177. )
  178. // button suspense
  179. $("#main-button").html(btn);
  180. $("#main-button").attr("disabled", false);
  181. });
  182. $(document).on('change', '#check', function() {
  183. if (this.checked) {
  184. $("input[name='check']").prop("checked", true);
  185. } else {
  186. $("input[name='check']").prop("checked", false);
  187. }
  188. });
  189. // if at least one checkbox is checked, remove .disabled from #justificar-btn
  190. $(document).on('change', 'input[name="check"]', function() {
  191. if ($(".prof[name='check']:checked").length > 0) {
  192. $("#justificar-btn").removeClass("disabled");
  193. } else {
  194. $("#justificar-btn").addClass("disabled");
  195. $('#check').prop('checked', false);
  196. }
  197. });
  198. // if no checkboxes are checked, add .disabled to #justificar-btn and uncheck #check
  199. $(document).on('click', '#justificar-btn', function() {
  200. if ($(".prof[name='check']:checked").length == 0) {
  201. $("#justificar-btn").addClass("disabled");
  202. }
  203. });
  204. $(document).on('click', '#scroll-down', function() {
  205. $('html, body').animate({
  206. scrollTop: $("#justificar-btn").offset().top
  207. }, 500);
  208. });
  209. $(document).on('click', '#scroll-up', function() {
  210. $('html, body').animate({
  211. scrollTop: 0
  212. }, 500);
  213. });
  214. // send fecha, hora_inicio, hora_fin y cada clave seleccionada
  215. $(document).on('click', '#justificar-btn', function() {
  216. // unless .disabled
  217. if ($(this).hasClass("disabled"))
  218. return;
  219. // check required fields
  220. if (!validateDatalist("#filter_fecha") || $("[required]").filter(function() {
  221. return this.value == "";
  222. }).length > 0) {
  223. triggerMessage("Faltan campos por llenar", "Error de datos");
  224. return;
  225. }
  226. // validar horas de inicio y fin
  227. var hora_inicio = $("#filter_hora_inicio").val();
  228. var hora_fin = $("#filter_hora_fin").val();
  229. if (hora_fin == '') hora_fin = hora_inicio; // if hora_fin is empty, set it to hora_inicio
  230. if (hora_inicio > hora_fin) {
  231. triggerMessage("La hora de inicio debe ser menor a la hora de fin", "Error de datos");
  232. return;
  233. }
  234. var claves = [];
  235. $("input.prof[name='check']:checked").each(function(index, data) {
  236. // if is the first element, continue
  237. claves.push({
  238. clave: this.value,
  239. id: $(this).parent().parent().attr("id"),
  240. hora: $(this).parent().next().next().next().text(),
  241. });
  242. });
  243. // return
  244. $.post(
  245. 'action/action_justificar.php', {
  246. fecha: $("#filter_fecha").val(),
  247. claves: claves
  248. },
  249. function(data) {
  250. if (data.success) {
  251. triggerMessage("Se han justificado las faltas", "Éxito", "success");
  252. $("#main-table").hide();
  253. } else
  254. triggerMessage("No se han podido justificar las faltas", "Error");
  255. // reset form
  256. $("input[name='check']").prop("checked", false);
  257. $("#justificar-btn").addClass("disabled");
  258. // hide table
  259. $(".main-table").hide();
  260. }, 'json'
  261. );
  262. });
  263. // on scroll and if the position of #justificar-btn is the scrolled position, hide #scroll-down
  264. $(window).scroll(function() {
  265. var btnPos = $("#justificar-btn").offset().top - $(window).height();
  266. var currentPos = $(window).scrollTop();
  267. if (currentPos < btnPos)
  268. $("#scroll-down").show();
  269. else
  270. $("#scroll-down").hide();
  271. // console.log(`
  272. // btnPos: ${btnPos}
  273. // currentPos: ${currentPos}
  274. // tablePos: ${tablePos}
  275. // `);
  276. // if not on top, show #scroll-up
  277. if (currentPos > 0)
  278. $("#scroll-up").show();
  279. else
  280. $("#scroll-up").hide();
  281. });
  282. </script>
  283. </body>
  284. </html>