From 3ae35c84f9d89181b80378a27ed77e34af2720e5 Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Fri, 11 Aug 2023 15:41:57 +0000 Subject: [PATCH] =?UTF-8?q?Stable=20without=20per=C3=ADodo=20in=20autidor?= =?UTF-8?q?=C3=ADa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- action/action_auditoria.php | 29 +- auditoría.php | 116 ++++-- class/c_login.php | 2 +- import/periodo.php | 2 +- js/auditoría.js | 432 ++++++++++++--------- js/client.js | 240 ++++++------ js/horario_profesor.js | 726 +++++++++++++++++------------------ js/reposiciones.js | 356 ++++++++--------- log/asistencias_2023_08.log | 3 + service/backend/periodos.php | 8 +- service/periodos.v1.php | 8 +- service/periodos.v2.php | 8 +- supervisor.php | 46 ++- ts/auditoría.ts | 103 +++-- 14 files changed, 1145 insertions(+), 934 deletions(-) create mode 100644 log/asistencias_2023_08.log diff --git a/action/action_auditoria.php b/action/action_auditoria.php index 0852b62..2a31bcf 100644 --- a/action/action_auditoria.php +++ b/action/action_auditoria.php @@ -22,12 +22,27 @@ try { } }); - $data = $db->query("SELECT *, horario_view.facultad_id, horario_view.periodo_id FROM registro - JOIN horario_view USING (horario_id) - LEFT JOIN estado_supervisor USING (estado_supervisor_id) - LEFT JOIN profesor USING (profesor_id) - LEFT JOIN usuario ON usuario.usuario_id = registro.supervisor_id - ORDER BY registro_fecha_ideal DESC, horario_hora ASC, registro_fecha_supervisor ASC"); + $data = $db->query( + "WITH horarios AS ( + SELECT * FROM horario_view WHERE (periodo_id, facultad_id) = (:periodo_id, :facultad_id) + ), + fechas AS ( + SELECT fechas_clase(h.horario_id) as registro_fecha_ideal, h.horario_id + FROM horarios h + ) + SELECT estado_supervisor.*, usuario.*, registro.*, profesor.*, horarios.*, fechas.* + FROM horarios + JOIN fechas using (horario_id) + JOIN horario_profesor using (horario_id) + JOIN profesor using (profesor_id) + LEFT JOIN registro USING (horario_id, registro_fecha_ideal, profesor_id) + left join estado_supervisor using (estado_supervisor_id) + LEFT JOIN USUARIO ON USUARIO.usuario_id = REGISTRO.supervisor_id", + [ + ':periodo_id' => $_GET['periodo_id'], + ':facultad_id' => $_GET['facultad_id'], + ] + ); $last_query = [ 'query' => $db->getLastQuery(), @@ -46,7 +61,7 @@ try { echo json_encode([ 'error' => $th->getMessage(), 'query' => $db->getLastQuery(), - ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR); exit; } catch (Exception $th) { http_response_code(500); diff --git a/auditoría.php b/auditoría.php index 9912ee1..ea33f11 100644 --- a/auditoría.php +++ b/auditoría.php @@ -25,8 +25,14 @@ "Sistema de gestión de checador", ); ?> -
+
@@ -37,12 +43,13 @@
Selecciona una facultad
    -
  • +
  • Todas las facultades
  • + @click="store.filters.facultad_id = facultad.facultad_id; store.current.page = 1;"> ( {{facultad.clave_dependencia}} ) {{ facultad.facultad_nombre }}
@@ -52,7 +59,7 @@
diff --git a/class/c_login.php b/class/c_login.php index 3afac45..b6205a7 100644 --- a/class/c_login.php +++ b/class/c_login.php @@ -24,7 +24,7 @@ class Login } public function print_to_log(string $desc, array $old = null, array $new = null): void { - $log = new classes\LogAsistencias($_ENV["RUTA_RAIZ"]); + $log = new classes\LogAsistencias(); if ($old) $desc .= " |#| OLD:" . json_encode($old); if ($new) diff --git a/import/periodo.php b/import/periodo.php index a22dcdc..c68341e 100644 --- a/import/periodo.php +++ b/import/periodo.php @@ -60,6 +60,6 @@ makeRequiredDatalist("#periodo", true); $(document).on('click', '#dlPeriodo ul li:not(.not-selectable)', function () { - $('#formaPeriodo').submit(); + $('form#formaPeriodo').submit(); }); \ No newline at end of file diff --git a/js/auditoría.js b/js/auditoría.js index 8e98bcb..bc3c9ed 100644 --- a/js/auditoría.js +++ b/js/auditoría.js @@ -1,189 +1,247 @@ -import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'; -const store = reactive({ - loading: false, - current: { - comentario: '', - clase_vista: null, - empty: '', - }, - facultades: { - data: [], - async fetch() { - this.data = []; - const res = await fetch('action/action_facultad.php'); - this.data = await res.json(); - }, - }, - filters: { - facultad_id: null, - fecha: null, - fecha_inicio: null, - fecha_fin: null, - profesor: null, - bloque_horario: null, - estados: [], - switchFecha: false, - switchFechas() { - $(function () { - store.filters.fecha_inicio = store.filters.fecha_fin = store.filters.fecha = null; - $("#fecha, #fecha_inicio, #fecha_fin").datepicker({ - minDate: -3, - maxDate: new Date(), - dateFormat: "yy-mm-dd", - showAnim: "slide", - }); - const fecha = $("#fecha"), inicio = $("#fecha_inicio"), fin = $("#fecha_fin"); - inicio.on("change", function () { - store.filters.fecha_inicio = inicio.val(); - fin.datepicker("option", "minDate", inicio.val()); - }); - fin.on("change", function () { - store.filters.fecha_fin = fin.val(); - inicio.datepicker("option", "maxDate", fin.val()); - }); - fecha.on("change", function () { - store.filters.fecha = fecha.val(); - }); - }); - } - }, - estados: { - data: [], - async fetch() { - this.data = []; - const res = await fetch('action/action_estado_supervisor.php'); - this.data = await res.json(); - }, - getEstado(id) { - return this.data.find((estado) => estado.estado_supervisor_id === id); - }, - printEstados() { - if (store.filters.estados.length > 0) +import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'; +const store = reactive({ + loading: false, + current: { + comentario: '', + clase_vista: null, + empty: '', + page: 1, + maxPages: 10, + perPage: 10, + modal_state: "Cargando datos...", + }, + facultades: { + data: [], + async fetch() { + this.data = []; + const res = await fetch('action/action_facultad.php'); + this.data = await res.json(); + }, + }, + filters: { + facultad_id: null, + fecha: null, + fecha_inicio: null, + fecha_fin: null, + profesor: null, + periodo_id: null, + bloque_horario: null, + estados: [], + switchFecha: false, + switchFechas() { + $(function () { + store.filters.fecha_inicio = store.filters.fecha_fin = store.filters.fecha = null; + $("#fecha, #fecha_inicio, #fecha_fin").datepicker({ + minDate: -15, + maxDate: new Date(), + dateFormat: "yy-mm-dd", + showAnim: "slide", + }); + const fecha = $("#fecha"), inicio = $("#fecha_inicio"), fin = $("#fecha_fin"); + inicio.on("change", function () { + store.filters.fecha_inicio = inicio.val(); + fin.datepicker("option", "minDate", inicio.val()); + }); + fin.on("change", function () { + store.filters.fecha_fin = fin.val(); + inicio.datepicker("option", "maxDate", fin.val()); + }); + fecha.on("change", function () { + store.filters.fecha = fecha.val(); + }); + }); + } + }, + estados: { + data: [], + async fetch() { + this.data = []; + const res = await fetch('action/action_estado_supervisor.php'); + this.data = await res.json(); + }, + getEstado(id) { + return this.data.find((estado) => estado.estado_supervisor_id === id); + }, + printEstados() { + if (store.filters.estados.length > 0) document.querySelector('#estados').innerHTML = store.filters.estados.map((estado) => ` ${store.estados.getEstado(estado).nombre} - `).join(''); - else - document.querySelector('#estados').innerHTML = `Todos los registros`; - } - }, - bloques_horario: { - data: [], - async fetch() { - this.data = []; - const res = await fetch('action/action_grupo_horario.php'); - this.data = await res.json(); - if (this.data.every((bloque) => !bloque.selected)) - this.data[0].selected = true; - }, - }, - toggle(arr, element) { - const newArray = arr.includes(element) ? arr.filter((item) => item !== element) : [...arr, element]; - // if all are selected, then unselect all - if (newArray.length === this.estados.data.length) - return []; - return newArray; - }, -}); -createApp({ - store, - get clase_vista() { - return store.current.clase_vista; - }, - registros: { - data: [], - async fetch() { - this.loading = true; - this.data = []; - const res = await fetch('action/action_auditoria.php'); - this.data = await res.json(); - this.loading = false; - }, - invertir() { - this.data = this.data.reverse(); - }, - mostrarComentario(registro_id) { - const registro = this.data.find((registro) => registro.registro_id === registro_id); - store.current.comentario = registro.comentario; - $('#ver-comentario').modal('show'); - }, - get relevant() { - /* - facultad_id: null, - fecha: null, - fecha_inicio: null, - fecha_fin: null, - profesor: null, - asistencia: null, - estado_id: null, - if one of the filters is null, then it is not relevant - - */ - const filters = Object.keys(store.filters).filter((filtro) => store.filters[filtro] || store.filters[filtro]?.length > 0); - return this.data.filter((registro) => { - return filters.every((filtro) => { - switch (filtro) { - case 'fecha': - return registro.registro_fecha_ideal === store.filters[filtro]; - case 'fecha_inicio': - return registro.registro_fecha_ideal >= store.filters[filtro]; - case 'fecha_fin': - return registro.registro_fecha_ideal <= store.filters[filtro]; - case 'profesor': - const textoFiltro = store.filters[filtro].toLowerCase(); - if (/^\([^)]+\)\s[\s\S]+$/.test(textoFiltro)) { - const clave = registro.profesor_clave.toLowerCase(); - const filtroClave = textoFiltro.match(/\((.*?)\)/)?.[1]; - console.log(clave, filtroClave); - return clave.includes(filtroClave); - } - else { - const nombre = registro.profesor_nombre.toLowerCase(); - return nombre.includes(textoFiltro); - } - case 'facultad_id': - return registro.facultad_id === store.filters[filtro]; - case 'estados': - if (store.filters[filtro].length === 0) - return true; - return store.filters[filtro].includes(registro.estado_supervisor_id); - case 'bloque_horario': - const bloque = store.bloques_horario.data.find((bloque) => bloque.id === store.filters[filtro]); - return registro.horario_hora <= bloque.hora_fin && registro.horario_fin >= bloque.hora_inicio; - default: - return true; - } - }); - }); - }, - async descargar() { - if (this.relevant.length === 0) - return; - const res = await fetch('export/supervisor_excel.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(this.relevant) - }); - const blob = await res.blob(); - window.saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`); - } - }, - get profesores() { - return this.registros.data.map((registro) => ({ - profesor_id: registro.profesor_id, - profesor_nombre: registro.profesor_nombre, - profesor_correo: registro.profesor_correo, - profesor_clave: registro.profesor_clave, - profesor_grado: registro.profesor_grado, - })).sort((a, b) => a.profesor_nombre.localeCompare(b.profesor_nombre)); - }, - async mounted() { - await this.registros.fetch(); - await store.facultades.fetch(); - await store.estados.fetch(); - await store.bloques_horario.fetch(); - store.filters.bloque_horario = store.bloques_horario.data.find((bloque) => bloque.selected)?.id; - store.filters.switchFechas(); - } -}).mount('#app'); + `).join(''); + else + document.querySelector('#estados').innerHTML = `Todos los registros`; + } + }, + bloques_horario: { + data: [], + async fetch() { + this.data = []; + const res = await fetch('action/action_grupo_horario.php'); + this.data = await res.json(); + if (this.data.every((bloque) => !bloque.selected)) + this.data[0].selected = true; + }, + }, + toggle(arr, element) { + const newArray = arr.includes(element) ? arr.filter((item) => item !== element) : [...arr, element]; + // if all are selected, then unselect all + if (newArray.length === this.estados.data.length) { + setTimeout(() => { + document.querySelectorAll('#dlAsistencia>ul>li.selected').forEach(element => element.classList.remove('selected')); + }, 100); + return []; + } + return newArray; + }, +}); +$('div.modal#cargando').modal({ + backdrop: 'static', + keyboard: false, + show: false, +}); +createApp({ + store, + get clase_vista() { + return store.current.clase_vista; + }, + registros: { + data: [], + async fetch() { + // if (!store.filters.facultad_id || !store.filters.periodo_id) return + this.loading = true; + this.data = []; + const params = { + facultad_id: 19, + periodo_id: 2, + }; + const paramsUrl = new URLSearchParams(params).toString(); + const res = await fetch(`action/action_auditoria.php?${paramsUrl}`, { + method: 'GET', + }); + this.data = await res.json(); + this.loading = false; + }, + invertir() { + this.data = this.data.reverse(); + }, + mostrarComentario(registro_id) { + const registro = this.data.find((registro) => registro.registro_id === registro_id); + store.current.comentario = registro.comentario; + $('#ver-comentario').modal('show'); + }, + get relevant() { + /* + facultad_id: null, + fecha: null, + fecha_inicio: null, + fecha_fin: null, + profesor: null, + asistencia: null, + estado_id: null, + if one of the filters is null, then it is not relevant + + */ + const filters = Object.keys(store.filters).filter((filtro) => store.filters[filtro] || store.filters[filtro]?.length > 0); + /* + store.current + page: 1, + maxPages: 10, + perPage: 10, + */ + return this.data.filter((registro) => { + return filters.every((filtro) => { + switch (filtro) { + case 'fecha': + return registro.registro_fecha_ideal === store.filters[filtro]; + case 'fecha_inicio': + return registro.registro_fecha_ideal >= store.filters[filtro]; + case 'fecha_fin': + return registro.registro_fecha_ideal <= store.filters[filtro]; + case 'profesor': + const textoFiltro = store.filters[filtro].toLowerCase(); + if (/^\([^)]+\)\s[\s\S]+$/.test(textoFiltro)) { + const clave = registro.profesor_clave.toLowerCase(); + const filtroClave = textoFiltro.match(/\((.*?)\)/)?.[1]; + // console.log(clave, filtroClave); + return clave.includes(filtroClave); + } + else { + const nombre = registro.profesor_nombre.toLowerCase(); + return nombre.includes(textoFiltro); + } + case 'facultad_id': + return registro.facultad_id === store.filters[filtro]; + case 'estados': + if (store.filters[filtro].length === 0) + return true; + return store.filters[filtro].includes(registro.estado_supervisor_id); + case 'bloque_horario': + const bloque = store.bloques_horario.data.find((bloque) => bloque.id === store.filters[filtro]); + return registro.horario_hora <= bloque.hora_fin && registro.horario_fin >= bloque.hora_inicio; + default: + return true; + } + }); + }); + }, + async descargar() { + store.current.modal_state = 'Generando reporte en Excel...'; + $('div.modal#cargando').modal('show'); + this.loading = true; + if (this.relevant.length === 0) + return; + try { + const res = await fetch('export/supervisor_excel.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(this.relevant) + }); + const blob = await res.blob(); + window.saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`); + } + catch (error) { + if (error.response && error.response.status === 413) { + alert('Your request is too large! Please reduce the data size and try again.'); + } + else { + alert('An error occurred: ' + error.message); + } + } + finally { + $('#cargando').modal('hide'); + this.loading = false; + } + }, + loading: false, + get pages() { + return Math.ceil(this.relevant.length / store.current.perPage); + } + }, + get profesores() { + return this.registros.data + .map((registro) => ({ + profesor_id: registro.profesor_id, + profesor_nombre: registro.profesor_nombre, + profesor_correo: registro.profesor_correo, + profesor_clave: registro.profesor_clave, + profesor_grado: registro.profesor_grado, + })) + .reduce((acc, current) => { + if (!acc.some(item => item.profesor_id === current.profesor_id)) { + acc.push(current); + } + return acc; + }, []) + .sort((a, b) => a.profesor_nombre.localeCompare(b.profesor_nombre)); + }, + async mounted() { + $('div.modal#cargando').modal('show'); + await this.registros.fetch(); + await store.facultades.fetch(); + await store.estados.fetch(); + await store.bloques_horario.fetch(); + store.filters.switchFechas(); + $('div.modal#cargando').modal('hide'); + } +}).mount('#app'); diff --git a/js/client.js b/js/client.js index 9087072..236d201 100644 --- a/js/client.js +++ b/js/client.js @@ -1,120 +1,120 @@ -// @ts-ignore Import module -import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'; -const webServices = { - getPeriodosV1: async () => { - try { - const response = await fetch('periodos.v1.php'); - return await response.json(); - } - catch (error) { - console.log(error); - return []; - } - }, - getPeriodosV2: async () => { - try { - const response = await fetch('periodos.v2.php'); - return await response.json(); - } - catch (error) { - console.log(error); - return []; - } - } -}; -const store = reactive({ - periodosV1: [], - periodosV2: [], - errors: [], - fechas(idPeriodo) { - const periodo = this.periodosV2.find((periodo) => periodo.IdPeriodo === idPeriodo); - return { - inicio: periodo ? periodo.FechaInicio : '', - fin: periodo ? periodo.FechaFin : '' - }; - }, - periodov1(idPeriodo) { - return this.periodosV1.find((periodo) => periodo.IdPeriodo === idPeriodo); - }, - periodov2(idPeriodo) { - return this.periodosV2.filter((periodo) => periodo.IdPeriodo === idPeriodo); - }, - async addPeriodo(periodo) { - try { - const result = await fetch('backend/periodos.php', { - method: 'POST', - body: JSON.stringify({ - ...periodo, - ...this.fechas(periodo.IdPeriodo) - }), - headers: { - 'Content-Type': 'application/json' - } - }).then((response) => response.json()); - if (result.success) { - this.periodosV1 = this.periodosV1.map((periodoV1) => { - if (periodoV1.IdPeriodo === periodo.IdPeriodo) { - periodoV1.in_db = true; - } - return periodoV1; - }); - return result; - } - else { - this.errors.push(result.message); - } - } - catch (error) { - this.errors.push(error); - } - }, - async addCarreras(idPeriodo) { - try { - const periodoV1 = this.periodov1(idPeriodo); - const periodoV2 = this.periodov2(idPeriodo); - const data = periodoV2.map(({ ClaveCarrera, NombreCarrera }) => ({ - ClaveCarrera: ClaveCarrera, - NombreCarrera: NombreCarrera, - IdNivel: periodoV1.IdNivel, - })); - const result = await fetch('backend/carreras.php', { - method: 'POST', - body: JSON.stringify(data), - headers: { - 'Content-Type': 'application/json' - } - }).then((response) => response.json()); - if (result.success) { - await webServices.getPeriodosV1().then((periodosV1) => { - this.periodosV1 = periodosV1; - }); - await webServices.getPeriodosV2().then((periodosV2) => { - this.periodosV2 = periodosV2; - }); - } - } - catch (error) { - this.errors.push(error); - } - } -}); -createApp({ - store, - info(IdPeriodo) { - const periodo = store.periodosV2.find((periodo) => periodo.IdPeriodo === IdPeriodo && - periodo.FechaInicio != '' && periodo.FechaFin != ''); - return periodo; - }, - complete(IdPeriodo) { - const info = this.info(IdPeriodo); - return info !== undefined; - }, - mounted: async () => { - await webServices.getPeriodosV1().then((periodosV1) => { - store.periodosV1 = periodosV1; - }); - await webServices.getPeriodosV2().then((periodosV2) => { - store.periodosV2 = periodosV2; - }); - } -}).mount(); +// @ts-ignore Import module +import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'; +const webServices = { + getPeriodosV1: async () => { + try { + const response = await fetch('periodos.v1.php'); + return await response.json(); + } + catch (error) { + console.log(error); + return []; + } + }, + getPeriodosV2: async () => { + try { + const response = await fetch('periodos.v2.php'); + return await response.json(); + } + catch (error) { + console.log(error); + return []; + } + } +}; +const store = reactive({ + periodosV1: [], + periodosV2: [], + errors: [], + fechas(idPeriodo) { + const periodo = this.periodosV2.find((periodo) => periodo.IdPeriodo === idPeriodo); + return { + inicio: periodo ? periodo.FechaInicio : '', + fin: periodo ? periodo.FechaFin : '' + }; + }, + periodov1(idPeriodo) { + return this.periodosV1.find((periodo) => periodo.IdPeriodo === idPeriodo); + }, + periodov2(idPeriodo) { + return this.periodosV2.filter((periodo) => periodo.IdPeriodo === idPeriodo); + }, + async addPeriodo(periodo) { + try { + const result = await fetch('backend/periodos.php', { + method: 'POST', + body: JSON.stringify({ + ...periodo, + ...this.fechas(periodo.IdPeriodo) + }), + headers: { + 'Content-Type': 'application/json' + } + }).then((response) => response.json()); + if (result.success) { + this.periodosV1 = this.periodosV1.map((periodoV1) => { + if (periodoV1.IdPeriodo === periodo.IdPeriodo) { + periodoV1.in_db = true; + } + return periodoV1; + }); + return result; + } + else { + this.errors.push(result.message); + } + } + catch (error) { + this.errors.push(error); + } + }, + async addCarreras(idPeriodo) { + try { + const periodoV1 = this.periodov1(idPeriodo); + const periodoV2 = this.periodov2(idPeriodo); + const data = periodoV2.map(({ ClaveCarrera, NombreCarrera }) => ({ + ClaveCarrera: ClaveCarrera, + NombreCarrera: NombreCarrera, + IdNivel: periodoV1.IdNivel, + })); + const result = await fetch('backend/carreras.php', { + method: 'POST', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json' + } + }).then((response) => response.json()); + if (result.success) { + await webServices.getPeriodosV1().then((periodosV1) => { + this.periodosV1 = periodosV1; + }); + await webServices.getPeriodosV2().then((periodosV2) => { + this.periodosV2 = periodosV2; + }); + } + } + catch (error) { + this.errors.push(error); + } + } +}); +createApp({ + store, + info(IdPeriodo) { + const periodo = store.periodosV2.find((periodo) => periodo.IdPeriodo === IdPeriodo && + periodo.FechaInicio != '' && periodo.FechaFin != ''); + return periodo; + }, + complete(IdPeriodo) { + const info = this.info(IdPeriodo); + return info !== undefined; + }, + mounted: async () => { + await webServices.getPeriodosV1().then((periodosV1) => { + store.periodosV1 = periodosV1; + }); + await webServices.getPeriodosV2().then((periodosV2) => { + store.periodosV2 = periodosV2; + }); + } +}).mount(); diff --git a/js/horario_profesor.js b/js/horario_profesor.js index 41c239f..41ddeab 100644 --- a/js/horario_profesor.js +++ b/js/horario_profesor.js @@ -1,135 +1,135 @@ -const compareHours = (hora1, hora2) => { - const [h1, m1] = hora1.split(":").map(Number); - const [h2, m2] = hora2.split(":").map(Number); - if (h1 !== h2) { - return h1 > h2 ? 1 : -1; - } - if (m1 !== m2) { - return m1 > m2 ? 1 : -1; - } - return 0; -}; -let horarios = []; -const table = document.querySelector("table"); -if (!(table instanceof HTMLTableElement)) { - triggerMessage("No se ha encontrado la tabla", "Error", "error"); - throw new Error("No se ha encontrado la tabla"); -} -[...Array(16).keys()].map(x => x + 7).forEach(hora => { - // add 7 rows for each hour - [0, 15, 30, 45].map((minute) => `${minute}`.padStart(2, '0')).forEach((minute) => { - const tr = document.createElement("tr"); - tr.id = `hora-${hora}:${minute}`; - tr.classList.add(hora > 13 ? "tarde" : "mañana"); - if (minute == "00") { - const th = document.createElement("th"); - th.classList.add("text-center"); - th.scope = "row"; - th.rowSpan = 4; - th.innerText = `${hora}:00`; - th.style.verticalAlign = "middle"; - tr.appendChild(th); - } - ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"].forEach(día => { - const td = document.createElement("td"); - td.id = `hora-${hora}:${minute}-${día}`; - tr.appendChild(td); - }); - const tbody = document.querySelector("tbody#horario"); - if (!(tbody instanceof HTMLTableSectionElement)) { - throw new Error("No se ha encontrado el tbody"); - } - tbody.appendChild(tr); - }); -}); -const empty_table = table.cloneNode(true); -document.querySelectorAll('.hidden').forEach((element) => { - element.style.display = "none"; -}); -// hide the table -table.style.display = "none"; -function moveHorario(id, día, hora) { - const formData = new FormData(); - formData.append("id", id); - formData.append("hora", hora); - formData.append("día", día); - fetch("action/action_horario_update.php", { - method: "POST", - body: formData - }).then(res => res.json()).then(response => { - if (response.status == "success") { - triggerMessage("Horario movido", "Éxito", "success"); - } - else { - triggerMessage(response.message, "Error"); - } - }).then(() => { - renderHorario(); - }).catch(err => { - triggerMessage(err, "Error"); - }); -} -function renderHorario() { - if (horarios.length == 0) { - triggerMessage("Este profesor hay horarios para mostrar", "Error", "info"); - table.style.display = "none"; - document.querySelectorAll('.hidden').forEach((element) => element.style.display = "none"); - return; - } - // show the table - table.style.display = "table"; - document.querySelectorAll('.hidden').forEach((element) => element.style.display = "block"); - // clear the table - table.innerHTML = empty_table.outerHTML; - function conflicts(horario1, horario2) { - const { hora: hora_inicio1, hora_final: hora_final1, dia: dia1 } = horario1; - const { hora: hora_inicio2, hora_final: hora_final2, dia: dia2 } = horario2; - if (dia1 !== dia2) { - return false; - } - const compareInicios = compareHours(hora_inicio1, hora_inicio2); - const compareFinales = compareHours(hora_final1, hora_final2); - if (compareInicios >= 0 && compareInicios <= compareFinales || - compareFinales >= 0 && compareFinales <= -compareInicios) { - return true; - } - return false; - } - // remove the next 5 cells - function removeNextCells(horas, minutos, dia, cells = 5) { - for (let i = 1; i <= cells; i++) { - const minute = minutos + i * 15; - const nextMinute = (minute % 60).toString().padStart(2, "0"); - const nextHour = horas + Math.floor(minute / 60); - const cellId = `hora-${nextHour}:${nextMinute}-${dia}`; - const cellElement = document.getElementById(cellId); - if (cellElement) { - cellElement.remove(); - } - else { - console.log(`No se ha encontrado la celda ${cellId}`); - break; - } - } - } - function newBlock(horario, edit = false) { - function move(horario, cells = 5) { - const [horas, minutos] = horario.hora.split(":").map(Number); - const cell = document.getElementById(`hora-${horas}:${minutos.toString().padStart(2, "0")}-${horario.dia}`); - const { top, left } = cell.getBoundingClientRect(); - const block = document.getElementById(`block-${horario.id}`); - block.style.top = `${top}px`; - block.style.left = `${left}px`; - removeNextCells(horas, minutos, horario.dia, cells); - } - const [horas, minutos] = horario.hora.split(":").map(x => parseInt(x)); - const hora = `${horas}:${minutos.toString().padStart(2, "0")}`; - horario.hora = hora; - const cell = document.getElementById(`hora-${horario.hora}-${horario.dia}`); - if (!cell) - return; - cell.dataset.ids = `${horario.id}`; - const float_menu = edit ? +const compareHours = (hora1, hora2) => { + const [h1, m1] = hora1.split(":").map(Number); + const [h2, m2] = hora2.split(":").map(Number); + if (h1 !== h2) { + return h1 > h2 ? 1 : -1; + } + if (m1 !== m2) { + return m1 > m2 ? 1 : -1; + } + return 0; +}; +let horarios = []; +const table = document.querySelector("table"); +if (!(table instanceof HTMLTableElement)) { + triggerMessage("No se ha encontrado la tabla", "Error", "error"); + throw new Error("No se ha encontrado la tabla"); +} +[...Array(16).keys()].map(x => x + 7).forEach(hora => { + // add 7 rows for each hour + [0, 15, 30, 45].map((minute) => `${minute}`.padStart(2, '0')).forEach((minute) => { + const tr = document.createElement("tr"); + tr.id = `hora-${hora}:${minute}`; + tr.classList.add(hora > 13 ? "tarde" : "mañana"); + if (minute == "00") { + const th = document.createElement("th"); + th.classList.add("text-center"); + th.scope = "row"; + th.rowSpan = 4; + th.innerText = `${hora}:00`; + th.style.verticalAlign = "middle"; + tr.appendChild(th); + } + ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"].forEach(día => { + const td = document.createElement("td"); + td.id = `hora-${hora}:${minute}-${día}`; + tr.appendChild(td); + }); + const tbody = document.querySelector("tbody#horario"); + if (!(tbody instanceof HTMLTableSectionElement)) { + throw new Error("No se ha encontrado el tbody"); + } + tbody.appendChild(tr); + }); +}); +const empty_table = table.cloneNode(true); +document.querySelectorAll('.hidden').forEach((element) => { + element.style.display = "none"; +}); +// hide the table +table.style.display = "none"; +function moveHorario(id, día, hora) { + const formData = new FormData(); + formData.append("id", id); + formData.append("hora", hora); + formData.append("día", día); + fetch("action/action_horario_update.php", { + method: "POST", + body: formData + }).then(res => res.json()).then(response => { + if (response.status == "success") { + triggerMessage("Horario movido", "Éxito", "success"); + } + else { + triggerMessage(response.message, "Error"); + } + }).then(() => { + renderHorario(); + }).catch(err => { + triggerMessage(err, "Error"); + }); +} +function renderHorario() { + if (horarios.length == 0) { + triggerMessage("Este profesor hay horarios para mostrar", "Error", "info"); + table.style.display = "none"; + document.querySelectorAll('.hidden').forEach((element) => element.style.display = "none"); + return; + } + // show the table + table.style.display = "table"; + document.querySelectorAll('.hidden').forEach((element) => element.style.display = "block"); + // clear the table + table.innerHTML = empty_table.outerHTML; + function conflicts(horario1, horario2) { + const { hora: hora_inicio1, hora_final: hora_final1, dia: dia1 } = horario1; + const { hora: hora_inicio2, hora_final: hora_final2, dia: dia2 } = horario2; + if (dia1 !== dia2) { + return false; + } + const compareInicios = compareHours(hora_inicio1, hora_inicio2); + const compareFinales = compareHours(hora_final1, hora_final2); + if (compareInicios >= 0 && compareInicios <= compareFinales || + compareFinales >= 0 && compareFinales <= -compareInicios) { + return true; + } + return false; + } + // remove the next 5 cells + function removeNextCells(horas, minutos, dia, cells = 5) { + for (let i = 1; i <= cells; i++) { + const minute = minutos + i * 15; + const nextMinute = (minute % 60).toString().padStart(2, "0"); + const nextHour = horas + Math.floor(minute / 60); + const cellId = `hora-${nextHour}:${nextMinute}-${dia}`; + const cellElement = document.getElementById(cellId); + if (cellElement) { + cellElement.remove(); + } + else { + console.log(`No se ha encontrado la celda ${cellId}`); + break; + } + } + } + function newBlock(horario, edit = false) { + function move(horario, cells = 5) { + const [horas, minutos] = horario.hora.split(":").map(Number); + const cell = document.getElementById(`hora-${horas}:${minutos.toString().padStart(2, "0")}-${horario.dia}`); + const { top, left } = cell.getBoundingClientRect(); + const block = document.getElementById(`block-${horario.id}`); + block.style.top = `${top}px`; + block.style.left = `${left}px`; + removeNextCells(horas, minutos, horario.dia, cells); + } + const [horas, minutos] = horario.hora.split(":").map(x => parseInt(x)); + const hora = `${horas}:${minutos.toString().padStart(2, "0")}`; + horario.hora = hora; + const cell = document.getElementById(`hora-${horario.hora}-${horario.dia}`); + if (!cell) + return; + cell.dataset.ids = `${horario.id}`; + const float_menu = edit ? `` - : ''; - cell.innerHTML = + ` + : ''; + cell.innerHTML = `
${horario.hora} ${horario.materia}
@@ -148,27 +148,27 @@ function renderHorario() { ${horario.profesores.map((profesor) => ` ${profesor.grado ?? ''} ${profesor.profesor}`).join("
")}
- ${float_menu}`; - cell.classList.add("bloque-clase", "position-relative"); - cell.rowSpan = horario.bloques; - // draggable - cell.draggable = write; - if (horario.bloques > 0) { - removeNextCells(horas, minutos, horario.dia, horario.bloques - 1); - } - } - function newConflictBlock(horarios, edit = false) { - const first_horario = horarios[0]; - const [horas, minutos] = first_horario.hora.split(":").map(x => parseInt(x)); - const hora = `${horas}:${minutos.toString().padStart(2, "0")}`; - const ids = horarios.map(horario => horario.id); - const cell = document.getElementById(`hora-${hora}-${first_horario.dia}`); - if (cell == null) { - console.error(`Error: No se encontró la celda: hora-${hora}-${first_horario.dia}`); - return; - } - cell.dataset.ids = ids.join(","); - // replace the content of the cell + ${float_menu}`; + cell.classList.add("bloque-clase", "position-relative"); + cell.rowSpan = horario.bloques; + // draggable + cell.draggable = write; + if (horario.bloques > 0) { + removeNextCells(horas, minutos, horario.dia, horario.bloques - 1); + } + } + function newConflictBlock(horarios, edit = false) { + const first_horario = horarios[0]; + const [horas, minutos] = first_horario.hora.split(":").map(x => parseInt(x)); + const hora = `${horas}:${minutos.toString().padStart(2, "0")}`; + const ids = horarios.map(horario => horario.id); + const cell = document.getElementById(`hora-${hora}-${first_horario.dia}`); + if (cell == null) { + console.error(`Error: No se encontró la celda: hora-${hora}-${first_horario.dia}`); + return; + } + cell.dataset.ids = ids.join(","); + // replace the content of the cell cell.innerHTML = ` ${hora} @@ -183,17 +183,17 @@ function renderHorario() { Ver horarios … - `; - // Add classes and attributes - cell.classList.add("conflict", "bloque-clase"); - cell.setAttribute("role", "button"); - // Add event listener for the cell - cell.addEventListener("click", () => { - $("#modal-choose").modal("show"); - const ids = cell.getAttribute("data-ids").split(",").map(x => parseInt(x)); - const tbody = document.querySelector("#modal-choose tbody"); - tbody.innerHTML = ""; - horarios.filter(horario => ids.includes(horario.id)).sort((a, b) => compareHours(a.hora, b.hora)).forEach(horario => { + `; + // Add classes and attributes + cell.classList.add("conflict", "bloque-clase"); + cell.setAttribute("role", "button"); + // Add event listener for the cell + cell.addEventListener("click", () => { + $("#modal-choose").modal("show"); + const ids = cell.getAttribute("data-ids").split(",").map(x => parseInt(x)); + const tbody = document.querySelector("#modal-choose tbody"); + tbody.innerHTML = ""; + horarios.filter(horario => ids.includes(horario.id)).sort((a, b) => compareHours(a.hora, b.hora)).forEach(horario => { tbody.innerHTML += ` ${horario.hora.slice(0, -3)}-${horario.hora_final.slice(0, -3)} @@ -214,201 +214,201 @@ function renderHorario() { ` : ""} - `; - }); - document.querySelectorAll(".dismiss-editar").forEach(btn => { - btn.addEventListener("click", () => $("#modal-choose").modal("hide")); - }); - }); - function getDuration(hora_i, hora_f) { - const [horas_i, minutos_i] = hora_i.split(":").map(x => parseInt(x)); - const [horas_f, minutos_f] = hora_f.split(":").map(x => parseInt(x)); - const date_i = new Date(0, 0, 0, horas_i, minutos_i); - const date_f = new Date(0, 0, 0, horas_f, minutos_f); - const diffInMilliseconds = date_f.getTime() - date_i.getTime(); - const diffInMinutes = diffInMilliseconds / (1000 * 60); - const diffIn15MinuteIntervals = diffInMinutes / 15; - return Math.floor(diffIn15MinuteIntervals); - } - const maxHoraFinal = horarios.reduce((max, horario) => { - const [horas, minutos] = horario.hora_final.split(":").map(x => parseInt(x)); - const date = new Date(0, 0, 0, horas, minutos); - return date > max ? date : max; - }, new Date(0, 0, 0, 0, 0)); - const horaFinalMax = new Date(0, 0, 0, maxHoraFinal.getHours(), maxHoraFinal.getMinutes()); - const blocks = getDuration(first_horario.hora, `${horaFinalMax.getHours()}:${horaFinalMax.getMinutes()}`); - cell.setAttribute("rowSpan", blocks.toString()); - removeNextCells(horas, minutos, first_horario.dia, blocks - 1); - } - const conflictBlocks = horarios.filter((horario, index, arrayHorario) => arrayHorario.filter((_, i) => i != index).some(horario2 => conflicts(horario, horario2))) - .sort((a, b) => compareHours(a.hora, b.hora)); - const classes = horarios.filter(horario => !conflictBlocks.includes(horario)); - const conflictBlocksPacked = []; // array of sets - conflictBlocks.forEach(horario => { - const setIndex = conflictBlocksPacked.findIndex(set => set.some(horario2 => conflicts(horario, horario2))); - if (setIndex === -1) { - conflictBlocksPacked.push([horario]); - } - else { - conflictBlocksPacked[setIndex].push(horario); - } - }); - classes.forEach(horario => newBlock(horario, write)); - conflictBlocksPacked.forEach(horarios => newConflictBlock(horarios, write)); - // remove the elements that are not in the limits - let max_hour = Math.max(...horarios.map(horario => { - const lastMoment = moment(horario.hora, "HH:mm").add(horario.bloques * 15, "minutes"); - const lastHour = moment(`${lastMoment.hours()}:00`, "HH:mm"); - const hourInt = parseInt(lastMoment.format("HH")); - return lastMoment.isSame(lastHour) ? hourInt - 1 : hourInt; - })); - let min_hour = Math.min(...horarios.map(horario => parseInt(horario.hora.split(":")[0]))); - document.querySelectorAll("tbody#horario tr").forEach(hora => { - const hora_id = parseInt(hora.id.split("-")[1].split(":")[0]); - (hora_id < min_hour || hora_id > max_hour) ? hora.remove() : null; - }); - // if there is no sábado, remove the column - if (!horarios.some(horario => horario.dia == "sábado")) { - document.querySelectorAll("tbody#horario td").forEach(td => { - if (td.id.split("-")[2] == "sábado") { - td.remove(); - } - }); - // remove the header (the last) - document.querySelector("#headers").lastElementChild.remove(); - } - // adjust width - const ths = document.querySelectorAll("tr#headers th"); - ths.forEach((th, key) => th.style.width = (key == 0) ? "5%" : `${95 / (ths.length - 1)}%`); - // search item animation - const menúFlontantes = document.querySelectorAll(".menu-flotante"); - menúFlontantes.forEach((element) => { - element.classList.add("d-none"); - element.parentElement.addEventListener("mouseover", () => element.classList.remove("d-none")); - element.parentElement.addEventListener("mouseout", (e) => element.classList.add("d-none")); - }); - // droppables - // forall the .bloque-elements add the event listeners for drag and drop - document.querySelectorAll(".bloque-clase").forEach(element => { - function dragStart() { - this.classList.add("dragging"); - } - function dragEnd() { - this.classList.remove("dragging"); - } - element.addEventListener("dragstart", dragStart); - element.addEventListener("dragend", dragEnd); - }); - // forall the cells that are not .bloque-clase add the event listeners for drag and drop - document.querySelectorAll("td:not(.bloque-clase)").forEach(element => { - function dragOver(e) { - e.preventDefault(); - this.classList.add("dragging-over"); - } - function dragLeave() { - this.classList.remove("dragging-over"); - } - function drop() { - this.classList.remove("dragging-over"); - const dragging = document.querySelector(".dragging"); - const id = dragging.getAttribute("data-ids"); - const hora = this.id.split("-")[1]; - const días = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"]; - let día = this.id.split("-")[2]; - día = días.indexOf(día) + 1; - // rowspan - const bloques = parseInt(dragging.getAttribute("rowspan")); - const horaMoment = moment(hora, "HH:mm"); - const horaFin = horaMoment.add(bloques * 15, "minutes"); - const limit = moment('22:00', 'HH:mm'); - if (horaFin.isAfter(limit)) { - triggerMessage("No se puede mover el bloque a esa hora", "Error"); - // scroll to the top - window.scrollTo(0, 0); - return; - } - // get the horario - // remove the horario - const bloque = document.querySelector(`.bloque-clase[data-ids="${id}"]`); - // remove all children - while (bloque.firstChild) { - bloque.removeChild(bloque.firstChild); - } - // prepend a loading child + `; + }); + document.querySelectorAll(".dismiss-editar").forEach(btn => { + btn.addEventListener("click", () => $("#modal-choose").modal("hide")); + }); + }); + function getDuration(hora_i, hora_f) { + const [horas_i, minutos_i] = hora_i.split(":").map(x => parseInt(x)); + const [horas_f, minutos_f] = hora_f.split(":").map(x => parseInt(x)); + const date_i = new Date(0, 0, 0, horas_i, minutos_i); + const date_f = new Date(0, 0, 0, horas_f, minutos_f); + const diffInMilliseconds = date_f.getTime() - date_i.getTime(); + const diffInMinutes = diffInMilliseconds / (1000 * 60); + const diffIn15MinuteIntervals = diffInMinutes / 15; + return Math.floor(diffIn15MinuteIntervals); + } + const maxHoraFinal = horarios.reduce((max, horario) => { + const [horas, minutos] = horario.hora_final.split(":").map(x => parseInt(x)); + const date = new Date(0, 0, 0, horas, minutos); + return date > max ? date : max; + }, new Date(0, 0, 0, 0, 0)); + const horaFinalMax = new Date(0, 0, 0, maxHoraFinal.getHours(), maxHoraFinal.getMinutes()); + const blocks = getDuration(first_horario.hora, `${horaFinalMax.getHours()}:${horaFinalMax.getMinutes()}`); + cell.setAttribute("rowSpan", blocks.toString()); + removeNextCells(horas, minutos, first_horario.dia, blocks - 1); + } + const conflictBlocks = horarios.filter((horario, index, arrayHorario) => arrayHorario.filter((_, i) => i != index).some(horario2 => conflicts(horario, horario2))) + .sort((a, b) => compareHours(a.hora, b.hora)); + const classes = horarios.filter(horario => !conflictBlocks.includes(horario)); + const conflictBlocksPacked = []; // array of sets + conflictBlocks.forEach(horario => { + const setIndex = conflictBlocksPacked.findIndex(set => set.some(horario2 => conflicts(horario, horario2))); + if (setIndex === -1) { + conflictBlocksPacked.push([horario]); + } + else { + conflictBlocksPacked[setIndex].push(horario); + } + }); + classes.forEach(horario => newBlock(horario, write)); + conflictBlocksPacked.forEach(horarios => newConflictBlock(horarios, write)); + // remove the elements that are not in the limits + let max_hour = Math.max(...horarios.map(horario => { + const lastMoment = moment(horario.hora, "HH:mm").add(horario.bloques * 15, "minutes"); + const lastHour = moment(`${lastMoment.hours()}:00`, "HH:mm"); + const hourInt = parseInt(lastMoment.format("HH")); + return lastMoment.isSame(lastHour) ? hourInt - 1 : hourInt; + })); + let min_hour = Math.min(...horarios.map(horario => parseInt(horario.hora.split(":")[0]))); + document.querySelectorAll("tbody#horario tr").forEach(hora => { + const hora_id = parseInt(hora.id.split("-")[1].split(":")[0]); + (hora_id < min_hour || hora_id > max_hour) ? hora.remove() : null; + }); + // if there is no sábado, remove the column + if (!horarios.some(horario => horario.dia == "sábado")) { + document.querySelectorAll("tbody#horario td").forEach(td => { + if (td.id.split("-")[2] == "sábado") { + td.remove(); + } + }); + // remove the header (the last) + document.querySelector("#headers").lastElementChild.remove(); + } + // adjust width + const ths = document.querySelectorAll("tr#headers th"); + ths.forEach((th, key) => th.style.width = (key == 0) ? "5%" : `${95 / (ths.length - 1)}%`); + // search item animation + const menúFlontantes = document.querySelectorAll(".menu-flotante"); + menúFlontantes.forEach((element) => { + element.classList.add("d-none"); + element.parentElement.addEventListener("mouseover", () => element.classList.remove("d-none")); + element.parentElement.addEventListener("mouseout", (e) => element.classList.add("d-none")); + }); + // droppables + // forall the .bloque-elements add the event listeners for drag and drop + document.querySelectorAll(".bloque-clase").forEach(element => { + function dragStart() { + this.classList.add("dragging"); + } + function dragEnd() { + this.classList.remove("dragging"); + } + element.addEventListener("dragstart", dragStart); + element.addEventListener("dragend", dragEnd); + }); + // forall the cells that are not .bloque-clase add the event listeners for drag and drop + document.querySelectorAll("td:not(.bloque-clase)").forEach(element => { + function dragOver(e) { + e.preventDefault(); + this.classList.add("dragging-over"); + } + function dragLeave() { + this.classList.remove("dragging-over"); + } + function drop() { + this.classList.remove("dragging-over"); + const dragging = document.querySelector(".dragging"); + const id = dragging.getAttribute("data-ids"); + const hora = this.id.split("-")[1]; + const días = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"]; + let día = this.id.split("-")[2]; + día = días.indexOf(día) + 1; + // rowspan + const bloques = parseInt(dragging.getAttribute("rowspan")); + const horaMoment = moment(hora, "HH:mm"); + const horaFin = horaMoment.add(bloques * 15, "minutes"); + const limit = moment('22:00', 'HH:mm'); + if (horaFin.isAfter(limit)) { + triggerMessage("No se puede mover el bloque a esa hora", "Error"); + // scroll to the top + window.scrollTo(0, 0); + return; + } + // get the horario + // remove the horario + const bloque = document.querySelector(`.bloque-clase[data-ids="${id}"]`); + // remove all children + while (bloque.firstChild) { + bloque.removeChild(bloque.firstChild); + } + // prepend a loading child const loading = `
Loading... -
`; - bloque.insertAdjacentHTML("afterbegin", loading); - // add style vertical-align: middle - bloque.style.verticalAlign = "middle"; - bloque.classList.add("text-center"); - // remove draggable - bloque.removeAttribute("draggable"); - moveHorario(id, día, hora); - } - element.addEventListener("dragover", dragOver); - element.addEventListener("dragleave", dragLeave); - element.addEventListener("drop", drop); - }); -} -const form = document.getElementById('form'); -if (!(form instanceof HTMLFormElement)) { - triggerMessage('No se ha encontrado el formulario', 'Error', 'danger'); - throw new Error("No se ha encontrado el formulario"); -} -form.querySelector('#clave_profesor').addEventListener('input', function (e) { - const input = form.querySelector('#clave_profesor'); - const option = form.querySelector(`option[value="${input.value}"]`); - if (input.value == "") { - input.classList.remove("is-invalid", "is-valid"); - return; - } - if (!option) { - input.classList.remove("is-valid"); - input.classList.add("is-invalid"); - } - else { - const profesor_id = form.querySelector('#profesor_id'); - profesor_id.value = option.dataset.id; - input.classList.remove("is-invalid"); - input.classList.add("is-valid"); - } -}); -form.addEventListener('submit', async function (e) { - e.preventDefault(); - const input = form.querySelector('#clave_profesor'); - if (input.classList.contains("is-invalid")) { - triggerMessage('El profesor no se encuentra registrado', 'Error', 'danger'); - return; - } - const formData = new FormData(form); - try { - const buttons = document.querySelectorAll("button"); - buttons.forEach(button => { - button.disabled = true; - button.classList.add("disabled"); - }); - const response = await fetch('action/action_horario_profesor.php', { - method: 'POST', - body: formData, - }); - const data = await response.json(); - buttons.forEach(button => { - button.disabled = false; - button.classList.remove("disabled"); - }); - if (data.status == 'success') { - horarios = data.data; - renderHorario(); - } - else { - triggerMessage(data.message, 'Error en la consulta', 'warning'); - } - } - catch (error) { - triggerMessage('Fallo al consutar los datos ', 'Error', 'danger'); - console.log(error); - } -}); -const input = form.querySelector('#clave_profesor'); -const option = form.querySelector(`option[value="${input.value}"]`); + `; + bloque.insertAdjacentHTML("afterbegin", loading); + // add style vertical-align: middle + bloque.style.verticalAlign = "middle"; + bloque.classList.add("text-center"); + // remove draggable + bloque.removeAttribute("draggable"); + moveHorario(id, día, hora); + } + element.addEventListener("dragover", dragOver); + element.addEventListener("dragleave", dragLeave); + element.addEventListener("drop", drop); + }); +} +const form = document.getElementById('form'); +if (!(form instanceof HTMLFormElement)) { + triggerMessage('No se ha encontrado el formulario', 'Error', 'danger'); + throw new Error("No se ha encontrado el formulario"); +} +form.querySelector('#clave_profesor').addEventListener('input', function (e) { + const input = form.querySelector('#clave_profesor'); + const option = form.querySelector(`option[value="${input.value}"]`); + if (input.value == "") { + input.classList.remove("is-invalid", "is-valid"); + return; + } + if (!option) { + input.classList.remove("is-valid"); + input.classList.add("is-invalid"); + } + else { + const profesor_id = form.querySelector('#profesor_id'); + profesor_id.value = option.dataset.id; + input.classList.remove("is-invalid"); + input.classList.add("is-valid"); + } +}); +form.addEventListener('submit', async function (e) { + e.preventDefault(); + const input = form.querySelector('#clave_profesor'); + if (input.classList.contains("is-invalid")) { + triggerMessage('El profesor no se encuentra registrado', 'Error', 'danger'); + return; + } + const formData = new FormData(form); + try { + const buttons = document.querySelectorAll("button"); + buttons.forEach(button => { + button.disabled = true; + button.classList.add("disabled"); + }); + const response = await fetch('action/action_horario_profesor.php', { + method: 'POST', + body: formData, + }); + const data = await response.json(); + buttons.forEach(button => { + button.disabled = false; + button.classList.remove("disabled"); + }); + if (data.status == 'success') { + horarios = data.data; + renderHorario(); + } + else { + triggerMessage(data.message, 'Error en la consulta', 'warning'); + } + } + catch (error) { + triggerMessage('Fallo al consutar los datos ', 'Error', 'danger'); + console.log(error); + } +}); +const input = form.querySelector('#clave_profesor'); +const option = form.querySelector(`option[value="${input.value}"]`); diff --git a/js/reposiciones.js b/js/reposiciones.js index 9a3575a..03156eb 100644 --- a/js/reposiciones.js +++ b/js/reposiciones.js @@ -1,178 +1,178 @@ -// Get references to the HTML elements -const form = document.getElementById('form'); -const steps = Array.from(form.querySelectorAll('.step')); -const nextButton = document.getElementById('next-button'); -const prevButton = document.getElementById('prev-button'); -let currentStep = 0; -// #clave_profesor on change => show step 2 -const clave_profesor = document.getElementById('clave_profesor'); -const horario_reponer = document.getElementById('horario_reponer'); -const fechas_clase = document.getElementById('fechas_clase'); -const fecha_reponer = $('#fecha_reponer'); -const hora_reponer = $('#hora_reponer'); -const minutos_reponer = $('#minutos_reponer'); -clave_profesor.addEventListener('change', async () => { - const step2 = document.getElementById('step-2'); - clave_profesor.disabled = true; - // get option which value is the same as clave_profesor.value - const option = document.querySelector(`option[value="${clave_profesor.value}"]`); - // make a form data with #form - const profesor_id = document.getElementById('profesor_id'); - profesor_id.value = option.dataset.id; - const formData = new FormData(form); - const response = await fetch(`./action/action_horario_profesor.php`, { - method: 'POST', - body: formData - }); - const data = await response.json(); - if (data['success'] === false) { - const message = "Hubo un error al obtener los horarios del profesor."; - const title = 'Error'; - const color = 'danger'; - triggerMessage(message, title, color); - return; - } - const horarios = data.data; - const initial = document.createElement('option'); - initial.value = ''; - initial.textContent = 'Seleccione un horario'; - initial.selected = true; - initial.disabled = true; - horario_reponer.innerHTML = ''; - horario_reponer.appendChild(initial); - horarios.forEach((horario) => { - const dias = ['Lunes', 'Martes', 'Miercoles', 'Jueves', 'Viernes', 'Sabado', 'Domingo']; - const option = document.createElement('option'); - option.value = `${horario.id}`; - // materia máx 25 caracteres, if materia.length > 25 then slice(0, 20) - const max = 25; - option.textContent = `${horario.materia.slice(0, max) + (horario.materia.length > max ? '...' : '')} - Grupo: ${horario.grupo} - ${horario.hora.slice(0, 5)}-${horario.hora_final.slice(0, 5)} - Salon: ${horario.salon} - ${horario.dia}`; - option.dataset.materia = `${horario.materia}`; - option.dataset.grupo = `${horario.grupo}`; - option.dataset.hora = `${horario.hora.slice(0, 5)}`; // slice(0, 5) => HH:MM - option.dataset.hora_final = `${horario.hora_final.slice(0, 5)}`; - option.dataset.salon = `${horario.salon}`; - option.dataset.dia = `${horario.dia}`; - option.dataset.id = `${horario.id}`; - horario_reponer.appendChild(option); - }); - currentStep = 1; - step2.style.display = 'block'; - prevButton.disabled = false; -}); -// disable clave_profesor -// from second step to first step -prevButton.addEventListener('click', () => { - const inputs = [clave_profesor, horario_reponer, fechas_clase, fecha_reponer, hora_reponer]; - switch (currentStep) { - case 1: - case 2: - case 3: - const step = document.getElementById(`step-${currentStep + 1}`); - step.style.display = 'none'; - inputs[currentStep - 1].disabled = false; - inputs[currentStep - 1].value = ''; - if (--currentStep === 0) { - prevButton.disabled = true; - } - break; - case 4: - const step5 = document.getElementById('step-5'); - step5.style.display = 'none'; - fecha_reponer.prop('disabled', false); - fecha_reponer.val(''); - hora_reponer.parent().removeClass('disabled'); - hora_reponer.siblings('.datalist-input').text('hh'); - hora_reponer.val(''); - minutos_reponer.parent().removeClass('disabled'); - minutos_reponer.siblings('.datalist-input').text('mm'); - minutos_reponer.val(''); - currentStep--; - break; - } - nextButton.disabled = true; -}); -// #horario_reponer on change => show step 3 -horario_reponer.addEventListener('change', async () => { - const selected = horario_reponer.querySelector(`option[value="${horario_reponer.value}"]`); - horario_reponer.title = `Materia: ${selected.dataset.materia} - Grupo: ${selected.dataset.grupo} - Horario: ${selected.dataset.hora}-${selected.dataset.hora_final} - Salon: ${selected.dataset.salon} - Día: ${selected.dataset.dia}`; - const step3 = document.getElementById('step-3'); - horario_reponer.disabled = true; - // make a form data with #form - const response = await fetch(`./action/action_fechas_clase.php?horario_id=${horario_reponer.value}`, { - method: 'GET', - }); - const data = await response.json(); - if (data['success'] === false) { - const message = "Hubo un error al obtener las fechas de clase."; - const title = 'Error'; - const color = 'danger'; - triggerMessage(message, title, color); - return; - } - const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']; - const fechas = data.data; - const initial = document.createElement('option'); - initial.value = ''; - initial.textContent = 'Seleccione la fecha de la falta'; - initial.selected = true; - initial.disabled = true; - fechas_clase.innerHTML = ''; - fechas_clase.appendChild(initial); - fechas_clase.title = 'Seleccione la fecha de la falta'; - fechas.forEach((fecha) => { - const option = document.createElement('option'); - option.value = `${fecha}`; - option.textContent = `${fecha.dia_mes} de ${meses[fecha.month - 1]} de ${fecha.year}`; - fechas_clase.appendChild(option); - }); - step3.style.display = 'block'; - currentStep = 2; -}); -// #fechas_clase on change => show step 4 -fechas_clase.addEventListener('change', () => { - const step4 = document.getElementById('step-4'); - step4.style.display = 'block'; - fechas_clase.disabled = true; - currentStep = 3; -}); -// when both #fecha_reponer and #hora_reponer are selected => show step 5 -const lastStep = () => { - // timeout to wait for the value to be set - setTimeout(() => { - if (fecha_reponer.val() !== '' && hora_reponer.val() !== '' && minutos_reponer.val() !== '') { - const step5 = document.getElementById('step-5'); - step5.style.display = 'block'; - // disable both - fecha_reponer.prop('disabled', true); - hora_reponer.parent().addClass('disabled'); - minutos_reponer.parent().addClass('disabled'); - const nextButton = document.getElementById('next-button'); - // remove property disabled - nextButton.removeAttribute('disabled'); - currentStep = 4; - } - }, 100); -}; -fecha_reponer.on('change', lastStep); -// on click on the sibling ul>li of #hora_reponer and #minutos_reponer -hora_reponer.siblings('ul').children('li').on('click', lastStep); -minutos_reponer.siblings('ul').children('li').on('click', lastStep); -// Initialize the form -hideSteps(); -showCurrentStep(); -function hideSteps() { - steps.forEach((step) => { - step.style.display = 'none'; - }); -} -function showCurrentStep() { - steps[currentStep].style.display = 'block'; - prevButton.disabled = currentStep === 0; -} -function handleSubmit(event) { - event.preventDefault(); - // Handle form submission - // You can access the form data using the FormData API or serialize it manually -} -export {}; +// Get references to the HTML elements +const form = document.getElementById('form'); +const steps = Array.from(form.querySelectorAll('.step')); +const nextButton = document.getElementById('next-button'); +const prevButton = document.getElementById('prev-button'); +let currentStep = 0; +// #clave_profesor on change => show step 2 +const clave_profesor = document.getElementById('clave_profesor'); +const horario_reponer = document.getElementById('horario_reponer'); +const fechas_clase = document.getElementById('fechas_clase'); +const fecha_reponer = $('#fecha_reponer'); +const hora_reponer = $('#hora_reponer'); +const minutos_reponer = $('#minutos_reponer'); +clave_profesor.addEventListener('change', async () => { + const step2 = document.getElementById('step-2'); + clave_profesor.disabled = true; + // get option which value is the same as clave_profesor.value + const option = document.querySelector(`option[value="${clave_profesor.value}"]`); + // make a form data with #form + const profesor_id = document.getElementById('profesor_id'); + profesor_id.value = option.dataset.id; + const formData = new FormData(form); + const response = await fetch(`./action/action_horario_profesor.php`, { + method: 'POST', + body: formData + }); + const data = await response.json(); + if (data['success'] === false) { + const message = "Hubo un error al obtener los horarios del profesor."; + const title = 'Error'; + const color = 'danger'; + triggerMessage(message, title, color); + return; + } + const horarios = data.data; + const initial = document.createElement('option'); + initial.value = ''; + initial.textContent = 'Seleccione un horario'; + initial.selected = true; + initial.disabled = true; + horario_reponer.innerHTML = ''; + horario_reponer.appendChild(initial); + horarios.forEach((horario) => { + const dias = ['Lunes', 'Martes', 'Miercoles', 'Jueves', 'Viernes', 'Sabado', 'Domingo']; + const option = document.createElement('option'); + option.value = `${horario.id}`; + // materia máx 25 caracteres, if materia.length > 25 then slice(0, 20) + const max = 25; + option.textContent = `${horario.materia.slice(0, max) + (horario.materia.length > max ? '...' : '')} - Grupo: ${horario.grupo} - ${horario.hora.slice(0, 5)}-${horario.hora_final.slice(0, 5)} - Salon: ${horario.salon} - ${horario.dia}`; + option.dataset.materia = `${horario.materia}`; + option.dataset.grupo = `${horario.grupo}`; + option.dataset.hora = `${horario.hora.slice(0, 5)}`; // slice(0, 5) => HH:MM + option.dataset.hora_final = `${horario.hora_final.slice(0, 5)}`; + option.dataset.salon = `${horario.salon}`; + option.dataset.dia = `${horario.dia}`; + option.dataset.id = `${horario.id}`; + horario_reponer.appendChild(option); + }); + currentStep = 1; + step2.style.display = 'block'; + prevButton.disabled = false; +}); +// disable clave_profesor +// from second step to first step +prevButton.addEventListener('click', () => { + const inputs = [clave_profesor, horario_reponer, fechas_clase, fecha_reponer, hora_reponer]; + switch (currentStep) { + case 1: + case 2: + case 3: + const step = document.getElementById(`step-${currentStep + 1}`); + step.style.display = 'none'; + inputs[currentStep - 1].disabled = false; + inputs[currentStep - 1].value = ''; + if (--currentStep === 0) { + prevButton.disabled = true; + } + break; + case 4: + const step5 = document.getElementById('step-5'); + step5.style.display = 'none'; + fecha_reponer.prop('disabled', false); + fecha_reponer.val(''); + hora_reponer.parent().removeClass('disabled'); + hora_reponer.siblings('.datalist-input').text('hh'); + hora_reponer.val(''); + minutos_reponer.parent().removeClass('disabled'); + minutos_reponer.siblings('.datalist-input').text('mm'); + minutos_reponer.val(''); + currentStep--; + break; + } + nextButton.disabled = true; +}); +// #horario_reponer on change => show step 3 +horario_reponer.addEventListener('change', async () => { + const selected = horario_reponer.querySelector(`option[value="${horario_reponer.value}"]`); + horario_reponer.title = `Materia: ${selected.dataset.materia} - Grupo: ${selected.dataset.grupo} - Horario: ${selected.dataset.hora}-${selected.dataset.hora_final} - Salon: ${selected.dataset.salon} - Día: ${selected.dataset.dia}`; + const step3 = document.getElementById('step-3'); + horario_reponer.disabled = true; + // make a form data with #form + const response = await fetch(`./action/action_fechas_clase.php?horario_id=${horario_reponer.value}`, { + method: 'GET', + }); + const data = await response.json(); + if (data['success'] === false) { + const message = "Hubo un error al obtener las fechas de clase."; + const title = 'Error'; + const color = 'danger'; + triggerMessage(message, title, color); + return; + } + const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']; + const fechas = data.data; + const initial = document.createElement('option'); + initial.value = ''; + initial.textContent = 'Seleccione la fecha de la falta'; + initial.selected = true; + initial.disabled = true; + fechas_clase.innerHTML = ''; + fechas_clase.appendChild(initial); + fechas_clase.title = 'Seleccione la fecha de la falta'; + fechas.forEach((fecha) => { + const option = document.createElement('option'); + option.value = `${fecha}`; + option.textContent = `${fecha.dia_mes} de ${meses[fecha.month - 1]} de ${fecha.year}`; + fechas_clase.appendChild(option); + }); + step3.style.display = 'block'; + currentStep = 2; +}); +// #fechas_clase on change => show step 4 +fechas_clase.addEventListener('change', () => { + const step4 = document.getElementById('step-4'); + step4.style.display = 'block'; + fechas_clase.disabled = true; + currentStep = 3; +}); +// when both #fecha_reponer and #hora_reponer are selected => show step 5 +const lastStep = () => { + // timeout to wait for the value to be set + setTimeout(() => { + if (fecha_reponer.val() !== '' && hora_reponer.val() !== '' && minutos_reponer.val() !== '') { + const step5 = document.getElementById('step-5'); + step5.style.display = 'block'; + // disable both + fecha_reponer.prop('disabled', true); + hora_reponer.parent().addClass('disabled'); + minutos_reponer.parent().addClass('disabled'); + const nextButton = document.getElementById('next-button'); + // remove property disabled + nextButton.removeAttribute('disabled'); + currentStep = 4; + } + }, 100); +}; +fecha_reponer.on('change', lastStep); +// on click on the sibling ul>li of #hora_reponer and #minutos_reponer +hora_reponer.siblings('ul').children('li').on('click', lastStep); +minutos_reponer.siblings('ul').children('li').on('click', lastStep); +// Initialize the form +hideSteps(); +showCurrentStep(); +function hideSteps() { + steps.forEach((step) => { + step.style.display = 'none'; + }); +} +function showCurrentStep() { + steps[currentStep].style.display = 'block'; + prevButton.disabled = currentStep === 0; +} +function handleSubmit(event) { + event.preventDefault(); + // Handle form submission + // You can access the form data using the FormData API or serialize it manually +} +export {}; diff --git a/log/asistencias_2023_08.log b/log/asistencias_2023_08.log new file mode 100644 index 0000000..ef4e20b --- /dev/null +++ b/log/asistencias_2023_08.log @@ -0,0 +1,3 @@ +2023-08-09 12:21:46||2||Permisos||Alejandro Lara +2023-08-09 12:21:49||2||Permisos||Alejandro Lara +2023-08-09 12:21:54||2||Avisos||Alejandro Lara diff --git a/service/backend/periodos.php b/service/backend/periodos.php index 0e5b636..13dc2b1 100644 --- a/service/backend/periodos.php +++ b/service/backend/periodos.php @@ -1,11 +1,13 @@ insert('periodo', [ - 'id_reference' => $data['IdPeriodo'], + 'id_periodo_sgu' => $data['IdPeriodo'], 'periodo_nombre' => "{$data['NombreNivel']}: {$formatter->format($inicio)} - {$formatter->format($fin)} " . date('Y', $inicio), 'nivel_id' => $data['IdNivel'], 'periodo_fecha_inicio' => $data['inicio'], 'periodo_fecha_fin' => $data['fin'], 'estado_id' => 4, 'periodo_clave' => $data['NombrePeriodo'] - ], ['id_reference']); + ], ['id_periodo_sgu']); } catch (PDOException $th) { echo json_encode([ 'success' => false, diff --git a/service/periodos.v1.php b/service/periodos.v1.php index 5812d8e..6c0adc4 100644 --- a/service/periodos.v1.php +++ b/service/periodos.v1.php @@ -1,4 +1,8 @@ true, CURLOPT_POSTFIELDS => "", CURLOPT_HTTPHEADER => [ - "token: f65f921264e4ab135472adb5a946212dd4b995d929294afec31eef192b8de8d6a076648906f70012c9803e5918d0fc99499d7d1fb7c998cc06c7a10eef61f66a", + "token: 5b892845736a29b5846073be0a11f0fc87113648aae1e8279830c4bc05f585eba13e0b1b4f0c42a12d694bb8091d23f7564b15f3141768dfa6ed2aa709864986", "username: SGU_APSA_AUD_ASIST" ], ]); @@ -24,7 +28,7 @@ if ($err) $data = json_decode($response, true); $in_db = array_map(function ($item) use ($db) { - $item['in_db'] = $db->where('id_reference', $item['IdPeriodo'])->has('periodo'); + $item['in_db'] = $db->where('id_periodo_sgu', $item['IdPeriodo'])->has('periodo'); return $item; }, $data); diff --git a/service/periodos.v2.php b/service/periodos.v2.php index 9acdfec..7cd6af9 100644 --- a/service/periodos.v2.php +++ b/service/periodos.v2.php @@ -1,4 +1,8 @@ true, CURLOPT_POSTFIELDS => "", CURLOPT_HTTPHEADER => [ - "token: f65f921264e4ab135472adb5a946212dd4b995d929294afec31eef192b8de8d6a076648906f70012c9803e5918d0fc99499d7d1fb7c998cc06c7a10eef61f66a", + "token: 5b892845736a29b5846073be0a11f0fc87113648aae1e8279830c4bc05f585eba13e0b1b4f0c42a12d694bb8091d23f7564b15f3141768dfa6ed2aa709864986", "username: SGU_APSA_AUD_ASIST" ], ]); @@ -28,7 +32,7 @@ $json = json_decode($response, true); $selectedData = array_map(function ($item) use ($db) { $item['in_db'] = $db->where('carrera_nombre', $item['NombreCarrera'], 'ILIKE')->has('carrera'); - $item['linked'] = $db->where('id_referencia', $item['ClaveCarrera'], 'ILIKE')->has('carrera'); + $item['linked'] = $db->where('clave_carrera', $item['ClaveCarrera'], 'ILIKE')->has('carrera'); return $item; }, $json); diff --git a/supervisor.php b/supervisor.php index 2e801d3..fe15264 100644 --- a/supervisor.php +++ b/supervisor.php @@ -115,7 +115,7 @@

- {{ store.hora_inicio.slice(0, 5) }} - {{ store.hora_fin.slice(0, 5) }} + {{ store.hora_inicio?.slice(0, 5) }} - {{ store.hora_fin?.slice(0, 5) }}

@@ -182,12 +182,12 @@ - {{ clase.horario_hora.slice(0, 5) }} - {{ clase.horario_fin.slice(0, 5) }} + {{ clase.horario_hora?.slice(0, 5) }} - {{ clase.horario_fin?.slice(0, 5) }} + + + + ruta.horarios.length > 0 && ruta.horarios.every(horario => horario.estado_supervisor_id)); const lasts = this.rutas.data.filter(ruta => ruta.horarios.length == 0); const notLasts = this.rutas.data.filter(ruta => ruta.horarios.some(horario => !horario.estado_supervisor_id)); - // console.log("finals", finals, "lasts", lasts, "notLasts", notLasts) this.rutas.data = [...notLasts, ...finals, ...lasts]; }, // clases @@ -533,9 +541,9 @@ }, // estado - async cambiarEstado(horario_id, estadoId) { + async cambiarEstado(horario_id, profesor_id, estadoId) { const ruta = store.rutas.data.find(ruta => ruta.salon_id == this.rutas.selected); - const clase = ruta.horarios.find(clase => clase.horario_id == horario_id); + const clase = ruta.horarios.find(clase => clase.horario_id == horario_id && clase.profesor_id == profesor_id); clase.estado_supervisor_id = estadoId; try { @@ -588,12 +596,15 @@ const ruta = store.rutas.data.find(ruta => ruta.salon_id == this.rutas.selected); const clase = ruta.horarios.find(clase => clase.horario_id == this.editor.id); clase.comentario = this.editor.texto; - store.cambiarEstado(clase.horario_id, clase.estado_supervisor_id); + store.cambiarEstado(clase.horario_id, clase.profesor_id, clase.estado_supervisor_id); }, limpiarComentario() { this.editor.texto = ""; }, - profesor_selected: null, + profesor_selected: { + horario_id: null, + profesor_id: null, + }, }); $(document).ready(function () { @@ -632,7 +643,6 @@ }, get clases() { const clases = store.rutas.data.find(ruta => ruta.salon_id == store.rutas.selected)?.horarios ?? []; - // console.log("All clases", JSON.parse(JSON.stringify(clases)), "Selected: ", store.rutas.selected); return clases; }, async guardarCambios() { @@ -641,7 +651,6 @@ if (!navigator.onLine) throw "No hay conexión a internet"; - console.log(store.rutas.data.map(ruta => ruta.horarios).flat(1).filter(clase => clase.pendiente)); const cambio = await fetch("action/registro_supervisor.php", { method: "POST", headers: { @@ -681,7 +690,6 @@ async mounted() { store.bloquesHorario.data = await fetch('action/action_grupo_horario.php').then(res => res.json()); store.bloquesHorario.selected = store.bloquesHorario.data.findIndex(bloque => bloque.selected); - // console.log(store.bloquesHorario.selected); this.header = (store.bloquesHorario.selected == -1) ? "Seleccione un horario" : "Seleccione una ruta"; this.catálogo_rutas.data = await fetch('action/rutas.php').then(res => res.json()); this.loading = false; @@ -725,7 +733,7 @@ }, get clase_vista() { - return this.clases.find(clase => clase.horario_id == store.profesor_selected) ?? false; + return this.clases.find(clase => clase.horario_id == store.profesor_selected.horario_id && clase.profesor_id == store.profesor_selected.profesor_id) ?? false; }, }).mount('#app') diff --git a/ts/auditoría.ts b/ts/auditoría.ts index aea5137..10f876c 100644 --- a/ts/auditoría.ts +++ b/ts/auditoría.ts @@ -70,6 +70,10 @@ const store = reactive({ comentario: '', clase_vista: null, empty: '', + page: 1, + maxPages: 10, + perPage: 10, + modal_state: "Cargando datos...", }, facultades: { data: [] as Facultad[], @@ -85,6 +89,7 @@ const store = reactive({ fecha_inicio: null, fecha_fin: null, profesor: null, + periodo_id: null, bloque_horario: null, estados: [], @@ -94,7 +99,7 @@ const store = reactive({ store.filters.fecha_inicio = store.filters.fecha_fin = store.filters.fecha = null $("#fecha, #fecha_inicio, #fecha_fin").datepicker({ - minDate: -3, + minDate: -15, maxDate: new Date(), dateFormat: "yy-mm-dd", showAnim: "slide", @@ -150,7 +155,12 @@ const store = reactive({ toggle(arr: any, element: any) { const newArray = arr.includes(element) ? arr.filter((item: any) => item !== element) : [...arr, element] // if all are selected, then unselect all - if (newArray.length === this.estados.data.length) return [] + if (newArray.length === this.estados.data.length) { + setTimeout(() => { + document.querySelectorAll('#dlAsistencia>ul>li.selected')!.forEach(element => element.classList.remove('selected')); + }, 100) + return [] + } return newArray }, }) @@ -166,7 +176,11 @@ type Profesor = { profesor_clave: string; profesor_grado: string; } - +$('div.modal#cargando').modal({ + backdrop: 'static', + keyboard: false, + show: false, +}) createApp({ store, get clase_vista() { @@ -175,9 +189,17 @@ createApp({ registros: { data: [] as Registro[], async fetch() { + // if (!store.filters.facultad_id || !store.filters.periodo_id) return this.loading = true this.data = [] as Registro[] - const res = await fetch('action/action_auditoria.php') + const params = { + facultad_id: 19, + periodo_id: 2, + } + const paramsUrl = new URLSearchParams(params as any).toString() + const res = await fetch(`action/action_auditoria.php?${paramsUrl}`, { + method: 'GET', + }) this.data = await res.json() this.loading = false }, @@ -203,9 +225,15 @@ createApp({ */ const filters = Object.keys(store.filters).filter((filtro) => store.filters[filtro] || store.filters[filtro]?.length > 0) + /* + store.current + page: 1, + maxPages: 10, + perPage: 10, + */ return this.data.filter((registro: Registro) => { return filters.every((filtro) => { - + switch (filtro) { case 'fecha': return registro.registro_fecha_ideal === store.filters[filtro]; @@ -218,7 +246,7 @@ createApp({ if (/^\([^)]+\)\s[\s\S]+$/.test(textoFiltro)) { const clave = registro.profesor_clave.toLowerCase(); const filtroClave = textoFiltro.match(/\((.*?)\)/)?.[1]; - console.log(clave, filtroClave); + // console.log(clave, filtroClave); return clave.includes(filtroClave); } else { const nombre = registro.profesor_nombre.toLowerCase(); @@ -238,40 +266,67 @@ createApp({ }) }) }, - async descargar() { + store.current.modal_state = 'Generando reporte en Excel...' + $('div.modal#cargando').modal('show'); + this.loading = true; if (this.relevant.length === 0) return; - const res = await fetch('export/supervisor_excel.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(this.relevant) - }); - const blob = await res.blob(); - (window as any).saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`); + try { + const res = await fetch('export/supervisor_excel.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(this.relevant) + }); + const blob = await res.blob(); + (window as any).saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`); + + } catch (error) { + if (error.response && error.response.status === 413) { + alert('Your request is too large! Please reduce the data size and try again.'); + } else { + alert('An error occurred: ' + error.message); + } + } + finally { + $('#cargando').modal('hide'); + this.loading = false; + } + }, + loading: false, + get pages() { + return Math.ceil(this.relevant.length / store.current.perPage) } }, get profesores() { - return this.registros.data.map((registro: Registro) => ( - { + return this.registros.data + .map((registro: Registro) => ({ profesor_id: registro.profesor_id, profesor_nombre: registro.profesor_nombre, profesor_correo: registro.profesor_correo, profesor_clave: registro.profesor_clave, profesor_grado: registro.profesor_grado, - } - )).sort((a: Profesor, b: Profesor) => - a.profesor_nombre.localeCompare(b.profesor_nombre) - ) + })) + .reduce((acc: Profesor[], current: Profesor) => { + if (!acc.some(item => item.profesor_id === current.profesor_id)) { + acc.push(current); + } + return acc; + }, []) + .sort((a: Profesor, b: Profesor) => + a.profesor_nombre.localeCompare(b.profesor_nombre) + ); + }, async mounted() { + $('div.modal#cargando').modal('show'); + await this.registros.fetch() await store.facultades.fetch() await store.estados.fetch() await store.bloques_horario.fetch() - - store.filters.bloque_horario = store.bloques_horario.data.find((bloque: Bloque_Horario) => bloque.selected)?.id store.filters.switchFechas() + $('div.modal#cargando').modal('hide'); } }).mount('#app')