Stable 2-ago-2023
This commit is contained in:
57
action/action_auditoria.php
Normal file
57
action/action_auditoria.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?
|
||||
#input $_GET['id_espacio_sgu']
|
||||
#output rutas: [ ...ruta, salones: [{...salon}] ]
|
||||
header('Content-Type: application/json charset=utf-8');
|
||||
$information = [
|
||||
'GET' => [
|
||||
#'periodo_id',
|
||||
],
|
||||
];
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
// check method
|
||||
try {
|
||||
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
array_walk($information['GET'], function ($value) {
|
||||
if (!array_key_exists($value, $_GET)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => "$value is required"]);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
|
||||
$data = $db->query("SELECT *, horario_view.facultad_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");
|
||||
|
||||
$last_query = [
|
||||
'query' => $db->getLastQuery(),
|
||||
];
|
||||
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
} 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);
|
||||
exit;
|
||||
} catch (Exception $th) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => $th->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
42
action/action_estado_supervisor.php
Normal file
42
action/action_estado_supervisor.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?
|
||||
#input $_GET['id_espacio_sgu']
|
||||
define("INFORMATION", [
|
||||
'GET' => [
|
||||
],
|
||||
]);
|
||||
#output rutas: [ ...ruta, salones: [{...salon}] ]
|
||||
header('Content-Type: application/json charset=utf-8');
|
||||
#return html
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
// check method
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// check parameters
|
||||
$raw = file_get_contents('php://input');
|
||||
$post_get = json_decode($raw, true);
|
||||
|
||||
$data = $db->get('estado_supervisor');
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
} 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(),
|
||||
'post_data' => $post_get,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
} catch (Exception $th) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => $th->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
@@ -1,11 +1,51 @@
|
||||
<?php
|
||||
<?
|
||||
$information = [
|
||||
'GET' => [],
|
||||
];
|
||||
header('Content-Type: application/json charset=utf-8');
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
|
||||
// check if the session is started
|
||||
if (!isset($_SESSION['user']))
|
||||
die(json_encode(['error' => 'No se ha iniciado sesión']));
|
||||
if (!isset($_SESSION['user'])) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => 'No se ha iniciado sesión'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = unserialize($_SESSION['user']);
|
||||
$ruta = "../";
|
||||
require '../include/bd_pdo.php';
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// check parameters
|
||||
array_walk($information['GET'], function ($value) {
|
||||
if (!array_key_exists($value, $_GET)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => "$value is required"]);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
// step 1: get subrutas
|
||||
$data = $db->get('facultad');
|
||||
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
} 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);
|
||||
exit;
|
||||
} catch (Exception $th) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => $th->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
26
action/action_fechas_clase.php
Normal file
26
action/action_fechas_clase.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
|
||||
if (!isset($_SESSION['user']))
|
||||
die(json_encode(['error' => 'No se ha iniciado sesión']));
|
||||
|
||||
$user = unserialize($_SESSION['user']);
|
||||
$ruta = "../";
|
||||
require_once "../include/bd_pdo.php";
|
||||
|
||||
// if method is get
|
||||
header("Content-Type: application/json");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$user->print_to_log("Acceso a reposiciones");
|
||||
if (empty($_GET['horario_id']))
|
||||
die(json_encode(['error' => 'No se ha enviado el id del horario']));
|
||||
// fecha greater than today
|
||||
$reposiciones = $db->query("SELECT fecha, EXTRACT(DOW FROM fecha) as day, EXTRACT(MONTH FROM fecha) as month, EXTRACT(YEAR FROM fecha) as year, EXTRACT(DAY FROM fecha) as dia_mes FROM fechas_clase(:horario_id) WHERE fecha > CURRENT_DATE", [
|
||||
'horario_id' => $_GET['horario_id']
|
||||
]);
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => $reposiciones
|
||||
]);
|
||||
}
|
||||
@@ -10,13 +10,12 @@ $user = unserialize($_SESSION['user']);
|
||||
$ruta = "../";
|
||||
require_once("../include/bd_pdo.php");
|
||||
extract($_POST);
|
||||
$params = ['per' => $periodo, 'fac' => $facultad, 'car' => $carrera];
|
||||
$params = ['per' => $_POST['periodo'], 'fac' => $_POST['facultad'], 'car' => $_POST['carrera']];
|
||||
|
||||
$user->print_to_log("Acceso a grupos", old: $params);
|
||||
$grupos = queryAll("SELECT DISTINCT LENGTH(GRUPO), GRUPO FROM fs_horario_basic WHERE PERIODO_ID = COALESCE(:per, PERIODO_ID) AND FACULTAD_ID = COALESCE(:fac, FACULTAD_ID) AND CARRERA_ID = COALESCE(:car, CARRERA_ID) ORDER BY LENGTH(GRUPO), GRUPO", $params);
|
||||
$grupos = array_map(function ($grupo) {
|
||||
return $grupo['grupo'];
|
||||
}, $grupos);
|
||||
|
||||
$grupos = array_map(fn ($grupo) => $grupo['grupo'], $grupos);
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
|
||||
28
action/action_grupo_horario.php
Normal file
28
action/action_grupo_horario.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$ruta = "../";
|
||||
require_once("../include/bd_pdo.php");
|
||||
|
||||
$grupo = isset($_GET['grupo']) ? $_GET['grupo'] : 1;
|
||||
$grupo_horarios = $db->querySingle(
|
||||
"WITH bloques AS (
|
||||
SELECT id, hora_inicio, hora_fin
|
||||
FROM public.bloque_horario
|
||||
WHERE grupo = ?
|
||||
ORDER BY hora_inicio ASC
|
||||
)
|
||||
|
||||
SELECT json_agg(json_build_object(
|
||||
'id', id,
|
||||
'hora_inicio', hora_inicio,
|
||||
'hora_fin', hora_fin,
|
||||
'selected', current_time between hora_inicio and hora_fin
|
||||
)) AS bloque_horario
|
||||
FROM bloques
|
||||
",
|
||||
[$grupo]
|
||||
)['bloque_horario'];
|
||||
|
||||
|
||||
echo $grupo_horarios;
|
||||
@@ -1,4 +1,38 @@
|
||||
<?php
|
||||
die(json_encode([
|
||||
'message' => 'ok',
|
||||
]));
|
||||
header('Content-Type: application/json');
|
||||
$ruta = "../";
|
||||
require_once("../include/bd_pdo.php");
|
||||
|
||||
$dias = array("domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado");
|
||||
|
||||
try {
|
||||
if(empty($_POST['profesor_id']))
|
||||
throw new Exception("No se ha especificado un profesor");
|
||||
|
||||
// RECORD LAST QUERY
|
||||
$horarios = $db->query("SELECT * FROM fs_horario(_periodo_id => ?, _last => true, _profesor_id => ?) ORDER BY MATERIA", [
|
||||
$_POST['periodo_id'],
|
||||
$_POST['profesor_id'],
|
||||
]);
|
||||
|
||||
$horarios = array_map(function ($horario) use ($dias, $db) {
|
||||
$horario['profesores'] = array_map(
|
||||
fn ($profesor) =>
|
||||
$db->where("id", $profesor)->getOne("fs_profesor"),
|
||||
explode(",", substr($horario['profesores'], 1, -1))
|
||||
);
|
||||
$horario['dia'] = $dias[$horario['dia']];
|
||||
return $horario;
|
||||
}, $horarios);
|
||||
die(json_encode([
|
||||
"status" => "success",
|
||||
"data" => $horarios,
|
||||
// "data" => [],
|
||||
]));
|
||||
} catch (Exception $e) {
|
||||
die(json_encode([
|
||||
"status" => "error",
|
||||
"message" => $e->getMessage(),
|
||||
"query" => $db->getLastQuery(),
|
||||
]));
|
||||
}
|
||||
|
||||
@@ -1,40 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* Valida usuario con la BD y devuelve contraseña para validar con PHP
|
||||
*
|
||||
* Recibe:
|
||||
* POST: usuario, password
|
||||
*
|
||||
* Error:
|
||||
* 0 - No se recibieron datos
|
||||
* 1 - Usuario/Contraseña incorrectos
|
||||
* 2 - Usuario no esta en BD
|
||||
* 3 - No existe usuario
|
||||
*
|
||||
* Success:
|
||||
* Redirecciona a inicio.php
|
||||
*/
|
||||
include_once("../include/nocache.php"); //continue on error
|
||||
$ruta = "../";
|
||||
require_once("../include/bd_pdo.php"); //die on error
|
||||
require_once("../class/c_login.php");
|
||||
require_once("../include/util.php");
|
||||
require_once("../include/nusoap/nusoap.php");
|
||||
<?php
|
||||
/*
|
||||
* Valida usuario con la BD y devuelve contraseña para validar con PHP
|
||||
*
|
||||
* Recibe:
|
||||
* POST: usuario, password
|
||||
*
|
||||
* Error:
|
||||
* 0 - No se recibieron datos
|
||||
* 1 - Usuario/Contraseña incorrectos
|
||||
* 2 - Usuario no esta en BD
|
||||
* 3 - No existe usuario
|
||||
*
|
||||
* Success:
|
||||
* Redirecciona a inicio.php
|
||||
*/
|
||||
include_once("../include/nocache.php"); //continue on error
|
||||
$ruta = "../";
|
||||
require_once("../include/bd_pdo.php"); //die on error
|
||||
require_once("../class/c_login.php");
|
||||
require_once("../include/util.php");
|
||||
require_once("../include/nusoap/nusoap.php");
|
||||
|
||||
if (!isset($_POST["username"]) || !isset($_POST["passwd"]))
|
||||
die(header("Location: ../index.php?error=0"));
|
||||
if (!isset($_POST["username"]) || !isset($_POST["passwd"]))
|
||||
die(header("Location: ../index.php?error=0"));
|
||||
|
||||
$usr = trim(filter_input(INPUT_POST, "username")); //limpia texto
|
||||
$pass = $_POST["passwd"];
|
||||
$usr = trim(filter_input(INPUT_POST, "username")); //limpia texto
|
||||
$pass = $_POST["passwd"];
|
||||
|
||||
$user = Login::validUser($usr, $pass);
|
||||
$user = Login::validUser($usr, $pass);
|
||||
|
||||
if ($user === false) {
|
||||
$_SESSION['error'] = true;
|
||||
header("Location: ../");
|
||||
} else {
|
||||
$_SESSION['user'] = serialize($user);
|
||||
header("Location: ../main.php");
|
||||
}
|
||||
if (is_array($user)) {
|
||||
$_SESSION['error'] = true;
|
||||
// build query params
|
||||
$params = http_build_query($user);
|
||||
header("Location: ../index.php?$params");
|
||||
} else {
|
||||
$_SESSION['user'] = serialize($user);
|
||||
|
||||
exit;
|
||||
header("Location: " . (isset($_SESSION['ruta']) ? $_SESSION['ruta'] : "../main.php"));
|
||||
}
|
||||
|
||||
exit;
|
||||
54
action/action_reposiciones.php
Normal file
54
action/action_reposiciones.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
|
||||
if (!isset($_SESSION['user']))
|
||||
die(json_encode(['error' => 'No se ha iniciado sesión']));
|
||||
|
||||
$user = unserialize($_SESSION['user']);
|
||||
$ruta = "../";
|
||||
require_once "../include/bd_pdo.php";
|
||||
|
||||
// if method is get
|
||||
header("Content-Type: application/json");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$user->print_to_log("Acceso a reposiciones");
|
||||
$reposiciones = $db
|
||||
->where('periodo_id', $_GET['periodo_id'] ?? null)
|
||||
->where('profesor_id', $_GET['profesor_id'] ?? [])
|
||||
->get("reposicion");
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'reposiciones' => $reposiciones
|
||||
]);
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$user->print_to_log("Creación de reposición", new: $params);
|
||||
try {
|
||||
$requiredParams = ['horario_id', 'fecha', 'hora', 'duracion_id', 'descripcion', 'profesor_id', 'salon', 'unidad', 'periodo_id', 'fecha_clase'];
|
||||
|
||||
// Filter params based on requiredParams
|
||||
$params = array_filter($_POST, function ($key) use ($requiredParams) {
|
||||
return in_array($key, $requiredParams);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
// Check if all required params are present
|
||||
if (count($params) !== count($requiredParams)) {
|
||||
throw new Exception('Falta uno o más parámetros requeridos');
|
||||
}
|
||||
|
||||
$db->insert("reposicion", $params);
|
||||
|
||||
// Return success response
|
||||
echo json_encode([
|
||||
"status" => "success",
|
||||
"message" => "Reposición creada correctamente",
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
// Return error response
|
||||
echo json_encode([
|
||||
"status" => "error",
|
||||
"message" => "No se pudo crear la reposición",
|
||||
"error" => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,8 @@ $user = [
|
||||
];
|
||||
|
||||
$user = new Login($user, $facultad, $rol, $admin, $periodo);
|
||||
|
||||
session_start();
|
||||
if (isset($_SESSION))
|
||||
session_start();
|
||||
$_SESSION['user'] = serialize($user);
|
||||
|
||||
header("Location: ../main.php");
|
||||
|
||||
60
action/registro_supervisor.php
Normal file
60
action/registro_supervisor.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?
|
||||
#input $_GET['id_espacio_sgu']
|
||||
define("INFORMATION", [
|
||||
'POST' => [
|
||||
'profesor_id',
|
||||
'horario_id',
|
||||
'estado',
|
||||
'comentario',
|
||||
'supervisor_id',
|
||||
],
|
||||
]);
|
||||
#output rutas: [ ...ruta, salones: [{...salon}] ]
|
||||
header('Content-Type: application/json charset=utf-8');
|
||||
#return html
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
// check method
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// check parameters
|
||||
$raw = file_get_contents('php://input');
|
||||
$post_data = json_decode($raw, true);
|
||||
// if it's a list
|
||||
// step 1: get subrutas
|
||||
if (empty($post_data)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No hay clases pendientes']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = $db->query(
|
||||
'INSERT INTO registro (profesor_id, horario_id, registro_fecha_supervisor, estado_supervisor_id, registro_fecha_ideal, supervisor_id, comentario)
|
||||
VALUES' .
|
||||
implode(',', array_map(fn($x) => "({$x['profesor_id']} , {$x['horario_id']}, NOW()," . (is_null($x['estado']) ? 'null' : $x['estado']) . ", NOW(), {$x['supervisor_id']}," . (empty($x['comentario']) ? 'null' : "'{$x['comentario']}'") . ')', $post_data))
|
||||
. ' ON CONFLICT (profesor_id, horario_id, registro_fecha_ideal) DO UPDATE SET estado_supervisor_id = EXCLUDED.estado_supervisor_id, registro_fecha_supervisor = NOW(), comentario = EXCLUDED.comentario
|
||||
RETURNING *'
|
||||
);
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
} 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(),
|
||||
'post_data' => $post_data,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
} catch (Exception $th) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => $th->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
28
action/rutas.php
Normal file
28
action/rutas.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?
|
||||
header('Content-Type: application/json charset=utf-8');
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
|
||||
$universidad_la_salle = $db
|
||||
->where('salon', 'UNIVERSIDAD LA SALLE', 'ILIKE')
|
||||
->getOne('salon_view');
|
||||
|
||||
$rutas =
|
||||
array_map(
|
||||
function (&$ruta) use ($db) {
|
||||
$ruta['subrutas'] =
|
||||
$db
|
||||
->where('id_espacio_padre', $ruta['id_espacio_sgu'])
|
||||
->orderBy('salon')
|
||||
->get('salon_view');
|
||||
return $ruta;
|
||||
|
||||
},
|
||||
$db
|
||||
->where('id_espacio_padre', $universidad_la_salle['id_espacio_sgu'])
|
||||
->orderBy('salon')
|
||||
->get('salon_view')
|
||||
);
|
||||
|
||||
// echo json_encode($universidad_la_salle, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); EXIT;
|
||||
echo json_encode($rutas, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
73
action/rutas_salón_horario.php
Normal file
73
action/rutas_salón_horario.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?
|
||||
#input $_GET['id_espacio_sgu']
|
||||
$information = [
|
||||
'GET' => [
|
||||
'id_espacio_sgu',
|
||||
'bloque_horario_id',
|
||||
],
|
||||
];
|
||||
#output rutas: [ ...ruta, salones: [{...salon}] ]
|
||||
header('Content-Type: application/json charset=utf-8');
|
||||
$ruta = "../";
|
||||
require_once "../class/c_login.php";
|
||||
// check method
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// check parameters
|
||||
array_walk($information['GET'], function ($value) {
|
||||
if (!array_key_exists($value, $_GET)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => "$value is required"]);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
// step 1: get subrutas
|
||||
$data = $db
|
||||
->where('tiene_salones')
|
||||
->where("{$_GET['id_espacio_sgu']} = ANY(id_espacio_sgu_array)")
|
||||
->get('salon_view');
|
||||
|
||||
// step 3: get horarios
|
||||
$data = array_map(
|
||||
fn($ruta) => array_merge(
|
||||
[
|
||||
'horarios' => $db
|
||||
->join('periodo', 'periodo.periodo_id = horario_view.periodo_id')
|
||||
->join('bloque_horario', '(bloque_horario.hora_inicio, bloque_horario.hora_fin) OVERLAPS (horario_view.horario_hora, horario_view.horario_hora + horario_view.duracion)')
|
||||
->join('salon_view', 'salon_view.salon_id = horario_view.salon_id')
|
||||
->join('horario_profesor', 'horario_profesor.horario_id = horario_view.horario_id')
|
||||
->join('profesor', 'profesor.profesor_id = horario_profesor.profesor_id')
|
||||
->join('registro', '(registro.profesor_id, registro.horario_id, registro.registro_fecha_ideal) = (profesor.profesor_id, horario_view.horario_id, CURRENT_DATE)', 'LEFT')
|
||||
->where('CURRENT_DATE BETWEEN periodo.periodo_fecha_inicio AND periodo.periodo_fecha_fin')
|
||||
->where('horario_dia = EXTRACT(DOW FROM CURRENT_DATE)')
|
||||
->where('bloque_horario.id', $_GET['bloque_horario_id'])
|
||||
->where('id_espacio_padre', $ruta['id_espacio_sgu'])
|
||||
->get('horario_view', null, '*, horario_view.horario_id, profesor.profesor_id'),
|
||||
],
|
||||
$ruta
|
||||
),
|
||||
$data
|
||||
);
|
||||
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
} 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);
|
||||
exit;
|
||||
} catch (Exception $th) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'error' => $th->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
24
action/schemas/registro_supervisor.json
Normal file
24
action/schemas/registro_supervisor.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"profesor_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"horario_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"estado": {
|
||||
"type": ["integer", "null"]
|
||||
},
|
||||
"comentario": {
|
||||
"type": "string"
|
||||
},
|
||||
"supervisor_id": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": ["profesor_id", "horario_id", "comentario", "supervisor_id"]
|
||||
}
|
||||
}
|
||||
377
auditoría.php
Normal file
377
auditoría.php
Normal file
@@ -0,0 +1,377 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Supervisor</title>
|
||||
<?php
|
||||
include 'import/html_css_files.php';
|
||||
?>
|
||||
<style>
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?
|
||||
$redirect = $_SERVER['PHP_SELF'];
|
||||
include "import/html_header.php";
|
||||
global $user;
|
||||
html_header(
|
||||
"Registro de asistencia - Vicerrectoría Académica",
|
||||
"Sistema de gestión de checador",
|
||||
);
|
||||
|
||||
#include "import/periodo.php";
|
||||
?>
|
||||
|
||||
<main class="container-fluid px-4 mt-4" id="app" v-cloak @vue:mounted="mounted">
|
||||
<form action="">
|
||||
<div class="form-box">
|
||||
<div class="form-group row">
|
||||
<label for="periodo" class="col-4 col-form-label">Facultad</label>
|
||||
<div class="col-6">
|
||||
<div id="dlPeriodo" class="datalist datalist-select mb-1 w-100">
|
||||
<div class="datalist-input">Selecciona una facultad</div>
|
||||
<span class="ing-buscar icono"></span>
|
||||
<ul style="display:none">
|
||||
<li class="datalist-option" data-id="0" @click="store.filters.facultad_id = null;">
|
||||
Todas las facultades
|
||||
</li>
|
||||
<li class="datalist-option" v-for="facultad in store.facultades.data"
|
||||
:key="facultad.facultad_id" :data-id="facultad.facultad_id"
|
||||
@click="store.filters.facultad_id = facultad.facultad_id">
|
||||
(<small> {{facultad.clave_dependencia}} </small>) {{ facultad.facultad_nombre }}
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" id="facultad_id" name="id">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row align-items-center">
|
||||
<label for="switchFecha" class="col-4 col-form-label">
|
||||
Fecha
|
||||
<!-- switch -->
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="switchFecha"
|
||||
v-model="store.filters.switchFecha" @input="store.filters.switchFechas">
|
||||
<label class="custom-control-label" for="switchFecha"></label>
|
||||
</div>
|
||||
</label>
|
||||
<div class="col-3" v-if="store.filters.switchFecha">
|
||||
<div class="form-row">
|
||||
<input id="fecha_inicio" name="fecha_inicio" class="form-control date-picker"
|
||||
placeholder="Seleccione una fecha de inicio" readonly
|
||||
v-model="store.filters.fecha_inicio">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3" v-if="store.filters.switchFecha">
|
||||
<div class="form-row">
|
||||
<input id="fecha_fin" name="fecha_fin" class="form-control date-picker"
|
||||
placeholder="Seleccione una fecha final" readonly v-model="store.filters.fecha_fin">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6" v-if="!store.filters.switchFecha">
|
||||
<div class="form-row">
|
||||
<input id="fecha" name="fecha" class="form-control date-picker"
|
||||
placeholder="Seleccione una fecha" readonly v-model="store.filters.fecha">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="profesor" class="col-4 col-form-label">Profesor</label>
|
||||
<div class="col-6">
|
||||
<div class="form-row justify-content-around align-items-center">
|
||||
<input id="profesor" name="profesor" class="form-control col-11 mr-1 px-2"
|
||||
placeholder="Seleccione una profesor" list="dlProfesor"
|
||||
v-model="store.filters.profesor">
|
||||
<button type="button" class="btn btn-info btn-sm form-control col ml-auto"
|
||||
@click="store.filters.profesor = null">
|
||||
<i class="ing-borrar"></i>
|
||||
</button>
|
||||
</div>
|
||||
<datalist id="dlProfesor">
|
||||
<option v-for="profesor in profesores" :key="profesor.profesor_id"
|
||||
:value="`(${profesor.profesor_clave}) ${profesor.profesor_nombre}`">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="periodo" class="col-4 col-form-label">Asistencia</label>
|
||||
<div class="col-6">
|
||||
<div class="form-row justify-content-around align-items-center">
|
||||
<div id="dlPeriodo" class="datalist datalist-select mb-1 w-100">
|
||||
<div class="datalist-input" id="estados">Selecciona un estado de asistencia</div>
|
||||
<span class="ing-buscar icono"></span>
|
||||
<ul style="display:none">
|
||||
<li class="datalist-option" data-id="0" @click="store.filters.estados = [];">
|
||||
Todos los registros
|
||||
</li>
|
||||
<li class="datalist-option" v-for="estado in store.estados.data"
|
||||
:key="estado.estado_supervisor_id" :data-id="estado.estado_supervisor_id"
|
||||
@click="store.filters.estados = store.toggle(store.filters.estados, estado.estado_supervisor_id); ; setTimeout(store.estados.printEstados, 0);">
|
||||
<span class="badge"
|
||||
:class="`badge-${store.filters.estados.includes(estado.estado_supervisor_id) ? 'dark' : estado.estado_color}`"><i
|
||||
:class="estado.estado_icon"></i> {{estado.nombre}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" id="estado_id" name="estado_id">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<div class="mt-3 d-flex justify-content-center flex-wrap">
|
||||
<!-- refresh -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped table-bordered table-sm">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col" class="text-center align-middle px-2">
|
||||
<button @click="registros.invertir" class="btn btn-info mr-3"
|
||||
v-if="registros.relevant.length > 1">
|
||||
<i class="ing-cambiar ing-rotate-90"></i>
|
||||
</button>
|
||||
Fecha
|
||||
</th>
|
||||
<th scope="col" class="text-center align-middle px-2">Salón</th>
|
||||
<th scope="col" class="text-center align-middle px-2">Profesor</th>
|
||||
|
||||
<th scope="col" class="text-center align-middle px-2">Horario</th>
|
||||
<th scope="col" class="text-center align-middle px-2">Registro</th>
|
||||
<th scope="col" class="text-center align-middle px-2">Supervisor</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="registros.relevant.length == 0">
|
||||
<td colspan="7" class="text-center">No hay clases en este horario</td>
|
||||
</tr>
|
||||
<tr v-for="registro in registros.relevant" :key="registro.registro_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="text-center align-middle px-2">
|
||||
<div class="col-12">
|
||||
<strong>{{ registro.profesor_clave }}</strong>
|
||||
{{ registro.profesor_nombre }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="button" class="btn btn-outline-dark btn-sm"
|
||||
@click="store.current.clase_vista = registro" data-toggle="modal"
|
||||
data-target="#ver-detalle">
|
||||
Ver detalle <i class="ing-ojo"></i>
|
||||
</button>
|
||||
</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">
|
||||
<div v-if="registro.registro_fecha">
|
||||
<div class="col-12">
|
||||
Registro <small>{{ registro.registro_fecha.slice(11,16) }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<strong>
|
||||
<div class="col-12">
|
||||
<span class="badge badge-danger"><i class="ing-cancelar"></i></span>
|
||||
</div>
|
||||
<div class="col-12 mt-2">
|
||||
Sin registro
|
||||
</div>
|
||||
</strong>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Sí checó supervisor -->
|
||||
<td class="text-center align-middle px-2">
|
||||
<div v-if="registro.registro_fecha_supervisor">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<strong>{{ registro.usuario_nombre }}</strong>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
Hora
|
||||
<small>{{ registro.registro_fecha_supervisor.slice(11,19) }}</small>
|
||||
</div>
|
||||
<div class="col-12 mt-2">
|
||||
<span class="badge" :class="`badge-${registro.estado_color}`">
|
||||
<i :class="`${registro.estado_icon}`"></i>
|
||||
<strong>{{ registro.nombre }}</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- comentario -->
|
||||
<hr v-if="registro.comentario">
|
||||
<div class="col-12 " @click="registros.mostrarComentario(registro.registro_id)"
|
||||
v-if="registro.comentario" style="cursor: pointer;">
|
||||
<strong class="badge badge-primary">Observaciones:</strong>
|
||||
<small class="text-truncate">{{registro.comentario.slice(0,
|
||||
25)}}{{registro.comentario.length > 10 ? '...' : ''}}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- No checó -->
|
||||
<div v-else>
|
||||
<div class="col-12">
|
||||
<span class="badge badge-danger"><i class="ing-cancelar"></i></span>
|
||||
</div>
|
||||
<div class="col-12 mt-2">
|
||||
<strong>Sin registro</strong>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal" tabindex="-1" id="ver-comentario">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Comentario</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="input-group">
|
||||
<textarea class="form-control" aria-label="Comentarios de la clase" rows="5"
|
||||
v-model="store.current.comentario" disabled></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-primary" data-dismiss="modal">
|
||||
Aceptar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</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 }}
|
||||
</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 }}
|
||||
</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>
|
||||
<!-- hora hh:mm:ss to hh:mm -->
|
||||
{{ 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 }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="row">
|
||||
<section class="col-12">
|
||||
<h4 class="h4 mt-4">Registro</h4>
|
||||
<div class="row">
|
||||
<div class="col-12 text-center" v-if="!clase_vista.registro_fecha">
|
||||
<strong><span class="badge badge-danger"><i class="ing-cancelar"></i></span>
|
||||
El profesor aún no ha registrado su asistencia</strong>
|
||||
</div>
|
||||
<div class="col-6 text-center" v-else>
|
||||
El profesor registró su asistencia a las
|
||||
<code>{{clase_vista.registro_fecha.slice(11, 16)}}</code>
|
||||
<hr>
|
||||
<p v-if="!clase_vista.registro_retardo" class="text-center">
|
||||
<span class="badge badge-success"><i class="ing-aceptar"></i></span>
|
||||
A tiempo
|
||||
</p>
|
||||
<p v-else class="text-center">
|
||||
<span class="badge badge-warning"><i class="ing-retardo"></i></span>
|
||||
Con retardo
|
||||
</p>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/jquery-ui.js"></script>
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
<script src="js/datalist.js"></script>
|
||||
<script src="js/datepicker-es.js"></script>
|
||||
<script src="js/auditoría.js" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
require_once 'class/c_login.php';
|
||||
if (isset($_GET["error"]) && is_numeric($_GET["error"])) {
|
||||
switch ($_GET["error"]) {
|
||||
case 0:
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
<?php
|
||||
require_once ($ruta ?? '') . "include/bd_pdo.php";
|
||||
require_once ($ruta ?? '') . "class/c_logasistencia.php";
|
||||
require_once ($ruta ?? '') . "include/nusoap/nusoap.php";
|
||||
date_default_timezone_set('America/Mexico_City');
|
||||
$currentTime = time();
|
||||
$endOfDay = strtotime('tomorrow') - 1;
|
||||
$remainingTime = $endOfDay - $currentTime;
|
||||
|
||||
session_set_cookie_params($remainingTime, '/', $_SERVER['HTTP_HOST'], false, true);
|
||||
|
||||
require_once($ruta ?? '') . "include/bd_pdo.php";
|
||||
require_once($ruta ?? '') . "class/c_logasistencia.php";
|
||||
require_once($ruta ?? '') . "include/nusoap/nusoap.php";
|
||||
|
||||
session_start();
|
||||
class Login
|
||||
{
|
||||
public string $acceso;
|
||||
public function __construct(public array $user, public array $facultad, public array $rol, public bool $admin, public ?int $periodo)
|
||||
public function __construct(public array $user, public array $facultad, public array $rol, public bool $admin, public ?int $periodo, public bool $supervisor, public bool $jefe_carrera, public bool $profesor)
|
||||
{
|
||||
}
|
||||
public function print_to_log(string $desc, array $old = null, array $new = null): void
|
||||
{
|
||||
$log = new classes\LogAsistencias($_ENV["RUTA_RAIZ"]);
|
||||
if ($old) $desc .= " |#| OLD:" . json_encode($old);
|
||||
if ($new) $desc .= " |#| NEW:" . json_encode($new);
|
||||
if ($old)
|
||||
$desc .= " |#| OLD:" . json_encode($old);
|
||||
if ($new)
|
||||
$desc .= " |#| NEW:" . json_encode($new);
|
||||
$log->appendLog($this->user["id"], $this->user["nombre"], $desc);
|
||||
}
|
||||
public function access(string $pagina = null): void
|
||||
@@ -25,59 +34,93 @@ class Login
|
||||
}
|
||||
|
||||
# print_r( $access );
|
||||
$this->acceso = query('SELECT tipo FROM PERMISO_VIEW WHERE ID = :usr AND PAGINA_RUTA ILIKE :ruta', array(
|
||||
':usr' => $this->user["id"],
|
||||
':ruta' => $pagina ?? substr(basename($_SERVER['PHP_SELF']), 0, -4)
|
||||
))["tipo"] ?? 'n';
|
||||
$this->acceso = query(
|
||||
'SELECT tipo FROM PERMISO_VIEW WHERE ID = :usr AND PAGINA_RUTA ILIKE :ruta',
|
||||
array(
|
||||
':usr' => $this->user["id"],
|
||||
':ruta' => $pagina ?? substr(basename($_SERVER['PHP_SELF']), 0, -4)
|
||||
)
|
||||
)["tipo"] ?? 'n';
|
||||
}
|
||||
public function __toString(): string
|
||||
{
|
||||
return "Usuario: {$this->user["nombre"]} ({$this->user["id"]})";
|
||||
return "Usuario: {$this->user["nombre"]} ({$this->user["id"]}), Es admin: {$this->admin}, supervisor: {$this->supervisor}, jefe carrera: {$this->jefe_carrera}, profesor: {$this->profesor}";
|
||||
}
|
||||
private static function validaUsuario($user, $pass): bool
|
||||
{
|
||||
file_put_contents('php://stderr', $user);
|
||||
if (in_array($user, ['ad012821']) and $pass == "admin") return true;
|
||||
if (in_array($user, ['ad017045']) and $pass == "admin") return true ;
|
||||
if (in_array($user, ['ad017045']) and $pass == "admin")
|
||||
return true;
|
||||
$client = new nusoap_client('http://200.13.89.2/validacion.php?wsdl', 'wsdl');
|
||||
$error = $client->getError();
|
||||
|
||||
if ($error) return false;
|
||||
|
||||
$client->getError() and die('Error al crear el cliente: ' . $client->getError());
|
||||
$pass = utf8_decode($pass);
|
||||
$result = $client->call("valida_user", array($user, $pass));
|
||||
|
||||
if ($client->fault) return false;
|
||||
|
||||
$client->fault and die('Error al llamar al servicio: ' . $client->getError());
|
||||
return $result;
|
||||
}
|
||||
public static function validUser(string $user, string $pass): Login | false
|
||||
public static function validUser(string $user, string $pass): Login|array
|
||||
{
|
||||
$fs_validaclaveulsa = query(
|
||||
'SELECT * FROM FS_VALIDACLAVEULSA(:usr)', [':usr' => $user]
|
||||
);
|
||||
if (!Login::validaUsuario($user, $pass)) {
|
||||
return [
|
||||
'error' => true,
|
||||
'msg' => 'Error al autenticar usuario'
|
||||
];
|
||||
}
|
||||
global $db;
|
||||
|
||||
if (empty($fs_validaclaveulsa["id"])) return false;
|
||||
#die (Login::validaUsuario($user, $pass));
|
||||
if (!Login::validaUsuario($user, $pass)) return false;
|
||||
if ($db->has("FS_VALIDACLAVEULSA('$user')")) {
|
||||
#die (Login::validaUsuario($user, $pass));
|
||||
$fs_validaclaveulsa = $db->querySingle(
|
||||
'SELECT * FROM FS_VALIDACLAVEULSA(?)',
|
||||
[$user]
|
||||
);
|
||||
|
||||
$user = array(
|
||||
'id' => $fs_validaclaveulsa["id"],
|
||||
'nombre' => $fs_validaclaveulsa["nombre"],
|
||||
);
|
||||
$facultades = query("SELECT FACULTAD_ID id, FACULTAD f FROM FS_PERIODO WHERE ID = :id", [':id' => $fs_validaclaveulsa["periodo_id"]]);
|
||||
$facultad = array(
|
||||
'facultad_id' => $fs_validaclaveulsa["facultad_id"] ?? $facultades["id"],
|
||||
'facultad' => $fs_validaclaveulsa["facultad"] ?? $facultades["f"],
|
||||
);
|
||||
$rol = array(
|
||||
'id' => $fs_validaclaveulsa["rol_id"],
|
||||
'rol' => $fs_validaclaveulsa["rol"]
|
||||
);
|
||||
$user = array(
|
||||
'id' => $fs_validaclaveulsa["id"],
|
||||
'nombre' => $fs_validaclaveulsa["nombre"],
|
||||
);
|
||||
$facultad = array(
|
||||
'facultad_id' => $fs_validaclaveulsa["facultad_id"],
|
||||
'facultad' => $fs_validaclaveulsa["facultad"],
|
||||
);
|
||||
$rol = array(
|
||||
'id' => $fs_validaclaveulsa["rol_id"],
|
||||
'rol' => $fs_validaclaveulsa["rol"]
|
||||
);
|
||||
$supervisor = $db
|
||||
->join('rol', 'rol.rol_id = usuario.rol_id')
|
||||
->where('usuario_id', $user["id"])
|
||||
->where('rol.rol_titulo', 'Supervisor')
|
||||
->has('usuario');
|
||||
$jefe_carrera = $db->where('usuario_id', $user["id"])->has('usuario_carrera');
|
||||
|
||||
$admin = $fs_validaclaveulsa["is_admin"];
|
||||
$periodo = $fs_validaclaveulsa["periodo_id"];
|
||||
return new Login($user, $facultad, $rol, $admin, $periodo);
|
||||
$admin = $fs_validaclaveulsa["is_admin"];
|
||||
$periodo = $fs_validaclaveulsa["periodo_id"];
|
||||
|
||||
return new Login($user, $facultad, $rol, $admin, $periodo, $supervisor, $jefe_carrera, false);
|
||||
} else if ($db->where('profesor_clave', preg_replace('/^do0*/', '', $user))->has("profesor")) {
|
||||
$profesor = $db->where('profesor_clave', preg_replace('/^do0*/', '', $user))->getOne("profesor");
|
||||
$user = array(
|
||||
'id' => $profesor["profesor_clave"],
|
||||
'nombre' => $profesor["profesor_nombre"],
|
||||
);
|
||||
$facultad = $rol = array(
|
||||
'facultad_id' => null,
|
||||
'facultad' => 'Docente',
|
||||
);
|
||||
|
||||
$supervisor = false;
|
||||
$jefe_carrera = false;
|
||||
$admin = false;
|
||||
$periodo = null;
|
||||
// CREATE A COOKIE FOR THE REST OF THE day for example: 23:00 then duration will be 1 hour
|
||||
setcookie("profesor", $user["id"], strtotime('today midnight') + 86400, "/");
|
||||
return new Login($user, $facultad, $rol, $admin, $periodo, $supervisor, $jefe_carrera, true);
|
||||
} else
|
||||
return [
|
||||
'error' => true,
|
||||
'msg' => 'Usuario no encontrado'
|
||||
];
|
||||
}
|
||||
public static function log_out(): void
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"require": {
|
||||
"vlucas/phpdotenv": "^5.5",
|
||||
"phpoffice/phpspreadsheet": "^1.25",
|
||||
"seinopsys/postgresql-database-class": "^3.1"
|
||||
"seinopsys/postgresql-database-class": "^3.1",
|
||||
"justinrainbow/json-schema": "^5.2"
|
||||
}
|
||||
}
|
||||
|
||||
72
composer.lock
generated
72
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5e701c768afe8ce8feabe1b539fa7234",
|
||||
"content-hash": "2b67052b0f31b7059a262343c2640316",
|
||||
"packages": [
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
@@ -129,6 +129,76 @@
|
||||
],
|
||||
"time": "2022-07-30T15:56:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "justinrainbow/json-schema",
|
||||
"version": "5.2.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
|
||||
"json-schema/json-schema-test-suite": "1.2.0",
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"bin": [
|
||||
"bin/validate-json"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"JsonSchema\\": "src/JsonSchema/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Bruno Prieto Reis",
|
||||
"email": "bruno.p.reis@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Justin Rainbow",
|
||||
"email": "justin.rainbow@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Igor Wiedler",
|
||||
"email": "igor@wiedler.ch"
|
||||
},
|
||||
{
|
||||
"name": "Robert Schönthal",
|
||||
"email": "seroscho@googlemail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library to validate a json schema.",
|
||||
"homepage": "https://github.com/justinrainbow/json-schema",
|
||||
"keywords": [
|
||||
"json",
|
||||
"schema"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/justinrainbow/json-schema/issues",
|
||||
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.12"
|
||||
},
|
||||
"time": "2022-04-13T08:02:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maennchen/zipstream-php",
|
||||
"version": "2.2.1",
|
||||
|
||||
@@ -17,7 +17,9 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Consultar horario | <?= $user->facultad['facultad'] ?? 'General' ?></title>
|
||||
<title>Consultar horario |
|
||||
<?= $user->facultad['facultad'] ?? 'General' ?>
|
||||
</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="content-type" content="text/plain; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
@@ -61,11 +63,11 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<ul style="display:none">
|
||||
<?php
|
||||
foreach ($carreras as $carrera) {
|
||||
?>
|
||||
?>
|
||||
<li data-id="<?= $carrera['id'] ?>">
|
||||
<?= $carrera['carrera'] ?>
|
||||
</li>
|
||||
<?php
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
@@ -91,7 +93,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
|
||||
<div class="form-group mt-4 row justify-content-center">
|
||||
<?php if ($write) { ?>
|
||||
<button type="button" id="nuevo" class="btn btn-outline-primary ml-4 d-none" title="Nuevo horario" data-toggle="modal" data-target="#modal-editar">
|
||||
<button type="button" id="nuevo" class="btn btn-outline-primary ml-4 d-none"
|
||||
title="Nuevo horario" data-toggle="modal" data-target="#modal-editar">
|
||||
<span class="ing-mas ing-fw"></span> Nuevo
|
||||
</button>
|
||||
<?php } ?>
|
||||
@@ -127,7 +130,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modal-editar" tabindex="-1" aria-labelledby="modal-editar" aria-hidden="true" data-backdrop="static" data-keyboard="false">
|
||||
<div class="modal fade" id="modal-editar" tabindex="-1" aria-labelledby="modal-editar" aria-hidden="true"
|
||||
data-backdrop="static" data-keyboard="false">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@@ -158,7 +162,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<div class="form-grupo row mb-3">
|
||||
<div class="col-4"></div>
|
||||
<div class="col-6">
|
||||
<input type="text" id="grupo" name="grupo" value="" class="form-control" placeholder="Grupo" required="required" hidden>
|
||||
<input type="text" id="grupo" name="grupo" value="" class="form-control"
|
||||
placeholder="Grupo" required="required" hidden>
|
||||
<div class="invalid-feedback">
|
||||
Por favor, ingrese un grupo.
|
||||
</div>
|
||||
@@ -167,7 +172,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<div class="form-group row">
|
||||
<label for="materia" class="col-4 col-form-label">Materia</label>
|
||||
<div class="col-6">
|
||||
<input list="lista_materias" name="dlMateria" id="dlMateria" class="form-control text-center" placeholder="Materia" required="required">
|
||||
<input list="lista_materias" name="dlMateria" id="dlMateria"
|
||||
class="form-control text-center" placeholder="Materia" required="required">
|
||||
<datalist id="lista_materias"></datalist>
|
||||
<input type="hidden" id="materia" name="materia" value="">
|
||||
<div class="invalid-feedback">
|
||||
@@ -196,7 +202,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<span class="ing-buscar icono"></span>
|
||||
<ul style="display:none">
|
||||
<?php foreach (range(0, 45, 15) as $minuto) { ?>
|
||||
<li data-id='<?= $minuto ?>'><?= str_pad($minuto, 2, "0", STR_PAD_LEFT) ?></li>
|
||||
<li data-id='<?= $minuto ?>'><?= str_pad($minuto, 2, "0", STR_PAD_LEFT) ?>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<input type="hidden" id="selector_minutos" name="minutos" value="">
|
||||
@@ -238,9 +245,10 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
$nombre = $duración['duracion_nombre'];
|
||||
$id = $duración['duracion_id'];
|
||||
$bloques = $duración['duracion_bloques'];
|
||||
?>
|
||||
<li data-id="<?= $id; ?>" data-bloques="<?= $bloques; ?>"><?= $nombre; ?></li>
|
||||
<?php
|
||||
?>
|
||||
<li data-id="<?= $id; ?>" data-bloques="<?= $bloques; ?>"><?= $nombre; ?>
|
||||
</li>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
@@ -256,7 +264,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<div class="form-group row">
|
||||
<label for="editor_profesor" class="col-4 col-form-label">Profesor</label>
|
||||
<div class="col-6">
|
||||
<input list="lista_profesores" name="dlProfesor" id="dlProfesor" class="form-control" placeholder="Profesor" required="required">
|
||||
<input list="lista_profesores" name="dlProfesor" id="dlProfesor"
|
||||
class="form-control" placeholder="Profesor" required="required">
|
||||
<div class="valid-feedback">
|
||||
Profesor encontrado
|
||||
</div>
|
||||
@@ -265,11 +274,10 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
</div>
|
||||
<datalist id="lista_profesores">
|
||||
<?php
|
||||
$profesores = $db->where('facultad_id', $user->facultad['facultad_id'])->get("fs_profesor");
|
||||
$profesores = $db->get("profesor");
|
||||
foreach ($profesores as $profesor) {
|
||||
extract($profesor);
|
||||
?>
|
||||
<option data-grado="<?= $grado ?>" data-clave="<?= $clave ?>" data-profesor="<?= $profesor ?>" data-id="<?= $id; ?>" value="<?= "$clave | $grado $profesor" ?>"></option>
|
||||
<option data-clave="<?= $profesor['profesor_clave'] ?>" data-profesor="<?= $profesor['profesor_nombre'] ?>" data-id="<?= $id; ?>" value="<?= "{$profesor['profesor_clave']} | {$profesor['profesor_grado']} {$profesor['profesor_nombre']}" ?>"></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
@@ -283,7 +291,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<div class="form-group row">
|
||||
<label for="editor_salón" class="col-4 col-form-label">Salón</label>
|
||||
<div class="col-6">
|
||||
<input type="text" class="form-control" id="editor_salón" name="salón" placeholder="Salón" maxlength="100" required="required">
|
||||
<input type="text" class="form-control" id="editor_salón" name="salón"
|
||||
placeholder="Salón" maxlength="100" required="required">
|
||||
<div class="invalid-feedback">
|
||||
El salón no puede estar vacío.
|
||||
</div>
|
||||
@@ -292,7 +301,8 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button data-id="" type="button" class="btn btn-primary" id="btn-guardar"><i class="ing-guardar ing"></i> Guardar</button>
|
||||
<button data-id="" type="button" class="btn btn-primary" id="btn-guardar"><i
|
||||
class="ing-guardar ing"></i> Guardar</button>
|
||||
<button type="button" class="btn btn-outline-primary" data-dismiss="modal">Cancelar</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -425,8 +435,8 @@ require_once("import/html_footer.php");
|
||||
|
||||
function compareHours(hora1, hora2) {
|
||||
// parseInt each hour and minute
|
||||
const [h1, m1, ] = hora1.split(":").map(x => parseInt(x));
|
||||
const [h2, m2, ] = hora2.split(":").map(x => parseInt(x));
|
||||
const [h1, m1,] = hora1.split(":").map(x => parseInt(x));
|
||||
const [h2, m2,] = hora2.split(":").map(x => parseInt(x));
|
||||
|
||||
if (h1 > h2)
|
||||
return 1;
|
||||
@@ -588,7 +598,7 @@ require_once("import/html_footer.php");
|
||||
<b class="title">${materia}</b> <br>
|
||||
<br><span>Salón: </span>${salon} <br>
|
||||
<small class="my-2">
|
||||
${profesores.map(({grado, profesor}) => /*html*/ ` <span class="ing ing-formacion mx-1"></span>${grado ?? ''} ${profesor}`).join("<br>")}
|
||||
${profesores.map(({ grado, profesor }) => /*html*/ ` <span class="ing ing-formacion mx-1"></span>${grado ?? ''} ${profesor}`).join("<br>")}
|
||||
</small>
|
||||
</div>
|
||||
${edit && float_menu}`
|
||||
@@ -712,8 +722,8 @@ require_once("import/html_footer.php");
|
||||
}
|
||||
|
||||
const conflictBlocks = horarios.filter((horario, index, arrayHorario) =>
|
||||
arrayHorario.filter((_, i) => i != index).some(horario2 =>
|
||||
conflicts(horario, horario2)))
|
||||
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));
|
||||
@@ -749,7 +759,7 @@ require_once("import/html_footer.php");
|
||||
|
||||
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;
|
||||
(hora_id < min_hour || hora_id > max_hour) ? hora.remove() : null;
|
||||
})
|
||||
|
||||
// if there is no sábado, remove the column
|
||||
@@ -788,7 +798,7 @@ require_once("import/html_footer.php");
|
||||
|
||||
// droppables
|
||||
// forall the .bloque-elements add the event listeners for drag and drop
|
||||
<?php if ($write) : ?>
|
||||
<?php if ($write): ?>
|
||||
document.querySelectorAll(".bloque-clase").forEach(element => {
|
||||
function dragStart() {
|
||||
this.classList.add("dragging");
|
||||
@@ -802,69 +812,69 @@ require_once("import/html_footer.php");
|
||||
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");
|
||||
// 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 = /* for data-ids */ dragging.getAttribute("data-ids");
|
||||
const hora = /* for data-hora */ this.id.split("-")[1];
|
||||
const días = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"];
|
||||
let día = /* for data-dia */ 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;
|
||||
}
|
||||
|
||||
function dragLeave() {
|
||||
this.classList.remove("dragging-over");
|
||||
// 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);
|
||||
}
|
||||
|
||||
function drop() {
|
||||
this.classList.remove("dragging-over");
|
||||
const dragging = document.querySelector(".dragging");
|
||||
|
||||
const id = /* for data-ids */ dragging.getAttribute("data-ids");
|
||||
const hora = /* for data-hora */ this.id.split("-")[1];
|
||||
const días = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"];
|
||||
let día = /* for data-dia */ 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 = `<div class="spinner-border" role="status" style="width: 3rem; height: 3rem;">
|
||||
// prepend a loading child
|
||||
const loading = `<div class="spinner-border" role="status" style="width: 3rem; height: 3rem;">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>`;
|
||||
bloque.insertAdjacentHTML("afterbegin", loading);
|
||||
// add style vertical-align: middle
|
||||
bloque.style.verticalAlign = "middle";
|
||||
bloque.classList.add("text-center");
|
||||
// remove draggable
|
||||
bloque.removeAttribute("draggable");
|
||||
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);
|
||||
}
|
||||
moveHorario(id, día, hora);
|
||||
}
|
||||
|
||||
element.addEventListener("dragover", dragOver);
|
||||
element.addEventListener("dragleave", dragLeave);
|
||||
element.addEventListener("drop", drop);
|
||||
});
|
||||
element.addEventListener("dragover", dragOver);
|
||||
element.addEventListener("dragleave", dragLeave);
|
||||
element.addEventListener("drop", drop);
|
||||
});
|
||||
<?php endif; ?>
|
||||
}
|
||||
async function guardar(id) {
|
||||
@@ -980,7 +990,6 @@ require_once("import/html_footer.php");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function guardarHorario() {
|
||||
let goBack = false;
|
||||
const data = {
|
||||
@@ -1111,7 +1120,6 @@ require_once("import/html_footer.php");
|
||||
|
||||
// buscarGrupo();
|
||||
}
|
||||
|
||||
function moveHorario(id, día, hora) {
|
||||
|
||||
const formData = new FormData();
|
||||
@@ -1136,7 +1144,6 @@ require_once("import/html_footer.php");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function extractFromModal() {
|
||||
// remove all is-valid and is-invalid
|
||||
document.querySelectorAll(".is-valid, .is-invalid").forEach(el =>
|
||||
@@ -1213,7 +1220,6 @@ require_once("import/html_footer.php");
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
function resetFormModal() {
|
||||
const modalNuevo = document.querySelector("#modal-editar");
|
||||
modalNuevo.querySelectorAll("input").forEach(input => input.value = "");
|
||||
@@ -1231,7 +1237,6 @@ require_once("import/html_footer.php");
|
||||
// remove bg-info
|
||||
modalNuevo.querySelectorAll(".bg-info").forEach(el => el.classList.remove("bg-info"));
|
||||
}
|
||||
|
||||
function insertHorario(horario) {
|
||||
const fetchOptions = {
|
||||
method: "POST",
|
||||
@@ -1256,7 +1261,6 @@ require_once("import/html_footer.php");
|
||||
triggerMessage(err, "Error");
|
||||
});
|
||||
}
|
||||
|
||||
// initial state
|
||||
{
|
||||
// fill the table with empty cells
|
||||
@@ -1289,7 +1293,7 @@ require_once("import/html_footer.php");
|
||||
// query selector All tds and ths inside the tbody#horario
|
||||
// previous query selector: "tbody#horario td, tbody#horario tr"
|
||||
document.querySelectorAll("tbody#horario td, tbody#horario tr").forEach(element => element.style.height = "2.5rem");
|
||||
document.getElementById('dlProfesor').addEventListener('input', function(e) {
|
||||
document.getElementById('dlProfesor').addEventListener('input', function (e) {
|
||||
var input = document.getElementById('dlProfesor');
|
||||
var value = input.value;
|
||||
var option = document.querySelector(`option[value="${value}"]`);
|
||||
@@ -1318,7 +1322,7 @@ require_once("import/html_footer.php");
|
||||
});
|
||||
e.target.value = "";
|
||||
});
|
||||
document.getElementById('dlMateria').addEventListener('input', function(e) {
|
||||
document.getElementById('dlMateria').addEventListener('input', function (e) {
|
||||
var input = document.getElementById('dlMateria');
|
||||
var value = input.value;
|
||||
var option = document.querySelector(`option[value="${value}"]`);
|
||||
@@ -1373,9 +1377,9 @@ require_once("import/html_footer.php");
|
||||
}, 0));
|
||||
|
||||
fetch("export/horario_excel.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.blob())
|
||||
.then(blob => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
@@ -1479,7 +1483,7 @@ require_once("import/html_footer.php");
|
||||
|
||||
})
|
||||
// on modal edit, show the data
|
||||
$("#modal-editar").on("show.bs.modal", async function(event) {
|
||||
$("#modal-editar").on("show.bs.modal", async function (event) {
|
||||
document.querySelectorAll("#modal-editar .is-invalid, #modal-editar .is-valid")?.forEach(element => element.classList.remove("is-invalid", "is-valid"));
|
||||
|
||||
|
||||
@@ -1597,7 +1601,7 @@ require_once("import/html_footer.php");
|
||||
backdrop: "static",
|
||||
keyboard: false,
|
||||
})
|
||||
$("#modal-borrar").on("show.bs.modal", async function(event) {
|
||||
$("#modal-borrar").on("show.bs.modal", async function (event) {
|
||||
const button = event.relatedTarget;
|
||||
const id = button.parentElement.parentElement.getAttribute("data-ids");
|
||||
|
||||
|
||||
18
css/sgi.css
18
css/sgi.css
@@ -1054,3 +1054,21 @@ footer .tab-pane p {
|
||||
align-items: center;
|
||||
}*/
|
||||
}
|
||||
|
||||
.movie {
|
||||
transition: all 0.1s;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.movie:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.movie:active {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -5,3 +5,95 @@
|
||||
.azul {
|
||||
color: #00a6CE;
|
||||
}
|
||||
|
||||
/* for sm */
|
||||
|
||||
.custom-switch.custom-switch-sm .custom-control-label {
|
||||
padding-left: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-sm .custom-control-label::before {
|
||||
height: 1rem;
|
||||
width: calc(1rem + 0.75rem);
|
||||
border-radius: 2rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-sm .custom-control-label::after {
|
||||
width: calc(1rem - 4px);
|
||||
height: calc(1rem - 4px);
|
||||
border-radius: calc(1rem - (1rem / 2));
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-sm .custom-control-input:checked ~ .custom-control-label::after {
|
||||
transform: translateX(calc(1rem - 0.25rem));
|
||||
}
|
||||
|
||||
/* for md */
|
||||
|
||||
.custom-switch.custom-switch-md .custom-control-label {
|
||||
padding-left: 2rem;
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-md .custom-control-label::before {
|
||||
height: 1.5rem;
|
||||
width: calc(2rem + 0.75rem);
|
||||
border-radius: 3rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-md .custom-control-label::after {
|
||||
width: calc(1.5rem - 4px);
|
||||
height: calc(1.5rem - 4px);
|
||||
border-radius: calc(2rem - (1.5rem / 2));
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-md .custom-control-input:checked ~ .custom-control-label::after {
|
||||
transform: translateX(calc(1.5rem - 0.25rem));
|
||||
}
|
||||
|
||||
/* for lg */
|
||||
|
||||
.custom-switch.custom-switch-lg .custom-control-label {
|
||||
padding-left: 3rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-lg .custom-control-label::before {
|
||||
height: 2rem;
|
||||
width: calc(3rem + 0.75rem);
|
||||
border-radius: 4rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-lg .custom-control-label::after {
|
||||
width: calc(2rem - 4px);
|
||||
height: calc(2rem - 4px);
|
||||
border-radius: calc(3rem - (2rem / 2));
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-lg .custom-control-input:checked ~ .custom-control-label::after {
|
||||
transform: translateX(calc(2rem - 0.25rem));
|
||||
}
|
||||
|
||||
/* for xl */
|
||||
|
||||
.custom-switch.custom-switch-xl .custom-control-label {
|
||||
padding-left: 4rem;
|
||||
padding-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-xl .custom-control-label::before {
|
||||
height: 2.5rem;
|
||||
width: calc(4rem + 0.75rem);
|
||||
border-radius: 5rem;
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-xl .custom-control-label::after {
|
||||
width: calc(2.5rem - 4px);
|
||||
height: calc(2.5rem - 4px);
|
||||
border-radius: calc(4rem - (2.5rem / 2));
|
||||
}
|
||||
|
||||
.custom-switch.custom-switch-xl .custom-control-input:checked ~ .custom-control-label::after {
|
||||
transform: translateX(calc(2.5rem - 0.25rem));
|
||||
}
|
||||
235
demo.html
Normal file
235
demo.html
Normal file
@@ -0,0 +1,235 @@
|
||||
<link rel="stylesheet" href="css/indivisa.css">
|
||||
<p class="container">
|
||||
<i class="ing-fb1"></i> ing-fb1
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-fb2"></i> ing-fb2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-tw1"></i> ing-tw1
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-tw2"></i> ing-tw2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-in1"></i> ing-in1
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-in2"></i> ing-in2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-instra1"></i> ing-instra1
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-instra2"></i> ing-instra2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-youtube"></i> ing-youtube
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-telefono"></i> ing-telefono
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-mail"></i> ing-mail
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-link"></i> ing-link
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-ubicacion"></i> ing-ubicacion
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-puntos"></i> ing-puntos
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-usuario"></i> ing-usuario
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-pass"></i> ing-pass
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-menu"></i> ing-menu
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-salir"></i> ing-salir
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-flecha"></i> ing-flecha
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-cambiar"></i> ing-cambiar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-caret"></i> ing-caret
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-aceptar"></i> ing-aceptar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-cancelar"></i> ing-cancelar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-mas"></i> ing-mas
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-menos"></i> ing-menos
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-editar"></i> ing-editar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-buscar"></i> ing-buscar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-ojo"></i> ing-ojo
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-borrar"></i> ing-borrar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-basura"></i> ing-basura
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-camara"></i> ing-camara
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-importante"></i> ing-importante
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-bullet"></i> ing-bullet
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-home"></i> ing-home
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-formacion"></i> ing-formacion
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-empleo"></i> ing-empleo
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-insignia1"></i> ing-insignia1
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-insignia2"></i> ing-insignia2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-insignia3"></i> ing-insignia3
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-insignia4"></i> ing-insignia4
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-eventos"></i> ing-eventos
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-reporte"></i> ing-reporte
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-catalogo"></i> ing-catalogo
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-evalua-cartel"></i> ing-evalua-cartel
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-revision-cartel"></i> ing-revision-cartel
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-reporte-resultados"></i> ing-reporte-resultados
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-mi-cartel"></i> ing-mi-cartel
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-galeria1"></i> ing-galeria1
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-galeria2"></i> ing-galeria2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-iniciar-sesion"></i> ing-iniciar-sesion
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-finalistas"></i> ing-finalistas
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-comite"></i> ing-comite
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-administrador"></i> ing-administrador
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-estrella1"></i> ing-estrella1
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-estrella2"></i> ing-estrella2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-carga-archivo"></i> ing-carga-archivo
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-carga-multiple"></i> ing-carga-multiple
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-descarga"></i> ing-descarga
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-autorizar"></i> ing-autorizar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-negar"></i> ing-negar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-no-cargado"></i> ing-no-cargado
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-alumnos"></i> ing-alumnos
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-cardex"></i> ing-cardex
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-configuracion"></i> ing-configuracion
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-listado-menus"></i> ing-listado-menus
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-mi-cuenta"></i> ing-mi-cuenta
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-ver"></i> ing-ver
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-grafica"></i> ing-grafica
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-clic"></i> ing-clic
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-guardar"></i> ing-guardar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-regresar"></i> ing-regresar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-cuadrado"></i> ing-cuadrado
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-imprimir"></i> ing-imprimir
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-importante2"></i> ing-importante2
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-copiar"></i> ing-copiar
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-reloj"></i> ing-reloj
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-retardo"></i> ing-retardo
|
||||
</p>
|
||||
<p class="container">
|
||||
<i class="ing-justificar"></i> ing-justificar
|
||||
</p>
|
||||
@@ -28,7 +28,11 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<script src="js/bootstrap/bootstrap.min.js" defer></script>
|
||||
|
||||
<script src="js/messages.js" defer></script>
|
||||
<script src="js/horarios_profesor.js" defer></script>
|
||||
<script>
|
||||
const write = <?= $write ? 'true' : 'false' ?>;
|
||||
</script>
|
||||
<script src="js/moment.js" defer></script>
|
||||
<script src="js/horario_profesor.js" defer></script>
|
||||
</head>
|
||||
<!-- -->
|
||||
|
||||
@@ -47,20 +51,31 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<div class="form-group">
|
||||
<div class="form-box">
|
||||
<input type="hidden" name="periodo" value="<?= $user->periodo ?>" />
|
||||
<div class="form-box">
|
||||
<div class="form-group row">
|
||||
<label for="clave" class="col-4 col-form-label">Carrera</label>
|
||||
<div class="col-6">
|
||||
<input type="text" class="form-control" id="clave" name="clave" placeholder="Clave del profesor (opcional)" value="<?= $clave ?? '' ?>" pattern="(do)?[0-9]{3,6}" title="La clave debe tener 8 caracteres, los primeros 2 deben ser letras y los últimos 6 números" minlength="3" maxlength="8">
|
||||
<div class="form-group row">
|
||||
<label for="clave_profesor" class="col-4 col-form-label">Profesor</label>
|
||||
<div class="col-6">
|
||||
<input list="lista_profesores" name="clave_profesor" id="clave_profesor" class="form-control" placeholder="Profesor" required="required">
|
||||
<div class="valid-feedback">
|
||||
Profesor encontrado
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="profesor" class="col-4 col-form-label">Nombre</label>
|
||||
<div class="col-6 ">
|
||||
<input type="text" class="form-control" id="profesor" name="nombre" placeholder="Nombre del profesor (opcional)">
|
||||
<div class="invalid-feedback">
|
||||
Profesor no encontrado
|
||||
</div>
|
||||
<datalist id="lista_profesores">
|
||||
<?php
|
||||
$profesores = $db->where('facultad_id', $user->facultad['facultad_id'])->get("fs_profesor");
|
||||
foreach ($profesores as $profesor) {
|
||||
extract($profesor);
|
||||
?>
|
||||
<option data-grado="<?= $grado ?>" data-clave="<?= $clave ?>" data-profesor="<?= $profesor ?>" data-id="<?= $id; ?>" value="<?= "$clave | $grado $profesor" ?>"></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</datalist>
|
||||
<ul class="list-group" id="profesores"></ul>
|
||||
<input type="hidden" id="periodo_id" name="periodo_id" value="<?= $user->periodo ?>">
|
||||
<input type="hidden" id="profesor_id" name="profesor_id" value="">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ICO-BUSCAR FILTRAR & ICO-BORRAR LIMPIAR -->
|
||||
@@ -69,7 +84,7 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
<span class="ing-buscar icono"></span>
|
||||
Buscar horario
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" onclick="">
|
||||
<button type="button" class="btn btn-outline-danger" onclick="location.reload()">
|
||||
<span class="ing-borrar icono"></span>
|
||||
Limpiar
|
||||
</button>
|
||||
@@ -99,7 +114,7 @@ $write = $user->admin || in_array($user->acceso, ['w']);
|
||||
</div>
|
||||
<!-- Table responsive -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-sm table-responsive-sm" id="table-horario">
|
||||
<table class="table table-bordered table-sm table-responsive-md" id="table-horario">
|
||||
<thead class="thead-dark">
|
||||
<tr id="headers">
|
||||
<th scope="col" class="text-center">Hora</th>
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
<link rel="stylesheet" href="css/indivisa.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/sgi.css?rand=<?php echo rand(); ?>" type="text/css">
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
<link rel="stylesheet" href="css/jquery-ui.css">
|
||||
@@ -6,8 +6,12 @@ require_once 'class/c_login.php';
|
||||
$ruta = "../";
|
||||
require_once 'include/bd_pdo.php';
|
||||
|
||||
if (!isset($_SESSION['user']))
|
||||
die(header('Location: index.php'));
|
||||
if (!isset($_SESSION['user'])) {
|
||||
if (isset($redirect))
|
||||
$_SESSION['ruta'] = $redirect;
|
||||
|
||||
header('Location: index.php');
|
||||
}
|
||||
|
||||
$user = unserialize($_SESSION['user']);
|
||||
|
||||
@@ -21,10 +25,11 @@ function html_header($title, $header = null)
|
||||
else
|
||||
$paginas = queryAll("SELECT * FROM PERMISO_VIEW WHERE id = :id ORDER BY pagina_ruta", array(":id" => $user->user['id']));
|
||||
|
||||
?>
|
||||
?>
|
||||
<aside id="sidebar" class="bg-light defaultShadow d-flex flex-column p-4">
|
||||
<div class="d-flex align-items-center mb-5">
|
||||
<div class="logotipo"><a href="https://lasalle.mx/" target="_blank"><img src="imagenes/logo_lasalle.png"></a></div>
|
||||
<div class="logotipo"><a href="https://lasalle.mx/" target="_blank"><img src="imagenes/logo_lasalle.png"></a>
|
||||
</div>
|
||||
<div class="flex-grow-1 d-flex justify-content-end">
|
||||
<nav class="navbar navbar-expand d-none d-flex">
|
||||
<ul class="navbar-nav">
|
||||
@@ -33,10 +38,12 @@ function html_header($title, $header = null)
|
||||
</nav>
|
||||
<div class="d-flex mainMenu justify-content-center align-items-center">
|
||||
<div class="max-h iconSesion">
|
||||
<a href="salir.php" class="iconOff max-h pl-3 d-flex justify-content-start align-items-center"><i class="ing-salir"></i></a>
|
||||
<a href="salir.php" class="iconOff max-h pl-3 d-flex justify-content-start align-items-center"><i
|
||||
class="ing-salir"></i></a>
|
||||
</div>
|
||||
<div class="max-h">
|
||||
<div class="bg-primary rounded-circle pointer max-h max-w d-flex justify-content-center align-items-center" id="dismiss">
|
||||
<div class="bg-primary rounded-circle pointer max-h max-w d-flex justify-content-center align-items-center"
|
||||
id="dismiss">
|
||||
<span class="text-white iconMenuSidebar ing-cancelar"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,7 +58,7 @@ function html_header($title, $header = null)
|
||||
</p>
|
||||
|
||||
<?php
|
||||
if($user->admin){ ?>
|
||||
if ($user->admin) { ?>
|
||||
<p class="mb-0 mt-3 ml-4 pl-1">
|
||||
<a href="permisos.php" class="d-block side-menu">
|
||||
<span class="ing-pass"></span> Permisos
|
||||
@@ -60,11 +67,13 @@ function html_header($title, $header = null)
|
||||
<?php }
|
||||
$cont = 0;
|
||||
foreach ($grupos as $grupo) {
|
||||
?>
|
||||
?>
|
||||
<p class="mb-0 mt-3">
|
||||
<a class="d-block side-menu collapsed" data-toggle="collapse" href="#menu_<?= $cont ?>" role="button" aria-expanded="flase">
|
||||
<a class="d-block side-menu collapsed" data-toggle="collapse" href="#menu_<?= $cont ?>" role="button"
|
||||
aria-expanded="false">
|
||||
<i class="ing-caret ing-fw mr-2"></i>
|
||||
<span class="<?= $grupo['grupo_icon'] ?>"></span> <?= ucfirst($grupo['grupo_nombre']) ?>
|
||||
<span class="<?= $grupo['grupo_icon'] ?>"></span>
|
||||
<?= ucfirst($grupo['grupo_nombre']) ?>
|
||||
</a>
|
||||
</p>
|
||||
<div id="menu_<?= $cont ?>" class="collapse" data-parent="#accordionMenu" style>
|
||||
@@ -76,19 +85,19 @@ function html_header($title, $header = null)
|
||||
$user->access($pagina['pagina_ruta'] ?? '');
|
||||
if ($grupo['grupo_id'] == $pagina['grupo_id']) {
|
||||
if ($user->admin || $user->acceso != 'n') {
|
||||
?>
|
||||
?>
|
||||
<li class="mt-1">
|
||||
<a href="<?= $pagina['pagina_ruta'] ?>.php">
|
||||
<?= $page ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php }
|
||||
<?php }
|
||||
}
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php $cont++;
|
||||
<?php $cont++;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
@@ -96,37 +105,54 @@ function html_header($title, $header = null)
|
||||
<div class="overlay"></div>
|
||||
<header class="sticky-top bg-white">
|
||||
<div class="container marco menu d-flex align-items-center">
|
||||
<div class="logotipo"><a href="https://lasalle.mx/" target="_blank"><img src="imagenes/logo_lasalle.png"></a></div>
|
||||
<div class="logotipo">
|
||||
<a href="https://lasalle.mx/" target="_blank">
|
||||
<img src="imagenes/logo_lasalle.png">
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex-grow-1 d-flex justify-content-end">
|
||||
<nav class="navbar navbar-expand d-none d-flex">
|
||||
<ul class="navbar-nav">
|
||||
|
||||
</ul>
|
||||
<nav class="navbar navbar-expand-lg d-flex">
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<!-- Add your navigation items here -->
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="d-flex mainMenu justify-content-center align-items-center">
|
||||
<div class="max-h iconSesion">
|
||||
<a href="salir.php" class="iconOff max-h pl-3 d-flex justify-content-start align-items-center"><i class="ing-salir"></i></a>
|
||||
<a href="salir.php" class="iconOff max-h pl-3 d-flex justify-content-start align-items-center">
|
||||
<i class="ing-salir"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="max-h">
|
||||
<span id="sidebarCollapse" style="font-size: 44px;" class="ing-menu bg-white rounded-circle pointer max-w d-flex justify-content-center align-items-center"></span>
|
||||
<span id="sidebarCollapse" style="font-size: 44px;"
|
||||
class="ing-menu bg-white rounded-circle pointer max-w d-flex justify-content-center align-items-center"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="row bg-info mx-0 barra-gris d-flex flex-column">
|
||||
<?php
|
||||
if ($header != null) {
|
||||
?>
|
||||
?>
|
||||
<div class="marco">
|
||||
<div class="col-sm-12">
|
||||
<h2 class="text-muted"><?= $header; ?>
|
||||
<h2 class="text-muted">
|
||||
<?= $header; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="marco">
|
||||
<div class="col-sm-12 py-3">
|
||||
<h2 class="text-uppercase"><?= $title; ?></h2>
|
||||
<h2 class="text-uppercase">
|
||||
<?= $title; ?>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
<div class="col-12">
|
||||
<?php
|
||||
$target = $target ?? strtok($_SERVER["REQUEST_URI"], '?');
|
||||
$periodos = queryAll("SELECT * FROM FS_PERIODO WHERE FACULTAD_ID = COALESCE(:fac, FACULTAD_ID) ORDER BY INICIO DESC", [":fac" => $user->admin ? null : $user->facultad['facultad_id']]);
|
||||
$niveles = array_map(
|
||||
fn($nivel) => array_merge(
|
||||
$nivel,
|
||||
['periodos' => $db->where('nivel_id', $nivel['nivel_id'])->get('periodo_view')]
|
||||
), $db->get("nivel")
|
||||
);
|
||||
|
||||
// collect facultad_id's with facultad from $periodos
|
||||
if ($user->admin)
|
||||
$facultades = array_unique(array_column($periodos, 'facultad', 'facultad_id'));
|
||||
?>
|
||||
<input type="hidden" name="target" value="<?= $target ?>">
|
||||
<div class="form-box">
|
||||
@@ -19,27 +23,26 @@
|
||||
<div class="datalist-input">Selecciona un periodo</div>
|
||||
<span class="ing-buscar icono"></span>
|
||||
<ul style="display:none">
|
||||
<?php if (!$user->admin) foreach ($periodos as $periodo) { ?>
|
||||
|
||||
<li data-id="<?= $periodo['id'] ?>">
|
||||
<?= "{$periodo['nivel']} - {$periodo['periodo']} ({$periodo['estado']})" ?>
|
||||
</li>
|
||||
<?php }
|
||||
else {
|
||||
foreach ($facultades as $facultad_id => $facultad) {
|
||||
<?php
|
||||
foreach ($niveles as $nivel) {
|
||||
?>
|
||||
<li class="facultad not-selectable" data-id="<?= $facultad_id ?>">
|
||||
<?= $facultad ?>
|
||||
</li>
|
||||
<?php
|
||||
foreach (array_filter($periodos, fn ($p) => $p['facultad_id'] == $facultad_id) as $periodo) {
|
||||
<li data-id="<?= $nivel['nivel_id'] ?>" class="not-selectable disable">
|
||||
<?= $nivel['nivel_nombre'] ?>
|
||||
</li>
|
||||
<?
|
||||
$periodos_rs = $db->query(
|
||||
'SELECT * FROM fs_periodo(NULL, :nivel, 4)',
|
||||
[':nivel' => $nivel['nivel_id']]
|
||||
);
|
||||
foreach ($periodos_rs as $per) {
|
||||
?>
|
||||
<li data-id="<?= $periodo['id'] ?>">
|
||||
<?= "{$periodo['nivel']} - {$periodo['periodo']} ({$periodo['estado']})" ?>
|
||||
</li>
|
||||
<?php }
|
||||
}
|
||||
} ?>
|
||||
<li data-id="<?= $per['periodo_id'] ?>" <?php if ($user->periodo == $per["periodo_id"]) {
|
||||
echo 'class="selected"';
|
||||
} ?>>
|
||||
<?= $per['periodo_nombre'] ?>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<input type="hidden" id="periodo" name="id" value="">
|
||||
</div>
|
||||
@@ -55,7 +58,7 @@
|
||||
setDatalist('#periodo', <?php echo $user->periodo; ?>)
|
||||
makeRequiredDatalist("#periodo", true);
|
||||
|
||||
$(document).on('click', '#dlPeriodo ul li', function() {
|
||||
$(document).on('click', '#dlPeriodo ul li', function () {
|
||||
$('#formaPeriodo').submit();
|
||||
});
|
||||
</script>
|
||||
@@ -3,6 +3,7 @@ require_once 'class/c_login.php';
|
||||
$error = isset($_SESSION["error"]);
|
||||
unset($_SESSION["error"]);
|
||||
if ($error) $errorDesc = "El usuario y/o contraseña son incorrectos.";
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es" prefix="og: http://ogp.me/ns#">
|
||||
|
||||
160
js/auditoría.js
Normal file
160
js/auditoría.js
Normal file
@@ -0,0 +1,160 @@
|
||||
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,
|
||||
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)
|
||||
document.querySelector('#estados').innerHTML = store.filters.estados.map((estado) => `<span class="mx-2 badge badge-${store.estados.getEstado(estado).estado_color}">
|
||||
<i class="${store.estados.getEstado(estado).estado_icon}"></i> ${store.estados.getEstado(estado).nombre}
|
||||
</span>`).join('');
|
||||
else
|
||||
document.querySelector('#estados').innerHTML = `Todos los registros`;
|
||||
}
|
||||
},
|
||||
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);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
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();
|
||||
store.filters.switchFechas();
|
||||
}
|
||||
}).mount('#app');
|
||||
120
js/client.js
Normal file
120
js/client.js
Normal file
@@ -0,0 +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();
|
||||
@@ -70,7 +70,7 @@ $(function () {
|
||||
$.each($(".datalist").find('ul li:not(.not-selectable)'), function () {
|
||||
if ($(this).hasClass("selected")) {
|
||||
var elementRoot = $(this).parents('.datalist');
|
||||
elementRoot.find('.datalist-input').text($(this).html().replace(/[\t\n]+/g, ' ').trim());
|
||||
elementRoot.find('.datalist-input').html($(this).html().replace(/[\t\n]+/g, ' ').trim());
|
||||
var cid = $(this).data('id');
|
||||
elementRoot.find("input[type=hidden]").val(cid);
|
||||
}
|
||||
@@ -82,7 +82,7 @@ $(function () {
|
||||
|
||||
$(document).on('click', '.datalist-select > ul li:not(.not-selectable)', function () {
|
||||
var elementRoot = $(this).parents('.datalist');
|
||||
elementRoot.find('.datalist-input').text($(this).html().replace(/[\t\n]+/g, ' ').trim());
|
||||
elementRoot.find('.datalist-input').html($(this).html().replace(/[\t\n]+/g, ' ').trim());
|
||||
// $(this).parent('ul').siblings('input[type="text"]').blur();
|
||||
ocultaList({ "data": { "padre": elementRoot } });
|
||||
var cid = $(this).data('id');
|
||||
|
||||
0
js/declaration.js
Normal file
0
js/declaration.js
Normal file
414
js/horario_profesor.js
Normal file
414
js/horario_profesor.js
Normal file
@@ -0,0 +1,414 @@
|
||||
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 ?
|
||||
`<div class="menu-flotante p-2" style="opacity: .7;">
|
||||
<a class="mx-2" href="#" data-toggle="modal" data-target="#modal-editar">
|
||||
<i class="ing-editar ing"></i>
|
||||
</a>
|
||||
<a class="mx-2" href="#" data-toggle="modal" data-target="#modal-borrar">
|
||||
<i class="ing-basura ing"></i>
|
||||
</a>
|
||||
</div>`
|
||||
: '';
|
||||
cell.innerHTML =
|
||||
`<div style="overflow-y: auto; overflow-x: hidden; height: 100%;" id="block-${horario.id}" class="position-absolute w-100 h-100">
|
||||
<small class="text-gray">${horario.hora}</small>
|
||||
<b class="title">${horario.materia}</b> <br>
|
||||
<br><span>Salón: </span>${horario.salon} <br>
|
||||
<small class="my-2">
|
||||
${horario.profesores.map((profesor) => ` <span class="ing ing-formacion mx-1"></span>${profesor.grado ?? ''} ${profesor.profesor}`).join("<br>")}
|
||||
</small>
|
||||
</div>
|
||||
${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 = `
|
||||
<small class='text-danger'>
|
||||
${hora}
|
||||
</small>
|
||||
<div class="d-flex justify-content-center align-items-center mt-4">
|
||||
<div class="d-flex flex-column justify-content-center align-items-center">
|
||||
<span class="ing ing-importante text-danger" style="font-size: 2rem;"></span>
|
||||
<b class='text-danger'>
|
||||
Empalme de ${ids.length} horarios
|
||||
</b>
|
||||
<hr>
|
||||
<i class="text-danger">Ver horarios …</i>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
// 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 += `
|
||||
<tr data-ids="${horario.id}">
|
||||
<td><small>${horario.hora.slice(0, -3)}-${horario.hora_final.slice(0, -3)}</small></td>
|
||||
<td>${horario.materia}</td>
|
||||
<td>
|
||||
${horario.profesores.map(({ grado, profesor }) => `${grado ?? ''} ${profesor}`).join(", ")}
|
||||
</td>
|
||||
<td>${horario.salon}</td>
|
||||
${edit ? `
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-primary dismiss-editar" data-toggle="modal" data-target="#modal-editar">
|
||||
<i class="ing-editar ing"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-danger dismiss-editar" data-toggle="modal" data-target="#modal-borrar">
|
||||
<i class="ing-basura ing"></i>
|
||||
</button>
|
||||
</td>
|
||||
` : ""}
|
||||
</tr>`;
|
||||
});
|
||||
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 = `<div class="spinner-border" role="status" style="width: 3rem; height: 3rem;">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>`;
|
||||
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}"]`);
|
||||
@@ -1,20 +0,0 @@
|
||||
document.getElementById('form').addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(this);
|
||||
try {
|
||||
const response = await fetch('action/action_horario_profesor.php', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status == 'ok') {
|
||||
|
||||
}
|
||||
else {
|
||||
triggerMessage(data.message, 'Error en la consulta', 'warning');
|
||||
}
|
||||
} catch (error) {
|
||||
triggerMessage('Fallo al consutar los datos ', 'Error', 'danger');
|
||||
}
|
||||
});
|
||||
11
js/jquery-ui.touch-punch.min.js
vendored
Normal file
11
js/jquery-ui.touch-punch.min.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
* jQuery UI Touch Punch 0.2.3
|
||||
*
|
||||
* Copyright 2011–2014, Dave Furfero
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.mouse.js
|
||||
*/
|
||||
!function (a) { function f(a, b) { if (!(a.originalEvent.touches.length > 1)) { a.preventDefault(); var c = a.originalEvent.changedTouches[0], d = document.createEvent("MouseEvents"); d.initMouseEvent(b, !0, !0, window, 1, c.screenX, c.screenY, c.clientX, c.clientY, !1, !1, !1, !1, 0, null), a.target.dispatchEvent(d) } } if (a.support.touch = "ontouchend" in document, a.support.touch) { var e, b = a.ui.mouse.prototype, c = b._mouseInit, d = b._mouseDestroy; b._touchStart = function (a) { var b = this; !e && b._mouseCapture(a.originalEvent.changedTouches[0]) && (e = !0, b._touchMoved = !1, f(a, "mouseover"), f(a, "mousemove"), f(a, "mousedown")) }, b._touchMove = function (a) { e && (this._touchMoved = !0, f(a, "mousemove")) }, b._touchEnd = function (a) { e && (f(a, "mouseup"), f(a, "mouseout"), this._touchMoved || f(a, "click"), e = !1) }, b._mouseInit = function () { var b = this; b.element.bind({ touchstart: a.proxy(b, "_touchStart"), touchmove: a.proxy(b, "_touchMove"), touchend: a.proxy(b, "_touchEnd") }), c.call(b) }, b._mouseDestroy = function () { var b = this; b.element.unbind({ touchstart: a.proxy(b, "_touchStart"), touchmove: a.proxy(b, "_touchMove"), touchend: a.proxy(b, "_touchEnd") }), d.call(b) } } }(jQuery);
|
||||
178
js/reposiciones.js
Normal file
178
js/reposiciones.js
Normal file
@@ -0,0 +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 {};
|
||||
70
main.php
70
main.php
@@ -11,7 +11,12 @@ $user = unserialize($_SESSION['user']);
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Editar Horarios | <?php echo $user->facultad['facultad'] ?? "Administrador"; ?></title>
|
||||
<title>Editar Horarios |
|
||||
<?= $user->facultad['facultad'] ?? "Administrador"; ?>
|
||||
</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<?php
|
||||
include 'import/html_css_files.php';
|
||||
?>
|
||||
@@ -28,10 +33,65 @@ $user = unserialize($_SESSION['user']);
|
||||
<main class="content marco">
|
||||
<section id="message"></section>
|
||||
<h3 class="text-center mt-3">
|
||||
<b><?= $user->user['nombre']; ?></b> | <?= $user->facultad['facultad'] ?? "General"; ?> | <?= $user->rol['rol']; ?>
|
||||
<b>
|
||||
<?= $user->user['nombre']; ?>
|
||||
</b>
|
||||
<i>
|
||||
<?= $user->facultad['facultad'] ?? "General"; ?>
|
||||
</i>
|
||||
<small>
|
||||
<?= $user->rol['rol']; ?>
|
||||
</small>
|
||||
</h3>
|
||||
<hr>
|
||||
|
||||
<div class="d-flex justify-content-center align-items-center flex-wrap">
|
||||
<?php
|
||||
$has_token = $db->querySingle("SELECT FALSE") or die($db->getLastError());
|
||||
if (array_pop($has_token)) {
|
||||
?>
|
||||
<div class="movie card col-10 col-md-3 border-primary border-3 m-2">
|
||||
<div class="card-body text-center bg-light">
|
||||
<a href="http://200.13.89.27/checador_otros/main"
|
||||
class="card-link text-decoration-none text-primary d-flex flex-column align-items-center">
|
||||
<i class="fa fa-clock" aria-hidden="true"></i>
|
||||
<h5 class="card-title mt-2">Checador</h5>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<div class="card col-10 col-md-3 border-primary border-3 m-2 disabled bg-dark">
|
||||
<div class="card-body text-center disabled">
|
||||
<a href="#"
|
||||
class="card-link text-decoration-none text-primary d-flex flex-column align-items-center disabled text-danger">
|
||||
<i class="ing-cancelar" aria-hidden="true"></i>
|
||||
<h5 class="card-title mt-2">Checador</h5>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div class="movie card col-10 col-md-3 border-primary border-3 m-2">
|
||||
<div class="card-body text-center bg-light">
|
||||
<a href="#"
|
||||
class="card-link text-decoration-none text-primary d-flex flex-column align-items-center">
|
||||
<i class="fa fa-calendar" aria-hidden="true"></i>
|
||||
<h5 class="card-title mt-2">Mis horarios</h5>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="movie card col-10 col-md-3 border-primary border-3 m-2">
|
||||
<div class="card-body text-center bg-light">
|
||||
<a href="#"
|
||||
class="card-link text-decoration-none text-primary d-flex flex-column align-items-center">
|
||||
<i class="fa fa-table" aria-hidden="true"></i>
|
||||
<h5 class="card-title mt-2">Mis asistencias</h5>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<?php
|
||||
include "import/html_footer.php";
|
||||
@@ -40,9 +100,9 @@ $user = unserialize($_SESSION['user']);
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
<?php include_once 'js/messages.php'; ?>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$(document).ready(function () {
|
||||
// constantly update the date and time
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
$('.fecha_hora').html(new Date().toLocaleString());
|
||||
}, 1000);
|
||||
|
||||
|
||||
222
package-lock.json
generated
222
package-lock.json
generated
@@ -1,19 +1,26 @@
|
||||
{
|
||||
"name": "admin_checador",
|
||||
"name": "admin_checador (pruebas)",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"axios": "^1.4.0",
|
||||
"es6-promise": "^4.2.8",
|
||||
"moment": "^2.29.4",
|
||||
"petite-vue": "^0.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bootstrap": "^5.2.6",
|
||||
"@types/jquery": "^3.5.14"
|
||||
"@types/jquery": "^3.5.14",
|
||||
"@types/node": "^20.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
|
||||
"dev": true,
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
|
||||
"integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
@@ -37,19 +44,132 @@
|
||||
"@types/sizzle": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.1.tgz",
|
||||
"integrity": "sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/sizzle": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
|
||||
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/petite-vue": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/petite-vue/-/petite-vue-0.4.1.tgz",
|
||||
"integrity": "sha512-/gtYKQe9r1OV4IEwn2RsPXAHgFTe1nVq4QhldAP6/l8DSe9I754K6Oe1+Ff6dbnT5P8X2XP7PTUZkGRz5uFnFQ=="
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@popperjs/core": {
|
||||
"version": "2.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
|
||||
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
|
||||
"dev": true
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz",
|
||||
"integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw=="
|
||||
},
|
||||
"@types/bootstrap": {
|
||||
"version": "5.2.6",
|
||||
@@ -69,11 +189,93 @@
|
||||
"@types/sizzle": "*"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "20.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.1.tgz",
|
||||
"integrity": "sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/sizzle": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
|
||||
"integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "4.2.8",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"requires": {
|
||||
"mime-db": "1.52.0"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||
},
|
||||
"petite-vue": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/petite-vue/-/petite-vue-0.4.1.tgz",
|
||||
"integrity": "sha512-/gtYKQe9r1OV4IEwn2RsPXAHgFTe1nVq4QhldAP6/l8DSe9I754K6Oe1+Ff6dbnT5P8X2XP7PTUZkGRz5uFnFQ=="
|
||||
},
|
||||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
package.json
10
package.json
@@ -1,6 +1,14 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@types/bootstrap": "^5.2.6",
|
||||
"@types/jquery": "^3.5.14"
|
||||
"@types/jquery": "^3.5.14",
|
||||
"@types/node": "^20.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"axios": "^1.4.0",
|
||||
"es6-promise": "^4.2.8",
|
||||
"moment": "^2.29.4",
|
||||
"petite-vue": "^0.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
284
reposiciones.php
Normal file
284
reposiciones.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
|
||||
require_once 'class/c_login.php';
|
||||
if (!isset($_SESSION['user']))
|
||||
die(header('Location: index.php'));
|
||||
|
||||
$user = unserialize($_SESSION['user']);
|
||||
$user->access();
|
||||
if (!$user->admin && in_array($user->acceso, ['n']))
|
||||
die(header('Location: main.php?error=1'));
|
||||
|
||||
$user->print_to_log('Consultar horario');
|
||||
|
||||
$write = $user->admin || in_array($user->acceso, ['w']);
|
||||
|
||||
$en_fecha = $db->querySingle("SELECT ESTA_EN_PERIODO(NOW()::DATE, :periodo_id)", [':periodo_id' => $user->periodo])['esta_en_periodo'];
|
||||
$periodo_fin = $db->querySingle("SELECT periodo_fecha_fin FROM periodo WHERE periodo_id = :periodo_id", [':periodo_id' => $user->periodo])['periodo_fecha_fin'];
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Consultar horario | <?= $user->facultad['facultad'] ?? 'General' ?></title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="content-type" content="text/plain; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<?php include_once "import/html_css_files.php"; ?>
|
||||
<link rel="stylesheet" href="css/jquery-ui.css">
|
||||
<link rel="stylesheet" href="css/richtext.css" type="text/css">
|
||||
<link rel="stylesheet" href="css/clockpicker.css">
|
||||
<link rel="stylesheet" href="css/calendar.css">
|
||||
<link rel="stylesheet" href="css/fa_all.css" type="text/css">
|
||||
|
||||
<script src="js/scrollables.js" defer></script>
|
||||
<script>
|
||||
const write = <?= $write ? 'true' : 'false' ?>;
|
||||
</script>
|
||||
<script src="js/moment.js" defer></script>
|
||||
|
||||
</head>
|
||||
<!-- -->
|
||||
|
||||
<body style="display: block;">
|
||||
<?php
|
||||
include('include/constantes.php');
|
||||
include("import/html_header.php");
|
||||
html_header("Consultar horario", "Sistema de gestión de checador");
|
||||
?>
|
||||
<?= "<!-- $user -->" ?>
|
||||
<main class="container content marco content-margin" id="local-app">
|
||||
<section id="message"></section>
|
||||
<?php require('import/periodo.php') ?>
|
||||
|
||||
<!-- Botón para abrir el modal -->
|
||||
<span class="d-inline-block" tabindex="0" data-toggle="tooltip" <?php if (!$en_fecha) : ?> title="No se puede crear una reposición fuera de la fecha de reposición" <?php endif; ?>>
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" <?php if ($en_fecha) : ?>data-target="#crearReposición" <?php else : ?>disabled style="pointer-events: none;" <?php endif; ?>>
|
||||
Crear Reposición
|
||||
</button>
|
||||
</span>
|
||||
|
||||
|
||||
<!-- Modal del formulario -->
|
||||
<div class="modal fade" id="crearReposición" tabindex="-1" role="dialog" aria-labelledby="crearReposiciónLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="crearReposiciónLabel">Crear Reposición</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="form" class="form-horizontal">
|
||||
<div class="form-group step" id="step-1">
|
||||
<div class="form-box">
|
||||
<div class="form-group row">
|
||||
<label for="clave_profesor" class="col-4 col-form-label">Profesor</label>
|
||||
<div class="col-8">
|
||||
<input list="lista_profesores" name="clave_profesor" id="clave_profesor" class="form-control" placeholder="Profesor" required="required">
|
||||
<div class="valid-feedback">
|
||||
Profesor encontrado
|
||||
</div>
|
||||
<div class="invalid-feedback">
|
||||
Profesor no encontrado
|
||||
</div>
|
||||
<datalist id="lista_profesores">
|
||||
|
||||
<?php
|
||||
$profesores = $db->query('SELECT * FROM fs_profesor A WHERE facultad_id = :facultad_id AND EXISTS (
|
||||
SELECT * FROM horario join HORARIO_PROFESOR ON horario.HORARIO_ID = HORARIO_PROFESOR.horario_id WHERE HORARIO_PROFESOR.profesor_id = A.id AND HORARIO.periodo_id = :periodo_id
|
||||
) ORDER BY grado, nombre', [':facultad_id' => $user->facultad['facultad_id'], ':periodo_id' => $user->periodo]);
|
||||
|
||||
foreach ($profesores as $profesor) {
|
||||
extract($profesor);
|
||||
?>
|
||||
<option data-grado="<?= $grado ?>" data-clave="<?= $clave ?>" data-profesor="<?= $profesor ?>" data-id="<?= $id; ?>" value="<?= "$clave | $grado $profesor" ?>"></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</datalist>
|
||||
<ul class="list-group" id="profesores"></ul>
|
||||
<input type="hidden" id="periodo_id" name="periodo_id" value="<?= $user->periodo ?>">
|
||||
<input type="hidden" id="profesor_id" name="profesor_id" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group step" id="step-2">
|
||||
<div class="form-box">
|
||||
<div class="form-group row">
|
||||
<label for="horario_reponer" class="col-4 col-form-label">Horario a reponer</label>
|
||||
<div class="col-8">
|
||||
<select name="horario_reponer" id="horario_reponer" class="form-control" required="required">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="horario_id" id="horario_id">
|
||||
</div>
|
||||
<div class="form-group step" id="step-3">
|
||||
<div class="form-box">
|
||||
<div class="form-group row">
|
||||
<label for="fechas_clase" class="col-4 col-form-label">Fecha de clase</label>
|
||||
<div class="col-8">
|
||||
<select name="fechas_clase" id="fechas_clase" class="form-control" required="required">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group step" id="step-4">
|
||||
<div class="form-box">
|
||||
<div class="form-group row">
|
||||
<label for="fecha_reponer" class="col-4 col-form-label">Fecha de reposición</label>
|
||||
<div class="col-6">
|
||||
<input type="text" placeholder="dd/mm/aaaa" name="fecha_reponer" id="fecha_reponer" class="form-control date-picker" required="required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="hora" class="col-4 col-form-label">Hora</label>
|
||||
<div class="col-3">
|
||||
<div id="hora" class="datalist datalist-select mb-1">
|
||||
<div class="datalist-input text-center">hh</div>
|
||||
<span class="ing-buscar icono"></span>
|
||||
<ul style="display:none">
|
||||
<?php foreach (range(7, 21) as $hora) { ?>
|
||||
<li data-id='<?= $hora ?>'><?= str_pad($hora, 2, "0", STR_PAD_LEFT) ?></li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<input type="hidden" id="hora_reponer" name="horas" value="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div id="minutos" class="datalist datalist-select mb-1">
|
||||
<div class="datalist-input text-center">mm</div>
|
||||
<span class="ing-buscar icono"></span>
|
||||
<ul style="display:none">
|
||||
<?php foreach (range(0, 45, 15) as $minuto) { ?>
|
||||
<li data-id='<?= $minuto ?>'><?= str_pad($minuto, 2, "0", STR_PAD_LEFT) ?></li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<input type="hidden" id="minutos_reponer" name="minutos" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group step" id="step-5">
|
||||
<div class="form-box">
|
||||
<div class="form-group row">
|
||||
<label for="descripcion_reposicion" class="col-4 col-form-label">Comentarios</label>
|
||||
<div class="col-6">
|
||||
<textarea name="descripcion_reposicion" id="descripcion_reposicion" rows="4" required="required" placeholder="Se requiere proyector, etc." maxlength="255" class="form-control"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row align-items-center">
|
||||
<label class="col-4 col-form-label" for="sala">¿En sala de cómputo?</label>
|
||||
<div class="col-6">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="sala">
|
||||
<label class="custom-control-label" for="sala"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button class="btn btn-secondary" type="button" id="prev-button">Anterior</button>
|
||||
<button class="btn btn-secondary" type="button" id="next-button" disabled data-toggle="modal" data-target="#confirmationModal">Proponer reposición</button>
|
||||
</div>
|
||||
|
||||
<!-- Modal confirmación -->
|
||||
<div class="modal fade" id="confirmationModal" tabindex="-1" role="dialog" aria-labelledby="confirmationModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="confirmationModalLabel">Confirmación</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>¿Estás seguro de que deseas proponer la reposición?</p>
|
||||
<small>Recuerda que la aprobará tu jefe de carrera.</small>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-info" onclick="$('#confirmationModal').modal('hide');">Cancelar</button>
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal">Aceptar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap/popper.min.js"></script>
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
<script src="js/richtext.js"></script>
|
||||
<script src="js/clockpicker.js"></script>
|
||||
<script src="js/jquery-ui.js"></script>
|
||||
<script src="js/datepicker-es.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.richtext').richText({
|
||||
fontList: ['indivisa-text', 'Arial'],
|
||||
imageUpload: true,
|
||||
placeholder: 'Escribe aquí la información de la reposición: necesito un proyector, etc.',
|
||||
});
|
||||
});
|
||||
$(".date-picker").datepicker($.datepicker.regional.es);
|
||||
$(".date-picker").datepicker({
|
||||
dateFormat: "dd/mm/yyyy",
|
||||
changeMonth: true,
|
||||
beforeShowDay: function(date) {
|
||||
// Disable Sundays (0 represents Sunday)
|
||||
return [date.getDay() != 0, ''];
|
||||
// Disable 2020-05-01
|
||||
}
|
||||
});
|
||||
// the minimum is today + 3 laboral days
|
||||
function getNextWorkingDay(date) {
|
||||
const day = date.getDay(); // Get the day of the week (0-6, where 0 is Sunday)
|
||||
|
||||
// Check if it's Saturday (6), if so, add 2 days
|
||||
if (day === 6) {
|
||||
date.setDate(date.getDate() + 2);
|
||||
}
|
||||
// Add 1 day to skip to the next day
|
||||
date.setDate(date.getDate() + 1);
|
||||
|
||||
// Check if it's a Sunday (0), if so, add 1 day
|
||||
if (date.getDay() === 0) {
|
||||
date.setDate(date.getDate() + 1);
|
||||
}
|
||||
|
||||
// Add laboral days
|
||||
let laboralDaysCount = 1; // Start with 1 to account for the current day
|
||||
while (laboralDaysCount < 3) {
|
||||
date.setDate(date.getDate() + 1); // Add a day
|
||||
if (date.getDay() !== 6) { // Skip Saturdays
|
||||
laboralDaysCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
$(".date-picker").datepicker("option", "minDate", getNextWorkingDay(new Date()));
|
||||
// the maximum is periodo_fin
|
||||
$(".date-picker").datepicker("option", "maxDate", new Date("<?= $periodo_fin ?>T03:24:00"));
|
||||
|
||||
$(function() {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
</script>
|
||||
<script src="js/messages.js"></script>
|
||||
<script type="module" src="js/reposiciones.js"></script>
|
||||
|
||||
</html>
|
||||
136
route.php
136
route.php
@@ -1,136 +0,0 @@
|
||||
<?php
|
||||
//@NabiKAZ
|
||||
//https://gist.github.com/NabiKAZ/91f716faa89aab747317fe09db694be8
|
||||
//For show advanced list of files and directories with sort, date, size, icon type,...,
|
||||
//Save bellow content code as route.php file, and then run this command:
|
||||
// php -S 0.0.0.0:8080 -t . route.php
|
||||
//And then open http://localhost:8080/ in the browser.
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// This block MUST be at the very top of the page!
|
||||
@ob_start('ob_gzhandler');
|
||||
if(isset($_GET['icon']))
|
||||
{
|
||||
$e=$_GET['icon'];
|
||||
$I['file']='R0lGODlhEAAPAOYAAIyMlu7u9PHx9vDw9fT0+PPz97u7vvf3+vb2+d/f4vn5+/39/vv7/Pr6+/b29+3t7pCRnI6PmZOVn5ibpZWYopqeqJ2hq6KnsaClr9fZ3ff4+t/g4qSqtKmwuqeuuM3P0vHz9tze4be6vuzv8+vu8urt8eXo7Kuzva61vquyu9/k6uXp7uTo7cvU3dHZ4dDY4Nfe5d3j6dzi6Nvh5+Po7eLn7OHm69HV2ejs8Ofr7+7x9OHk597h5PT2+PP19/Hz9evt7+Lk5t3f4fr7/Pn6+/b3+PX299Tc49rh5+ru8fDz9ff5+vb4+fP19vz9/f////7+/vv7+/Pz8+/v7+zs7Orq6ubm5uHh4d7e3sDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAFkALAAAAAAQAA8AAAevgFmCJ4SFhDuCiYIdUI1QDQ9NKCGKgh4LjQsOG0smKRmVHAxPUAtGQktLPxxTihcKjU5FQR8iBhdXihgHC05DTEA8NwkYWIoWCENEGj1KJCsIFsaJFQRMPT5KIzk2BBXTghMFIEo6JDk1MQUT4FkUAiVJOCs2MTACFO0SAyw0NiozYLgYIKEdhAAyZiCBceRFiwAQ2kUIQLFixQjtAGjcyBFAuyhSqFgZSXJklSyBAAA7';
|
||||
$I['dir']='R0lGODlhEAAOAOYAAP79uv//4/j43f//5f7+5v7+6f//7f//7//8qf/9vf/9wf/+xP/9yf793P/+4f32hP/6iv/6pP/9zP/4kf/4nP/4n/373//1jf/2nP32tvnodfzuff/xhv/3uP/revftrvz32v/ocf/odP/rhPnz0/z44+DAPOHAPe/gnPXrvv3zyM6hAMicAMeaANStJdu4PNy7RODDWN7EaOLIauLIa//10ffuzcudAMiaAMSWAMOVAMGTAL2PALqMAMilLtayPNe2TNW0TNKxTNm7V9e5V82vU9zEd+7jvurfvPXryfbu072NALiKALaIALOEALKDALGCAK+AAMinRMalRMOiRNCuTM6sTMuqTMmnTLyfUdm8ZtW9et3Kl+XUoa19AKp6AK6EG7GHIr2YNr+cQ7iXQ8akTMSjTb6jXs2waMOqbNjEkNDDpM2xctfQwPz8/Pv7+/r6+vX19fHx8ezs7Ovr6+jo6Ofn5+bm5uHh4eDg4N/f397e3t3d3cDAwAAAAAAAACH5BAEAAH0ALAAAAAAQAA4AAAe4gEkzMkNERl19iYqKMSkGjwZbRVJTVGhqijACEJwQEpCPWXKJOBYPp6ioFjh2bn06IBuys7MgOnpwfTwkGiY/QEFCVVZXWGVmYl9MNic0BwHQ0QMOBUhgT0cuJQoJ3d4LAARja15aKAwI6QgRFRQYGVxke1AvDRMXHB4iIfwjSmFt6iz50KGGwYMGVbBJgyeODxYrbrTIsYNHjyZOopzhM8fVHT17QooUmYdOrj5v4KhcyRKOqz6BAAA7';
|
||||
$I['doc']='R0lGODlhEAAQAPcAAAEyeCg+bQgviwU2ggg8iAZCmwlLsiFMmjpamDJbtipitzhhrjppuE1qp0BmtERquVVtpF11q2d+s0JuxEl0zFJ3ylV7zl99w1h+0XeKnG6Ov3KQv3KRv3aTvXqVu3uVvH6XulWAyFmBxliCxV2ExF6ExGCBzWGIw2KJw2WKwmeLwWmMwWyOwGeK1XeR1XyX2P8A/4KavIWdvoOc2oCe5oigwIuiwoyiwouk3ZGnxpesyZCu1p2xzYml6ZOr5qO20am71K260K+836q+8a/A2LPD2rfI9MnS4tbc6tLi+tTj+tbk+tfl+9zi9Nnm+9vn+9zo+97p++Dq/OHs/OPt/OXu/Obv/Ojw/erx/evy/e3z/e/0/fDy+vD2/vL3/vT4/vX5/vf6/vn7/////wCpEQAAABLs7NS5srGlQNcVPRQCgBQCQBLtDNdNrxQCgGQCeNdN4xQCgBLtFAAAAJEFyCNr8BLt4JEFURQHqJEFbRLuOAAAABLtPAAAAJEFyFiHuBLuCJEFURQHSBLtWAAAAJEFyFiHuBLuJJEFURQHSJEFbRLuaAAABAAAAOaERAAAAgAABAAAMAAAACNr+NSLsf3QAAAAMAAABBQAABLrmJD7bAAAIAAAAFiHwBLuOAAAAAAAIADwqgAAIAAAAAAAAJDnvJDVhhLuCJD7bJD7cZDVhpDnvBQAABLt5JDnyBLujJDuGJD7eAH//wAABBLtaAAAABLujJDuGJEFcP///5EFbZEJvBQAAAAAAFiHwBLuSJEJkliHwAAAABLunN3tDt3tIGKmyAABxGKm1AAAAAAAAAAAAAAAAAAAABLuaBLu7BS3YBS3YBLuoOb8I8OlLsYaoBLu2MLCzQAABMLC4xS04BS3YAAAAxSwbsXS4BSwABLu1BLupP///xLvQMNclMEgcP///8LC40SV1RS3YGMboGMboEUEtRQAABS04IoASAAAAAAAAOqG1OqG1OqG1OqG1AAC8BLvJN1sdBLvLIoASIoASObgowAACeaCsAAABCH5BAEAADAALAAAAAAQABAAAAjhAGEILALkBw8dOWzIAAFCoEMYRMSEAfPFS5ctIMY0hOHDRw8aL1pgqDBBgZaMGjmOWclypYEsKDX2GDLDBBITTSDgMICFoU8aTWZcaPKgSYMMBq5YqUJlCggXY1w8EHIAB4IjBZY2lQKixRgJDyIMSBBgTIEqO3ZIieLBwhgICIwMGBBkDIGtUaB0oDBGwAIuAxysHDAlLZQnGxi0bAlg7WEnLBQYmFygMoEBAKKkdcJEhcMbWqc4fsJ5CQqHNZimXZ12iZISDmXgfczEdRIRDmN8+NCBg4YVKU6QGBEiREAAADs=';
|
||||
$I['xls']='R0lGODlhEAAQAPcAADVJGjRNGTVNGDRSFzRTFzRYFTReEzReFDVeGjNpDzNtDjRtDjRjETRkEjZjGztoHjpvHjNwDTlwHjp3GTx3Hz57HjJoKDtxIjp9Jz99IWB+XEKAJUaELEeFLUKJNUeIO02MNk+OOUiSP1OTQFKXSFiYR1qaSVieUl6YVV+hU1uiWHCbbWGiVGGgWGWnW2eqX2SoYGmtYmqsZW2wZ3SlcG6Ov3KQv3KRv3aTvXqVu3uVvH6XulWAyFmBxliCxV2ExF6ExGGIw2KJw2WKwmeLwWmMwWyOwP8A/4KavIWdvoephZC9i5ywm6Gzn4igwIuiwoyiwpGnxpesyZCu1p2xzaO20am71JPCjpPEjZbEkZrGlq/A2LPD2sDRwMzay8/dz9bi1t/p39Li+tTj+tbk+tfl+9nm+9vn+9zo+97p++Xs5eDq/OHs/OPt/OXu/Obv/Ojw/erx/evy/e3z/e/0/fD2/vL3/vT4/vX5/vf6/vn7/////xLtPAAAAJEFyCLVGBLuCJEFURQHSBLtWAAAAJEFyCLVGBLuJJEFURQHSJEFbRLuaAAABAAAAOaERAAAAgAABAAAMAAAAFeQiNSLsf3QAAAAMAAABBQAABLrmJD7bAAAIAAAACLVIBLuOAAAAAAAIADwqgAAIAAAAAAAAJDnvJDVhhLuCJD7bJD7cZDVhpDnvBQAABLt5JDnyBLujJDuGJD7eAH//wAABBLtaAAAABLujJDuGJEFcP///5EFbZEJvBQAAAAAACLVIBLuSJEJkiLVIAAAABLunN3tDt3tIGKmyAABwGKm1AAAAAAAAAAAAAAAAAAAABLuaBLu7BSuABSuABLuoOb8I8OlLsYaoBLu2MLCzQAABMLC4xSsQBSuAAAAAxSg2MXS4BSgABLu1BLupP///xLvQMNclMEgcP///8LC40SV1RSuAGMboGMboEUEtRQAABSsQKR+UAAAAAAAAOqG1OqG1OqG1OqG1AACBBLvJN1sdBLvLKR+UKR+UObgowAACeaCsAAABCH5BAEAAEcALAAAAAAQABAAAAjhAI8I5GKlChUpUZ4k2bFDoMMjW/TkwXPHTh06O/Y0PDIjhosUJkaA6LChwpyMGjnuWclyZQQ5KDXOuAKDxB4vKFAwURCHoc8XarDI8ECjxQcwCeC8cdOGzQ4We75oUYHBQpc9DJY2XbOjxJ4lJ7KouLAizAE3U6asSZMjxBIKEBwsEfGgSYGtadDg4NBSiQQEGgawSYvmjI0MLVsKWFvYjJEJERYkaGCgAIEAANKkNVOGiEMoWtkwPsOZjBCHTpimXZ2WzBggDpPgbVzGtZgeDpHo0IHjRo0iQ4L88MGDR0AAADs=';
|
||||
$I['jpg']=$I['gif']=$I['png']='R0lGODlhEAAQAPcAAPuBhP0RI9fU1r24vL25vn+CmKSxzLfF4cPL28bO3srO1oOk4WF4opyuzpWlwpurx6i30qm30ae1zqa0zb3M57G/2Kq3z6m2zcTR6aSvw9Dd9crW7NPe8tXg88PM3OLs/tXe7+Lr/Njh8eHp98/W4wBe9Yibup+vyZemv6e2z6e2zsXW8rLC26e1zK+91Kezx8jU6Nvo/eDr/dzm9uLs/OTt/MvT4ODn897l8dvi7uvy/gxn7Keyw7K9zsTQ4uPu/t7p+dzm9c/Y5t7n9dri7svb8djh7ejw++vw9+fs87jI2+Hu/lem/vH3/lSp/uTx/vP5/snj9+33/qGoqPz+/v3+/lXSYAC1AH3GdS6qHnDIW1OmL+Hp173JqoGaKby9srurRv3slf7dbf7cc/7Xb/7QZ/3SdP7FVd6wUP7LaP6/SP68SeS7cv6vMP62QNycN+KuWOCuW/vt1fueGf6qL/6vN/6xOf65V+C/jP2XEv6eGvmeH/6gI/6iJOCSKM+WR9ScUPjEff6ZG8qELbB/ROG0f/2EAPeAAf6IAuR5BP6JCP6QENKTTdmseuC7kbBhEKlfFa5iGMR0I8d+M45aJr6BRM2VW9ikcNuugNG8pvLk1v37+f17AO1zAKlUBrNiFqBdHplZHr97PcB/Q690Pc6KTcWHT7F9T9CYY9Opg9KujN25l6NJAJJFBK5sMpNnQcqcdKaGbL+fhPfw6qpKALeDWtijebGReOC4mdm2nKuQe+XDqfjy7tm0nedXBa+ZjPjz8OfOwauSh/BvO/55QcY0AN7a2d4dA+kwFdsTBv7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAMsALAAAAAAQABAAAAj/AJcJXIbDSI6DREDAQKBs4MAgVZRJVNaEwwYbDgUaozJRGZQON46QcLgplaNLmFZpoiLDh5AKHgTuKmVJlzBVqEbBSlIEQgMTy2wxkhOFiZMFXLoQOiWCwgEHuCQ1KvRlRwkGWqxgERVrBYYHkwAF68VmSgE8Wa5sCRQqgYYTg8LEgWOGTJk0YLz8uQOJQAwDptCMEXNmjZs6dIgBGOZpwJIJs3JVeqPGDp0+c5AFSNZKwQ8JAnnV8tOGj6A8vo4VozTjg4qBwFzt0bNIESJDrH49oZHCoaxHiQ5x6kTr1QggIXoPFJCJVKRPoG4hkVJDRwSHQ5T04JHhBQsXF1pYA0AREAA7';
|
||||
$I['txt']='R0lGODlhEAAQAPcAAB6Kcm6Ov3KQv3KRv3aTvXqVu3uVvH6XulWAyFmBxliCxV2ExF6ExGGIw2KJw2WKwmeLwWmMwWyOwP8A/4KavIWdvoigwIuiwoyiwo+lxJGnxpKoxpWryJesyZmtypCu1p2xzaO20am71K/A2LPD2tLi+tTj+tbk+tfl+9nm+9vn+9zo+97p++Dq/OHs/OPt/OXu/Obv/Ojw/erx/evy/e3z/e/0/fD2/vL3/vT4/vX5/vf6/vn7/////xLuYAAAQAAAAAAAABLuqBLuaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBLurJDuGAAAAAAAAgAAAQACEBLuYAACEJEZcAa6SAa6KAAAAAAAAAHskAABHhLsBJDuGBLswNS5TACpEQCpERLs1NS47q0nqACpEQAAABLs7NS5sq0nqNcVPRQCgBQCQBLtDNdNrxQCgCoFBtdN4xQCgBLtFAAAAJEFyCLqgBLt4JEFURQHqJEFbRLuOAAAABLtPAAAAJEFyAcKIBLuCJEFURQHSBLtWAAAAJEFyAcKIBLuJJEFURQHSJEFbRLuaAAABAAAAOaERAAAAgAABAAAMAAAACLqiNSLsf3wAAAAMAAABBQAABLrmJD7bAAAIAAAAAcKKBLuOAAAAAAAIADwqgAAIAAAAAAAAJDnvJDVhhLuCJD7bJD7cZDVhpDnvBQAABLt5JDnyBLujJDuGJD7eAH//wAABBLtaAAAABLujJDuGJEFcP///5EFbZEJvBQAAAAAAAcKKBLuSJEJkgcKKAAAABLunN3tDt3tIGKmyAABsGKm1AAAAAAAAAAAAAAAAAAAABLuaBLu7BR5EBR5EBLuoOb8I8OlLsYaoBLu2MLCzQAABMLC4xR2sBR5EAAAAxRwicXS4BRwABLu1BLupP///xLvQMNclMEgcP///8LC40SV1RR5EGMboGMboEUEtRQAABR2sIPdOAAAAAAAAOqG1OqG1OqG1OqG1AABsBLvJN1sdBLvLIPdOIPdOObgowAACeaCsAAABCH5BAEAABMALAAAAAAQABAAAAipACcIJCEiBIgOGi5UOHBAoMMJI3js0JEDxw0bB3o0nACgo8ePHXto5CiyBwCTKEuKPBCypcmTABgybEkTJowXLljChPnSJM4WOkGCbNGCRQGHHmrQmCEjxk0XRVcQcMhh6YerWD+sUCHA4QamTn+y2JpCgsMMTbNiTYECgkMMYaGOVcH2hAOHFm6qvXrCBAOHFcSSRdG3RAKHFAwYIDAgQIQHDRYoQIAgIAA7';
|
||||
$I['avi']=$I['mpg']=$I['mpeg']=$I['mp3']='R0lGODlhEAAQAPcAAEhHSHd2d//+/+/q9+7r9KalqPLx9NrZ3HZ1e4mJjx0dHoeHi+Dg4/n5++np6+jo6tLS1NjY2ZqamxYelholkUBHhIWGjHB2lREwshozpCdDujZPuoOEiIWNqBI7tJWWmdTV2B1NwihTuC9iyDZqzlF60X+Vw/z9/xtbyxZczvb5/h1m0EqA0EZ/z1aP3srd9h502hh24VSc6SuD3oqLjNfY2SGN70ik6cHe9ODn6urv8MTGxnG+AYTVDXiuKaPOX6DEZ+nu4XO3AWWgAU56AYypWoWWaNPcw22pAWacAVyKCTpTCm2bFF+BHHehLEVnAWGNBk9zBXWDV1BhKsbKvZm0VajRMHKKKJG5AUlaBnp9b4ulGoqMgYiIhoKCgLOzseDg325qVf/++dqvAdmvAei7As2oBtCoB8qlB+jAF+jAG6eLE5yDGezHK+fFN4RyIN7AOOTEOuPDO4BuIu3MQ+XIR+rLSuXHSOXHSu3VaO3Wc+7YeIuFa/ry0vz343lxVn16cnh2cMHAvpOOiPjx6evq6eHc2v9zTfV6WfnRxuHLxfvx7v9KG/lHG/hIG/lOIvxNI/dOJP5ZLv5aMexYMvVeOPRjPv9xTP92U/BzU/99Xet2WPKijvWvnu+unvGxofnMwNvLx/r19P////v7+/Ly8uzs7Ojo6Obm5uTk5OLi4uDg4N/f39jY2NbW1tLS0s/Pz87Ozs3NzczMzMfHx7Ozs62traenp56enpycnJqamnl5eWtra2RkZE5OTiEhIRwcHBsbGxMTEwgICAQEBP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAMUALAAAAAAQABAAAAj/AIsJXMRpE6UtPopQMSCwYTFQlRpBeoSFxxAlRg44TBRpEiZNiKz0EJKEiBQIAkVZkoTpU6gINY78QALlCRdVxTw5utSJEK8dDggMAMIkypQCpDIxOqSIEABeu2gJCOKkyRIvq5QeMuTU169hvR5UuZJFC6wWM27kcBqMGDBYDPLAMcNnFosYNnAQUiDsFqwTfuy4OQNolokUMGToGCSowahRe+6oWWMhFggRKFa4eKFCTB89deSM+SOBVbEOHkKMIFGCDh08ccq8+WBLYKELGTBo2NCmTRoyczjkStUQTIIKFCagQcMmDA1crRwWK/WlC4JAARboqnVKekNTrmTJBnqFapTDgAA7';
|
||||
$I['pdf']='R0lGODlhEAAQAPcAAFoAAGMAAHMYGG6Ov3KQv3KRv3aTvXqVu3uVvH6XulWAyFmBxliCxV2ExF6ExGGIw2KJw2WKwmeLwWmMwWyOwIwACJQAAJwhIa0ACLUAAL05OZxCQr1KSr1SWsYAAM4ICM4QENYYGM4pMd45OecIEPcQEPcYGO85OfcpKf8xOc5KSt5KStZja+dKSu9CSu9KSudaWu9SUu9SWudaY+dzc+97e/9zc/8A/4KavIWdvoigwIuiwoyiwo+lxJGnxpKoxpWryJesyZmtypywzJ2xzaO20am71Ky+1q/A2LPD2t6EhN61veeMjO+cnO+trdLi+tTj+tbk+tfl+9nm+9vn+9zo+97p++/W1ufv9+Dq/OHs/OPt/OXu/Obv/Ojw/erx/evy/e3z/e/0/fD2/vL3/vT4/vX5/vf6/vn7/////xQCgBQCQBLtDNdNrxQCgBEGqNdN4xQCgBLtFAAAAJEFyCJ8mBLt4JEFURQHqJEFbRLuOAAAABLtPAAAAJEFyFWi2BLuCJEFURQHSBLtWAAAAJEFyFWi2BLuJJEFURQHSJEFbRLuaAAABAAAAOaERAAAAgAABAAAMAAAACJ8oNSLsf3QAAAAMAAABBQAABLrmJD7bAAAIAAAAFWi4BLuOAAAAAAAIADwqgAAIAAAAAAAAJDnvJDVhhLuCJD7bJD7cZDVhpDnvBQAABLt5JDnyBLujJDuGJD7eAH//wAABBLtaAAAABLujJDuGJEFcP///5EFbZEJvBQAAAAAAFWi4BLuSJEJklWi4AAAABLunN3tDt3tIGKmyAACvGKm1AAAAAAAAAAAAAAAAAAAABLuaBLu7BSjUBSjUBLuoOb8I8OlLsYaoBLu2MLCzQAABMLC4xSo8BSjUAAAAxSgLcXS4BSgABLu1BLupP///xLvQMNclMEgcP///8LC40SV1RSjUGMboGMboEUEtRQAABSo8KR+UAAAAAAAAOqG1OqG1OqG1OqG1AACXBLvJN1sdBLvLKR+UKR+UObgowAACeaCsAAABCH5BAEAADcALAAAAAAQABAAAAjcAG8ITGKkCJEgPnbkSJBAoMMbSNCcMVOGzBgxCdI0fHhkYsWLYTJqvNGBgwYVM2DIgOFCBBiRGm28SIHChIkSJkhg+MKw54kQIEB4+OABQ4YKXrpw2aIlgYUAAARsmLrhwgalTLNsvDFETBgwX1Zg1ZLFygGHQr5+uTKiSVYrVQw4BALWSw0sTJyUrUKFgMMfX7xcaeHEyYoWMZRMoeCwR1IYS8jCXcJCigSHPLrMYCKZ7xQpUSA41EGDRmcqn6NAceAwx1vPoKE8WeAQBwIEBgoMmBDhQQMGChQEBAA7';
|
||||
$I['rar']=$I['zip']='R0lGODlhDwAQAOYAAMjY9gRLsBJPqRZQpydpx7TP9iFbrSNfsChltixquzt6xEmH0VqU2G2h4HSk4HGc05jC9ZCz3jmF1zqA0EOK106P2JC235S335a535i531am9FOa5FOa4l2l61uZ13Cv7ne09IKx3ziZ81yr9mGz/2i3/2Go62as7XK7/26x7ni+/3W38Ha38HKx5nu88XWx5IjD84HA8oTD84vK+ZvP9YnJ963b+bzl+8fs/fT///79mf//r///uf//xPr2k//9pP/4hv/6kPr1kPr1kf/7mvfvgvPkbdm/Ktm/LPn25dm8L/vwvfvzzt61AfbNLNm3KfzUMdm3Mdu5M9q6Nd6+O/3ZR/vdZPzkhfvnl/roodq0Kv7TOP3VOf3aUfzZWPzebfvedPziffrjj/rprfrrs/juytKgB9SjCNWlC8+hDtWmEM+hENKmFNWmFtSnG9WqH9SqH9WqIdWrItiuKOjRh7J9DrR8D7mFKaptDbJ7I6ttDax0HfEZAf///wAAAAAAACH5BAEAAH0ALAAAAAAPABAAAAe8gH2CgzIxLispJyaCdGmOdCY5ODc2NDAsC31pfJxpBJ+gEyMeAWtaT0dISlFSU1R2fSEvAXI6PTw7P0RBQEZ1fRYyAW8+SWVjYmBeW02/GDMBcENMZFhhVlVQzX3P0dPV19nbF9BxQktZV19dXE7bGTUBbEVzbm1qaGdm2xEtAXd59ODZAwrUgw8dAgwStKgECRQqVHRQsHCQiQQIDhwwoLCiRxOLPA7i4KABgwoUJAgQuQFAAQggNIgYEAgAOw==';
|
||||
header('Cache-control: max-age=2592000');
|
||||
header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T',time()+2592000));
|
||||
header('Content-type: image/gif');
|
||||
print base64_decode(isset($I[$e])?$I[$e]:$I['file']);
|
||||
exit;
|
||||
}
|
||||
// End block
|
||||
|
||||
// Start configs
|
||||
$sitename='My files';
|
||||
$date='Y-m-d H:i:s'; // date format
|
||||
$ignore=array('.','..','.htaccess','index.php','icon.php','Thumbs.db','web.config'); // ignore these files
|
||||
// End configs
|
||||
$root=dirname(__FILE__);
|
||||
$dir=isset($_GET['dir'])?$_GET['dir']:'';if(strstr($dir,'..'))$dir='';
|
||||
$path="$root/$dir/";
|
||||
$dirs=$files=array();
|
||||
if(!is_dir($path)||false==($h=opendir($path)))exit('Directory does not exist.');
|
||||
while(false!==($f=readdir($h)))
|
||||
{
|
||||
if(in_array($f,$ignore))continue;
|
||||
if(is_dir($path.$f))$dirs[]=array('name'=>$f,'date'=>filemtime($path.$f),'url'=>'index.php?dir='.rawurlencode(trim("$dir/$f",'/')));
|
||||
else$files[]=array('name'=>$f,'size'=>filesize($path.$f),'date'=>filemtime($path.$f),'url'=>trim("$dir/".rawurlencode($f),'/'));
|
||||
}
|
||||
closedir($h);
|
||||
$current_dir_name = basename($dir);
|
||||
$up_dir=dirname($dir);
|
||||
$up_url=($up_dir!=''&&$up_dir!='.')?'index.php?dir='.rawurlencode($up_dir):'index.php';
|
||||
// END PHP ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><?=$current_dir_name==''?'Directory list':$current_dir_name?></title>
|
||||
<style type="text/css">
|
||||
body { font-family: tahoma, verdana, arial; font-size: 0.7em; color: black; padding-top: 8px; cursor: default; background-color: #fff; }
|
||||
#idx { border: 3px solid #fff; width: 500px; }
|
||||
#idx td.center { text-align: center; }
|
||||
#idx td { border-bottom: 1px solid #f0f0f0; }
|
||||
#idx img { margin-bottom: -2px; }
|
||||
#idx table { color: #606060; width: 100%; margin-top:3px; }
|
||||
#idx span.link { color: #0066DF; cursor: pointer; }
|
||||
#idx .rounded { padding: 10px 7px 10px 10px; -moz-border-radius:6px; }
|
||||
#idx .gray { background-color:#fafafa;border-bottom: 1px solid #e5e5e5; }
|
||||
#idx p { padding: 0px; margin: 0px;line-height:1.4em;}
|
||||
#idx p.left { float:left;width:60%;padding:3px;color:#606060;}
|
||||
#idx p.right {float:right;width:35%;text-align:right;color:#707070;padding:3px;}
|
||||
#idx strong { font-family: "Trebuchet MS", tahoma, arial; font-size: 1.2em; font-weight: bold; color: #202020; padding-bottom: 3px; margin: 0px; }
|
||||
#idx a:link { color: #0066CC; }
|
||||
#idx a:visited { color: #003366; }
|
||||
#idx a:hover { text-decoration: none; }
|
||||
#idx a:active { color: #9DCC00; }
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
var _c1='#fefefe'; var _c2='#fafafa'; var _ppg=100; var _cpg=1; var _files=[]; var _dirs=[]; var _tpg=null; var _tsize=0; var _sort='date'; var _sdir={'type':0,'name':0,'size':0,'date':1}; var idx=null; var tbl=null;
|
||||
function _obj(s){return document.getElementById(s);}
|
||||
function _ge(n){n=n.substr(n.lastIndexOf('.')+1);return n.toLowerCase();}
|
||||
function _nf(n,p){if(p>=0){var t=Math.pow(10,p);return Math.round(n*t)/t;}}
|
||||
function _s(v,u){if(!u)u='B';if(v>1024&&u=='B')return _s(v/1024,'KB');if(v>1024&&u=='KB')return _s(v/1024,'MB');if(v>1024&&u=='MB')return _s(v/1024,'GB');return _nf(v,1)+' '+u;}
|
||||
function _f(name,size,date,url,rdate){_files[_files.length]={'dir':0,'name':name,'size':size,'date':date,'type':_ge(name),'url':url,'rdate':rdate,'icon':'index.php?icon='+_ge(name)};_tsize+=size;}
|
||||
function _d(name,date,url){_dirs[_dirs.length]={'dir':1,'name':name,'date':date,'url':url,'icon':'index.php?icon=dir'};}
|
||||
function _np(){_cpg++;_tbl();}
|
||||
function _pp(){_cpg--;_tbl();}
|
||||
function _sa(l,r){return(l['size']==r['size'])?0:(l['size']>r['size']?1:-1);}
|
||||
function _sb(l,r){return(l['type']==r['type'])?0:(l['type']>r['type']?1:-1);}
|
||||
function _sc(l,r){return(l['rdate']==r['rdate'])?0:(l['rdate']>r['rdate']?1:-1);}
|
||||
function _sd(l,r){var a=l['name'].toLowerCase();var b=r['name'].toLowerCase();return(a==b)?0:(a>b?1:-1);}
|
||||
function _srt(c){switch(c){case'type':_sort='type';_files.sort(_sb);if(_sdir['type'])_files.reverse();break;case'name':_sort='name';_files.sort(_sd);if(_sdir['name'])_files.reverse();break;case'size':_sort='size';_files.sort(_sa);if(_sdir['size'])_files.reverse();break;case'date':_sort='date';_files.sort(_sc);if(_sdir['date'])_files.reverse();break;}_sdir[c]=!_sdir[c];_obj('sort_type').style.fontStyle=(c=='type'?'italic':'normal');_obj('sort_name').style.fontStyle=(c=='name'?'italic':'normal');_obj('sort_size').style.fontStyle=(c=='size'?'italic':'normal');_obj('sort_date').style.fontStyle=(c=='date'?'italic':'normal');_tbl();return false;}
|
||||
|
||||
function _head()
|
||||
{
|
||||
if(!idx)return;
|
||||
_tpg=Math.ceil((_files.length+_dirs.length)/_ppg);
|
||||
idx.innerHTML='<div class="rounded gray" style="padding:5px 10px 5px 7px;color:#202020">' +
|
||||
'<p class="left">' +
|
||||
'<strong><?=$current_dir_name==''?$sitename:$current_dir_name?></strong><?=$dir!=''?' (<a href="'.$up_url.'">Back</a>)':''?><br />' + (_files.length+_dirs.length) + ' objects in this folder, ' + _s(_tsize) + ' total.' +
|
||||
'</p>' +
|
||||
'<p class="right">' +
|
||||
'Sort: <span class="link" onmousedown="return _srt(\'name\');" id="sort_name">Name</span>, <span class="link" onmousedown="return _srt(\'type\');" id="sort_type">Type</span>, <span class="link" onmousedown="return _srt(\'size\');" id="sort_size">Size</span>, <span class="link" onmousedown="return _srt(\'date\');" id="sort_date">Date</span>' +
|
||||
'</p>' +
|
||||
'<div style="clear:both;"></div>' +
|
||||
'</div><div id="idx_tbl"></div>';
|
||||
tbl=_obj('idx_tbl');
|
||||
}
|
||||
|
||||
function _tbl()
|
||||
{
|
||||
var _cnt=_dirs.concat(_files);if(!tbl)return;if(_cpg>_tpg){_cpg=_tpg;return;}else if(_cpg<1){_cpg=1;return;}var a=(_cpg-1)*_ppg;var b=_cpg*_ppg;var j=0;var html='';
|
||||
if(_tpg>1)html+='<p style="padding:5px 5px 0px 7px;color:#202020;text-align:right;"><span class="link" onmousedown="_pp();return false;">Previous</span> ('+_cpg+'/'+_tpg+') <span class="link" onmousedown="_np();return false;">Next</span></p>';
|
||||
html+='<table cellspacing="0" cellpadding="5" border="0">';
|
||||
for(var i=a;i<b&&i<(_files.length+_dirs.length);++i)
|
||||
{
|
||||
var f=_cnt[i];var rc=j++&1?_c1:_c2;
|
||||
html+='<tr style="background-color:'+rc+'"><td><img src="'+f['icon']+'" alt="" /> <a href="'+f['url']+'">'+f['name']+'</a></td><td class="center" style="width:50px;">'+(f['dir']?'':_s(f['size']))+'</td><td class="center" style="width:110px;">'+f['date']+'</td></tr>';
|
||||
}
|
||||
tbl.innerHTML=html+'</table>';
|
||||
}
|
||||
<?php foreach($dirs as $d)print sprintf("_d('%s','%s','%s');\n",addslashes($d['name']),date($date,$d['date']),addslashes($d['url'])); ?>
|
||||
<?php foreach($files as $f)print sprintf("_f('%s',%d,'%s','%s',%d);\n",addslashes($f['name']),$f['size'],date($date,$f['date']),addslashes($f['url']),$f['date']);?>
|
||||
|
||||
window.onload=function()
|
||||
{
|
||||
idx=_obj('idx'); _head(); _srt('name');
|
||||
};
|
||||
-->
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="idx"><!-- do not remove --></div>
|
||||
</body>
|
||||
</html>
|
||||
803
sample/rutas.json
Normal file
803
sample/rutas.json
Normal file
@@ -0,0 +1,803 @@
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"ruta": "Edificio 1 / Piso 0",
|
||||
"horarios": [
|
||||
{
|
||||
"id": 0,
|
||||
"salon": "A-1",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"salon": "A-2",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"salon": "A-3",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"salon": "A-4",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"salon": "A-5",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"salon": "A-6",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"salon": "A-7",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"salon": "A-8",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"salon": "A-9",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"salon": "A-10",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"salon": "A-11",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"salon": "A-12",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"salon": "A-13",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"salon": "A-14",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"salon": "A-15",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"salon": "A-16",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"salon": "A-17",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"salon": "A-18",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"salon": "A-19",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"ruta": "Edificio 1 / Piso 1",
|
||||
"horarios": [
|
||||
{
|
||||
"id": 0,
|
||||
"salon": "A-1",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"salon": "A-2",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"salon": "A-3",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"salon": "A-4",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"salon": "A-5",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"salon": "A-6",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"salon": "A-7",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"salon": "A-8",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"salon": "A-9",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"salon": "A-10",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"ruta": "Edificio 1 / Piso 2",
|
||||
"horarios": [
|
||||
{
|
||||
"id": 0,
|
||||
"salon": "A-1",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"salon": "A-2",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"salon": "A-3",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"salon": "A-4",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"salon": "A-5",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"salon": "A-6",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"salon": "A-7",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"salon": "A-8",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"salon": "A-9",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"salon": "A-10",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"ruta": "Edificio 1 / Piso 3",
|
||||
"horarios": [
|
||||
{
|
||||
"id": 0,
|
||||
"salon": "A-1",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"salon": "A-2",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"salon": "A-3",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"salon": "A-4",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"salon": "A-5",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"salon": "A-6",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"salon": "A-7",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"salon": "A-8",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"salon": "A-9",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"salon": "A-10",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"salon": "A-11",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"salon": "A-12",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"salon": "A-13",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"ruta": "Edificio 1 / Piso 4",
|
||||
"horarios": [
|
||||
{
|
||||
"id": 0,
|
||||
"salon": "A-1",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"salon": "A-2",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"salon": "A-3",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"salon": "A-4",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"salon": "A-5",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"salon": "A-6",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"salon": "A-7",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"salon": "A-8",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"salon": "A-9",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"salon": "A-10",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"salon": "A-11",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"salon": "A-12",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"salon": "A-13",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"salon": "A-14",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"salon": "A-15",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"salon": "A-16",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"salon": "A-17",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"salon": "A-18",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"ruta": "Edificio 1 / Piso 5",
|
||||
"horarios": [
|
||||
{
|
||||
"id": 0,
|
||||
"salon": "A-1",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"salon": "A-2",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"salon": "A-3",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"salon": "A-4",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"salon": "A-5",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"salon": "A-6",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 2,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"salon": "A-7",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"salon": "A-8",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "11:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"salon": "A-9",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"salon": "A-10",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "10:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"salon": "A-11",
|
||||
"materia": "Sistemas",
|
||||
"hora_inicio": "9:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 3,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"salon": "A-12",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 1,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"salon": "A-13",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "10:00",
|
||||
"estado": 4,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"salon": "A-14",
|
||||
"materia": "Base de datos",
|
||||
"hora_inicio": "8:00",
|
||||
"hora_fin": "8:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"salon": "A-15",
|
||||
"materia": "Matemáticas",
|
||||
"hora_inicio": "11:00",
|
||||
"hora_fin": "9:00",
|
||||
"estado": 0,
|
||||
"comentario": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
224
selector_rutas.php
Normal file
224
selector_rutas.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Supervisor - Seleccionar rutas</title>
|
||||
<?php
|
||||
include 'import/html_css_files.php';
|
||||
?>
|
||||
<style>
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php
|
||||
include "import/html_header.php";
|
||||
html_header(
|
||||
"Selecciona la ruta a supervisar",
|
||||
"Sistema de gestión de checador",
|
||||
);
|
||||
?>
|
||||
<main class="container-fluid px-4" id="app" v-cloak @vue:mounted="mounted">
|
||||
<!-- filtros -->
|
||||
<div class="card mt-4">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center flex-wrap text-white">
|
||||
<h2 class="col-md-10 col-12 text-white font-weight-bold text-uppercase text-center">
|
||||
Facultad de ingeniería: {{ store.rutas.data[store.rutas.selected]?.ruta ?? '' }}
|
||||
</h2>
|
||||
<button type="button" class="btn btn-success btn-sm" data-toggle="modal"
|
||||
data-target="#editar-ubicaciones">
|
||||
<i class="ing-editar"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body bg-info">
|
||||
<div class="container-fluid">
|
||||
<div class="row flex-nowrap overflow-auto">
|
||||
<!-- size big -->
|
||||
<div class="col-9 col-sm-7 col-md-4 col-lg-3 col-xl-2 my-2"
|
||||
v-for="(ruta, index) in store.rutas.data" :key="ruta.id">
|
||||
<span class="shadow badge badge-pill py-2 px-4" @click="store.selectRuta(ruta.id)"
|
||||
:class="{ 'badge-primary': store.rutas.selected == ruta.id, 'badge-light text-primary': store.rutas.selected != ruta.id, 'badge-dark text-muted disabled' : ruta.horarios.every(({estado}) => estado != 0) && store.rutas.selected != ruta.id }">
|
||||
{{ ruta.ruta }}
|
||||
<span class="badge mx-3" v-if="ruta.horarios.some(({estado}) => estado == 0)"
|
||||
:class="{ 'badge-success': ruta.horarios.length > 0, 'badge-danger': ruta.horarios.length == 0 }">
|
||||
{{ruta.horarios.filter(({estado}) => estado != 0).length}} / {{
|
||||
ruta.horarios.length}}
|
||||
</span>
|
||||
<span v-else class="badge mx-3"
|
||||
:class="{ 'badge-success': ruta.horarios.length > 0, 'badge-danger': ruta.horarios.length == 0 }">
|
||||
|
||||
<i class="ing-aceptar"></i>
|
||||
</span>
|
||||
<span class="sr-only">Faltan {{ruta.horarios.filter(({estado}) => estado == 0).length}}
|
||||
horarios por registrar</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-dark d-flex justify-content-between align-items-center flex-wrap text-white">
|
||||
<button class="btn btn-info" :disabled="store.bloquesHorario.selected == 0"
|
||||
@click="store.selectBloque(store.bloquesHorario.selected - 1)">
|
||||
<i class="ing-caret ing-rotate-90"></i>
|
||||
<span class="d-none d-md-inline-block">
|
||||
Bloque horario anterior
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<h3 class="text-white font-weight-bold text-uppercase text-center">
|
||||
{{ store.hora_inicio.slice(0, 5) }} - {{ store.hora_fin.slice(0, 5) }}
|
||||
</h3>
|
||||
|
||||
<button class="btn btn-info" @click="store.selectBloque(store.bloquesHorario.selected + 1)"
|
||||
:disabled="store.bloquesHorario.selected == store.bloquesHorario.data.length - 1">
|
||||
<span class="d-none d-md-inline-block">
|
||||
Bloque horario siguiente
|
||||
</span>
|
||||
<i class="ing-caret ing-rotate-270"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 d-flex justify-content-center flex-wrap">
|
||||
<!-- refresh -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped table-bordered table-sm">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">
|
||||
<button @click="store.invertir" class="btn btn-info mr-3" v-if="clases.length > 0">
|
||||
<i class="ing-cambiar ing-rotate-90"></i>
|
||||
</button>
|
||||
Salón
|
||||
</th>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">Profesor</th>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">Horario</th>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="clases.length == 0">
|
||||
<td colspan="6" class="text-center">No hay clases en este horario</td>
|
||||
</tr>
|
||||
<tr v-for="clase in clases" :key="clase.id">
|
||||
<td class="text-center align-middle">{{ clase.salon }}</td>
|
||||
<td class="text-center align-middle">{{ clase.profesor }} {{ clase.materia }}
|
||||
<td class="text-center align-middle">
|
||||
{{ clase.hora_inicio }} - {{ clase.hora_fin }}
|
||||
</td>
|
||||
<td class="text-center align-middle text-nowrap">
|
||||
<button class="btn btn-outline text-center mx-2" v-for="estado in estados"
|
||||
:key="estado.id" @click="store.cambiarEstado(clase.id, estado.id)"
|
||||
:class="[{'active': estado.id === clase.estado}, `btn-outline-${estado.color}`]">
|
||||
<i :class="estado.icon"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-outline-primary text-center mx-2" data-toggle="modal"
|
||||
data-target="#editar-comentario" :class="{ 'active': clase.comentario != '' }"
|
||||
@click="store.selectEditor(clase.id)">
|
||||
<i class="ing-editar"></i>
|
||||
<span class="badge badge-pill badge-primary"
|
||||
v-if="clase.comentario != ''">...</span>
|
||||
<span class="sr-only">Editar comentario</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODAL -->
|
||||
<div class="modal" tabindex="-1" id="editar-ubicaciones">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Editar rutas</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<h2>Reordena las rutas</h2>
|
||||
<ul id="sortable" class="list-group">
|
||||
<li class="list-group-item" v-for="ruta in store.rutas.data" :key="ruta.id"
|
||||
:id="'ruta-' + ruta.id"
|
||||
:class="[ruta.horarios.every(horario => horario.estado != 0) ? ['disabled', 'bg-light', 'undraggable'] : '']">
|
||||
{{ruta.ruta}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block mb-4" @click="store.guardarCambios">
|
||||
<i class="ing-guardar"></i>
|
||||
Guardar cambios
|
||||
</button>
|
||||
|
||||
<div class="modal" tabindex="-1" id="editar-comentario">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Añadir comentario</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<h2 class="text-center">Comentarios de la clase</h2>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text bg-primary text-white">Comentario
|
||||
<button class="btn btn-light ml-2 text-primary"
|
||||
@click="store.limpiarComentario">
|
||||
<i class="ing-borrar"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<textarea class="form-control" aria-label="Comentarios de la clase"
|
||||
v-model="store.editor.texto"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-danger" data-dismiss="modal">
|
||||
<i class="ing-cancelar"></i>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal"
|
||||
@click="store.guardarComentario">
|
||||
Guardar comentario
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<?php
|
||||
include "import/html_footer.php";
|
||||
?>
|
||||
<!-- filtro modal -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/jquery-ui.js"></script>
|
||||
<script src="js/jquery-ui.touch-punch.min.js"></script>
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
<?php include_once 'js/messages.php'; ?>
|
||||
<script src="https://unpkg.com/petite-vue"></script>
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
27
service/auto.php
Normal file
27
service/auto.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?
|
||||
$ruta = "../";
|
||||
require_once "$ruta/include/bd_pdo.php";
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// json data from service\periodos.v1.php (input)
|
||||
|
||||
$urls = array(
|
||||
'periodos.v1',
|
||||
'periodos.v2',
|
||||
'horarios',
|
||||
);
|
||||
|
||||
$urls = array_map(fn($item) => "../$item.php", $urls);
|
||||
|
||||
ob_start();
|
||||
include_once 'periodos.v1.php';
|
||||
$periodos_v1 = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
ob_start();
|
||||
include_once 'periodos.v2.php';
|
||||
$periodos_v2 = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// echo $periodos_v1;
|
||||
echo $periodos_v2;
|
||||
76
service/backend/carreras.php
Normal file
76
service/backend/carreras.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?
|
||||
$ruta = "../../";
|
||||
require_once "$ruta/include/bd_pdo.php";
|
||||
header('Content-Type: application/json');
|
||||
global $db;
|
||||
// json data from service\periodos.v1.php (input)
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// check if the input is empty
|
||||
|
||||
if (is_response_empty($data)) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'No se recibieron datos',
|
||||
'data' => $data
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// check if data is array
|
||||
if (!is_array($data)) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'La información recibida no es válida',
|
||||
'data' => $data
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* [{
|
||||
* carrera_nombre
|
||||
* carrera_clave
|
||||
* id_nivel
|
||||
* },]
|
||||
*/
|
||||
// check for this schema
|
||||
array_walk($data, function ($item) {
|
||||
if (!isset($item['ClaveCarrera']) || !isset($item['NombreCarrera']) || !isset($item['IdNivel'])) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'Los datos recibidos no son validos',
|
||||
'data' => $item
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
array_walk($data, function ($item) use ($db) {
|
||||
|
||||
if ($db->where('carrera_nombre', "%{$item['NombreCarrera']}", 'ILIKE')->has('carrera'))
|
||||
$db
|
||||
->where('carrera_nombre', "%{$item['NombreCarrera']}", 'ILIKE')
|
||||
->update('carrera', [
|
||||
'carrera_nombre' => $item['NombreCarrera'],
|
||||
'id_referencia' => $item['ClaveCarrera'],
|
||||
]);
|
||||
else {
|
||||
try {
|
||||
$db->insert('carrera', [
|
||||
'carrera_nombre' => $item['NombreCarrera'],
|
||||
'id_referencia' => $item['ClaveCarrera'],
|
||||
'nivel_id' => $item['IdNivel'],
|
||||
]);
|
||||
} catch (PDOException $th) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $th->getMessage(),
|
||||
'last_query' => $db->getLastQuery(),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
});
|
||||
60
service/backend/periodos.php
Normal file
60
service/backend/periodos.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?
|
||||
$ruta = "../../";
|
||||
require_once "$ruta/include/bd_pdo.php";
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// json data from service\periodos.v1.php (input)
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// check if the input is empty
|
||||
|
||||
if (is_response_empty($data)) {
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'No se recibieron datos',
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"IdNivel": 1,
|
||||
"IdPeriodo": 635,
|
||||
"NombreNivel": "LICENCIATURA",
|
||||
"NombrePeriodo": "241",
|
||||
"in_db": false
|
||||
inicio,
|
||||
fin
|
||||
}
|
||||
*/
|
||||
|
||||
// insert into database
|
||||
setlocale(LC_TIME, 'es_MX.UTF-8');
|
||||
$formatter = new IntlDateFormatter('es_MX', IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'America/Mexico_City', IntlDateFormatter::GREGORIAN, 'MMMM');
|
||||
$inicio = strtotime($data['inicio']);
|
||||
$fin = strtotime($data['fin']);
|
||||
try {
|
||||
|
||||
$result = $db->insert('periodo', [
|
||||
'id_reference' => $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']);
|
||||
} catch (PDOException $th) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $th->getMessage()
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
echo json_encode($result ? [
|
||||
'success' => true,
|
||||
'message' => 'Periodo agregado correctamente'
|
||||
] : [
|
||||
'success' => false,
|
||||
'message' => 'Error al agregar el periodo'
|
||||
]);
|
||||
155
service/client.html
Normal file
155
service/client.html
Normal file
@@ -0,0 +1,155 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Cliente REST</title>
|
||||
<script type="module" src="../js/client.js" defer></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="../css/indivisa.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header class="container-fluid bg-dark text-white text-center">
|
||||
Página de Cliente REST
|
||||
</header>
|
||||
<main class="container" @vue:mounted="mounted">
|
||||
<div v-for="error in store.errors" class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<strong>Error!</strong> {{error}}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Periodos activos</h1>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
|
||||
<li href="#" class="list-group-item" v-for="(periodo, index) in store.periodosV1" :key="periodo.IdPeriodo">
|
||||
<!-- v1
|
||||
IdNivel: number;
|
||||
IdPeriodo: number;
|
||||
NombreNivel: string;
|
||||
NombrePeriodo: string;
|
||||
-->
|
||||
<div class="row">
|
||||
<span class="badge bg-secondary">{{index}}</span>
|
||||
<h2 class="text-center">
|
||||
Información
|
||||
</h2>
|
||||
<div class="col-md-3">
|
||||
<strong>ID Nivel:</strong> {{periodo.IdNivel}}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<strong>ID Periodo:</strong> {{periodo.IdPeriodo}}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<strong>Nombre Nivel:</strong> {{periodo.NombreNivel}}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<strong>Nombre Periodo:</strong> {{periodo.NombrePeriodo}}
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
FechaFin: string;
|
||||
FechaInicio: string;
|
||||
IdPeriodo: number;
|
||||
-- info(IdPeriodo) --
|
||||
-->
|
||||
<div class="row mt-2" v-if="complete(periodo.IdPeriodo)">
|
||||
<h2 class="text-center">
|
||||
Fechas
|
||||
</h2>
|
||||
<div class="col-md-2">
|
||||
<strong>Fecha Inicio:</strong> {{info(periodo.IdPeriodo).FechaInicio}}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<strong>Fecha Fin:</strong> {{info(periodo.IdPeriodo).FechaFin}}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!periodo.in_db" class="row mt-2">
|
||||
<!--
|
||||
PeriodoV2
|
||||
ClaveCarrera: string;
|
||||
NombreCarrera: string;
|
||||
PeriodoV1
|
||||
IdNivel: number;
|
||||
-->
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-primary float-end" @click="store.addPeriodo(periodo)"
|
||||
:disabled="!complete(periodo.IdPeriodo)">
|
||||
Agregar
|
||||
<i class="ing ing-mas"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="row mt-2">
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-success float-end disabled">
|
||||
Agregado
|
||||
<i class="ing-aceptar"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-secondary float-end" @click="store.addCarreras(periodo.IdPeriodo)">
|
||||
Sincronizar Carreras
|
||||
<i class="ing ing-link"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion mt-2">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" data-bs-toggle="collapse"
|
||||
:data-bs-target="`#collapse-${periodo.IdPeriodo}`">
|
||||
Horarios del periodo
|
||||
</button>
|
||||
</h2>
|
||||
<div :id="`collapse-${periodo.IdPeriodo}`" class="accordion-collapse collapse">
|
||||
<div class="accordion-body">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"
|
||||
v-for="periodo in store.periodosV2.filter(periodov2 => periodov2.IdPeriodo === periodo.IdPeriodo)">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<strong>Clave Carrera:</strong> {{periodo.ClaveCarrera}}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<strong>Nombre Carrera:</strong> {{periodo.NombreCarrera}}
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<span class="badge float-end mx-1"
|
||||
:class="periodo.linked ?'bg-success':'bg-secondary'">
|
||||
<i class="ing-link"></i>
|
||||
</span>
|
||||
<span class="badge float-end mx-1"
|
||||
:class="periodo.in_db ?'bg-success':'bg-secondary'">
|
||||
<i class="ing-aceptar"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
<footer>
|
||||
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
81
service/horarios.php
Normal file
81
service/horarios.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?
|
||||
/*
|
||||
• idPeriodo: identificador del periodo a consultar (obligatorio, número entero)
|
||||
• claveFacultad: clave de la facultad a consultar (opcional, cadena)
|
||||
• claveCarrera: clave de la carrera a consultar (opcional, cadena)
|
||||
• claveProfesor: clave del empleado a consultar (opcional, cadena)
|
||||
• fecha: fecha de la clase (opcional, cadena en formato yyyy-MM-dd)
|
||||
*/
|
||||
$required_params = [
|
||||
'idPeriodo'
|
||||
];
|
||||
|
||||
$optional_params = [
|
||||
'claveFacultad',
|
||||
'claveCarrera',
|
||||
'claveProfesor',
|
||||
'fecha'
|
||||
];
|
||||
|
||||
// Check if all required params are present in $_GET
|
||||
$params = array_map('strtolower', $_GET); // Convert keys to lowercase for case-insensitive comparison
|
||||
|
||||
// Check for missing required parameters
|
||||
$missing_params = array_diff($required_params, array_keys($params));
|
||||
if (!empty($missing_params)) {
|
||||
$missing_params_str = implode(', ', $missing_params);
|
||||
die("Missing required parameter(s): $missing_params_str");
|
||||
}
|
||||
|
||||
// Filter and retain only the required and optional parameters
|
||||
$params = array_filter($params, function ($key) use ($required_params, $optional_params) {
|
||||
return in_array($key, $required_params) || in_array($key, $optional_params);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
$curl = curl_init();
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://portal.ulsa.edu.mx/servicios/AuditoriaAsistencialRest/AuditoriaAsistencialService.svc/auditoriaAsistencial/seleccionar",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 0,
|
||||
CURLOPT_CUSTOMREQUEST => "POST",
|
||||
CURLOPT_POSTFIELDS => json_encode($params),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"token: e12e2dde0e95a32e274328fd274e07d53f127630c211d838efffacd3cafc4f14edf3f3de6a649eb23f98edf6a1863a008f60e78a316d4dec996b79aeea161a0c",
|
||||
"username: SGU_APSA_AUD_ASIST",
|
||||
"Content-Type: application/json"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
if ($err)
|
||||
die("cURL Error #:$err");
|
||||
|
||||
|
||||
$selectedData = json_decode($response, true);
|
||||
|
||||
$rawInput = file_get_contents('php://input');
|
||||
|
||||
$input = json_decode($rawInput, true);
|
||||
// check for {collect: []} in raw input
|
||||
if (isset($input['collect']) && is_array($input['collect'])) {
|
||||
$collect = $input['collect'];
|
||||
$selectedData = array_map(function ($item) use ($collect) {
|
||||
return array_intersect_key($item, array_flip($collect));
|
||||
}, $selectedData);
|
||||
// unique and distinct
|
||||
$selectedData = array_unique($selectedData, SORT_REGULAR);
|
||||
}
|
||||
else {
|
||||
// return invalid request error
|
||||
die($rawInput);
|
||||
}
|
||||
|
||||
// Output the selected data directly
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($selectedData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
35
service/periodos.v1.php
Normal file
35
service/periodos.v1.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?
|
||||
$ruta = "../";
|
||||
require_once '../include/bd_pdo.php';
|
||||
$curl = curl_init();
|
||||
global $db;
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://portal.ulsa.edu.mx/servicios/AuditoriaAsistencialRest/AuditoriaAsistencialService.svc/auditoriaAsistencial/catalogos/periodos/v1/seleccionar",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POSTFIELDS => "",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"token: f65f921264e4ab135472adb5a946212dd4b995d929294afec31eef192b8de8d6a076648906f70012c9803e5918d0fc99499d7d1fb7c998cc06c7a10eef61f66a",
|
||||
"username: SGU_APSA_AUD_ASIST"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
if ($err)
|
||||
die("cURL Error #:$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');
|
||||
return $item;
|
||||
}, $data);
|
||||
|
||||
$selectedData = array_unique($in_db, SORT_REGULAR);
|
||||
|
||||
// Output the selected data directly
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($selectedData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
37
service/periodos.v2.php
Normal file
37
service/periodos.v2.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?
|
||||
$ruta = "../";
|
||||
require_once '../include/bd_pdo.php';
|
||||
global $db;
|
||||
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://portal.ulsa.edu.mx/servicios/AuditoriaAsistencialRest/AuditoriaAsistencialService.svc/auditoriaAsistencial/catalogos/periodos/v2/seleccionar",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POSTFIELDS => "",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"token: f65f921264e4ab135472adb5a946212dd4b995d929294afec31eef192b8de8d6a076648906f70012c9803e5918d0fc99499d7d1fb7c998cc06c7a10eef61f66a",
|
||||
"username: SGU_APSA_AUD_ASIST"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
if ($err)
|
||||
die("cURL Error #:$err");
|
||||
|
||||
|
||||
$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');
|
||||
return $item;
|
||||
}, $json);
|
||||
|
||||
// Output the selected data directly
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($selectedData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
734
supervisor.php
Normal file
734
supervisor.php
Normal file
@@ -0,0 +1,734 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Supervisor</title>
|
||||
<?php
|
||||
include 'import/html_css_files.php';
|
||||
?>
|
||||
<style>
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?
|
||||
$redirect = $_SERVER['PHP_SELF'];
|
||||
include "import/html_header.php";
|
||||
// 200.0.0.1/checador_otros/admin_checdor/[this_page].php => ruta = [this_page].php
|
||||
global $user;
|
||||
html_header(
|
||||
"Registro de asistencia - Vicerrectoría Académica",
|
||||
"Sistema de gestión de checador",
|
||||
);
|
||||
?>
|
||||
<main class="container-fluid px-4" id="app" v-cloak @vue:mounted="mounted">
|
||||
<!-- error messages -->
|
||||
<div class="container mb-4 mt-2">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-dismissible fade show" role="alert" v-for="message in messages.data"
|
||||
:class="`alert-${message.color}`" :key="message.hora">
|
||||
<!-- messages: {error, hora} -->
|
||||
<div :key="message" class="d-flex justify-content-between">
|
||||
<span>
|
||||
<code>[{{message.hora}}]</code>
|
||||
<strong>{{message.prefix}}</strong>
|
||||
</span>
|
||||
{{ message.message }}
|
||||
<button type="button" class="close"
|
||||
@click="messages.data.splice(messages.data.indexOf(message), 1)" data-dismiss="alert">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- filtros -->
|
||||
<div v-if="store.rutas.data.length > 0">
|
||||
<div class="card mt-4">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center flex-wrap text-white">
|
||||
<h2 class="col-md-10 col-12 text-white font-weight-bold text-uppercase text-center">
|
||||
{{ JSON.parse(store.rutas.data.find(ruta => ruta.salon_id === store.rutas.selected)?.salon_array
|
||||
?? null)?.splice(1)?.join('/') ?? 'No datos' }}
|
||||
</h2>
|
||||
<div>
|
||||
<button type="button" class="btn btn-info btn-sm"
|
||||
@click="store.rutas.data = []; header = 'Seleccione una ruta'">
|
||||
<i class="ing-flecha ing-rotate-180"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-success btn-sm" data-toggle="modal"
|
||||
data-target="#editar-ubicaciones">
|
||||
<i class="ing-editar"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body bg-info">
|
||||
<div class="container-fluid">
|
||||
<div class="row flex-nowrap mw-100 overflow-auto">
|
||||
<!-- size big -->
|
||||
<div class="mx-2 my-2 col-auto" v-for="ruta in store.rutas.data" :key="ruta.salon_id">
|
||||
<span class="shadow badge badge-pill py-2 px-4" @click="store.selectRuta(ruta.salon_id)"
|
||||
:class="{ 'badge-primary': store.rutas.selected == ruta.salon_id, 'badge-light text-primary': store.rutas.selected != ruta.salon_id, 'badge-dark text-muted disabled' : ruta.horarios.every(({estado_supervisor_id}) => estado_supervisor_id) && store.rutas.selected != ruta.salon_id }">
|
||||
{{ JSON.parse(ruta.salon_array).splice(1).join('/') }}
|
||||
<span class="badge mx-3"
|
||||
v-if="ruta.horarios.some(({estado_supervisor_id}) => !estado_supervisor_id)"
|
||||
:class="{ 'badge-success': ruta.horarios.length > 0, 'badge-danger': ruta.horarios.length == 0 }">
|
||||
{{ ruta.horarios.filter(({estado_supervisor_id}) => estado_supervisor_id).length
|
||||
}} / {{
|
||||
ruta.horarios.length }}
|
||||
</span>
|
||||
<span v-else class="badge mx-3"
|
||||
:class="{ 'badge-success': ruta.horarios.length > 0, 'badge-danger': ruta.horarios.length == 0 }">
|
||||
|
||||
<i class="ing-aceptar"></i>
|
||||
</span>
|
||||
<span class="sr-only">
|
||||
Faltan {{ ruta.horarios.filter(({estado_supervisor_id}) =>
|
||||
estado_supervisor_id).length }} horarios
|
||||
por registrar
|
||||
</span>
|
||||
|
||||
<span class="badge mx-1 badge-warning" @click="location.hash = '#sin-internet'"
|
||||
v-if="ruta.horarios.some(({pendiente}) => pendiente)">
|
||||
<i class="ing-importante2"></i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer bg-dark d-flex justify-content-between align-items-center flex-wrap text-white">
|
||||
<button class="btn btn-info" :disabled="store.bloquesHorario.selected == 0"
|
||||
@click="store.selectBloque(store.bloquesHorario.selected - 1); rutas(current_espacio)">
|
||||
<i class="ing-caret ing-rotate-90"></i>
|
||||
<span class="d-none d-md-inline-block">
|
||||
Bloque horario anterior
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<h3 class="text-white font-weight-bold text-uppercase text-center">
|
||||
{{ store.hora_inicio.slice(0, 5) }} - {{ store.hora_fin.slice(0, 5) }}
|
||||
</h3>
|
||||
|
||||
<button class="btn btn-info"
|
||||
@click="store.selectBloque(store.bloquesHorario.selected + 1); rutas(current_espacio)"
|
||||
:disabled="store.bloquesHorario.selected == store.bloquesHorario.data.length - 1">
|
||||
<span class="d-none d-md-inline-block">
|
||||
Bloque horario siguiente
|
||||
</span>
|
||||
<i class="ing-caret ing-rotate-270"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<section id="#warnings" class="mt-4" v-if="clases.some(clase => clase.pendiente)">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<h4 class="alert-heading"><i class="ing-importante2"></i> Sin conexión a internet</h4>
|
||||
<p>
|
||||
Hay datos en esta ruta que no pudieron guardarse, por favor, revise su conexión a internet y dé
|
||||
click en
|
||||
<button class="btn btn-outline-dark btn-sm mb-4" @click="guardarCambios"><i
|
||||
class="ing-guardar"></i> Guardar cambios</button>
|
||||
</p>
|
||||
<hr>
|
||||
<p class="mb-0">
|
||||
Los datos se mantendrán mientras tenga la página abierta, pero si la cierra o la refresca, se
|
||||
perderán.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<div class="mt-3 d-flex justify-content-center">
|
||||
<!-- refresh -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped table-bordered table-sm">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">
|
||||
<button @click="invertir" class="btn btn-info mr-3" v-if="clases.length > 0">
|
||||
<i class="ing-cambiar ing-rotate-90"></i>
|
||||
</button>
|
||||
Salón
|
||||
</th>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">Profesor</th>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">Horario</th>
|
||||
<th scope="col" class="text-center align-middle text-nowrap px-2">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="clases.length == 0">
|
||||
<td colspan="6" class="text-center">No hay clases en este horario</td>
|
||||
</tr>
|
||||
<tr v-for="clase in clases" :key="clase.horario_id">
|
||||
<td class="text-center align-middle">{{ clase.salon }}</td>
|
||||
<td class="text-center align-middle">
|
||||
<div class="col-12">
|
||||
{{ clase.profesor_nombre }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="button" class="btn btn-outline-dark btn-sm"
|
||||
@click="store.profesor_selected = clase.horario_id" data-toggle="modal"
|
||||
data-target="#ver-detalle">
|
||||
Ver detalle <i class="ing-ojo"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</td>
|
||||
<td class="text-center align-middle">
|
||||
{{ clase.hora_inicio.slice(0, 5) }} - {{ clase.hora_fin.slice(0, 5) }}
|
||||
</td>
|
||||
<td class="text-center align-middle text-nowrap">
|
||||
<!-- data-toggle="button" -->
|
||||
<button class="btn text-center mx-2" v-for="estado in estados" :key="estado.id"
|
||||
@click="store.cambiarEstado(clase.horario_id, estado.id === clase.estado_supervisor_id ? null : estado.id)"
|
||||
:class="[{'active': estado.id === clase.estado_supervisor_id}, `btn-outline-${estado.color}`]"
|
||||
:aria-pressed="estado.id === clase.estado_supervisor_id">
|
||||
<i :class="estado.icon"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-outline-primary text-center mx-2" data-toggle="modal"
|
||||
data-target="#editar-comentario" :class="{ 'active': clase.comentario }"
|
||||
@click="store.selectEditor(clase.horario_id)">
|
||||
<i class="ing-editar"></i>
|
||||
<span class="badge badge-pill badge-primary" v-if="clase.comentario">...</span>
|
||||
<span class="sr-only">Editar comentario</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block mb-4" @click="guardarCambios">
|
||||
<i class="ing-guardar"></i>
|
||||
Guardar cambios
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<div v-else-if="store.bloquesHorario.selected === -1">
|
||||
<div class="list-group my-4 container">
|
||||
<div class="card text-center">
|
||||
<div class="card-header bg-dark text-white">
|
||||
<h2 class="text-center">
|
||||
{{header}}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body" v-if="!loading">
|
||||
<a :href="`#horario-${horario.id}`" class="list-group-item list-group-item-action"
|
||||
v-for="horario in store.bloquesHorario.data" :key="horario.id"
|
||||
@click="store.bloquesHorario.selected = store.bloquesHorario.data.indexOf(horario)">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">{{ horario.hora_inicio.slice(0, 5) }} - {{horario.hora_fin.slice(0, 5)
|
||||
}}</h5>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body" v-else>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Cargando...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer text-muted bg-dark text-white">
|
||||
Lista de bloques horario
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="list-group my-4 container">
|
||||
<div class="card text-center">
|
||||
<div class="card-header bg-dark text-white">
|
||||
<h2 class="text-center">
|
||||
{{header}}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body" v-if="!loading">
|
||||
<a :href="`#ruta-${ruta.id_espacio_sgu}`" class="list-group-item list-group-item-action"
|
||||
v-for="ruta in catálogo_rutas.data" :key="ruta.salon_id" @click="rutas(ruta.id_espacio_sgu)"
|
||||
disabled>
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">{{ ruta.salon }}</h5>
|
||||
<small v-if="ruta.subrutas.length > 0">{{ ruta.subrutas.length }} espacios</small>
|
||||
<small v-else class="text-danger">Sin espacios</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body" v-else>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="sr-only">Cargando...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer text-muted bg-dark text-white">
|
||||
Rutas de la Universidad La Salle
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- MODAL -->
|
||||
<div class="modal" tabindex="-1" id="editar-ubicaciones">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Editar rutas</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<h2>Reordena las rutas</h2>
|
||||
<ul id="sortable" class="list-group">
|
||||
<li class="list-group-item" v-for="ruta in store.rutas.data" :key="ruta.salon_id"
|
||||
:id="'ruta-' + ruta.salon_id"
|
||||
:class="[ruta.horarios.every(horario => horario.estado_supervisor_id) ? ['disabled', 'bg-light', 'undraggable'] : '']">
|
||||
{{ JSON.parse(ruta.salon_array).join('/') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal" tabindex="-1" id="editar-comentario">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Añadir comentario</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<h2 class="text-center">Comentarios de la clase</h2>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text bg-primary text-white">Comentario
|
||||
<button class="btn btn-light ml-2 text-primary"
|
||||
@click="store.limpiarComentario">
|
||||
<i class="ing-borrar"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<textarea class="form-control" aria-label="Comentarios de la clase"
|
||||
v-model="store.editor.texto"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-danger" data-dismiss="modal">
|
||||
<i class="ing-cancelar"></i>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal"
|
||||
@click="store.guardarComentario">
|
||||
Guardar comentario
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>Detalle de la clase</h2>
|
||||
<button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container" v-if="store.profesor_selected">
|
||||
<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 }}
|
||||
</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 }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Carrera:</strong>
|
||||
{{ clase_vista.carrera }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Grupo:</strong>
|
||||
{{ clase_vista.horario_grupo }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Horario:</strong>
|
||||
<!-- hora hh:mm:ss to hh:mm -->
|
||||
{{ clase_vista.hora_inicio?.slice(0, 5) }} - {{
|
||||
clase_vista.hora_fin?.slice(0, 5) }}
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Salón:</strong>
|
||||
{{ clase_vista.salon }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="row">
|
||||
<section class="col-12">
|
||||
<h4 class="h4 mt-4">Registro</h4>
|
||||
<div class="row">
|
||||
<div class="col-12 text-center" v-if="!clase_vista.registro_fecha">
|
||||
<strong><span class="badge badge-danger"><i class="ing-cancelar"></i></span>
|
||||
El profesor aún no ha registrado su asistencia</strong>
|
||||
</div>
|
||||
<div class="col-6 text-center" v-else>
|
||||
El profesor registró su asistencia a las
|
||||
<code>{{clase_vista.registro_fecha.slice(11, 16)}}</code>
|
||||
<hr>
|
||||
<p v-if="!clase_vista.registro_retardo" class="text-center">
|
||||
<span class="badge badge-success"><i class="ing-aceptar"></i></span>
|
||||
A tiempo
|
||||
</p>
|
||||
<p v-else class="text-center">
|
||||
<span class="badge badge-warning"><i class="ing-retardo"></i></span>
|
||||
Con retardo
|
||||
</p>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<?php
|
||||
include "import/html_footer.php";
|
||||
?>
|
||||
<!-- filtro modal -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/jquery-ui.js"></script>
|
||||
<script src="js/jquery-ui.touch-punch.min.js"></script>
|
||||
<script src="js/bootstrap/bootstrap.min.js"></script>
|
||||
<?php include_once 'js/messages.php'; ?>
|
||||
<script src="https://unpkg.com/petite-vue"></script>
|
||||
<script>
|
||||
const estados = [
|
||||
{
|
||||
color: "success",
|
||||
icon: "ing-autorizar",
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
color: "danger",
|
||||
icon: "ing-negar",
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
color: "warning",
|
||||
icon: "ing-retardo",
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
color: "info",
|
||||
icon: "ing-justificar",
|
||||
id: 4,
|
||||
},
|
||||
];
|
||||
|
||||
const messages = PetiteVue.reactive({
|
||||
data: [],
|
||||
push_message(message, silent = false) {
|
||||
if (silent) {
|
||||
console.log(message);
|
||||
return
|
||||
}
|
||||
// go to the top
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
this.data.push(message);
|
||||
setTimeout(() => {
|
||||
this.data.pop();
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
const store = PetiteVue.reactive({
|
||||
messages,
|
||||
bloquesHorario: {
|
||||
data: [],
|
||||
selected: 0
|
||||
},
|
||||
rutas: {
|
||||
data: [],
|
||||
selected: 0
|
||||
},
|
||||
editor: {
|
||||
id: 0,
|
||||
texto: "",
|
||||
},
|
||||
get hora_inicio() {
|
||||
return this.bloquesHorario.data[this.bloquesHorario.selected]?.hora_inicio ?? "";
|
||||
},
|
||||
get hora_fin() {
|
||||
return this.bloquesHorario.data[this.bloquesHorario.selected]?.hora_fin ?? "";
|
||||
},
|
||||
|
||||
selectRuta(index) {
|
||||
this.rutas.selected = index;
|
||||
},
|
||||
order() {
|
||||
const finals = this.rutas.data.filter(ruta => 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
|
||||
selectBloque(bloqueIndex) {
|
||||
this.bloquesHorario.selected = bloqueIndex;
|
||||
},
|
||||
|
||||
// estado
|
||||
async cambiarEstado(horario_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);
|
||||
clase.estado_supervisor_id = estadoId;
|
||||
|
||||
try {
|
||||
if (!navigator.onLine) {
|
||||
clase.pendiente = true;
|
||||
throw ("No hay conexión a internet");
|
||||
}
|
||||
|
||||
const cambio = await fetch("action/registro_supervisor.php", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify([{
|
||||
horario_id: horario_id,
|
||||
estado: estadoId,
|
||||
profesor_id: clase.profesor_id,
|
||||
comentario: clase.comentario,
|
||||
supervisor_id: <?= $user->user['id'] ?>,
|
||||
}])
|
||||
}).then(res => res.json());
|
||||
|
||||
if (cambio.error) throw cambio.error;
|
||||
clase.pendiente = false;
|
||||
|
||||
} catch (error) {
|
||||
messages.push_message({
|
||||
message: error,
|
||||
hora: new Date().toLocaleTimeString('es-MX', { timeZone: 'America/Mexico_City' }),
|
||||
color: "danger",
|
||||
prefix: "Error",
|
||||
}, true);
|
||||
}
|
||||
// scroll to the top only if this ruta has no clases with estado 0
|
||||
if (ruta.horarios.every(clase => clase.estado_supervisor_id != null))
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
this.order();
|
||||
},
|
||||
|
||||
// editor
|
||||
selectEditor(horario_id) {
|
||||
this.editor.id = horario_id;
|
||||
this.editor.texto = store.rutas.data.find(ruta => ruta.salon_id == this.rutas.selected).horarios.find(clase => clase.horario_id == horario_id).comentario;
|
||||
},
|
||||
guardarComentario() {
|
||||
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);
|
||||
},
|
||||
limpiarComentario() {
|
||||
this.editor.texto = "";
|
||||
},
|
||||
profesor_selected: null,
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
$("#sortable").sortable({
|
||||
update: function (event, ui) {
|
||||
// get the new order
|
||||
var newOrder = $(this).children().map(function () {
|
||||
// id = ruta-{id}
|
||||
return parseInt(this.id.split('-')[1]);
|
||||
|
||||
}).get();
|
||||
// store the new order
|
||||
store.rutas.data = newOrder.map(function (id) {
|
||||
return store.rutas.data.find(function (ruta) {
|
||||
return ruta.salon_id === id;
|
||||
});
|
||||
});
|
||||
},
|
||||
items: "li:not(.undraggable)"
|
||||
}).disableSelection();
|
||||
|
||||
$('#sortable>li:not(.undraggable)').draggable({
|
||||
axis: 'y',
|
||||
containment: 'parent',
|
||||
})
|
||||
});
|
||||
|
||||
PetiteVue.createApp({
|
||||
store,
|
||||
messages,
|
||||
header: "Cargando auditoría",
|
||||
loading: true,
|
||||
catálogo_rutas: {
|
||||
data: [],
|
||||
selected: 0
|
||||
},
|
||||
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() {
|
||||
|
||||
try {
|
||||
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: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(store.rutas.data.map(ruta => ruta.horarios).flat(1).filter(clase => clase.pendiente).map(clase => ({
|
||||
horario_id: clase.horario_id,
|
||||
estado: clase.estado_supervisor_id,
|
||||
profesor_id: clase.profesor_id,
|
||||
comentario: clase.comentario,
|
||||
supervisor_id: <?= $user->user['id'] ?>,
|
||||
}))),
|
||||
}).then(res => res.json());
|
||||
|
||||
if (cambio.error) throw cambio.error;
|
||||
} catch (error) {
|
||||
messages.push_message({
|
||||
message: error,
|
||||
hora: new Date().toLocaleTimeString('es-MX', { timeZone: 'America/Mexico_City' }),
|
||||
color: "danger",
|
||||
prefix: "Error",
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
store.rutas.data.map(ruta => ruta.horarios).flat(1).filter(clase => clase.pendiente).forEach(clase => clase.pendiente = false);
|
||||
messages.push_message({
|
||||
message: "Cambios guardados",
|
||||
hora: new Date().toLocaleTimeString('es-MX', { timeZone: 'America/Mexico_City' }),
|
||||
color: "success",
|
||||
prefix: "Éxito",
|
||||
});
|
||||
},
|
||||
invertir() {
|
||||
this.clases.reverse();
|
||||
},
|
||||
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);
|
||||
if (store.bloquesHorario.selected == -1) {
|
||||
this.header = "Seleccione un horario";
|
||||
}
|
||||
else {
|
||||
this.header = "Seleccione una ruta";
|
||||
}
|
||||
|
||||
|
||||
this.catálogo_rutas.data = await fetch('action/rutas.php').then(res => res.json());
|
||||
this.loading = false;
|
||||
|
||||
},
|
||||
current_espacio: null,
|
||||
async rutas(id_espacio_sgu) {
|
||||
store.rutas.data = [];
|
||||
store.rutas.selected = 0;
|
||||
this.loading = true;
|
||||
this.current_espacio = id_espacio_sgu;
|
||||
this.loading = true;
|
||||
this.header = `Cargando rutas para ${this.catálogo_rutas.data.find(ruta => ruta.id_espacio_sgu == id_espacio_sgu).salon}`;
|
||||
this.catálogo_rutas.selected = id_espacio_sgu;
|
||||
const url = 'action/rutas_salón_horario.php'
|
||||
const searchParams = new URLSearchParams({
|
||||
id_espacio_sgu: id_espacio_sgu,
|
||||
bloque_horario_id: store.bloquesHorario.data[store.bloquesHorario.selected].id
|
||||
});
|
||||
const rutas = await fetch(`${url}?${searchParams}`).then(res => res.json());
|
||||
store.rutas.data = rutas.filter(ruta => ruta.horarios.length > 0);
|
||||
|
||||
if (store.rutas.data.length == 0) {
|
||||
this.header = `No hay clases en este horario`;
|
||||
this.loading = false;
|
||||
return
|
||||
}
|
||||
|
||||
store.rutas.selected = store.rutas.data[0].salon_id;
|
||||
|
||||
store.order();
|
||||
// inject horarios
|
||||
this.loading = false;
|
||||
|
||||
},
|
||||
get clase_vista() {
|
||||
return this.clases.find(clase => clase.horario_id == store.profesor_selected) ?? false;
|
||||
},
|
||||
}).mount('#app')
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
237
ts/auditoría.ts
Normal file
237
ts/auditoría.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'
|
||||
import { text } from 'stream/consumers';
|
||||
type Registro = {
|
||||
carrera: string;
|
||||
carrera_id: number;
|
||||
comentario: null;
|
||||
dia: string;
|
||||
duracion: string;
|
||||
duracion_id: number;
|
||||
estado_supervisor_id: number;
|
||||
facultad: string;
|
||||
facultad_id: number;
|
||||
horario_dia: number;
|
||||
horario_fin: string;
|
||||
horario_grupo: string;
|
||||
horario_hora: string;
|
||||
horario_id: number;
|
||||
materia: string;
|
||||
materia_id: number;
|
||||
nombre: string;
|
||||
periodo: string;
|
||||
periodo_id: number;
|
||||
profesor_clave: string;
|
||||
profesor_correo: string;
|
||||
profesor_grado: null;
|
||||
profesor_id: number;
|
||||
profesor_nombre: string;
|
||||
registro_fecha: null;
|
||||
registro_fecha_ideal: Date;
|
||||
registro_fecha_supervisor: Date;
|
||||
registro_id: number;
|
||||
registro_justificada: null;
|
||||
registro_retardo: null;
|
||||
salon: string;
|
||||
salon_id: number;
|
||||
supervisor_id: number;
|
||||
}
|
||||
|
||||
type Estado = {
|
||||
color: string;
|
||||
icon: string;
|
||||
estado_supervisor_id: number;
|
||||
}
|
||||
|
||||
type Facultad = {
|
||||
clave_dependencia: string;
|
||||
facultad_id: number;
|
||||
facultad_nombre: string;
|
||||
}
|
||||
|
||||
type Filter = {
|
||||
type: string;
|
||||
value: string;
|
||||
icon: string;
|
||||
field: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const store = reactive({
|
||||
loading: false,
|
||||
current: {
|
||||
comentario: '',
|
||||
clase_vista: null,
|
||||
empty: '',
|
||||
},
|
||||
facultades: {
|
||||
data: [] as Facultad[],
|
||||
async fetch() {
|
||||
this.data = [] as Facultad[]
|
||||
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,
|
||||
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: [] as Estado[],
|
||||
async fetch() {
|
||||
this.data = [] as Estado[]
|
||||
const res = await fetch('action/action_estado_supervisor.php')
|
||||
this.data = await res.json()
|
||||
},
|
||||
getEstado(id: number): Estado {
|
||||
return this.data.find((estado: Estado) => estado.estado_supervisor_id === id)
|
||||
},
|
||||
printEstados() {
|
||||
if (store.filters.estados.length > 0)
|
||||
document.querySelector('#estados')!.innerHTML = store.filters.estados.map((estado: number) =>
|
||||
`<span class="mx-2 badge badge-${store.estados.getEstado(estado).estado_color}">
|
||||
<i class="${store.estados.getEstado(estado).estado_icon}"></i> ${store.estados.getEstado(estado).nombre}
|
||||
</span>`
|
||||
).join('')
|
||||
else
|
||||
document.querySelector('#estados')!.innerHTML = `Todos los registros`
|
||||
}
|
||||
},
|
||||
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 []
|
||||
return newArray
|
||||
},
|
||||
})
|
||||
|
||||
declare var $: any
|
||||
|
||||
|
||||
|
||||
type Profesor = {
|
||||
profesor_id: number;
|
||||
profesor_nombre: string;
|
||||
profesor_correo: string;
|
||||
profesor_clave: string;
|
||||
profesor_grado: string;
|
||||
}
|
||||
|
||||
createApp({
|
||||
store,
|
||||
get clase_vista() {
|
||||
return store.current.clase_vista
|
||||
},
|
||||
registros: {
|
||||
data: [] as Registro[],
|
||||
async fetch() {
|
||||
this.loading = true
|
||||
this.data = [] as Registro[]
|
||||
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: number) {
|
||||
const registro = this.data.find((registro: 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: 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);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
get profesores() {
|
||||
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)
|
||||
)
|
||||
},
|
||||
async mounted() {
|
||||
await this.registros.fetch()
|
||||
await store.facultades.fetch()
|
||||
await store.estados.fetch()
|
||||
store.filters.switchFechas()
|
||||
}
|
||||
}).mount('#app')
|
||||
149
ts/client.ts
Normal file
149
ts/client.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
// @ts-ignore Import module
|
||||
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'
|
||||
|
||||
export interface PeridoV1 {
|
||||
IdNivel: number;
|
||||
IdPeriodo: number;
|
||||
NombreNivel: string;
|
||||
NombrePeriodo: string;
|
||||
in_db: boolean;
|
||||
linked: boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface PeridoV2 {
|
||||
ClaveCarrera: string;
|
||||
FechaFin: string;
|
||||
FechaInicio: string;
|
||||
IdPeriodo: number;
|
||||
NombreCarrera: string;
|
||||
}
|
||||
|
||||
const webServices = {
|
||||
getPeriodosV1: async (): Promise<PeridoV1[]> => {
|
||||
try {
|
||||
const response = await fetch('periodos.v1.php');
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
getPeriodosV2: async (): Promise<PeridoV2[]> => {
|
||||
try {
|
||||
const response = await fetch('periodos.v2.php');
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const store = reactive({
|
||||
periodosV1: [] as PeridoV1[],
|
||||
periodosV2: [] as PeridoV2[],
|
||||
|
||||
errors: [] as string[],
|
||||
fechas(idPeriodo: number): { inicio: string, fin: string } {
|
||||
const periodo = this.periodosV2.find((periodo: PeridoV2) => periodo.IdPeriodo === idPeriodo);
|
||||
return {
|
||||
inicio: periodo ? periodo.FechaInicio : '',
|
||||
fin: periodo ? periodo.FechaFin : ''
|
||||
}
|
||||
},
|
||||
periodov1(idPeriodo: number): PeridoV1 | undefined {
|
||||
return this.periodosV1.find((periodo: PeridoV1) => periodo.IdPeriodo === idPeriodo);
|
||||
},
|
||||
periodov2(idPeriodo: number): PeridoV2[] {
|
||||
return this.periodosV2.filter((periodo: PeridoV2) => periodo.IdPeriodo === idPeriodo);
|
||||
},
|
||||
async addPeriodo(periodo: PeridoV1 | PeridoV2) {
|
||||
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: PeridoV1) => {
|
||||
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: number) {
|
||||
try {
|
||||
const periodoV1 = this.periodov1(idPeriodo) as PeridoV1;
|
||||
const periodoV2 = this.periodov2(idPeriodo) as PeridoV2[];
|
||||
|
||||
const data = periodoV2.map(({ ClaveCarrera, NombreCarrera }: PeridoV2) =>
|
||||
({
|
||||
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: number): PeridoV2 {
|
||||
const periodo = store.periodosV2.find((periodo: PeridoV2) => periodo.IdPeriodo === IdPeriodo &&
|
||||
periodo.FechaInicio != '' && periodo.FechaFin != '');
|
||||
return periodo
|
||||
},
|
||||
complete(IdPeriodo: number): boolean {
|
||||
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()
|
||||
@@ -1,347 +0,0 @@
|
||||
// initial state
|
||||
const días = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"];
|
||||
const horas_estándar = /* range from 7 to 22 */ Array.from(Array(22 - 7 + 1).keys()).map(x => x + 7);
|
||||
// fill the table with empty cells
|
||||
for (let i = 0; i < horas_estándar.length - 1; i++) {
|
||||
const hora = horas_estándar[i];
|
||||
const tr = document.createElement("tr");
|
||||
tr.id = `hora-${hora}-00`;
|
||||
tr.classList.add(hora > 13 ? "tarde" : "mañana");
|
||||
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);
|
||||
for (let j = 0; j < días.length; j++) {
|
||||
const día = días[j];
|
||||
const td = document.createElement("td");
|
||||
td.id = `hora-${hora}-00-${día}`;
|
||||
tr.appendChild(td);
|
||||
}
|
||||
document.querySelector("tbody#horario")?.appendChild(tr);
|
||||
|
||||
// add 7 rows for each hour
|
||||
const hours = [15, 30, 45];
|
||||
for (let j = 1; j < 4; j++) {
|
||||
const tr = document.createElement("tr");
|
||||
tr.id = `hora-${hora}-${hours[j - 1]}`;
|
||||
tr.classList.add(hora > 13 ? "tarde" : "mañana");
|
||||
for (let k = 0; k < días.length; k++) {
|
||||
const día = días[k];
|
||||
const td = document.createElement("td");
|
||||
td.id = `hora-${hora}-${hours[j - 1]}-${día}`;
|
||||
// td.innerText = `hora-${hora}-${hours[j - 1]}-${día}`;
|
||||
tr.appendChild(td);
|
||||
}
|
||||
document.querySelector("tbody#horario")?.appendChild(tr);
|
||||
}
|
||||
}
|
||||
|
||||
// add an inital height to the table cells
|
||||
const tds = document.querySelectorAll<HTMLTableRowElement>("tbody#horario td");
|
||||
tds.forEach(td => td.style.height = "2rem");
|
||||
|
||||
var table = document.querySelector("table") as HTMLTableElement;
|
||||
var empty_table = table?.innerHTML || "";
|
||||
|
||||
// hide the table
|
||||
table.style.display = "none";
|
||||
|
||||
|
||||
document.getElementById('dlProfesor')?.addEventListener('input', function () {
|
||||
var input = document.getElementById('dlProfesor') as HTMLInputElement;
|
||||
var value = input.value;
|
||||
var option = document.querySelector(`option[value="${value}"]`);
|
||||
if (option) {
|
||||
var id: string = option.getAttribute('data-id')!;
|
||||
const input_profesor: HTMLInputElement = document.getElementById('editor_profesor') as HTMLInputElement;
|
||||
input_profesor.value = id;
|
||||
|
||||
// remove is invalid class
|
||||
input.classList.remove("is-invalid");
|
||||
|
||||
// add is valid class
|
||||
input.classList.add("is-valid");
|
||||
} else {
|
||||
const input_profesor: HTMLInputElement = document.getElementById('editor_profesor') as HTMLInputElement;
|
||||
input_profesor.value = "";
|
||||
// remove is valid class
|
||||
input.classList.remove("is-valid");
|
||||
|
||||
// add is invalid class
|
||||
input.classList.add("is-invalid");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Functions and Methods
|
||||
**/
|
||||
|
||||
const buscarGrupo = async () => {
|
||||
|
||||
// Add loading animation in the button
|
||||
const btn = document.querySelector("#btn-buscar") as HTMLButtonElement;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Cargando...';
|
||||
btn.disabled = true;
|
||||
|
||||
|
||||
const carrera = document.querySelector<HTMLInputElement>("#filter_carrera")?.value as string;
|
||||
const grupo = document.querySelector<HTMLInputElement>("#filter_grupo")?.value as string;
|
||||
console.log(`Carrera: ${carrera}, Grupo: ${grupo}`);
|
||||
|
||||
if (carrera == "" || grupo == "") {
|
||||
triggerMessage("El nombre del grupo y la carrera son requeridos", "Faltan campos");
|
||||
|
||||
// Remove loading animation in the button
|
||||
btn.innerHTML = '<i class="ing-buscar ing"></i> Buscar';
|
||||
btn.disabled = false;
|
||||
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("carrera", carrera);
|
||||
formData.append("grupo", grupo);
|
||||
formData.append("periodo", document.querySelector<HTMLInputElement>("#periodo")?.value!);
|
||||
const thisScript = document.currentScript as HTMLScriptElement;
|
||||
const facultad = thisScript.getAttribute("data-facultad") as string;
|
||||
formData.append('facultad', facultad);
|
||||
|
||||
try {
|
||||
const response = await fetch("action/action_horario.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
}).then(res => res.json());
|
||||
if (response.status == "success") {
|
||||
let limits = {
|
||||
min: 22,
|
||||
max: 7
|
||||
};
|
||||
let sábado = false;
|
||||
const horario = response.horario;
|
||||
// show the table
|
||||
table.style.display = "table";
|
||||
|
||||
// clear the table
|
||||
table.innerHTML = empty_table;
|
||||
|
||||
// fill the table
|
||||
for (let i = 0; i < horario.length; i++) {
|
||||
const dia = horario[i].dia;
|
||||
if (dia == "sábado") sábado = true;
|
||||
const {
|
||||
hora,
|
||||
minutos
|
||||
} = {
|
||||
hora: parseInt(horario[i].hora.split(":")[0]),
|
||||
minutos: horario[i].hora.split(":")[1]
|
||||
}
|
||||
|
||||
// update the limits
|
||||
if (hora < limits.min) {
|
||||
limits.min = hora;
|
||||
}
|
||||
if (hora > limits.max) {
|
||||
limits.max = hora;
|
||||
}
|
||||
|
||||
const materia = horario[i].materia;
|
||||
const profesor = horario[i].profesor;
|
||||
const salon = horario[i].salon;
|
||||
const id = horario[i].horario_id;
|
||||
const prof_id = horario[i].profesor_id;
|
||||
|
||||
const cell = document.querySelector(`#hora-${hora}-${minutos}-${dia}`) as HTMLTableCellElement;
|
||||
cell.innerHTML =
|
||||
`
|
||||
<div>
|
||||
<small class="text-gray">${hora}:${minutos}</small>
|
||||
<b class="title">${materia}</b> <br>
|
||||
<br><span>Salón: </span>${salon} <br>
|
||||
<span class="ing ing-formacion mx-1"></span>${profesor}
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
const html = `<div class="menu-flotante p-2">
|
||||
<a
|
||||
class="mx-2"
|
||||
href="#"
|
||||
data-toggle="modal"
|
||||
data-target="#modal-editar"
|
||||
data-dia="${dia}"
|
||||
data-hora="${hora}:${minutos}"
|
||||
data-materia="${materia}"
|
||||
data-profesor="${prof_id}"
|
||||
data-salon="${salon}"
|
||||
data-id="${id}"
|
||||
>
|
||||
<i class="ing-editar ing"></i>
|
||||
</a>
|
||||
<a
|
||||
class="mx-2"
|
||||
href="#"
|
||||
data-toggle="modal"
|
||||
data-target="#modal-borrar"
|
||||
data-hoario_id="${id}"
|
||||
>
|
||||
<i class="ing-basura ing"></i>
|
||||
</a>
|
||||
</div>`;
|
||||
|
||||
const td = cell.closest("td") as HTMLTableCellElement;
|
||||
td.innerHTML += html;
|
||||
td.classList.add("position-relative");
|
||||
|
||||
// this cell spans 4 rows
|
||||
cell.rowSpan = 6;
|
||||
cell.classList.add("bloque-clase", "overflow");
|
||||
|
||||
for (let j = 1; j < 6; j++) {
|
||||
const minute = (parseInt(minutos) + j * 15)
|
||||
const next_minute = (minute % 60).toString().padStart(2, "0");
|
||||
const next_hour = (hora + Math.floor(minute / 60))
|
||||
const next_cell = document.querySelector(`#hora-${next_hour}-${next_minute}-${dia}`) as HTMLTableCellElement;
|
||||
next_cell.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// remove the elements that are not in the limits
|
||||
const horas = document.querySelectorAll("tbody#horario tr") as NodeListOf<HTMLTableRowElement>;
|
||||
for (let i = 0; i < horas.length; i++) {
|
||||
const hora = horas[i];
|
||||
const hora_id = parseInt(hora.id.split("-")[1]);
|
||||
if (hora_id < limits.min || hora_id > limits.max) {
|
||||
hora.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// if there is no sábado, remove the column
|
||||
if (!sábado) {
|
||||
document.querySelectorAll("tbody#horario td").forEach(td => {
|
||||
if (td.id.split("-")[3] == "sábado") {
|
||||
td.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// remove the header (the last)
|
||||
const headers = document.querySelector("#headers") as HTMLTableRowElement;
|
||||
headers.lastElementChild?.remove();
|
||||
}
|
||||
|
||||
// adjust width
|
||||
const ths = document.querySelectorAll("tr#headers th") as NodeListOf<HTMLTableHeaderCellElement>;
|
||||
const width = 95 / (ths.length - 1);
|
||||
|
||||
ths.forEach((th, key) =>
|
||||
th.style.width = (key == 0) ? "5%" : `${width}%`
|
||||
);
|
||||
|
||||
// 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", () => {
|
||||
element.classList.add("d-none");
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
triggerMessage(response.message, "Error");
|
||||
// Remove loading animation in the button
|
||||
btn.innerHTML = '<i class="ing-buscar ing"></i> Buscar';
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
triggerMessage("Error al cargar el horario", "Error");
|
||||
triggerMessage(error, "Error");
|
||||
}
|
||||
|
||||
// Remove loading animation in the button
|
||||
btn.innerHTML = '<i class="ing-buscar ing"></i> Buscar';
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
async function guardar(id: string) {
|
||||
interface Data {
|
||||
hora: HTMLInputElement;
|
||||
dia: HTMLInputElement;
|
||||
profesor: HTMLInputElement;
|
||||
salon: HTMLInputElement;
|
||||
}
|
||||
const btn: HTMLButtonElement = document.querySelector("#btn-guardar") as HTMLButtonElement;
|
||||
const clone: HTMLButtonElement = btn.cloneNode(true) as HTMLButtonElement;
|
||||
btn.innerHTML = '<i class="ing-cargando ing"></i> Guardando...';
|
||||
btn.disabled = true;
|
||||
|
||||
const data: Data = {
|
||||
hora: document.querySelector("#editor_hora") as HTMLInputElement,
|
||||
dia: document.querySelector("#editor_dia") as HTMLInputElement,
|
||||
salon: document.querySelector("#editor_salón") as HTMLInputElement,
|
||||
profesor: document.querySelector("#editor_profesor") as HTMLInputElement,
|
||||
};
|
||||
|
||||
const hora = data.hora.value; // h:mm
|
||||
const { compareHours } = await import('./date_functions');
|
||||
const hora_antes = compareHours(hora, "07:15") < 0;
|
||||
const hora_después = compareHours(hora, "21:30") > 0;
|
||||
|
||||
if (hora_antes || hora_después) {
|
||||
alert(`La hora ${hora} no es válida`);
|
||||
triggerMessage("Selecciona una hora", "Error");
|
||||
btn.innerHTML = clone.innerHTML;
|
||||
btn.disabled = false;
|
||||
data.hora.focus();
|
||||
data.hora.classList.add("is-invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
const dia = data.dia.value;
|
||||
const salon = data.salon.value;
|
||||
const profesor = data.profesor.value;
|
||||
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("id", id);
|
||||
formData.append("hora", hora);
|
||||
formData.append("dia", dia);
|
||||
formData.append("salon", salon);
|
||||
formData.append("profesor", profesor);
|
||||
|
||||
const response = await fetch("action/action_horario_update.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
}).then(res => res.json());
|
||||
|
||||
if (response.status == "success") {
|
||||
triggerMessage(response.message, "Éxito", "success");
|
||||
btn.innerHTML = '<i class="ing-aceptar ing"></i> Guardado';
|
||||
btn.classList.add("btn-success");
|
||||
btn.classList.remove("btn-primary");
|
||||
|
||||
// return to the initial state
|
||||
setTimeout(() => {
|
||||
buscarGrupo();
|
||||
btn.replaceWith(clone);
|
||||
$("#modal-editar").modal("hide");
|
||||
}, 1000);
|
||||
} else {
|
||||
triggerMessage(response.message, "Error");
|
||||
btn.replaceWith(clone);
|
||||
|
||||
$("#modal-editar").modal("hide");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function triggerMessage(message: string, header: string, colour: string = "danger") {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
// get this script tag
|
||||
const script = document.currentScript as HTMLScriptElement;
|
||||
// get data-facultad attribute from script tag
|
||||
const dataFacultad = script.getAttribute("data-facultad") as string;
|
||||
|
||||
|
||||
const table = document.querySelector("table") as HTMLTableElement;
|
||||
// hide the table
|
||||
table.style.display = "none";
|
||||
|
||||
disableDatalist("#filter_grupo");
|
||||
|
||||
const buscarGrupo = async () => {
|
||||
|
||||
// Add loading animation in the button
|
||||
const btn = document.querySelector("#btn-buscar") as HTMLButtonElement;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Cargando...';
|
||||
btn.disabled = true;
|
||||
|
||||
|
||||
const carrera = document.querySelector("#filter_carrera") as HTMLInputElement;
|
||||
const grupo = document.querySelector("#filter_grupo") as HTMLInputElement;
|
||||
const periodo = document.querySelector("#periodo") as HTMLInputElement;
|
||||
console.log(`Carrera: ${carrera}, Grupo: ${grupo}`);
|
||||
|
||||
if (carrera.value == "" || grupo.value == "") {
|
||||
triggerMessage("El nombre del grupo y la carrera son requeridos", "Faltan campos");
|
||||
|
||||
// Remove loading animation in the button
|
||||
btn.innerHTML = '<i class="ing-buscar ing"></i> Buscar';
|
||||
btn.disabled = false;
|
||||
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("carrera", carrera.value);
|
||||
formData.append("grupo", grupo.value);
|
||||
formData.append("periodo", periodo.value);
|
||||
formData.append('facultad', dataFacultad);
|
||||
|
||||
try {
|
||||
const response = await fetch("api/horario.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
}).then(res => res.json());
|
||||
|
||||
} catch (error) {
|
||||
triggerMessage("Error al cargar el horario", "Error");
|
||||
}
|
||||
|
||||
// Remove loading animation in the button
|
||||
btn.innerHTML = '<i class="ing-buscar ing"></i> Buscar';
|
||||
btn.disabled = false;
|
||||
}
|
||||
|
||||
// on click the li element, inside datalist #dlcarera
|
||||
const dlcarreras = document.querySelectorAll("#dlcarrera li");
|
||||
|
||||
dlcarreras.forEach(li => {
|
||||
li.addEventListener("click", async () => {
|
||||
// get the data-id from the li element
|
||||
const carrera= li.getAttribute("data-id") as string;
|
||||
const facultad = dataFacultad;
|
||||
const periodo = document.querySelector("#periodo") as HTMLSelectElement;
|
||||
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("carrera", carrera);
|
||||
formData.append("facultad", facultad);
|
||||
formData.append("periodo", periodo.value);
|
||||
|
||||
try {
|
||||
const {
|
||||
status,
|
||||
grupos
|
||||
} = await fetch("action/action_grupo.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
}).then(res => res.json());
|
||||
|
||||
if (status != "success") {
|
||||
throw new Error("Error al cargar los grupos");
|
||||
}
|
||||
|
||||
const dlgrupo = document.querySelector("#dlgrupo ul") as HTMLUListElement;
|
||||
const prompt = document.querySelector("#dlgrupo .datalist-input") as HTMLInputElement;
|
||||
dlgrupo.innerHTML = "";
|
||||
|
||||
grupos.forEach(grupo => {
|
||||
const li = document.createElement("li");
|
||||
// data-id is the id of the group
|
||||
li.setAttribute("data-id", grupo);
|
||||
li.textContent = grupo;
|
||||
dlgrupo.appendChild(li);
|
||||
});
|
||||
|
||||
// write Seleccionar grupo
|
||||
prompt.textContent = "Seleccionar grupo";
|
||||
|
||||
disableDatalist("#filter_grupo", false);
|
||||
} catch (error) {
|
||||
triggerMessage("Error al cargar los grupos", "Error");
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
// function that receives two hours in hh:mm format and compares as a spaceship operator
|
||||
export function compareHours(h1: string, h2: string): number {
|
||||
const [h1h, h1m] = h1.split(":").map(Number);
|
||||
const [h2h, h2m] = h2.split(":").map(Number);
|
||||
if (h1h > h2h) return 1;
|
||||
if (h1h < h2h) return -1;
|
||||
if (h1m > h2m) return 1;
|
||||
if (h1m < h2m) return -1;
|
||||
return 0;
|
||||
}
|
||||
1
ts/declaration.ts
Normal file
1
ts/declaration.ts
Normal file
@@ -0,0 +1 @@
|
||||
declare module 'https://*'
|
||||
532
ts/horario_profesor.ts
Normal file
532
ts/horario_profesor.ts
Normal file
@@ -0,0 +1,532 @@
|
||||
declare function triggerMessage(message: string, title: string, color?: string): void;
|
||||
declare const write: boolean;
|
||||
declare const moment: any;
|
||||
|
||||
/**
|
||||
* Funciones auxiliares
|
||||
*/
|
||||
type Profesor = {
|
||||
id: number,
|
||||
grado: string,
|
||||
profesor: string,
|
||||
clave: string,
|
||||
}
|
||||
|
||||
type Horario = {
|
||||
id: number,
|
||||
carrera_id: number,
|
||||
materia: string,
|
||||
salon: string,
|
||||
profesores: Profesor[],
|
||||
hora: string,
|
||||
hora_final: string,
|
||||
dia: string,
|
||||
duracion: number,
|
||||
bloques: number,
|
||||
grupo: string,
|
||||
materia_id: number,
|
||||
}
|
||||
|
||||
const compareHours = (hora1: string, hora2: string): number => {
|
||||
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 = [] as Horario[];
|
||||
const table = document.querySelector("table") as HTMLTableElement;
|
||||
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: number) => `${minute}`.padStart(2, '0')).forEach((minute: string) => {
|
||||
const tr = document.createElement("tr") as HTMLTableRowElement;
|
||||
tr.id = `hora-${hora}:${minute}`;
|
||||
tr.classList.add(hora > 13 ? "tarde" : "mañana");
|
||||
if (minute == "00") {
|
||||
const th = document.createElement("th") as HTMLTableCellElement;
|
||||
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") as HTMLTableCellElement;
|
||||
td.id = `hora-${hora}:${minute}-${día}`;
|
||||
tr.appendChild(td);
|
||||
});
|
||||
const tbody = document.querySelector("tbody#horario") as HTMLTableSectionElement;
|
||||
if (!(tbody instanceof HTMLTableSectionElement)) {
|
||||
throw new Error("No se ha encontrado el tbody");
|
||||
}
|
||||
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
});
|
||||
|
||||
const empty_table = table.cloneNode(true) as HTMLTableElement;
|
||||
document.querySelectorAll('.hidden').forEach((element: HTMLElement) => {
|
||||
element.style.display = "none";
|
||||
});
|
||||
// hide the table
|
||||
table.style.display = "none";
|
||||
|
||||
function moveHorario(id: string, día: string, hora: string) {
|
||||
|
||||
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: HTMLElement) => element.style.display = "none");
|
||||
return;
|
||||
}
|
||||
// show the table
|
||||
table.style.display = "table";
|
||||
document.querySelectorAll('.hidden').forEach((element: HTMLElement) => element.style.display = "block");
|
||||
// clear the table
|
||||
table.innerHTML = empty_table.outerHTML;
|
||||
|
||||
function conflicts(horario1: Horario, horario2: Horario): boolean {
|
||||
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: number, minutos: number, dia: string, cells: number = 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: Horario, edit = false) {
|
||||
function move(horario: Horario, cells: number = 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}`) as HTMLTableCellElement;
|
||||
if (!cell) return;
|
||||
|
||||
cell.dataset.ids = `${horario.id}`;
|
||||
|
||||
const float_menu = edit ?
|
||||
`<div class="menu-flotante p-2" style="opacity: .7;">
|
||||
<a class="mx-2" href="#" data-toggle="modal" data-target="#modal-editar">
|
||||
<i class="ing-editar ing"></i>
|
||||
</a>
|
||||
<a class="mx-2" href="#" data-toggle="modal" data-target="#modal-borrar">
|
||||
<i class="ing-basura ing"></i>
|
||||
</a>
|
||||
</div>`
|
||||
: '';
|
||||
|
||||
cell.innerHTML =
|
||||
`<div style="overflow-y: auto; overflow-x: hidden; height: 100%;" id="block-${horario.id}" class="position-absolute w-100 h-100">
|
||||
<small class="text-gray">${horario.hora}</small>
|
||||
<b class="title">${horario.materia}</b> <br>
|
||||
<br><span>Salón: </span>${horario.salon} <br>
|
||||
<small class="my-2">
|
||||
${horario.profesores.map((profesor: Profesor) => ` <span class="ing ing-formacion mx-1"></span>${profesor.grado ?? ''} ${profesor.profesor}`).join("<br>")}
|
||||
</small>
|
||||
</div>
|
||||
${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: Horario[], 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 = `
|
||||
<small class='text-danger'>
|
||||
${hora}
|
||||
</small>
|
||||
<div class="d-flex justify-content-center align-items-center mt-4">
|
||||
<div class="d-flex flex-column justify-content-center align-items-center">
|
||||
<span class="ing ing-importante text-danger" style="font-size: 2rem;"></span>
|
||||
<b class='text-danger'>
|
||||
Empalme de ${ids.length} horarios
|
||||
</b>
|
||||
<hr>
|
||||
<i class="text-danger">Ver horarios …</i>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 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 += `
|
||||
<tr data-ids="${horario.id}">
|
||||
<td><small>${horario.hora.slice(0, -3)}-${horario.hora_final.slice(0, -3)}</small></td>
|
||||
<td>${horario.materia}</td>
|
||||
<td>
|
||||
${horario.profesores.map(({ grado, profesor }) => `${grado ?? ''} ${profesor}`).join(", ")}
|
||||
</td>
|
||||
<td>${horario.salon}</td>
|
||||
${edit ? `
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-primary dismiss-editar" data-toggle="modal" data-target="#modal-editar">
|
||||
<i class="ing-editar ing"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-danger dismiss-editar" data-toggle="modal" data-target="#modal-borrar">
|
||||
<i class="ing-basura ing"></i>
|
||||
</button>
|
||||
</td>
|
||||
` : ""}
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".dismiss-editar").forEach(btn => {
|
||||
btn.addEventListener("click", () => $("#modal-choose").modal("hide"));
|
||||
});
|
||||
});
|
||||
|
||||
function getDuration(hora_i: string, hora_f: string): number {
|
||||
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: Date, horario: 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") as NodeListOf<HTMLTableCellElement>;
|
||||
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}"]`) as HTMLElement;
|
||||
|
||||
// remove all children
|
||||
while (bloque.firstChild) {
|
||||
bloque.removeChild(bloque.firstChild);
|
||||
}
|
||||
|
||||
// prepend a loading child
|
||||
const loading = `<div class="spinner-border" role="status" style="width: 3rem; height: 3rem;">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>`;
|
||||
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') as HTMLFormElement;
|
||||
|
||||
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') as HTMLInputElement;
|
||||
const option = form.querySelector(`option[value="${input.value}"]`) as HTMLOptionElement;
|
||||
|
||||
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') as HTMLInputElement;
|
||||
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') as HTMLInputElement;
|
||||
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") as NodeListOf<HTMLButtonElement>;
|
||||
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') as HTMLInputElement;
|
||||
const option = form.querySelector(`option[value="${input.value}"]`) as HTMLOptionElement;
|
||||
|
||||
272
ts/reposiciones.ts
Normal file
272
ts/reposiciones.ts
Normal file
@@ -0,0 +1,272 @@
|
||||
import { type } from "os";
|
||||
|
||||
declare function triggerMessage(message: string, title: string, type?: string): void;
|
||||
declare const write: boolean;
|
||||
declare const moment: any;
|
||||
// from this 'horario_id', 'fecha', 'hora', 'duracion_id', 'descripcion', 'profesor_id', 'salon', 'unidad', 'periodo_id', 'fecha_clase' make a type of ReposicionParams
|
||||
export interface ReposicionParams {
|
||||
horario_id: number;
|
||||
fecha: string;
|
||||
hora: string;
|
||||
duracion_id: number;
|
||||
descripcion: string;
|
||||
profesor_id: number;
|
||||
salon: string;
|
||||
unidad: number;
|
||||
periodo_id: number;
|
||||
fecha_clase: string;
|
||||
}
|
||||
|
||||
type Horario = {
|
||||
id: number;
|
||||
carrera_id: number;
|
||||
materia_id: number;
|
||||
grupo: string;
|
||||
profesores: Profesor[];
|
||||
dia: string;
|
||||
hora: string;
|
||||
hora_final: string;
|
||||
salon: string;
|
||||
fecha_inicio: string;
|
||||
fecha_final: string;
|
||||
fecha_carga: string;
|
||||
nivel_id: number;
|
||||
periodo_id: number;
|
||||
facultad_id: number;
|
||||
materia: string;
|
||||
horas: number;
|
||||
minutos: number;
|
||||
duracion: number;
|
||||
retardo: boolean;
|
||||
original_id: number;
|
||||
last: boolean;
|
||||
bloques: number;
|
||||
};
|
||||
|
||||
type Profesor = {
|
||||
id: number;
|
||||
clave: string;
|
||||
grado: string;
|
||||
profesor: string;
|
||||
nombre: string;
|
||||
facultad_id: number;
|
||||
};
|
||||
// Get references to the HTML elements
|
||||
const form = document.getElementById('form') as HTMLFormElement;
|
||||
const steps = Array.from(form.querySelectorAll('.step')) as HTMLElement[];
|
||||
|
||||
const nextButton = document.getElementById('next-button') as HTMLButtonElement;
|
||||
const prevButton = document.getElementById('prev-button') as HTMLButtonElement;
|
||||
|
||||
let currentStep = 0;
|
||||
|
||||
// #clave_profesor on change => show step 2
|
||||
const clave_profesor = document.getElementById('clave_profesor') as HTMLInputElement;
|
||||
const horario_reponer = document.getElementById('horario_reponer') as HTMLInputElement;
|
||||
const fechas_clase = document.getElementById('fechas_clase') as HTMLInputElement;
|
||||
|
||||
const fecha_reponer = $('#fecha_reponer') as JQuery<HTMLElement>;
|
||||
const hora_reponer = $('#hora_reponer') as JQuery<HTMLElement>;
|
||||
const minutos_reponer = $('#minutos_reponer') as JQuery<HTMLElement>;
|
||||
|
||||
clave_profesor.addEventListener('change', async () => {
|
||||
const step2 = document.getElementById('step-2') as HTMLElement;
|
||||
clave_profesor.disabled = true;
|
||||
// get option which value is the same as clave_profesor.value
|
||||
const option = document.querySelector(`option[value="${clave_profesor.value}"]`) as HTMLOptionElement;
|
||||
|
||||
// make a form data with #form
|
||||
const profesor_id = document.getElementById('profesor_id') as HTMLInputElement;
|
||||
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 as Horario[];
|
||||
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] as HTMLInputElement[];
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
const step = document.getElementById(`step-${currentStep + 1}`) as HTMLElement;
|
||||
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') as HTMLElement;
|
||||
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}"]`) as HTMLOptionElement;
|
||||
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') as HTMLElement;
|
||||
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;
|
||||
}
|
||||
type Fecha = {
|
||||
fecha: string;
|
||||
dia_mes: number;
|
||||
day: number;
|
||||
month: number;
|
||||
year: number;
|
||||
}
|
||||
|
||||
const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'];
|
||||
|
||||
const fechas = data.data as Fecha[];
|
||||
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') as HTMLElement;
|
||||
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') as HTMLElement;
|
||||
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') as HTMLButtonElement;
|
||||
// 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) {
|
||||
event.preventDefault();
|
||||
|
||||
// Handle form submission
|
||||
// You can access the form data using the FormData API or serialize it manually
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"target": "ES2020",
|
||||
"jsx": "react",
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"rootDir": "./ts",
|
||||
"outDir": "./js",
|
||||
"watch": true,
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/node_modules/*"
|
||||
]
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"dom"
|
||||
],
|
||||
"outDir": "js",
|
||||
"rootDir": "ts",
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"module": "ESNext"
|
||||
// ts/auditoría.ts:1:37 - error TS2307: Cannot find module 'https://unpkg.com/petite-vue?module' or its corresponding type declarations.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user