Merge branch 'master' of https://github.apps.lci.ulsa.mx/PAAD/paad
This commit is contained in:
12
.vscode/sftp.json
vendored
Normal file
12
.vscode/sftp.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "PAAD",
|
||||
"host": "paad.lci.ulsa.mx",
|
||||
"protocol": "sftp",
|
||||
"port": 22,
|
||||
"username": "miniroot",
|
||||
"remotePath": "/saa_dsk/www",
|
||||
"uploadOnSave": false,
|
||||
"useTempFile": false,
|
||||
"openSsh": false,
|
||||
"password": "T4nP0d3r0s1s1m0##"
|
||||
}
|
||||
@@ -8,8 +8,8 @@ ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$ruta = "../";
|
||||
require_once $ruta . "class/c_login.php";
|
||||
$ruta = "..";
|
||||
require_once "$ruta/class/c_login.php";
|
||||
if (!isset($_SESSION['user'])) {
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'unauthorized']));
|
||||
@@ -30,17 +30,48 @@ try {
|
||||
// ':periodo_id' => $_GET['periodo_id'] > 0 ? $user->periodo_id : null,
|
||||
':facultad_id' => $user->facultad['facultad_id'],
|
||||
':fecha_inicio' => $_GET['fecha'] ?? $_GET['fecha_inicio'] ?? date('Y-m-d'),
|
||||
':fecha_fin' => $baseDate ? date('Y-m-d H:i:s', strtotime($baseDate . ' +24 hours')) : date('Y-m-d H:i:s'),
|
||||
':fecha_fin' => $baseDate ? date('Y-m-d H:i:s', strtotime("$baseDate +24 hours")) : date('Y-m-d H:i:s'),
|
||||
'usuario_id' => $user->user['id'],
|
||||
];
|
||||
$data = $db->query(
|
||||
"SELECT * FROM PUBLIC.AUDITORIA_MAT
|
||||
"WITH AUDITORIA_DATA AS (
|
||||
SELECT * FROM PUBLIC.AUDITORIA_MAT
|
||||
WHERE FACULTAD_ID = COALESCE(:facultad_id, FACULTAD_ID)
|
||||
AND REGISTRO_FECHA_IDEAL + HORARIO_HORA between :fecha_inicio AND :fecha_fin
|
||||
ORDER BY registro_fecha_ideal asc, horario_hora asc",
|
||||
), DELETION AS (
|
||||
DELETE FROM last_auditoria
|
||||
WHERE usuario_id = :usuario_id
|
||||
), INSERTION AS (
|
||||
INSERT INTO last_auditoria
|
||||
SELECT :usuario_id, to_jsonb(AUDITORIA_DATA) FROM AUDITORIA_DATA
|
||||
WHERE REGISTRO_FECHA_IDEAL IS NOT NULL
|
||||
)
|
||||
SELECT
|
||||
registro_id,
|
||||
registro_fecha_ideal,
|
||||
horario_id,
|
||||
profesor_id,
|
||||
salon,
|
||||
profesor_clave,
|
||||
profesor_nombre,
|
||||
horario_hora,
|
||||
horario_fin,
|
||||
registro_fecha,
|
||||
color,
|
||||
estado_color,
|
||||
estado_icon,
|
||||
estado_supervisor_id,
|
||||
facultad_id,
|
||||
usuario_nombre,
|
||||
registro_fecha_supervisor,
|
||||
comentario,
|
||||
registro_justificada,
|
||||
reposicion_id
|
||||
FROM AUDITORIA_DATA",
|
||||
$params
|
||||
);
|
||||
|
||||
echo json_encode(array_merge($data), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
// Print the JSON file
|
||||
echo json_encode($data);
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'method not allowed']);
|
||||
|
||||
48
action/action_auditoria_detalle.php
Normal file
48
action/action_auditoria_detalle.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
#input $_GET['id_espacio_sgu']
|
||||
#output rutas: [ ...ruta, salones: [{...salon}] ]
|
||||
header('Content-Type: application/json charset=utf-8');
|
||||
ini_set('memory_limit', '2048M');
|
||||
ini_set('post_max_size', '2048M');
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$ruta = "..";
|
||||
require_once "$ruta/class/c_login.php";
|
||||
if (!isset($_SESSION['user'])) {
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'unauthorized']));
|
||||
}
|
||||
$user = unserialize($_SESSION['user']);
|
||||
|
||||
// check method
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
|
||||
$data = $db
|
||||
->where('registro_fecha_ideal', $_GET['registro_fecha_ideal'])
|
||||
->where('horario_id', $_GET['horario_id'])
|
||||
->where('profesor_id', $_GET['profesor_id'])
|
||||
->getOne('auditoria_mat');
|
||||
// Print the JSON file
|
||||
echo json_encode(array_merge($data, ['query' => $db->getLastQuery(), 'get' => $_GET]));
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'method not allowed']);
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $th) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => $th->getMessage(),
|
||||
// 'query' => $db->getLastQuery(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
|
||||
exit;
|
||||
} catch (Exception $th) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => $th->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
261
auditoria.php
261
auditoria.php
@@ -13,6 +13,7 @@
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="css/auditoria-ux.css">
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/jquery-ui.js"></script>
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
@@ -261,25 +262,30 @@
|
||||
:class="{ 'table-warning': registro.reposicion_id }">
|
||||
<td class="text-center align-middle px-2">{{ registro.registro_fecha_ideal }}
|
||||
</td>
|
||||
<td class="text-center align-middle px-2">{{ registro.salon }}</td>
|
||||
<td class="align-middle px-2">
|
||||
<td class="text-center align-middle px-2">{{
|
||||
`${registro.salon?.slice(0,15)}${registro.salon?.length > 15 ? '...' : ''}` }}</td>
|
||||
<td class="align-middle px-2 d-flex justify-content-center">
|
||||
<div class="w-75 d-flex justify-content-between" style="font-size: 1.2rem">
|
||||
<span>
|
||||
<strong>{{ registro.profesor_clave }}</strong>
|
||||
{{ registro.profesor_nombre }}
|
||||
<button type="button" class="ml-3 btn btn-sm btn-outline-primary"
|
||||
@click="store.current.clase_vista = registro" data-toggle="modal"
|
||||
data-target="#ver-detalle">
|
||||
<i class="ing-ojo"></i> detalle
|
||||
</button>
|
||||
</span>
|
||||
<a class="ml-3 text-info" style="cursor: pointer; font-size: 1.5rem;"
|
||||
@click="store.current.clase_vista = registro; detalle.obtener_detalle(registro.horario_id, registro.profesor_id, registro.registro_fecha_ideal)"
|
||||
data-toggle="modal" data-target="#ver-detalle">
|
||||
<i class="ing-ojo"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
<td class="text-center align-middle px-2">{{ registro.horario_hora?.slice(0,5) }} -
|
||||
{{registro.horario_fin?.slice(0,5) }}</td>
|
||||
<!-- -->
|
||||
<td class="text-center align-middle px-2">
|
||||
{{ `${registro.horario_hora?.slice(0,5)} - ${registro.horario_fin?.slice(0,5)}` }}
|
||||
</td>
|
||||
<td class="text-center align-middle px-2">
|
||||
<div v-if="registro.registro_fecha">
|
||||
<div class="col-12" :class="registro.color">
|
||||
Registro <small>{{ registro.registro_fecha?.slice(11,19) }}</small>
|
||||
Registro <small>{{ `${registro.registro_fecha?.slice(11,19)}` }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
@@ -299,21 +305,22 @@
|
||||
<span class="mr-2" :class="`text-${registro.estado_color}`">
|
||||
<i :class="`${registro.estado_icon} ing-2x`"></i>
|
||||
</span>
|
||||
<strong v-if="registro.usuario_nombre">{{ registro.usuario_nombre
|
||||
}}</strong>
|
||||
<strong v-if="registro.usuario_nombre">
|
||||
{{ registro.usuario_nombre }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-12" v-if="registro.registro_fecha_supervisor">
|
||||
Hora
|
||||
<small>{{ registro.registro_fecha_supervisor?.slice(11,19) }}</small>
|
||||
<small>{{ `${registro.registro_fecha_supervisor?.slice(11,19)}` }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 "
|
||||
@click="store.registros.mostrarComentario(registro.registro_id)"
|
||||
v-if="registro.comentario" style="cursor: pointer;">
|
||||
<strong class="badge border border-primary">Observaciones:</strong>
|
||||
<small
|
||||
class="text-truncate">{{registro.comentario?.slice(0,25)}}{{registro.comentario.length
|
||||
> 10 ? '...' : ''}}</small>
|
||||
<small class="text-truncate">{{`${registro.comentario?.slice(0,25)}
|
||||
${registro.comentario.length
|
||||
> 25 ? '...' : ''}`}}</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -389,165 +396,99 @@
|
||||
<div class="modal" tabindex="-1" id="ver-detalle">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl" v-if="clase_vista">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" :data-id="clase_vista.horario_id">Detalle de la clase</h2>
|
||||
<div class="modal-body detalle">
|
||||
<nav style="cursor: pointer;" class="modal-close pe-auto"
|
||||
@click="$('#ver-detalle').modal('hide');">
|
||||
<i class="ing-cancelar text-danger"></i>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<section class="col-12 col-md-6">
|
||||
<h4 class="h4">Profesor</h4>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<strong>Nombre:</strong>
|
||||
{{ clase_vista.profesor_nombre }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Correo:</strong>
|
||||
<a :href="`mailto:${clase_vista.profesor_correo}`"><strong>{{
|
||||
clase_vista.profesor_correo }}</strong></a>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Clave:</strong>
|
||||
{{ clase_vista.profesor_clave }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Facultad:</strong>
|
||||
{{ clase_vista.facultad }}
|
||||
<section class="profesor">
|
||||
<img class="profesor-icon" src="fonts/svg/portrait.svg" alt="profesor">
|
||||
<div>
|
||||
<span class="profesor-nombre">
|
||||
{{clase_vista.profesor_nombre}}
|
||||
</span>
|
||||
<div class="profesor-datos">
|
||||
<a :href="`mailto:${clase_vista.profesor_correo}`" class="profesor-correo">
|
||||
{{clase_vista.profesor_correo}}
|
||||
</a>
|
||||
<i class="pipe"></i>
|
||||
<span class="profesor-clave">
|
||||
{{clase_vista.profesor_clave}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="col-12 col-md-6">
|
||||
<h4 class="h4">Clase</h4>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<strong>Materia:</strong>
|
||||
{{ clase_vista.materia }}
|
||||
|
||||
<main class="my-5">
|
||||
<div class="pp-card" style="grid-row: span 5;">
|
||||
<div :class="`bg-${clase_vista.estado_color}`" class="text-white pp-card-icon ">
|
||||
<span>{{clase_vista.nombre}}</span>
|
||||
<i :class="clase_vista.estado_icon" class="mx-3"></i>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Carrera:</strong>
|
||||
{{ clase_vista.carrera }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Nivel:</strong>
|
||||
{{ clase_vista.nivel}}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Grupo:</strong>
|
||||
{{ clase_vista.horario_grupo }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Horario:</strong>
|
||||
{{ clase_vista.horario_hora?.slice(0, 5) }} -
|
||||
{{clase_vista.horario_fin?.slice(0, 5) }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Salón:</strong>
|
||||
{{ clase_vista.salon }}
|
||||
<main class="supervisor">
|
||||
<div v-if="clase_vista.usuario_nombre">
|
||||
<span class="supervisor_nombre text-dark">
|
||||
{{clase_vista.usuario_nombre}}
|
||||
</span>
|
||||
<span class="supervisor_hora">
|
||||
{{`${new Date(clase_vista.registro_fecha_supervisor).toTimeString().slice(0,
|
||||
5)}`}}
|
||||
</span>
|
||||
</div>
|
||||
<footer>Supervisor</footer>
|
||||
</main>
|
||||
</div>
|
||||
<div class="pp-card" style="grid-row: span 8">
|
||||
<div class="bg-primary text-white">{{clase_vista.carrera}}</div>
|
||||
<main class="horario_clase d-flex flex-column justify-content-center px-4"
|
||||
style="gap: .5rem;">
|
||||
<span class="materia_nombre">{{clase_vista.materia}}</span>
|
||||
<span class="horario text-dark mb-2">
|
||||
<section class="la-salle-regular">
|
||||
<span class="font-weight-bold">
|
||||
{{ new
|
||||
Date(clase_vista.registro_fecha_ideal).toLocaleDateString('es-ES', {
|
||||
weekday: 'long' }).replace(/^\w/, c => c.toUpperCase()) }}
|
||||
</span>
|
||||
<span class="font-italic font-weight-light">de</span>
|
||||
<span class="font-weight-bold">{{clase_vista.horario_hora.slice(0,
|
||||
5)}}</span>
|
||||
<span class="font-italic font-weight-light">a</span>
|
||||
<span class="font-weight-bold">{{clase_vista.horario_fin.slice(0,
|
||||
5)}}</span>
|
||||
</section>
|
||||
</div>
|
||||
<div class="row">
|
||||
<section class="col-12">
|
||||
<h4 class="h4 mt-4">Registro</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-6 text-center" v-if="!clase_vista.registro_fecha">
|
||||
<strong><span class="badge border border-dark"><i
|
||||
class="ing-cancelar"></i></span>
|
||||
Sin registro del profesor</strong>
|
||||
</div>
|
||||
<div class="col-md-6 text-center" v-else>
|
||||
El profesor registró su asistencia a las
|
||||
<code>{{clase_vista.registro_fecha?.slice(11, 19)}}</code>
|
||||
<hr>
|
||||
<p v-if="!clase_vista.registro_retardo" class="text-center">
|
||||
<span class="badge border border-success"><i
|
||||
class="ing-aceptar"></i></span>
|
||||
A tiempo
|
||||
</p>
|
||||
<p v-else class="text-center">
|
||||
<span class="badge border border-warning"><i
|
||||
class="ing-retardo"></i></span>
|
||||
Con retardo
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-center" v-if="clase_vista.registro_justificada">
|
||||
<strong>
|
||||
<span class="badge badge-success mr-2">
|
||||
<i class="ing-finalistas"></i>
|
||||
<span class="font-weight-bold la-salle-regular">
|
||||
Grupo {{clase_vista.horario_grupo}}
|
||||
</span>
|
||||
Justificada
|
||||
</strong>
|
||||
<span class="text-muted">
|
||||
por
|
||||
{{clase_vista.justificador_nombre}} de
|
||||
<strong>{{clase_vista.justificador_rol}}</strong>
|
||||
<span v-if="clase_vista.justificador_facultad"> de
|
||||
<strong>{{clase_vista.justificador_facultad}}</strong>
|
||||
<span class="salon font-italic font-weight-light">{{clase_vista.salon}}</span>
|
||||
</span>
|
||||
|
||||
el día {{clase_vista.registro_fecha_justificacion?.slice(0, 10)}} a
|
||||
las
|
||||
{{clase_vista.registro_fecha_justificacion?.slice(11, 16)}}
|
||||
</span>
|
||||
<div v-if="clase_vista.justificacion">
|
||||
<hr>
|
||||
<p class="text-center">
|
||||
<strong>Observación:</strong>
|
||||
{{clase_vista.justificacion}}
|
||||
</p>
|
||||
<footer>
|
||||
{{clase_vista.nivel}}
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div class="pp-card" style="grid-row: span 3">
|
||||
<div class="bg-warning text-white"
|
||||
v-if="clase_vista.registro_fecha && clase_vista.registro_retardo">
|
||||
<span>Con retardo</span>
|
||||
<i class="ing-retardo"></i>
|
||||
</div>
|
||||
<div class="col-md-6 text-center"
|
||||
v-else-if="clase_vista.registro_fecha_justificacion">
|
||||
<strong>
|
||||
<span class="badge border border-dark">
|
||||
<div class="bg-success text-white" v-else-if="clase_vista.registro_fecha">
|
||||
<span>A tiempo</span>
|
||||
<i class="ing-autorizar"></i>
|
||||
</div>
|
||||
<div class="bg-dark text-white" v-else>
|
||||
Sin registro
|
||||
<i class="ing-cancelar"></i>
|
||||
</span>
|
||||
Sin justificar, <span class="text-muted">
|
||||
{{clase_vista.justificador_nombre}}
|
||||
({{clase_vista.justificador_rol}}{{clase_vista.justificador_facultad
|
||||
? ' de ' + clase_vista.justificador_facultad : ''}})
|
||||
</span> borró la justificación, el día
|
||||
{{clase_vista.registro_fecha_justificacion?.slice(0, 10)}} a las
|
||||
{{clase_vista.registro_fecha_justificacion?.slice(11, 16)}}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="col-md-6 text-center" v-else>
|
||||
<strong>
|
||||
<span class="badge border border-dark">
|
||||
<i class="ing-cancelar"></i>
|
||||
</span>
|
||||
Sin justificar
|
||||
</strong>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<section class="col-12" v-if="clase_vista.reposicion_id">
|
||||
<h4 class="h4 mt-4">Reposición</h4>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
Esta clase se reprogramó para el día
|
||||
{{ clase_vista.reposicion_fecha }} a las
|
||||
{{ clase_vista.reposicion_hora?.slice(0, 5) }} -
|
||||
{{clase_vista.reposicion_hora_fin?.slice(0, 5) }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<!-- botón aceptar -->
|
||||
<button type="button" class="btn btn-outline-primary" data-dismiss="modal">
|
||||
<i class="ing-aceptar"></i>
|
||||
Aceptar
|
||||
</button>
|
||||
<footer class="facultad">
|
||||
{{clase_vista.facultad}}
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -588,7 +529,7 @@
|
||||
class="text-muted">{{store.current.justificada.registro_fecha_ideal}}</span>
|
||||
a
|
||||
las <span
|
||||
class="text-muted">{{store.current.justificada.horario_hora?.slice(0,5)}}</span>
|
||||
class="text-muted">{{$`{store.current.justificada.horario_hora?.slice(0,5)}`}}</span>
|
||||
para el profesor <span
|
||||
class="text-muted">{{store.current.justificada.profesor_nombre}}</span>
|
||||
</div>
|
||||
|
||||
195
css/auditoria-ux.css
Normal file
195
css/auditoria-ux.css
Normal file
@@ -0,0 +1,195 @@
|
||||
@font-face {
|
||||
font-family: 'La Salle Display Regular';
|
||||
src: url('../fonts/indivisaFont/woff/IndivisaDisplaySans-Regular.woff');
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'La Salle Display Heavy';
|
||||
src: url('../fonts/indivisaFont/woff/IndivisaDisplaySans-Heavy.woff');
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.la-salle-regular {
|
||||
font-family: 'La Salle Display Regular';
|
||||
}
|
||||
|
||||
.la-salle-heavy {
|
||||
font-family: 'La Salle Display Heavy';
|
||||
}
|
||||
|
||||
.detalle {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.modal-content:has(.detalle) {
|
||||
border-radius: 2rem;
|
||||
}
|
||||
|
||||
.profesor {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
/* flex-direction: column; */
|
||||
}
|
||||
|
||||
.profesor-icon {
|
||||
height: 6rem;
|
||||
fill: #001d68;
|
||||
}
|
||||
|
||||
.profesor-nombre,
|
||||
.materia_nombre {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 900;
|
||||
font-family: 'La Salle Display Regular';
|
||||
color: black;
|
||||
}
|
||||
|
||||
.profesor-clave {
|
||||
font-weight: 900;
|
||||
font-size: 1.2rem;
|
||||
font-family: 'La Salle Display Regular';
|
||||
}
|
||||
|
||||
.profesor-datos {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
gap: .75rem;
|
||||
color: #969696;
|
||||
}
|
||||
|
||||
.profesor-datos .profesor-correo {
|
||||
font-style: italic;
|
||||
text-decoration: underline;
|
||||
color: #969696;
|
||||
}
|
||||
|
||||
.profesor-datos .profesor-correo:hover {
|
||||
color: #3f3f3f;
|
||||
transition-timing-function: ease-in;
|
||||
transition-duration: .2s;
|
||||
}
|
||||
|
||||
.pipe::after {
|
||||
content: '';
|
||||
width: .1rem;
|
||||
height: 1.4rem;
|
||||
|
||||
display: block;
|
||||
|
||||
background-color: #969696;
|
||||
}
|
||||
|
||||
.detalle>main {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
/* Media query for mobile devices */
|
||||
@media (max-width: 768px) {
|
||||
|
||||
/* Adjust the max-width as needed for your target screen size */
|
||||
.detalle>main {
|
||||
grid-template-columns: 1fr;
|
||||
/* Single column */
|
||||
}
|
||||
}
|
||||
|
||||
.pp-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pp-card-icon {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pp-card div {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.pp-card>div {
|
||||
border-start-start-radius: 1rem;
|
||||
border-start-end-radius: 1rem;
|
||||
padding-inline: 1.5rem;
|
||||
padding-block: .5rem;
|
||||
font-family: 'La Salle Display Heavy';
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.pp-card main {
|
||||
background-color: #e1e1e1;
|
||||
height: 100%;
|
||||
|
||||
border-end-start-radius: 1rem;
|
||||
border-end-end-radius: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pp-card footer {
|
||||
font-style: italic;
|
||||
color: #969696;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.pp-card:not(:has(main)) {
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
height: 100%;
|
||||
|
||||
border-end-start-radius: 1rem;
|
||||
border-end-end-radius: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.supervisor div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
.supervisor_nombre {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.supervisor_hora {
|
||||
font-size: 1rem;
|
||||
color: #3f3f3f;
|
||||
}
|
||||
}
|
||||
|
||||
.horario {
|
||||
font-size: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
margin: 2rem;
|
||||
}
|
||||
|
||||
.facultad {
|
||||
font-family: 'La Salle Display Regular';
|
||||
font-size: 1.4rem;
|
||||
color: #969696;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 2rem;
|
||||
}
|
||||
@@ -1,12 +1,76 @@
|
||||
|
||||
<?php
|
||||
|
||||
$fecha = date('d_m_Y');
|
||||
header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
/* header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
header("Content-Disposition: attachment;filename=horario_$fecha.xlsx");
|
||||
header("Cache-Control: max-age=0");
|
||||
header("Cache-Control: max-age=0"); */
|
||||
|
||||
require_once "../vendor/autoload.php";
|
||||
$ruta = "..";
|
||||
require_once "$ruta/class/c_login.php";
|
||||
if (!isset($_SESSION['user'])) {
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'unauthorized']));
|
||||
}
|
||||
$user = unserialize($_SESSION['user']);
|
||||
|
||||
$json = file_get_contents('php://input');
|
||||
$params = json_decode($json, true);
|
||||
|
||||
$profesor_clave = isset($params['profesor'])
|
||||
? preg_replace('/[^\d]/', '', $params['profesor'])
|
||||
: null;
|
||||
|
||||
$data = $db->query(
|
||||
"SELECT DISTINCT
|
||||
auditoria->>'registro_fecha_ideal' as registro_fecha_ideal,
|
||||
auditoria->>'profesor_clave' as profesor_clave,
|
||||
auditoria->>'profesor_nombre' as profesor_nombre,
|
||||
NULLIF(auditoria->>'profesor_correo', '') as profesor_correo,
|
||||
auditoria->>'facultad' as facultad,
|
||||
auditoria->>'materia' as materia,
|
||||
auditoria->>'carrera' as carrera,
|
||||
NULLIF(auditoria->>'horario_grupo', '') as horario_grupo,
|
||||
auditoria->>'horario_hora_completa' as horario_hora_completa,
|
||||
auditoria->>'salon' as salon,
|
||||
(
|
||||
SELECT string_agg(value, ' / ' ORDER BY value)
|
||||
FROM jsonb_array_elements_text(auditoria->'salon_array') AS value
|
||||
) as salon_array,
|
||||
auditoria->>'asistencia' as asistencia,
|
||||
auditoria->>'registro_fecha' as registro_fecha,
|
||||
auditoria->>'usuario_nombre' as usuario_nombre,
|
||||
auditoria->>'nombre' as nombre,
|
||||
auditoria->>'registro_fecha_supervisor' as registro_fecha_supervisor,
|
||||
auditoria->>'comentario' as comentario,
|
||||
auditoria->>'justificacion' as justificacion,
|
||||
auditoria->>'horario_hora' as horario_hora,
|
||||
auditoria->>'horario_fin' as horario_fin,
|
||||
(auditoria->>'estado_supervisor_id')::integer as estado_id,
|
||||
auditoria->>'registro_retardo' as registro_retardo
|
||||
FROM last_auditoria
|
||||
-- JOIN BLOQUE_HORARIO ON ((auditoria->>'horario_hora')::TIME, (auditoria->>'horario_fin')::TIME) OVERLAPS (HORA_INICIO, HORA_FIN)
|
||||
WHERE USUARIO_ID = :usuario_id
|
||||
AND auditoria->>'facultad_id' = COALESCE(:facultad_id, auditoria->>'facultad_id')
|
||||
AND auditoria->>'profesor_clave' = COALESCE(:profesor_clave, auditoria->>'profesor_clave')
|
||||
--AND BLOQUE_HORARIO.ID = COALESCE(:bloque_horario_id, BLOQUE_HORARIO.ID)",
|
||||
[
|
||||
'usuario_id' => $user->user['id'],
|
||||
'facultad_id' => $params['facultad_id'],
|
||||
'profesor_clave' => $profesor_clave,
|
||||
/* 'bloque_horario_id' => $params['bloque_horario'] */
|
||||
]
|
||||
);
|
||||
|
||||
$estados = empty($params['estados']) ? null : $params['estados'];
|
||||
|
||||
$data = array_filter(
|
||||
$data,
|
||||
fn($fila) => in_array($fila['estado_id'], $estados ?? [$fila['estado_id']])
|
||||
);
|
||||
|
||||
$data = array_values($data);
|
||||
|
||||
empty($data) and die(json_encode(['error' => 'No se recibieron datos', 'data' => $data]));
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
@@ -28,11 +92,6 @@ $drawing->setName('La Salle')
|
||||
->setOffsetX(10)
|
||||
->setWorksheet($spreadsheet->getActiveSheet());
|
||||
|
||||
$json = file_get_contents('php://input');
|
||||
$data = json_decode($json, true);
|
||||
|
||||
empty($data) and die(json_encode(['error' => 'No se recibieron datos', 'data' => $data]));
|
||||
|
||||
$data_excel = array(
|
||||
"FECHA" => 'registro_fecha_ideal',
|
||||
"CLAVE" => 'profesor_clave',
|
||||
@@ -88,7 +147,7 @@ array_walk($keys, function ($key, $index) use ($sheet) {
|
||||
$sheet->getStyle($headers_range)->applyFromArray([
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 15,
|
||||
'size' => 12,
|
||||
'name' => 'Indivisa Text Sans',
|
||||
'color' => ['argb' => Color::COLOR_WHITE],
|
||||
],
|
||||
@@ -110,7 +169,7 @@ $sheet->setAutoFilter($headers_range);
|
||||
// Styles that are common for all rows can be set outside the loop
|
||||
|
||||
const DEFAULT_FONT = [
|
||||
'size' => 12,
|
||||
'size' => 10,
|
||||
'name' => 'Indivisa Text Sans',
|
||||
'color' => ['argb' => '001d68']
|
||||
];
|
||||
@@ -132,15 +191,21 @@ const DEFAULT_STYLE = [
|
||||
function getFormattedValue($key, $registro)
|
||||
{
|
||||
return match ($key) {
|
||||
'asistencia' => is_null($registro['registro_fecha']) ? "Sin registro" : ($registro['registro_retardo'] ? "Retardo " : "Asistencia "),
|
||||
'asistencia' => $registro['registro_fecha'] === null
|
||||
? "Sin registro"
|
||||
: ($registro['registro_retardo'] ? "Retardo " : "Asistencia "),
|
||||
'registro_fecha',
|
||||
'registro_fecha_supervisor' => is_null($registro[$key]) ? 'Sin registro' : date('H:i', strtotime($registro[$key])),
|
||||
'registro_fecha_supervisor' => $registro[$key] === null
|
||||
? 'Sin registro'
|
||||
: date('H:i', strtotime($registro[$key])),
|
||||
'nombre' => $registro[$key] ?? "Sin registro",
|
||||
'horario_hora_completa' => "{$registro['horario_hora']} - {$registro['horario_fin']}",
|
||||
'usuario_nombre',
|
||||
'salon_array' => implode(", ", json_decode($registro[$key], true)),
|
||||
'usuario_nombre' => $registro[$key] ?? "Sin registro",
|
||||
'salon_array' => $registro[$key],
|
||||
'justificacion',
|
||||
'comentario' => $registro[$key] ?? "Sin registro",
|
||||
'horario_grupo' => $registro[$key] ?? "Sin registro",
|
||||
'profesor_correo' => $registro[$key] ?? "Sin correo",
|
||||
default => $registro[$key]
|
||||
};
|
||||
}
|
||||
@@ -164,7 +229,7 @@ foreach ($data as $index => $registro) {
|
||||
|
||||
$sheet->setCellValue($cellLocation, $value);
|
||||
|
||||
if ($value == "Sin registro") {
|
||||
if (in_array($value, ["Sin registro", "Sin asignar", "Sin correo"])) {
|
||||
$sheet->getStyle($cellLocation)->applyFromArray(['font' => ['color' => ['argb' => 'ea0029']]]);
|
||||
}
|
||||
});
|
||||
@@ -172,7 +237,12 @@ foreach ($data as $index => $registro) {
|
||||
|
||||
|
||||
foreach ($sheet->getColumnIterator() as $column) {
|
||||
$sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
|
||||
$colIndex = $column->getColumnIndex();
|
||||
if (in_array($colIndex, ['Q', 'R'])) {
|
||||
$sheet->getColumnDimension($colIndex)->setWidth(100); // Ajusta el valor según el tamaño deseado
|
||||
} else {
|
||||
$sheet->getColumnDimension($colIndex)->setAutoSize(true);
|
||||
}
|
||||
}
|
||||
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
|
||||
|
||||
2
fonts/svg/portrait.svg
Normal file
2
fonts/svg/portrait.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Bold" viewBox="0 0 24 24" width="512" height="512"><path d="M18.5,0H5.5A5.506,5.506,0,0,0,0,5.5v13A5.506,5.506,0,0,0,5.5,24h13A5.506,5.506,0,0,0,24,18.5V5.5A5.506,5.506,0,0,0,18.5,0ZM21,18.5A2.5,2.5,0,0,1,18.5,21H18V20A6,6,0,0,0,6,20v1H5.5A2.5,2.5,0,0,1,3,18.5V5.5A2.5,2.5,0,0,1,5.5,3h13A2.5,2.5,0,0,1,21,5.5Z" fill="#001d68"/><circle cx="12" cy="8.5" r="3.5" fill="#001d68"/></svg>
|
||||
|
After Width: | Height: | Size: 466 B |
2
fonts/svg/presentation.svg
Normal file
2
fonts/svg/presentation.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><path d="M464.867 0H239.934C213.945 0 192.8 21.145 192.8 47.133v144.293l-41.766-20.77a90.857 90.857 0 0 0-4.289-1.996c12.262-12.8 19.82-30.144 19.82-49.226 0-39.278-31.953-71.235-71.23-71.235-39.281 0-71.234 31.957-71.234 71.235 0 21.343 9.449 40.515 24.37 53.582C20.993 188.476 0 218.636 0 256v48.2c0 20.75 13.473 38.41 32.133 44.687v83.847c0 8.282 6.715 15 15 15h96.398c8.285 0 15-6.718 15-15V264.2l48.024 24.008c18.379 9.188 43.308 3.711 54.582-17.207h53.781l-56.95 156.605c-2.831 7.786 1.188 16.395 8.973 19.223 8.899 3.238 16.813-2.344 19.223-8.969L346.84 271h10.515l61.297 166.906c2.328 6.332 10.184 12.239 19.254 8.906 7.774-2.855 11.766-11.472 8.907-19.25L389.313 271h75.554C490.352 271 512 250.441 512 223.867V47.133C512 20.555 490.352 0 464.867 0zM54.102 119.434c0-22.739 18.496-41.235 41.23-41.235 22.738 0 41.234 18.496 41.234 41.235 0 22.734-18.496 41.23-41.234 41.23-22.734 0-41.23-18.496-41.23-41.23zM235.5 254.96c-.04.11-.078.223-.113.336-1.024 2.855-3.336 5.262-6.168 6.426a11.232 11.232 0 0 1-9.246-.348c-28.524-14.27-69.739-34.86-69.739-34.86-9.953-4.976-21.703 2.274-21.703 13.419v177.8H62.133v-81.402c0-8.281-6.719-15-15-15-9.45 0-17.133-7.684-17.133-17.133V256c0-36.652 29.844-65.332 65.332-65.332h13.254c10.055 0 20.098 2.36 29.07 6.84 0 0 74.403 37 93.352 46.437 4.058 2.016 5.988 6.75 4.492 11.016zM482 223.867c0 4.582-1.777 8.883-5.012 12.117-3.183 3.188-7.601 5.016-12.12 5.016h-200.47c-.027-.082-.046-.164-.074-.242l37.227-74.45c3.703-7.41.703-16.417-6.707-20.124-7.406-3.704-16.418-.704-20.125 6.707l-31.75 63.496c-5.918-2.95-12.797-6.375-20.168-10.04V47.134c0-9.446 7.683-17.133 17.133-17.133h224.933c4.52 0 8.938 1.828 12.125 5.023 3.23 3.227 5.008 7.528 5.008 12.11zm0 0" fill="#000000" opacity="0.5411764705882353" data-original="#000000" class=""/><path d="M432.734 64.266H272.066c-8.285 0-15 6.714-15 15s6.715 15 15 15h160.668c8.282 0 15-6.715 15-15s-6.714-15-15-15zM432.734 120.5h-80.336c-8.28 0-15 6.715-15 15s6.72 15 15 15h80.336c8.282 0 15-6.715 15-15s-6.714-15-15-15zM432.734 176.734h-80.336c-8.28 0-15 6.715-15 15 0 8.282 6.72 15 15 15h80.336c8.282 0 15-6.718 15-15 0-8.285-6.714-15-15-15zm0 0" fill="#000000" opacity="0.5411764705882353" data-original="#000000" class=""/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -290,7 +290,12 @@ const store = reactive({
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(this.relevant)
|
||||
body: JSON.stringify({
|
||||
'facultad_id': store.filters.facultad_id,
|
||||
'estados': store.filters.estados,
|
||||
'profesor': store.filters.profesor,
|
||||
'bloque_horario': store.filters.bloque_horario,
|
||||
})
|
||||
});
|
||||
const blob = await res.blob();
|
||||
window.saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`);
|
||||
@@ -314,8 +319,54 @@ const store = reactive({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const detalle = reactive({
|
||||
correo: null,
|
||||
facultad: null,
|
||||
materia: null,
|
||||
carrera: null,
|
||||
nivel: null,
|
||||
horario_grupo: null,
|
||||
|
||||
reset() {
|
||||
this.correo = null
|
||||
this.facultad = null
|
||||
this.materia = null
|
||||
this.carrera = null
|
||||
this.nivel = null
|
||||
this.horario_grupo = null
|
||||
},
|
||||
|
||||
async obtener_detalle(horario_id, profesor_id, registro_fecha_ideal) {
|
||||
detalle.reset();
|
||||
|
||||
$('div.modal#cargando').modal('show');
|
||||
try {
|
||||
const resultado = await (await fetch(`action/action_auditoria_detalle.php?${new URLSearchParams({ horario_id, profesor_id, registro_fecha_ideal })}`)).json();
|
||||
|
||||
this.correo = resultado.profesor_correo;
|
||||
this.facultad = resultado.facultad;
|
||||
this.materia = resultado.materia;
|
||||
this.carrera = resultado.carrera;
|
||||
this.nivel = resultado.nivel;
|
||||
this.horario_grupo = resultado.horario_grupo;
|
||||
|
||||
this.registro_fecha =
|
||||
store.current.clase_vista = resultado
|
||||
store.current.clase_vista.supervisor_hora = resultado
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Error:', error)
|
||||
}
|
||||
finally {
|
||||
$('div.modal#cargando').modal('hide');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
createApp({
|
||||
store,
|
||||
detalle,
|
||||
messages: [],
|
||||
get clase_vista() {
|
||||
return store.current.clase_vista;
|
||||
|
||||
Reference in New Issue
Block a user