justificar_asistencias.php 13 KB

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