auditoría.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
  2. $('div.modal#cargando').modal({
  3. backdrop: 'static',
  4. keyboard: false,
  5. show: false,
  6. });
  7. const store = reactive({
  8. loading: false,
  9. current: {
  10. comentario: '',
  11. clase_vista: null,
  12. empty: '',
  13. page: 1,
  14. maxPages: 10,
  15. perPage: 10,
  16. modal_state: "Cargando datos...",
  17. justificada: null,
  18. },
  19. facultades: {
  20. data: [],
  21. async fetch() {
  22. this.data = [];
  23. const res = await fetch('action/action_facultad.php');
  24. this.data = await res.json();
  25. },
  26. },
  27. filters: {
  28. facultad_id: null,
  29. fecha: null,
  30. fecha_inicio: null,
  31. fecha_fin: null,
  32. profesor: null,
  33. periodo_id: null,
  34. bloque_horario: null,
  35. sin_registro: false,
  36. estados: [],
  37. switchFecha: false,
  38. async switchFechas() {
  39. const periodo = await fetch('action/periodo_datos.php');
  40. const periodo_data = await periodo.json();
  41. if (!store.filters.switchFecha) {
  42. $('div.modal#cargando').modal('show');
  43. await store.registros.fetch();
  44. $('div.modal#cargando').modal('hide');
  45. }
  46. $(function () {
  47. store.filters.fecha_inicio = store.filters.fecha_fin = store.filters.fecha = null;
  48. $("#fecha, #fecha_inicio, #fecha_fin").datepicker({
  49. minDate: new Date(`${periodo_data.periodo_fecha_inicio}:00:00:00`),
  50. maxDate: new Date(`${periodo_data.fecha_final}:00:00:00`),
  51. dateFormat: "yy-mm-dd",
  52. showAnim: "slide",
  53. });
  54. const fecha = $("#fecha"), inicio = $("#fecha_inicio"), fin = $("#fecha_fin");
  55. fecha.datepicker("setDate", new Date(`${periodo_data.fecha_final}:00:00:00`));
  56. inicio.on("change", function () {
  57. store.filters.fecha_inicio = inicio.val();
  58. fin.datepicker("option", "minDate", inicio.val());
  59. });
  60. fin.on("change", function () {
  61. store.filters.fecha_fin = fin.val();
  62. inicio.datepicker("option", "maxDate", fin.val());
  63. });
  64. fecha.on("change", async function () {
  65. store.filters.fecha = fecha.val();
  66. $('div.modal#cargando').modal('show');
  67. await store.registros.fetch(store.filters.fecha);
  68. $('div.modal#cargando').modal('hide');
  69. });
  70. });
  71. },
  72. async fetchByDate() {
  73. $('div.modal#cargando').modal('show');
  74. await store.registros.fetch(undefined, store.filters.fecha_inicio, store.filters.fecha_fin);
  75. store.current.page = 1;
  76. $('div.modal#cargando').modal('hide');
  77. }
  78. },
  79. estados: {
  80. data: [],
  81. async fetch() {
  82. this.data = [];
  83. const res = await fetch('action/action_estado_supervisor.php');
  84. this.data = await res.json();
  85. },
  86. getEstado(id) {
  87. return this.data.find((estado) => estado.estado_supervisor_id === id);
  88. },
  89. printEstados() {
  90. if (store.filters.estados.length > 0)
  91. document.querySelector('#estados').innerHTML = store.filters.estados.map((estado) => `<span class="mx-2 badge badge-${store.estados.getEstado(estado).estado_color}">
  92. <i class="${store.estados.getEstado(estado).estado_icon}"></i> ${store.estados.getEstado(estado).nombre}
  93. </span>`).join('');
  94. else
  95. document.querySelector('#estados').innerHTML = `Todos los registros`;
  96. }
  97. },
  98. bloques_horario: {
  99. data: [],
  100. async fetch() {
  101. this.data = [];
  102. const res = await fetch('action/action_grupo_horario.php');
  103. this.data = await res.json();
  104. if (this.data.every((bloque) => !bloque.selected))
  105. this.data[0].selected = true;
  106. },
  107. },
  108. toggle(arr, element) {
  109. const newArray = arr.includes(element) ? arr.filter((item) => item !== element) : [...arr, element];
  110. // if all are selected, then unselect all
  111. if (newArray.length === this.estados.data.length) {
  112. setTimeout(() => {
  113. document.querySelectorAll('#dlAsistencia>ul>li.selected').forEach(element => element.classList.remove('selected'));
  114. }, 100);
  115. return [];
  116. }
  117. return newArray;
  118. },
  119. async justificar() {
  120. if (!store.current.justificada)
  121. return;
  122. let data;
  123. try {
  124. const res = await fetch('action/action_justificar.php', {
  125. method: 'PUT',
  126. headers: {
  127. 'Content-Type': 'application/json'
  128. },
  129. body: JSON.stringify(store.current.justificada)
  130. });
  131. data = await res.json();
  132. }
  133. catch (error) {
  134. alert('Error al justificar');
  135. store.current.justificada = store.current.clone_justificada;
  136. }
  137. finally {
  138. delete store.current.clone_justificada;
  139. }
  140. store.current.justificada.justificador_nombre = data.justificador_nombre;
  141. store.current.justificada.justificador_clave = data.justificador_clave;
  142. store.current.justificada.justificador_facultad = data.justificador_facultad;
  143. store.current.justificada.justificador_rol = data.justificador_rol;
  144. store.current.justificada.registro_fecha_justificacion = data.registro_fecha_justificacion;
  145. },
  146. registros: {
  147. data: [],
  148. async fetch(fecha, fecha_inicio, fecha_fin) {
  149. // if (!store.filters.facultad_id || !store.filters.periodo_id) return
  150. this.loading = true;
  151. this.data = [];
  152. const params = {
  153. facultad_id: 19,
  154. periodo_id: 2,
  155. };
  156. if (fecha)
  157. params['fecha'] = fecha;
  158. if (fecha_inicio)
  159. params['fecha_inicio'] = fecha_inicio;
  160. if (fecha_fin)
  161. params['fecha_fin'] = fecha_fin;
  162. const paramsUrl = new URLSearchParams(params).toString();
  163. const res = await fetch(`action/action_auditoria.php?${paramsUrl}`, {
  164. method: 'GET',
  165. });
  166. this.data = await res.json();
  167. this.loading = false;
  168. },
  169. invertir() {
  170. this.data = this.data.reverse();
  171. },
  172. mostrarComentario(registro_id) {
  173. const registro = this.data.find((registro) => registro.registro_id === registro_id);
  174. store.current.comentario = registro.comentario;
  175. $('#ver-comentario').modal('show');
  176. },
  177. get relevant() {
  178. /*
  179. facultad_id: null,
  180. fecha: null,
  181. fecha_inicio: null,
  182. fecha_fin: null,
  183. profesor: null,
  184. asistencia: null,
  185. estado_id: null,
  186. if one of the filters is null, then it is not relevant
  187. */
  188. const filters = Object.keys(store.filters).filter((filtro) => store.filters[filtro] || store.filters[filtro]?.length > 0);
  189. return this.data.filter((registro) => {
  190. return filters.every((filtro) => {
  191. switch (filtro) {
  192. case 'fecha':
  193. return registro.registro_fecha_ideal === store.filters[filtro];
  194. case 'fecha_inicio':
  195. return registro.registro_fecha_ideal >= store.filters[filtro];
  196. case 'fecha_fin':
  197. return registro.registro_fecha_ideal <= store.filters[filtro];
  198. case 'profesor':
  199. const textoFiltro = store.filters[filtro].toLowerCase();
  200. if (/^\([^)]+\)\s[\s\S]+$/.test(textoFiltro)) {
  201. const clave = registro.profesor_clave.toLowerCase();
  202. const filtroClave = textoFiltro.match(/\((.*?)\)/)?.[1];
  203. // console.log(clave, filtroClave);
  204. return clave.includes(filtroClave);
  205. }
  206. else {
  207. const nombre = registro.profesor_nombre.toLowerCase();
  208. return nombre.includes(textoFiltro);
  209. }
  210. case 'facultad_id':
  211. return registro.facultad_id === store.filters[filtro];
  212. case 'estados':
  213. if (store.filters[filtro].length === 0)
  214. return true;
  215. return store.filters[filtro].includes(registro.estado_supervisor_id);
  216. case 'bloque_horario':
  217. const bloque = store.bloques_horario.data.find((bloque) => bloque.id === store.filters[filtro]);
  218. return registro.horario_hora < bloque.hora_fin && registro.horario_fin > bloque.hora_inicio;
  219. default: {
  220. if (store.filters.sin_registro)
  221. return !registro.registro_fecha_supervisor;
  222. else
  223. return registro.registro_fecha_supervisor;
  224. }
  225. }
  226. });
  227. });
  228. },
  229. async descargar() {
  230. store.current.modal_state = 'Generando reporte en Excel...';
  231. $('div.modal#cargando').modal('show');
  232. this.loading = true;
  233. if (this.relevant.length === 0)
  234. return;
  235. try {
  236. const res = await fetch('export/supervisor_excel.php', {
  237. method: 'POST',
  238. headers: {
  239. 'Content-Type': 'application/json'
  240. },
  241. body: JSON.stringify(this.relevant)
  242. });
  243. const blob = await res.blob();
  244. window.saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`);
  245. }
  246. catch (error) {
  247. if (error.response && error.response.status === 413) {
  248. alert('Your request is too large! Please reduce the data size and try again.');
  249. }
  250. else {
  251. alert('An error occurred: ' + error.message);
  252. }
  253. }
  254. finally {
  255. $('#cargando').modal('hide');
  256. this.loading = false;
  257. }
  258. },
  259. loading: false,
  260. get pages() {
  261. return Math.ceil(this.relevant.length / store.current.perPage);
  262. }
  263. },
  264. });
  265. createApp({
  266. store,
  267. get clase_vista() {
  268. return store.current.clase_vista;
  269. },
  270. set_justificar(horario_id, profesor_id, registro_fecha_ideal) {
  271. store.current.justificada = store.registros.relevant.find((registro) => registro.horario_id === horario_id && registro.profesor_id === profesor_id && registro.registro_fecha_ideal === registro_fecha_ideal);
  272. store.current.clone_justificada = JSON.parse(JSON.stringify(store.current.justificada));
  273. },
  274. cancelar_justificacion() {
  275. Object.assign(store.current.justificada, store.current.clone_justificada);
  276. delete store.current.clone_justificada;
  277. },
  278. get profesores() {
  279. return store.registros.data
  280. .map((registro) => ({
  281. profesor_id: registro.profesor_id,
  282. profesor_nombre: registro.profesor_nombre,
  283. profesor_correo: registro.profesor_correo,
  284. profesor_clave: registro.profesor_clave,
  285. profesor_grado: registro.profesor_grado,
  286. }))
  287. .reduce((acc, current) => {
  288. if (!acc.some(item => item.profesor_id === current.profesor_id)) {
  289. acc.push(current);
  290. }
  291. return acc;
  292. }, [])
  293. .sort((a, b) => a.profesor_nombre.localeCompare(b.profesor_nombre));
  294. },
  295. async mounted() {
  296. $('div.modal#cargando').modal('show');
  297. await store.registros.fetch();
  298. await store.facultades.fetch();
  299. await store.estados.fetch();
  300. await store.bloques_horario.fetch();
  301. await store.filters.switchFechas();
  302. $('div.modal#cargando').modal('hide');
  303. }
  304. }).mount('#app');