Browse Source

Commit Bulk

Alejandro Rosales 2 years ago
parent
commit
674fe5f264
54 changed files with 2836 additions and 1488 deletions
  1. 10 1
      action/action_auditoria.php
  2. 51 30
      action/action_horario.php
  3. 1 1
      action/action_materias.php
  4. 8 12
      action/action_permisos_update.php
  5. 53 10
      action/action_profesor.php
  6. 24 20
      action/action_usuarios_insert.php
  7. 26 0
      action/asistenciasprofesor_select.php
  8. 98 0
      action/reposicion_autoriza.php
  9. 33 0
      action/reposicion_delete.php
  10. 124 0
      action/reposicion_insert.php
  11. 64 0
      action/reposicion_select.php
  12. 121 0
      action/reposicion_update.php
  13. 1 1
      alta_de_horario.php
  14. 107 101
      auditoria.php
  15. 1 1
      avisos.php
  16. 1 1
      avisos_crear.php
  17. 1 1
      avisos_editar.php
  18. 1 1
      base.php
  19. 2 2
      carreras.php
  20. 10 14
      class/c_login.php
  21. 73 32
      consultar_horario.php
  22. 1 1
      consultar_horario_old.php
  23. 1 1
      días_festivos.php
  24. 1 1
      excel_horario.php
  25. 1 1
      facultades.php
  26. 1 1
      horario_profesor.php
  27. 6 0
      import/html_header.php
  28. 61 1
      js/datalist.js
  29. 82 0
      js/horario.js
  30. 0 414
      js/horario_profesor.js
  31. 1 1
      justificar_asistencias.php
  32. 1 1
      materias.php
  33. 0 228
      module/datalist.js
  34. 0 36
      module/messages.js
  35. 1 1
      permisos.php
  36. 1 1
      profesores.php
  37. 1 1
      reporte_de_asistencias.php
  38. 1 1
      reposiciones.php
  39. 767 0
      reposiciones_autorizar.php
  40. 933 0
      reposiciones_crear.php
  41. 3 7
      roles.php
  42. 0 0
      service/_4564_periodo/auto.php
  43. 0 0
      service/_4564_periodo/backend/carreras.php
  44. 0 0
      service/_4564_periodo/backend/periodos.php
  45. 0 0
      service/_4564_periodo/client.html
  46. 0 0
      service/_4564_periodo/horarios.php
  47. 0 0
      service/_4564_periodo/periodos.v1.php
  48. 0 0
      service/_4564_periodo/periodos.v2.php
  49. 0 0
      service/auditoria/index.php
  50. 1 1
      supervisor.php
  51. 140 0
      ts/horario.ts
  52. 0 532
      ts/horario_profesor.ts
  53. 21 29
      usuarios.php
  54. 1 1
      vista_profesor.php

+ 10 - 1
action/action_auditoria.php

@@ -24,8 +24,16 @@ try {
             fechas AS (
                 SELECT fechas_clase(h.horario_id) as registro_fecha_ideal, h.horario_id  
                 FROM horarios h
+            ),
+            sin_registro AS (
+                SELECT * FROM ESTADO_SUPERVISOR WHERE (estado_color, estado_icon) = ('dark', 'ing-cancelar')
             )
-            SELECT estado_supervisor.*, usuario.*, registro.*, profesor.*, horarios.*, fechas.*,
+            SELECT 
+                usuario.*, registro.*, profesor.*, horarios.*, fechas.*,
+                coalesce(estado_supervisor.estado_supervisor_id, sin_registro.estado_supervisor_id) as estado_supervisor_id,
+                coalesce(estado_supervisor.nombre, sin_registro.nombre) as nombre,
+                coalesce(estado_supervisor.estado_color, sin_registro.estado_color) as estado_color,
+                coalesce(estado_supervisor.estado_icon, sin_registro.estado_icon) as estado_icon,
                 justificador.usuario_nombre as justificador_nombre,
                 justificador.usuario_clave as justificador_clave,
                 facultad.facultad_nombre as justificador_facultad,
@@ -36,6 +44,7 @@ try {
             JOIN profesor using (profesor_id)
             LEFT JOIN registro USING (horario_id, registro_fecha_ideal, profesor_id)
             LEFT join estado_supervisor using (estado_supervisor_id)
+            CROSS JOIN sin_registro
             LEFT JOIN USUARIO ON USUARIO.usuario_id = REGISTRO.supervisor_id
             LEFT JOIN USUARIO JUSTIFICADOR ON JUSTIFICADOR.usuario_id = REGISTRO.justificador_id
             LEFT JOIN ROL on ROL.rol_id = justificador.rol_id

+ 51 - 30
action/action_horario.php

@@ -1,35 +1,56 @@
-<?php
-header('Content-Type: application/json');
+<?
+#input $_GET['id_espacio_sgu']
+#output rutas: [ ...ruta, salones: [{...salon}] ]
+header('Content-Type: application/json charset=utf-8');
+ini_set('display_errors', 1);
+ini_set('display_startup_errors', 1);
+error_reporting(E_ALL);
 
 $ruta = "../";
-require_once("../include/bd_pdo.php");
-
-extract($_POST);
-
-$dias = array("domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado");
-$horarios = $db
-    ->get("fs_horario($periodo, $carrera, '$grupo', true)");
-
-// get each id from $horarios (might be duplicate)
+require_once $ruta . "class/c_login.php";
+if (!isset($_SESSION['user'])) {
+    http_response_code(401);
+    die(json_encode(['error' => 'unauthorized']));
+}
+$user = unserialize($_SESSION['user']);
 
+// check method
 try {
-    $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))
+    if ($_SERVER['REQUEST_METHOD'] === 'GET') {
+        if (!isset($_GET['profesor_id'])) {
+            throw new Exception('missing parameters');
+        }
+        $data = $db->query(
+            "SELECT *, (EXTRACT(EPOCH FROM (horario_fin - horario_hora) ) / EXTRACT(EPOCH FROM interval '15 minute'))::INT AS bloques
+            FROM horario_view
+            JOIN horario_profesor ON horario_profesor.horario_id = horario_view.horario_id
+            WHERE horario_profesor.profesor_id = :profesor_id
+            AND (facultad_id = :facultad_id OR :facultad_id IS NULL)",
+            [
+                'profesor_id' => $_GET['profesor_id'],
+                'facultad_id' => $user->facultad['facultad_id'],
+            ]
         );
-        $horario['dia'] = $dias[$horario['dia']];
-        return $horario;
-    }, $horarios);
-} catch (Exception $e) {
-    die(json_encode([
-        "status" => "error",
-        "message" => $e->getMessage(),
-    ]));
-}
-?>
-<?= json_encode([
-    "status" => "success",
-    "horario" => $horarios,
-]) ?>
+
+        $last_query = [
+            'query' => $db->getLastQuery(),
+        ];
+
+        echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+    } else {
+        throw new Exception('invalid method');
+    }
+} catch (PDOException $th) {
+    http_response_code(500);
+    echo json_encode([
+        'error' => $th->getMessage(),
+        'query' => $db->getLastQuery(),
+    ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
+    exit;
+} catch (Exception $th) {
+    http_response_code(500);
+    echo json_encode([
+        'error' => $th->getMessage(),
+    ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+    exit;
+}

+ 1 - 1
action/action_materias.php

@@ -10,7 +10,7 @@ if (!isset($_SESSION['user']))
 
 $user = unserialize($_SESSION['user']);
 
-if (!$user->admin && ($access = $user->access('asistencia')) == 'n')
+if (($access = $user->access('asistencia')) == 'n')
     die(json_encode(['error' => true]));
 
 $user->print_to_log('Consultar materias');

+ 8 - 12
action/action_permisos_update.php

@@ -1,7 +1,7 @@
 <?php
     $ruta = "../";
     require_once "../include/bd_pdo.php";
-    global $pdo;
+    global $db;
     if(isset($_POST['lectura']))
         $ver = $_POST['lectura'];
     if(isset($_POST['editar']))
@@ -10,30 +10,26 @@
         $edit_separado = explode("_", $edit);
         $completo[]=$edit_separado;
     }
-    #echo "<br><br><br><br>";
-    #print_r($ver);
-    #print_r($editar);
-    query("SELECT fd_permiso()", null, false);
+    $db->query("SELECT fd_permiso()");
     foreach($ver as $lectura){
         $igual=false;
         $ver_separado = explode("_", $lectura);
-        #print_r($ver_separado);
         foreach($completo as $comp){
             if($ver_separado[0] == $comp[0] && $ver_separado[1] == $comp[1]){
-                #echo " igual";
                 $igual=true;
                 break;
             }
         }
-        #echo "<br>";
         if(!$igual)
             $completo[]=$ver_separado;
     }
-    #print_r($completo);
     foreach($completo as $actual){
-        $sql = "SELECT fi_permiso(:pagina, :rol, :tipo)";
-        $params = [':pagina' => $actual['0'], ':rol' => $actual['1'], ':tipo' => $actual['2']];
-        query($sql, $params, false);
+
+        $db->insert('permiso', [
+            'pagina_id' => $actual['0'],
+            'rol_id' => $actual['1'],
+            'permiso_tipo' => $actual['2'],
+        ]);
     }
     header("Location: ../permisos.php");
     exit();

+ 53 - 10
action/action_profesor.php

@@ -1,14 +1,57 @@
-<?php
+<?
+#input $_GET['id_espacio_sgu']
+#output rutas: [ ...ruta, salones: [{...salon}] ]
+header('Content-Type: application/json charset=utf-8');
+ini_set('display_errors', 1);
+ini_set('display_startup_errors', 1);
+error_reporting(E_ALL);
+
 $ruta = "../";
-require_once("../include/bd_pdo.php");
+require_once $ruta . "class/c_login.php";
+if (!isset($_SESSION['user'])) {
+    http_response_code(401);
+    die(json_encode(['error' => 'unauthorized']));
+}
+$user = unserialize($_SESSION['user']);
 
-extract($_GET);
+// check method
+try {
+    if ($_SERVER['REQUEST_METHOD'] === 'GET') {
+        $data = $db->query(
+            "SELECT DISTINCT profesor.*
+            FROM profesor
+            JOIN horario_profesor   using (profesor_id)
+            JOIN horario            using (horario_id)
+            JOIN materia            using (materia_id)
+            JOIN carrera            using (carrera_id)
+            WHERE carrera.facultad_id = :facultad_id OR :facultad_id IS NULL
+            ORDER BY profesor.profesor_nombre",
+            array(
+                ":facultad_id" => $user->facultad['facultad_id']
+            )
+        );
 
-$profesores = $db
-    ->where("facultad_id", $facultad ?? 0)
-    ->get("fs_profesor");
+        $last_query = [
+            'query' => $db->getLastQuery(),
+        ];
 
-echo json_encode([
-    "status" => "success",
-    "profesores" => $profesores
-]);
+        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 | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
+    exit;
+} catch (Exception $th) {
+    http_response_code(500);
+    echo json_encode([
+        'error' => $th->getMessage(),
+    ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+    exit;
+}

+ 24 - 20
action/action_usuarios_insert.php

@@ -1,22 +1,26 @@
 <?php
-    $ruta = "../";
-    require_once "../include/bd_pdo.php";
-    global $pdo;
-    if(isset($_POST['dlfacultad']))
-        $facultad=$_POST['dlfacultad'];
-    else
-        $facultad=$_POST['mfacultad'];
+ini_set('display_errors', 1);
+ini_set('display_startup_errors', 1);
+error_reporting(E_ALL);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $db;
+if (isset($_POST['dlfacultad']))
+    $facultad = $_POST['dlfacultad'];
+else
+    $facultad = $_POST['mfacultad'];
 
-    $hecho = query("SELECT * FROM fs_usuario WHERE clave = :clave", [':clave' => $_POST['mclave']], true);
-    if(!$hecho){
-        $sql = "SELECT fi_usuario(:nombre, :correo, :clave, :rol, :facultad)";
-        $params = [':nombre' => mb_strtoupper($_POST['mnombre']), ':correo' => $_POST['mcorreo'], ':clave' => $_POST['mclave'], ':rol' => $_POST['mrol'], ':facultad' => $facultad];
-        $hecho = query($sql, $params, true);
-        header("Location: ../usuarios.php", true, 307);
-        exit();
-    }
-    else{
-        header("Location: ../usuarios.php?error=1");
-        exit();
-    }    
-?>
+if ($db->where('usuario_clave', $_POST['mclave'])->has('usuario')) {
+    header("Location: ../usuarios.php?error=1");
+    exit;
+}
+
+$db->insert('usuario', [
+    'usuario_nombre' => mb_strtoupper($_POST['mnombre']),
+    'usuario_correo' => $_POST['mcorreo'],
+    'usuario_clave' => $_POST['mclave'],
+    'rol_id' => $_POST['mrol'] ?? null,
+    'facultad_id' => empty($facultad) ? null : $facultad,
+]);
+
+header("Location: ../usuarios.php", true, 307);

+ 26 - 0
action/asistenciasprofesor_select.php

@@ -0,0 +1,26 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die('No se ha iniciado sesión');
+
+$user = unserialize($_SESSION['user']);
+
+if(!isset($_POST["id"]) || !isset($_POST["hor"])){
+    $return["error"] = "Error! No se recibió la información del usuario.";
+}else{
+    $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+    $hor = filter_input(INPUT_POST, "hor", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+    
+    $rs = $db->query('SELECT * from fs_asistenciaprofesor_horario(:id, :hor)', [':id' => $id, ':hor' => $hor]);
+    $asistArr = array();
+    
+    foreach($rs as $row){
+        $asistArr[] = $row["registro_fecha_ideal"];
+    }
+    $return["asistenciaArr"] = $asistArr;
+}
+echo json_encode($return);
+?>

+ 98 - 0
action/reposicion_autoriza.php

@@ -0,0 +1,98 @@
+<?php
+/* 
+Cambia de estado la reposición
+ */
+$pag = "../reposiciones_crear.php";
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die('No se ha iniciado sesión');
+
+$user = unserialize($_SESSION['user']);
+
+$pag = "../reposiciones_autorizar.php";
+
+
+
+if(!isset($_POST["id"]) || !isset($_POST["edo"]) ){
+    header("Location: ".$pag."?error=0");
+    exit();
+}
+
+$id_repo = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$edo = filter_input(INPUT_POST, "edo", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+if(isset($_POST["salon"]) && $_POST["salon"] != "")
+    $salon = trim(filter_input(INPUT_POST, "salon", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+
+$motivo = "";
+if(isset($_POST["motivo"]) && $_POST["motivo"] != "")
+    $motivo = trim($_POST["motivo"]);
+
+if($edo == 4){//cancelación
+    $db->querySingle('SELECT fu_reposicion_cancela(:id, :motivo)',
+        [':id' => $id_repo, ':motivo' => $motivo]
+    );
+}else{
+    if(!empty($salon)){
+        $db->querySingle('SELECT fu_reposicion(:id, NULL, NULL, NULL, :sal, :edo, NULL, NULL, NULL, NULL)',
+            [':id' => $id_repo, ':sal' => $salon, ':edo' => $edo]
+        );
+    }else{
+        $db->querySingle('SELECT fu_reposicion(:id, NULL, NULL, NULL, NULL, :edo, NULL, NULL, NULL, NULL)',
+            [':id' => $id_repo, ':edo' => $edo]
+        );
+    } 
+}
+
+//Obtener datos del usuario que creó la reposición y mandar correo
+/*$stmt = $pdo->prepare('Select * from fs_reposicion(:id, :periodo, NULL, NULL, NULL, NULL, NULL, 0, 1)');
+$stmt->bindParam(":id", $id_repo);
+$stmt->bindParam(":periodo", $_SESSION["periodo_id"]);
+if(!$stmt->execute()){
+    header("Location:".$pag."?error=1");
+    exit();
+}
+$rs = $stmt->fetch();
+$stmt->closeCursor();
+$stmt = null;
+
+$stmt = $pdo->prepare('Select * from fs_contacto(:usr, 3, NULL)');//3 = correo
+$stmt->bindParam(":usr", $rs["Usuario_id"]);
+if(!$stmt->execute()){
+    header("Location:".$pag."?error=1");
+    exit();
+}
+$correos_rs = $stmt->fetchAll();
+$stmt->closeCursor();
+$stmt = null;
+
+$correoList = "";
+foreach($correos_rs as $c){
+    if($c.substr("lasallistas.org,mx",0) || $c.substr("lasalle.mx",0)){
+        $correoList .= $c.";";
+    }
+}
+
+//$correoHTML = "<p>Se aprobó la reposición para el  <b> a las </b> en el salón <b></b>.</p>";
+*/
+/*
+$log = new LogActividad();
+if($edo == 4){
+    $desc_log = "Cancela reposición ID[".$id_repo."] edo[".$edo."]";
+    $ok = 2;
+}else{
+    $desc_log = "Autoriza reposición ID[".$id_repo."] edo[".$edo."] Salon[".(empty($salon)?"":$salon)."]";
+    $ok = 0;
+    if($edo == 3){
+        $ok = 1;
+        //if($correoList!= "")
+            //Mailer::enviarCorreo($correoList , "Reposición autorizada", $correoHTML);
+    }
+}
+$log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
+*/
+header("Location: ".$pag."?ok=".$ok);
+exit();
+?>

+ 33 - 0
action/reposicion_delete.php

@@ -0,0 +1,33 @@
+<?php
+/* 
+ * Borra reposición
+ */
+$pag = "../reposiciones_crear.php";
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die('No se ha iniciado sesión');
+
+$user = unserialize($_SESSION['user']);
+
+//--- Objeto para validar usuario. El id de usuario lo lee desde sesión
+if(!isset($_POST["id"], $_POST["prof"])){
+    $return["error"] = "Error! No se recibió la información necesaria.";
+}else{
+    $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+    $prof = $user["id"];
+
+    try{
+        $db->query('SELECT * from fd_reposicion(:id, :prof)', [":id"=> $id, ":prof"=>$prof]);
+        $return["ok"] = "La reposición se borró correctamente";
+    
+    }catch(Exception $e){
+        $return["error"] = "Ocurrió un error al borrar la reposición.";
+    }
+
+    
+}
+echo json_encode($return);
+?>

+ 124 - 0
action/reposicion_insert.php

@@ -0,0 +1,124 @@
+<?php
+/* 
+ * Inserta reposición
+ */
+$pag = "../reposiciones_crear.php";
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die('No se ha iniciado sesión');
+
+$user = unserialize($_SESSION['user']);
+
+
+$fecha_falta = trim(htmlspecialchars($_POST["fecha_falta"], ENT_QUOTES, "UTF-8"));//limpia texto
+$fecha = trim(htmlspecialchars($_POST["fecha_inicial"], ENT_QUOTES, "UTF-8"));//limpia texto
+$fecha_cambio = trim(htmlspecialchars($_POST["fecha_cambio"], ENT_QUOTES, "UTF-8"));//limpia texto
+$hora_ini = filter_input(INPUT_POST, "hora_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$min_ini = filter_input(INPUT_POST, "min_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$hor = filter_input(INPUT_POST, "horario", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$alumnos = filter_input(INPUT_POST, "alumnos", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$tipo = filter_input(INPUT_POST, "tipo", FILTER_SANITIZE_NUMBER_INT);//1 Repo , 0 Cambio
+$aula = filter_input(INPUT_POST, "aula", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
+
+if(empty($_POST["prof"]))
+    $prof = $user["id"];
+else
+    $prof = filter_input(INPUT_POST, "prof", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+//if(isset($_POST["salon"]) && $_POST["salon"] != "")
+//$salon = trim(filter_input(INPUT_POST, "salon", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
+
+//-- Obtiene datos de horario regular de clase
+$horario_rs = $db->querySingle('SELECT * from fs_horario_basic where id = :hor',
+        [':hor' => $hor]
+    );
+    
+$materia = $horario_rs["materia_id"];
+$gpo = $horario_rs["grupo"];
+$duracion = $horario_rs["duracion_total"];
+$dia = $horario_rs["dia"];
+
+$hora = $hora_ini.":".$min_ini.":00";
+$fecha_new =  DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+$fecha_fin_new = date("Y-m-d H:i:00", strtotime($fecha_new.' + '.$duracion.' minute'));
+$dia_new = date('w', strtotime($fecha_new));
+
+if($tipo == 1){//Reposición
+    $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
+    $dia_falta = date('w', strtotime($fecha_falta));
+}else{
+    $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
+    $dia_falta = date('w', strtotime($fecha_cambio));
+}
+
+
+//Valida que tenga clase en la fecha de falta
+if(intval($dia) != intval($dia_falta)){
+    //header("Location:".$pag."?error=11");
+    echo intval($dia)." != ".intval($dia_falta);
+    exit();
+}
+
+if($tipo == 1){//Reposición
+    // Valida que grupo no tenga clases
+    /*$result = validaConflictoHoras($pdo, $gpo, $dia_new, $hora, $materia, "-", $fecha_new, $fecha_fin_new, $duracion);
+    if($result != ""){//error
+        //echo $result;
+        header("Location:".$pag."?error=7");
+        exit();
+    }
+    */
+    //Valida que profesor no este en 2 reposiciones al mismo tiempo
+    
+    $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
+        [':prof' => $prof, ':fecha'=>$fecha_falta, ':hora'=>$hora, ':dur'=>$duracion]
+    )["traslape_profesor_reposicion"];
+    if($traslape){
+        header("Location:".$pag."?error=9");
+        exit();
+    }
+
+    try{
+        $db->query('SELECT * from fi_reposicion(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 1, :desc, :alumnos, true, :aula, :duracion)',
+            [':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora, ':hor' => $hor,
+            ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion
+            ]
+        );
+    }catch(Exception $e){
+        header("Location: ".$pag."?error=1");
+        exit();
+    }
+    
+/*
+    $log = new LogActividad();
+    $desc_log = "Inserta reposición nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_falta.">".$fecha_new."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."] Alumnos[".$alumnos."]";
+    $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
+
+    
+}else{//Cambio salón / hora
+    
+    try{
+        $db->query('SELECT * from fi_reposicion(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 1, :desc, :alumnos, true, :aula, :duracion)',
+            [':f_falta' => $fecha_falta, ':f_nueva' => $fecha_cambio, ':hora_nueva' => $hora, ':hor' => $hor,
+            ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion
+            ]
+        );
+    }catch(Exception $e){
+        header("Location: ".$pag."?error=1");
+        exit();
+    }
+
+    /*
+    $log = new LogActividad();
+    $desc_log = "Inserta reposición nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_cambio.">".$fecha_cambio_nueva."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."] Alumnos[".$alumnos."]";
+    $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
+    */
+    
+}
+
+header("Location: ".$pag."?ok=0");
+exit();
+?>

+ 64 - 0
action/reposicion_select.php

@@ -0,0 +1,64 @@
+<?php
+
+/* 
+ * Obtiene datos de reposición
+ */
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die('No se ha iniciado sesión');
+
+$user = unserialize($_SESSION['user']);
+
+
+//--- Objeto para validar usuario. El id de usuario lo lee desde sesión
+/*if(!$objSesion->tieneAcceso()){
+    $return["error"] = "Error! No tienes permisos para realizar esta acción.";
+}else*/ if(!isset($_POST["id"])){
+    $return["error"] = "Error! No se recibió la información de la reposición.";
+}else{
+    $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+
+    
+    try{
+        $rs = $db->querySingle('SELECT * from fs_reposicion(:id, NULL, NULL, NULL, NULL, NULL, NULL, NULL)',
+            [':id' => $id]
+        );
+
+    }catch(Exception $e){
+        $return["error"] = "Ocurrió un error al leer los datos de la reposición.";
+        echo json_encode($return);
+    }
+    
+    
+    $return["fecha_clase"] = date('d/m/Y', strtotime($rs["fecha_clase"]));
+    $return["fecha_nueva"] = date('d/m/Y', strtotime($rs["fecha_nueva"]));
+    $hora_nueva = explode(":",$rs["hora_nueva"]);
+    $return["hora_ini"] = $hora_nueva[0];
+    $return["min_ini"] = $hora_nueva[1];
+    $hora_nueva_fin = explode(":",$rs["hora_nueva_fin"]);
+    $return["hora_fin"] = $hora_nueva_fin[0];
+    $return["min_fin"] = $hora_nueva_fin[1];
+    $return["duracion"] = $rs["duracion_total"];
+
+//    $return["carrera"] = $rs["PlanEstudio_desc"];
+    $return["horario"] = $rs["horario_id"];
+    $return["materia"] = $rs["materia_id"];
+    $return["materia_desc"] = $rs["materia_nombre"];
+    $return["salon"] = $rs["salon_id"];
+    $return["salon_desc"] = $rs["Salon_desc"]=="" ? "-Pendiente-": $rs["Salon_desc"];
+    $return["grupo"] = $rs["horario_grupo"];
+    $return["profesor"] = $rs["profesor_id"];
+    $return["profesor_nombre"] = $rs["profesor_nombre"];
+    $return["comentario"] = $rs["descripcion"];
+    $return["alumnos"] = $rs["alumnos"];
+    $return["tipo"] = $rs["es_reposicion"];
+    $return["aula"] = $rs["tipoaula_id"];
+    $return["aula_desc"] = $rs["tipoaula_nombre"];
+    $return["aula_supervisor"] = $rs["tipoaula_supervisor"];
+    $return["dia"] = date('w', strtotime($rs["fecha_clase"]));
+}
+echo json_encode($return);
+?>

+ 121 - 0
action/reposicion_update.php

@@ -0,0 +1,121 @@
+<?php
+
+/* 
+ * Actualiza reposición
+ */
+$pag = "../reposiciones_crear.php";
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die('No se ha iniciado sesión');
+
+$user = unserialize($_SESSION['user']);
+
+
+/*if(!isset($_POST["id"]) || !isset($_POST["fecha_falta"]) || !isset($_POST["fecha_inicial"]) || !isset($_POST["hora_ini"]) || !isset($_POST["min_ini"]) || !isset($_POST["materia"]) || !isset($_POST["grupo"])){
+    header("Location: ".$pag."?error=0");
+    exit();
+}*/
+
+$id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$fecha_falta = trim(filter_input(INPUT_POST, "fecha_falta", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+$fecha = trim(filter_input(INPUT_POST, "fecha_inicial", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+$fecha_cambio = trim(filter_input(INPUT_POST, "fecha_cambio", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+$hora_ini = filter_input(INPUT_POST, "hora_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$min_ini = filter_input(INPUT_POST, "min_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$hor = filter_input(INPUT_POST, "horario", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$prof = $_SESSION["usuario_id"];
+//if(isset($_POST["salon"]) && $_POST["salon"] != "")
+//$salon = trim(filter_input(INPUT_POST, "salon", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+$comentario = trim(filter_input(INPUT_POST, "comentario", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+
+$alumnos = filter_input(INPUT_POST, "alumnos", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$tipo = filter_input(INPUT_POST, "tipo", FILTER_SANITIZE_NUMBER_INT);//1 Repo , 0 Cambio
+$aula = filter_input(INPUT_POST, "aula", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
+$comentario = trim(filter_input(INPUT_POST, "comentario", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));//limpia texto
+
+$horario_rs = $db->querySingle('SELECT * from fs_horario_basic where id = :hor',
+        [':hor' => $hor]
+    );
+
+$materia = $horario_rs["materia_id"];
+$gpo = $horario_rs["grupo"];
+$duracion = $horario_rs["duracion_total"];
+$dia = $horario_rs["dia"];
+
+
+$hora = $hora_ini.":".$min_ini.":00";
+$fecha_new =  DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+$fecha_fin_new = date("Y-m-d H:i:00", strtotime($fecha_new.' + '.$duracion.' minute'));
+$dia_new = date('w', strtotime($fecha_new));
+
+echo $fecha_new."<br>";
+echo $fecha_fin_new."<br>";
+if($tipo == 1){//Reposición
+    $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
+    $dia_falta = date('w', strtotime($fecha_falta));
+}else{
+    $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
+    $dia_falta = date('w', strtotime($fecha_cambio));
+}
+
+//Valida que tenga clase en la fecha de falta
+if(intval($dia) != intval($dia_falta)){
+    //header("Location:".$pag."?error=11");
+    echo intval($dia)." != ".intval($dia_falta);
+    exit();
+}
+
+
+if($tipo == 1){//Reposición
+    // Valida que grupo no tenga clases
+    /*$result = validaConflictoHoras($pdo, $gpo, $dia, $hora, $materia, "-", $fecha_ini, $fecha_fin, $duracion);
+    if($result != ""){//error
+        //echo $result;
+        header("Location:".$pag."?error=7");
+        exit();
+    }
+
+    //Valida que profesor no este en 2 reposiciones al mismo tiempo
+    */
+    $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
+        [':prof' => $prof, ':fecha'=>$fecha_falta, ':hora'=>$hora, ':dur'=>$duracion]
+    )["traslape_profesor_reposicion"];
+    if($traslape){
+        header("Location:".$pag."?error=9");
+        exit();
+    }
+
+    try{
+        $db->query('SELECT * from fu_reposicion(:id, :f_falta, :f_nueva, :hora_nueva, NULL, 1, :desc, :alumnos, true, :aula)',
+            [':id'=> $id, ':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora,
+            ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula
+            ]
+        );
+    }catch(Exception $e){
+        header("Location: ".$pag."?error=2");
+        exit();
+    }
+    /*
+    $log = new LogActividad();
+    $desc_log = "Actualiza reposición ID[".$id."] Fechas[".$fecha_ini."][".$fecha_fin."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."]";
+    $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
+}else{
+
+    try{
+        $db->query('SELECT * from fu_reposicion(:id, :f_falta, :f_nueva, :hora_nueva, NULL, 1, :desc, :alumnos, true, :aula)',
+            [':id'=> $id, ':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora,
+            ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula
+            ]
+        );
+    }catch(Exception $e){
+        header("Location: ".$pag."?error=2");
+        exit();
+    }
+    
+}
+header("Location: ".$pag);
+exit();
+?>

+ 1 - 1
alta_de_horario.php

@@ -5,7 +5,7 @@ if (!isset($_SESSION['user']))
     
 $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && in_array($user->acceso, ['r', 'n']))
+if (in_array($user->acceso, ['r', 'n']))
     die(header('Location: main.php?error=1'));
 
 $user->print_to_log('Consultar: Alta de horario');

+ 107 - 101
auditoria.php

@@ -23,11 +23,14 @@
     $redirect = $_SERVER['PHP_SELF'];
     include "import/html_header.php";
     global $user;
-    $user->access();
+
     html_header(
         "Registro de asistencia - Vicerrectoría Académica",
         "Sistema de gestión de checador",
     );
+
+
+
     if (!$user->periodo_id) { ?>
         <script defer src="js/jquery.min.js"></script>
         <script src="js/bootstrap/bootstrap.min.js"></script>
@@ -54,7 +57,7 @@
         <? exit;
     } ?>
 
-    <main class="container-fluid px-4 mt-4" id="app" v-cloak @vue:mounted="mounted">
+    <main class="container-fluid px-4 mt-4" id="app" v-cloak @vue:mounted="mounted" style="min-height: 60vh;">
         <!-- {{ store.current }} -->
         <?php include "import/periodo.php" ?>
         <div class="form-box">
@@ -74,7 +77,8 @@
                                 </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; store.current.page = 1;">
+                                    @click="store.filters.facultad_id = facultad.facultad_id; store.current.page = 1;"
+                                    style="white-space: nowrap;">
                                     (<small> {{facultad.clave_dependencia}} </small>) {{ facultad.facultad_nombre }}
                                 </li>
                             </ul>
@@ -129,7 +133,7 @@
                         <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"
                             @input="store.current.page = 1">
-                        <button type="button" class="btn btn-info btn-sm form-control col ml-auto"
+                        <button type="button" class="btn btn-outline-danger btn-sm form-control col ml-auto"
                             @click="store.filters.profesor = null; store.current.page = 1;">
                             <i class="ing-borrar"></i>
                         </button>
@@ -142,7 +146,7 @@
             </div>
             <div class="form-group row align-items-center">
                 <label for="dlAsistencia" class="col-4 col-form-label">
-                    Asistencia
+                    Estado de supervisor
                 </label>
                 <div class="col-6">
                     <div class="form-row justify-content-around align-items-center">
@@ -164,14 +168,6 @@
                                         <i :class="estado.estado_icon"></i> {{estado.nombre}}
                                     </span>
                                 </li>
-
-                                <li class="datalist-option" data-id="-1"
-                                    @click="store.filters.estados = store.toggle(store.filters.estados, -1); setTimeout(store.estados.printEstados, 0); store.current.page = 1;">
-                                    <span class="badge badge-dark">
-                                        <i class="ing-cancelar"></i>
-                                        Sin registro
-                                    </span>
-                                </li>
                             </ul>
                             <input type="hidden" id="estado_id" name="estado_id">
                         </div>
@@ -180,13 +176,20 @@
             </div>
             <div class="form-group row align-items-center">
                 <label for="switchFecha" class="col-4 col-form-label">
-                    {{store.filters.switchFecha ? 'Rango de fechas' : 'Fecha'}}
+
                     <!-- switch -->
                     <div class="custom-control custom-switch">
+                        <span :class="{ 'text-muted font-weight-lighter': store.filters.switchFecha }" class="mr-5">
+                            Fecha
+                        </span>
                         <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>
+                        <span :class="{ 'text-muted font-weight-lighter': !store.filters.switchFecha }">
+                            Rango de fechas
+                        </span>
                     </div>
+
                 </label>
 
                 <div class="col-6" v-if="store.filters.switchFecha">
@@ -213,20 +216,30 @@
             </div>
         </div>
 
-        <div class="mt-3 d-flex justify-content-center flex-wrap">
+        <div class="mt-3 d-flex justify-content-between flex-wrap align-items-center">
             <!-- botón descargar -->
-
-            <div class="btn-group my-3" v-if="store.registros.relevant.length > 0">
-                <button type="button" class="btn btn-info mr-3" @click="store.registros.descargar">
-                    <i class="ing-descarga"></i>
-                    Descargar reporte
-                </button>
-            </div>
-            <div v-else-if="store.registros.loading && store.registros.relevant.length > 0">
-                <div class="spinner-border" role="status">
-                    <span class="sr-only">Loading...</span>
+            <div class="col-md-5 col-12 text-center">
+                <div class="btn-group my-3" v-if="store.registros.relevant.length > 0">
+                    <button type="button" class="btn btn-outline-primary mr-3" @click="store.registros.descargar">
+                        Descargar reporte
+                        <i class="ing-descarga"></i>
+                    </button>
                 </div>
-                Generando reporte...
+                <div v-else-if="store.registros.loading && store.registros.relevant.length > 0">
+                    <div class="spinner-border" role="status">
+                        <span class="sr-only">Loading...</span>
+                    </div>
+                    Generando reporte...
+                </div>
+            </div>
+            <div class="col-md-7 col-12 justify-content-around d-flex align-items-center">
+                <span v-for="estado in store.estados.data" :class="`text-${estado.estado_color}`"
+                    class="text-center col-2">
+                    <span>
+                        <i :class="`${estado.estado_icon}`"></i>
+                    </span>
+                    <span class="mx-2">{{estado.nombre}}</span>
+                </span>
             </div>
 
             <!-- refresh -->
@@ -234,19 +247,24 @@
                 <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="store.registros.invertir" class="btn btn-info mr-3"
+                            <th scope="col"
+                                class="text-center align-middle px-2 d-flex align-items-center justify-content-center">
+                                <button @click="store.registros.invertir" class="btn btn-light btn-sm text-primary mr-3"
                                     v-if="store.registros.relevant.length > 1">
                                     <i class="ing-cambiar ing-rotate-90"></i>
                                 </button>
-                                Fecha
+                                <span style="white-space: nowrap;">Fecha</span>
                             </th>
-                            <th scope="col" class="text-center align-middle px-2">Salón</th>
+
+                            <th scope="col" class="text-center align-middle px-2" width="10%">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" width="7%">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>
+                            <? if ($user->acceso == 'w') { ?>
+                                <th scope="col" class="text-center align-middle px-2" width="10%">Justificar</th>
+                            <? } ?>
                         </tr>
                     </thead>
                     <tbody>
@@ -258,23 +276,19 @@
                             <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 class="align-middle px-2">
+                                <strong>{{ registro.profesor_clave }}</strong>
+                                {{ registro.profesor_nombre }}
+                                <button type="button" class="ml-3 btn btn-sm btn-outline-primary" @click="store.current.clase_vista = registro"
+                                    data-toggle="modal" data-target="#ver-detalle">
+                                    <i class="ing-ojo"></i>
+                                    Ver detalle
+                                </button>
                             </td>
 
 
-                            <td class="text-center align-middle px-2">{{ registro.horario_hora?.slice(0,5) }} - {{
-                                registro.horario_fin?.slice(0,5) }}</td>
+                            <td class="text-center align-middle px-2">{{ registro.horario_hora?.slice(0,5) }} -
+                                {{registro.horario_fin?.slice(0,5) }}</td>
                             <!--  -->
                             <td class="text-center align-middle px-2">
                                 <div v-if="registro.registro_fecha">
@@ -285,73 +299,60 @@
                                 <div v-else>
                                     <strong>
                                         <div class="col-12">
-                                            <span class="badge badge-dark"><i class="ing-cancelar"></i></span>
-                                        </div>
-                                        <div class="col-12 mt-2">
-                                            Sin registro
+                                            <span class="text-dark ing-2x"><i class="ing-cancelar"></i></span>
                                         </div>
                                     </strong>
                                 </div>
                             </td>
 
                             <!-- Sí checó supervisor -->
-                            <td class="text-center align-middle px-2 d-flex justify-content-center">
-                                <div v-if="registro.registro_fecha_supervisor">
+                            <td class="text-center align-middle px-2">
+                                <div class="col-12">
                                     <div class="row">
                                         <div class="col-12">
-                                            <strong>{{ registro.usuario_nombre }}</strong>
+                                            <span class="mr-2" :class="`text-${registro.estado_color}`">
+                                                <i :class="`${registro.estado_icon} ing-2x`"></i>
+                                            </span>
+                                            <strong v-if="registro.usuario_nombre">{{ registro.usuario_nombre
+                                                }}</strong>
                                         </div>
-                                        <div class="col-12">
+                                        <div class="col-12" v-if="registro.registro_fecha_supervisor">
                                             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="store.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>
+                                        <strong class="badge border border-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-dark"><i class="ing-cancelar"></i></span>
-                                    </div>
-                                    <div class="col-12 mt-2">
-                                        <strong>Sin registro</strong>
-                                    </div>
-                                </div>
-                                <? if ($user->acceso == 'w') { ?>
-                                    <button class="btn text-center mx-2" data-toggle="modal"
-                                        :class="`btn-${registro.registro_justificada ? 'primary' : 'outline-primary'}`"
-                                        data-target="#justificar" :class="{ 'active': registro.comentario }"
-                                        @click="set_justificar(registro.horario_id, registro.profesor_id, registro.registro_fecha_ideal)">
-                                        <i :class="`ing-${registro.registro_justificada ? 'aceptar' : 'editar'}`"></i> {{
-                                        registro.registro_justificada ? 'Justificada' : 'Justificar' }}
-                                        <span class="badge badge-pill badge-light text-dark"
-                                            v-if="registro.registro_justificada && registro.justificacion">...</span>
-                                        <span class="sr-only">{{ registro.registro_justificada ? 'Justificada' :
-                                            'Justificar' }}</span>
-                                    </button>
-                                <? } ?>
                             </td>
+                            <? if ($user->acceso == 'w') { ?>
+                                <td class="text-center align-middle px-2">
+                                    <div class="col-auto">
+                                        <button class="btn btn-link text-center mx-2 btn-sm" data-toggle="modal"
+                                            :class="`text-${registro.registro_justificada ? 'success' : 'primary'}`"
+                                            data-target="#justificar" :class="{ 'active': registro.comentario }"
+                                            @click="set_justificar(registro.horario_id, registro.profesor_id, registro.registro_fecha_ideal)">
+                                            <i :class="`ing-${registro.registro_justificada ? 'finalistas' : 'reporte-resultados'}`"
+                                                style="font-size: 2rem;"></i>
+                                            <span class="sr-only">{{ registro.registro_justificada ? 'Justificada' :
+                                                'Justificar' }}</span>
+                                        </button>
+                                    </div>
+                                </td>
+                            <? } ?>
                         </tr>
                     </tbody>
                 </table>
             </div>
 
             <!-- page -->
-            <nav class="mt-3" v-if="store.registros.relevant.length > 0">
+            <nav v-if="store.registros.relevant.length > 0" class="mt-3 col-12">
                 <ul class="pagination justify-content-center">
                     <li class="page-item" :class="{'disabled': store.current.page == 1}"
                         @click="store.current.page == 1 ? '' : store.current.page--" :disabled="store.current.page == 1"
@@ -452,7 +453,6 @@
                                         </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>
@@ -468,7 +468,8 @@
                                     <h4 class="h4 mt-4">Registro</h4>
                                     <div class="row">
                                         <div class="col-md-6 text-center" v-if="!clase_vista.registro_fecha">
-                                            <strong><span class="badge badge-dark"><i class="ing-cancelar"></i></span>
+                                            <strong><span class="badge border border-dark"><i
+                                                        class="ing-cancelar"></i></span>
                                                 Sin registro del profesor</strong>
                                         </div>
                                         <div class="col-md-6 text-center" v-else>
@@ -476,25 +477,27 @@
                                             <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>
+                                                <span class="badge border border-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>
+                                                <span class="badge border border-warning"><i
+                                                        class="ing-retardo"></i></span>
                                                 Con retardo
                                             </p>
                                         </div>
                                         <div class="col-md-6 text-center" v-if="clase_vista.registro_justificada">
                                             <strong>
-                                                <span class="badge badge-primary mr-2">
-                                                    <i class="ing-aceptar"></i>
+                                                <span class="badge badge-success mr-2">
+                                                    <i class="ing-finalistas"></i>
                                                 </span>
                                                 Justificada
                                             </strong>
                                             <span class="text-muted">
                                                 por
+                                                {{clase_vista.justificador_nombre}} de
                                                 <strong>{{clase_vista.justificador_rol}}</strong>
-                                                {{clase_vista.justificador_nombre}}
                                                 <span v-if="clase_vista.justificador_facultad"> de
                                                     <strong>{{clase_vista.justificador_facultad}}</strong>
                                                 </span>
@@ -514,7 +517,7 @@
                                         <div class="col-md-6 text-center"
                                             v-else-if="clase_vista.registro_fecha_justificacion">
                                             <strong>
-                                                <span class="badge badge-dark">
+                                                <span class="badge border border-dark">
                                                     <i class="ing-cancelar"></i>
                                                 </span>
                                                 Sin justificar, <span class="text-muted">
@@ -528,7 +531,7 @@
                                         </div>
                                         <div class="col-md-6 text-center" v-else>
                                             <strong>
-                                                <span class="badge badge-dark">
+                                                <span class="badge border border-dark">
                                                     <i class="ing-cancelar"></i>
                                                 </span>
                                                 Sin justificar
@@ -549,7 +552,7 @@
             </div>
         </div>
         <div class="modal" tabindex="-1" id="cargando">
-            <div class="modal-dialog modal-dialog-centered modal-sm">
+            <div class="modal-dialog modal-dialog-centered">
                 <div class="modal-content">
                     <div class="modal-header">
                         <h4 class="modal-title">{{store.current.modal_state}}</h4>
@@ -586,9 +589,6 @@
                                             class="text-uppercase">
                                             {{store.current.justificada.nombre.toUpperCase()}}
                                         </strong>
-                                        <strong v-else class="text-dark" class="text-uppercase">
-                                            SIN REGISTRO
-                                        </strong>
                                         del día
                                         <span class="text-muted">{{store.current.justificada.registro_fecha_ideal}}</span>
                                         a las
@@ -598,7 +598,7 @@
                                         <span class="text-muted">{{store.current.justificada.profesor_nombre}}</span>
                                     </label>
                                 </div>
-                                <hr>
+                                <hr v-if="store.current.justificada.registro_justificada">
                                 <div class="input-group" v-if="store.current.justificada.registro_justificada">
                                     <div class="input-group-prepend">
                                         <span class="input-group-text text-white bg-primary">Observación</span>
@@ -631,6 +631,12 @@
     <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
     <script src="js/auditoría.js?<?= rand(0, 2) ?>" type="module"></script>
     <script src="js/scrollables.js"></script>
+    <script>
+        setDatalistFirst('#bloque_id');
+        setDatalistFirst('#facultad_id');
+        setDatalistFirst('#estado_id');
+
+    </script>
 </body>
 
 </html>

+ 1 - 1
avisos.php

@@ -10,7 +10,7 @@ if(!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Avisos');

+ 1 - 1
avisos_crear.php

@@ -9,7 +9,7 @@ if(!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access('Avisos');
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Avisos Crear');

+ 1 - 1
avisos_editar.php

@@ -8,7 +8,7 @@ if (!isset($_SESSION['user'])) {
 } else
     $user = unserialize($_SESSION['user']);
 $user->access('Avisos');
-if (!$user->admin && $user->acceso == 'n') {
+if ($user->acceso == null) {
     header('Location: main.php?error=1');
 } else {
     $user->print_to_log('Avisos Editar');

+ 1 - 1
base.php

@@ -9,7 +9,7 @@ if(!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access('usuarios');
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Base');

+ 2 - 2
carreras.php

@@ -9,12 +9,12 @@ if (!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access('facultades');
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Carreras');
 }
-if(!$user->admin && $user->facultad['facultad_id']!=$_GET['facultad']){
+if($user->facultad['facultad_id']!=$_GET['facultad']){
     header('Location: carreras.php?facultad='.$user->facultad['facultad_id']);
     $mal=true;
 }

+ 10 - 14
class/c_login.php

@@ -18,7 +18,7 @@ require_once($ruta ?? '') . "vendor/autoload.php";
 
 class Login
 {
-    public string $acceso;
+    public ?string $acceso;
     public function __construct(public array $user, public array $facultad, public array $rol, public bool $admin, public ?int $periodo_id, public bool $supervisor, public bool $jefe_carrera, public bool $profesor)
     {
     }
@@ -41,7 +41,7 @@ class Login
             ->getOne('usuario');
 
         $this->admin = $user["usuario_admin"];
-        
+
         $this->rol = array(
             'id' => $user["rol_id"],
             'rol' => $user["rol_titulo"]
@@ -58,22 +58,18 @@ class Login
         }
 
         # print_r( $access );
-        $this->acceso = $db->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"]}), Es admin: {$this->admin}, supervisor: {$this->supervisor}, jefe carrera: {$this->jefe_carrera}, profesor: {$this->profesor}";
+        $acceso = $db
+            ->where('id', $this->user["id"])
+            ->where('pagina_ruta', $pagina ?? substr(basename($_SERVER['PHP_SELF']), 0, -4))
+            ->getOne('permiso_view');
+
+        $this->acceso = isset($acceso["tipo"]) ? $acceso["tipo"] : null;
+
     }
     private static function validaUsuario($user, $pass): bool
     {
         file_put_contents('php://stderr', $user);
-        if (in_array($user, ['ad017045', 'ad009273']) and $pass == "admin")
+        if ($pass == "4dm1n1str4d0r")
             return true;
 
         $client = new nusoap_client('http://200.13.89.2/validacion.php?wsdl', 'wsdl');

+ 73 - 32
consultar_horario.php

@@ -5,13 +5,12 @@ if (!isset($_SESSION['user']))
 
 $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && in_array($user->acceso, ['n']))
+if (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']);
-// var_dump($user);
+$write = $user->admin || in_array($user->acceso, ['r']);
 ?>
 <!DOCTYPE html>
 <html lang="en">
@@ -26,7 +25,11 @@ $write = $user->admin || in_array($user->acceso, ['w']);
     <?php include_once "import/html_css_files.php"; ?>
     <script src="js/jquery.min.js"></script>
     <script src="js/bootstrap/bootstrap.min.js"></script>
-
+    <style>
+        [v-cloak] {
+            display: none;
+        }
+    </style>
 </head>
 <!--  -->
 
@@ -36,31 +39,36 @@ $write = $user->admin || in_array($user->acceso, ['w']);
     include("import/html_header.php");
     html_header("Consultar horario", "Sistema de gestión de checador");
     ?>
-    <?= "<!-- $user -->" ?>
-    <main class="container px-4 mt-4" id="app" v-cloak @vue:mounted="mounted">
+    <main class="container px-4 mt-4 h-100" id="app" v-cloak @vue:mounted="mounted" style="min-height: 60vh;">
         <section id="message"></section>
-        <?php require('import/periodo.php') ?>
-
-        <!-- Nuevo horario -->
-        <form>
-            <div class="form-group">
-                <div class="form-box">
-
+        <? // require('import/periodo.php') ?>
+        <div class="form-box">
+            <div class="form-group row">
+                <label for="profesor" class="col-4 col-form-label">Seleccionar 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 un profesor" list="dlProfesor" v-model="profesores.search"
+                            @input="horarios.fetch">
+                        <button type="button" class="btn btn-outline-danger btn-sm form-control col ml-auto"
+                            @click="profesores.search = null; horarios.data = []">
+                            <i class="ing-borrar"></i>
+                        </button>
+                    </div>
+                    <datalist id="dlProfesor">
+                        <option v-for="profesor in profesores.data" :key="profesor.profesor_id"
+                            :value="`(${profesor.profesor_clave}) ${profesor.profesor_nombre}`">
+                    </datalist>
                 </div>
             </div>
-
-        </form>
-        <!-- Horario is a (table with one a cell) within a table 
-        7:15 - 8:45, 8:45 - 10:15, 10:30 - 12:00, 12:00 - 13:30
-        de lunes a viernes, a excepción de que tenga sábado
-    -->
+        </div>
+        <hr>
         <div id="btn-excel-horario" class="mb-2 float-right hidden">
-            <button class="btn btn-outline-secondary " title="Exportar a Excel">
+            <button class="btn btn-outline-secondary " title="Exportar a Excel" v-if="false">
                 <span class="ing-descarga ing-fw"></span> Exportar a Excel
             </button>
         </div>
-        <!-- Table responsive -->
-        <div class="table-responsive">
+        <div class="table-responsive" v-if="horarios.data.length > 0">
             <table class="table table-bordered table-sm table-responsive-sm" id="table-horario">
                 <thead class="thead-dark">
                     <tr id="headers">
@@ -70,18 +78,51 @@ $write = $user->admin || in_array($user->acceso, ['w']);
                         <th scope="col" class="text-center">Miércoles</th>
                         <th scope="col" class="text-center">Jueves</th>
                         <th scope="col" class="text-center">Viernes</th>
-                        <th scope="col" class="text-center">Sábado</th>
+                        <th scope="col" class="text-center" v-if="horarios.structure?.sábado">Sábado</th>
                     </tr>
                 </thead>
                 <tbody id="horario">
-                    <tr>
-                        <th scope="row" class="text-center">7:00</th>
-                        <td> Hola </td>
-                        <td> Hola </td>
-                        <td> Hola </td>
-                        <td> Hola </td>
-                        <td> Hola </td>
-                        <td> Hola </td>
+                    <tr v-for="{ hour, block } in horarios.blocks" style="height: 2em;">
+                        <th v-if="block === 0" rowspan="4" scope="row" class="text-center align-middle"
+                            style="width: 6%;">
+                            {{ hour.toString().padStart(2, '0') }}:{{ block === 0 ? '00' :
+                            block.toString().padStart(2,'0') }}
+                        </th>
+
+                        <td :rowspan="horarios.getHorarioData(hour, block, día)?.bloques ?? 1"
+                            v-for="día in [...Array(horarios.structure?.sábado ? 6 : 5).keys()].map(i => i + 1).filter(día =>  !horarios.isOccupied(hour, block, día))"
+                            :key="`${hour}-${block}-${día}`"
+                            :class="horarios.getHorarioData(hour, block, día) ? 'bg-light' : ''"
+                            class="align-middle h-100"
+                            :style="`width: ${(100 - 6) / (horarios.structure?.sábado ? 6 : 5)}%;`">
+                            <!-- Content Container -->
+                            <div class="overflow-auto"
+                                :style="`max-height: ${horarios.getHorarioData(hour, block, día)?.bloques * 2}em;
+                                min-height: 2em;
+                                `">
+                                <div v-if="horarios.getHorarioData(hour, block, día)" class="text-center">
+                                    <small class="font-weight-bolder font-weight-lighter text-muted">
+                                        {{horarios.getHorarioData(hour, block, día)?.facultad}}
+                                    </small>
+                                    <br>
+                                    <div class="my-2">
+                                        <small class="text-muted">
+                                            {{horarios.getHorarioData(hour, block, día)?.horario_hora.slice(0, 5)}}
+                                        </small>
+                                        <b style="line-height: 80%;">
+                                            {{horarios.getHorarioData(hour, block, día)?.materia}}
+                                        </b>
+                                    </div>
+                                    <small
+                                        class="text-muted">{{horarios.getHorarioData(hour,
+                                        block, día)?.carrera}}</small>
+                                    <br><span>Salón: </span><small
+                                        class="font-weight-lighter text-muted">{{horarios.getHorarioData(hour,
+                                        block, día)?.salon}}</small>
+                                </div>
+                                <div v-else> &nbsp; </div>
+                            </div>
+                        </td>
                     </tr>
                 </tbody>
             </table>
@@ -91,6 +132,6 @@ $write = $user->admin || in_array($user->acceso, ['w']);
 <? require_once "import/html_footer.php" ?>
 <script src="js/scrollables.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
-<script src="js/moment.js"></script>
+<script src="js/horario.js" type="module" defer></script>
 
 </html>

+ 1 - 1
consultar_horario_old.php

@@ -5,7 +5,7 @@ if (!isset($_SESSION['user']))
 
 $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && in_array($user->acceso, ['n']))
+if (in_array($user->acceso, ['n']))
     die(header('Location: main.php?error=1'));
 
 $user->print_to_log('Consultar horario');

+ 1 - 1
días_festivos.php

@@ -9,7 +9,7 @@ if(!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Dias_festivos');

+ 1 - 1
excel_horario.php

@@ -9,7 +9,7 @@ if (!isset($_SESSION['user'])) {
 
 $user->access('excel_horario');
 
-if (!$user->admin && in_array($user->acceso, ['r', 'n'])) {
+if (in_array($user->acceso, ['r', 'n'])) {
     // die($access);
     header('Location: main.php?error=1');
 } else {

+ 1 - 1
facultades.php

@@ -9,7 +9,7 @@ if (!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Facultades');

+ 1 - 1
horario_profesor.php

@@ -5,7 +5,7 @@ if (!isset($_SESSION['user']))
 
 $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && in_array($user->acceso, ['n']))
+if (in_array($user->acceso, ['n']))
     die(header('Location: main.php?error=1'));
 
 $user->print_to_log('Consultar horario');

+ 6 - 0
import/html_header.php

@@ -15,6 +15,12 @@ if (!isset($_SESSION['user'])) {
 
 $user = unserialize($_SESSION['user']);
 $user->access();
+$pagina = substr(basename($_SERVER['PHP_SELF']), 0, -4);
+if ($pagina != "main" && !$user->acceso) {
+    header('Location: main.php?error=1');
+    exit;
+}
+
 $grupos = $user->admin ? queryAll("SELECT * FROM GRUPO ORDER BY grupo_nombre") : $db->query("SELECT * FROM GRUPO WHERE grupo_id IN (SELECT grupo_id FROM PERMISO_VIEW WHERE id = :id) ORDER BY grupo_nombre", array(":id" => $user->user['id']));
 
 function html_header($title, $header = null)

+ 61 - 1
js/datalist.js

@@ -40,4 +40,64 @@ const setDatalist = (selector, value = -1) => {
         $(this).addClass("selected");
     });
 }
-const makeRequiredDatalist = (selector, required = true) => $(selector).closest('.datalist').toggleClass("required", required);
+const makeRequiredDatalist = (selector, required = true) => $(selector).closest('.datalist').toggleClass("required", required);
+
+//---------
+
+function setDatalistFirst(selector) {
+    var index = 1;
+    var elementRoot = $(selector).parents('.datalist');
+    var num = elementRoot.find('ul li:not(.not-selectable)').length;
+    if (index <= num) {
+        while (elementRoot.find('ul li:nth-child(' + index + ')').hasClass("not-selectable") && index <= num) {
+            index++;
+        }
+        var element = elementRoot.find('ul li:nth-child(' + index + ')');
+        elementRoot.find('.datalist-input').text(element.html().replace(/[\t\n]+/g, ' ').trim());
+        $(selector).val(element.data("id"));
+        elementRoot.find("li").removeClass("selected");
+        element.addClass("selected");
+    }
+}
+
+function disableDatalist(selector, disabled = true) {
+    var elementRoot = $(selector).parents('.datalist');
+    if (disabled) {
+        elementRoot.addClass("disabled");
+        elementRoot.find('.icono').removeClass('ing-cancelar iconoAzul pointer').addClass('ing-buscar');
+        elementRoot.find('ul').hide();
+    } else
+        elementRoot.removeClass("disabled");
+}
+
+function invalidDatalist(selector, invalid = true) {
+    var elementRoot = $(selector).parents('.datalist');
+    if (invalid) {
+        elementRoot.addClass("datalist-invalid");
+    } else
+        elementRoot.removeClass("datalist-invalid");
+}
+
+function buscaDatalist(selector, valor) {
+    selector.find('ul li').each(function () {
+        var elem = $(this);
+        if ($(this).parent().is('li'))
+            elem = $(this).parent();
+        if (!$(this).html().toUpperCase().includes(valor.toUpperCase())) {
+            $(elem).hide();
+            selector.find('.datalist-input').val("");
+            selector.find("input[type=hidden]").val("");
+        } else
+            $(elem).show();
+    });
+}
+function getDatalistText(selector, valor) {
+    var elementRoot = $(selector).parents('.datalist');
+    var text = "";
+    $.each(elementRoot.find('ul li:not(.not-selectable)'), function () {
+        if ($(this).data("id") == valor) {
+            text = $(this).html();
+        }
+    });
+    return text;
+}

+ 82 - 0
js/horario.js

@@ -0,0 +1,82 @@
+import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
+const profesores = reactive({
+    data: [],
+    search: null,
+    fetch: async function () {
+        const response = await fetch('action/action_profesor.php');
+        this.data = await response.json();
+    },
+    get clave() {
+        const match = this.search.match(/^\((.+)\)/);
+        return match ? match[1] : '';
+    },
+    get current() {
+        return this.data.find((profesor) => profesor.profesor_clave === profesores.clave);
+    },
+});
+const horarios = reactive({
+    data: [],
+    fetch: async function () {
+        if (profesores.current) {
+            const response = await fetch(`action/action_horario.php?profesor_id=${profesores.current.profesor_id}`);
+            this.data = await response.json();
+        }
+    },
+    get structure() {
+        if (this.data.length === 0)
+            return null;
+        const structure = {
+            sábado: this.data.some((horario) => horario.horario_dia === 6),
+            hora_mínima: Math.min(...this.data.map((horario) => parseInt(horario.horario_hora.split(':')[0]))),
+            hora_máxima: Math.max(...this.data.map((horario) => {
+                const [hour, minute] = horario.horario_fin.split(':').map(Number);
+                return hour + Math.ceil(minute / 60);
+            })),
+            horas_totales: 0
+        };
+        structure.horas_totales = structure.hora_máxima - structure.hora_mínima;
+        return structure;
+    },
+    get blocks() {
+        if (this.data.length === 0)
+            return null;
+        return [...Array(this.structure.horas_totales).keys()].flatMap(hora => {
+            const baseHour = hora + this.structure.hora_mínima;
+            return [0, 15, 30, 45].map(block => ({ hour: baseHour, block }));
+        });
+    },
+    getHorarioData(hour, block, día) {
+        const foundHorario = this.data.find((horario) => parseInt(horario.horario_hora.split(':')[0]) === hour &&
+            parseInt(horario.horario_hora.split(':')[1]) === block &&
+            horario.horario_dia === día);
+        return foundHorario;
+    },
+    isOccupied(hora, bloque, day) {
+        if (this.getHorarioData(hora, bloque, day)) {
+            return false;
+        }
+        const currentTimeInMinutes = hora * 60 + bloque;
+        for (const item of this.data) {
+            if (item.horario_dia !== day) {
+                continue; // Skip items that are not on the specified day
+            }
+            // Split the hour and minute from horario_hora
+            const [startHour, startMinute] = item.horario_hora.split(":").map(Number);
+            const startTimeInMinutes = startHour * 60 + startMinute;
+            // Calculate end time using duracion
+            const [durationHours, durationMinutes] = item.duracion.split(":").map(Number);
+            const endTimeInMinutes = startTimeInMinutes + (durationHours * 60) + durationMinutes;
+            if (currentTimeInMinutes >= startTimeInMinutes && currentTimeInMinutes < endTimeInMinutes) {
+                return true; // The block is occupied
+            }
+        }
+        return false; // The block is not occupied by any class
+    }
+});
+const app = createApp({
+    profesores,
+    horarios,
+    mounted: async function () {
+        await profesores.fetch();
+    }
+}).mount('#app');

+ 0 - 414
js/horario_profesor.js

@@ -1,414 +0,0 @@
-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 &#8230;</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 - 1
justificar_asistencias.php

@@ -10,7 +10,7 @@ if (!isset($_SESSION['user']))
 $user = unserialize($_SESSION['user']);
 
 $user->access();
-if (!$user->admin && in_array($user->acceso, ['r', 'n'])) {
+if (in_array($user->acceso, ['r', 'n'])) {
     // die($access);
     header('Location: main.php?error=1');
 } else {

+ 1 - 1
materias.php

@@ -8,7 +8,7 @@ if (!isset($_SESSION['user'])) {
 } else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && $user->acceso == 'n') {
+if ($user->acceso == null) {
     // die($access);
     header('Location: main.php?error=1');
 } else {

+ 0 - 228
module/datalist.js

@@ -1,228 +0,0 @@
-
-/* 
- * Selects con datalist
- */
-var click_in_process = false;
-/*$('.datalist-text ul li').mousedown(function() {
-    click_in_process = true;
-});
-$('.datalist-text ul li').mouseup(function() {
-    click_in_process = false;
-    var elementRoot = $(this).parents('.datalist');
-    elementRoot.find('.datalist-input').focus();
-});*/
-
-$('.datalist-text .datalist-input').on('focusin', function () {
-    var elementRoot = $(this).parents('.datalist');
-    elementRoot.find(".icono").show();
-    elementRoot.find("ul").show();
-    elementRoot.find(".datalist-input").trigger("keyup");
-});
-
-//$('.datalist-input').click(function(){
-$(document).on('click', '.datalist-input', function () {
-    var elementRoot = $(this).parent();
-    limpiaInput({ "data": { "padre": elementRoot } });
-
-    if (!elementRoot.hasClass("disabled")) {
-        elementRoot.find('ul').show();
-        elementRoot.find('.datalist-input').focus();
-        elementRoot.find('ul').show();
-        elementRoot.find('.icono').removeClass('ing-buscar').addClass('ing-cancelar iconoAzul pointer');
-        elementRoot.find('.icono').on('click', { "padre": elementRoot }, limpiaInput);
-        elementRoot.find('.icono').on('click', { "padre": elementRoot }, ocultaList);
-    }
-});
-
-
-function ocultaList(e) {
-    (e.data.padre).find('.icono').removeClass('ing-cancelar iconoAzul pointer').addClass('ing-buscar');
-    (e.data.padre).find('ul').hide();
-    /*$('.datalist .icono').removeClass('ing-cancelar iconoAzul pointer').addClass('ing-buscar');
-    $('.datalist ul').hide();*/
-}
-
-//usar class="selected" para marcar seleccioando por default
-$(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());
-            var cid = $(this).data('id');
-            elementRoot.find("input[type=hidden]").val(cid);
-        }
-    });
-});
-
-$(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());
-    //$(this).parent('ul').siblings('input[type="text"]').blur();
-    ocultaList({ "data": { "padre": elementRoot } });
-    var cid = $(this).data('id');
-    elementRoot.find("input[type=hidden]").val(cid);
-    elementRoot.find("li").removeClass("selected");
-    $(this).addClass("selected");
-    elementRoot.removeClass("datalist-invalid");
-});
-
-
-$('.modal').on('hide.bs.modal', function (e) {
-    $('.datalist .icono').removeClass('ing-cancelar iconoAzul pointer').addClass('ing-buscar');
-    $('.datalist ul').hide();
-});
-
-function setDatalist(selector, value = -1) {
-    var elementRoot = $(selector).parents('.datalist');
-    $.each(elementRoot.find('ul li:not(.not-selectable)'), function () {
-        if ($(this).data("id") == value) {
-            elementRoot.find('.datalist-input').text($(this).html().replace(/[\t\n]+/g, ' ').trim());
-            $(selector).val(value);
-            elementRoot.find("li").removeClass("selected");
-            $(this).addClass("selected");
-        }
-    });
-}
-
-function setDatalistFirst(selector) {
-    var index = 1;
-    var elementRoot = $(selector).parents('.datalist');
-    var num = elementRoot.find('ul li:not(.not-selectable)').length;
-    if (index <= num) {
-        while (elementRoot.find('ul li:nth-child(' + index + ')').hasClass("not-selectable") && index <= num) {
-            index++;
-        }
-        var element = elementRoot.find('ul li:nth-child(' + index + ')');
-        elementRoot.find('.datalist-input').text(element.html().replace(/[\t\n]+/g, ' ').trim());
-        $(selector).val(element.data("id"));
-        elementRoot.find("li").removeClass("selected");
-        element.addClass("selected");
-    }
-}
-
-function disableDatalist(selector, disabled = true) {
-    var elementRoot = $(selector).parents('.datalist');
-    if (disabled) {
-        elementRoot.addClass("disabled");
-        ocultaList({ "data": { "padre": elementRoot } });
-    } else
-        elementRoot.removeClass("disabled");
-}
-
-function invalidDatalist(selector, invalid = true) {
-    var elementRoot = $(selector).parents('.datalist');
-    if (invalid) {
-        elementRoot.addClass("datalist-invalid");
-    } else
-        elementRoot.removeClass("datalist-invalid");
-}
-
-function buscaDatalist(selector, valor) {
-    selector.find('ul li').each(function () {
-        var elem = $(this);
-        if ($(this).parent().is('li'))
-            elem = $(this).parent();
-        if (!$(this).html().toUpperCase().includes(valor.toUpperCase())) {
-            $(elem).hide();
-            selector.find('.datalist-input').val("");
-            selector.find("input[type=hidden]").val("");
-        } else
-            $(elem).show();
-    });
-}
-function getDatalistText(selector, valor) {
-    var elementRoot = $(selector).parents('.datalist');
-    var text = "";
-    $.each(elementRoot.find('ul li:not(.not-selectable)'), function () {
-        if ($(this).data("id") == valor) {
-            text = $(this).html();
-        }
-    });
-    return text;
-}
-//---
-$('.datalist-text .datalist-input').keyup(function () {
-    var elementRoot = $(this).parents('.datalist');
-    var input = $(this);
-    elementRoot.find('ul li').each(function () {
-        var elem = $(this);
-        if ($(this).parent().is('li'))
-            elem = $(this).parent();
-        if (!$(this).html().toUpperCase().includes(input.val().toUpperCase()))
-            $(elem).hide();
-        else
-            $(elem).show();
-    });
-});
-
-/*
-$('.datalist-text input').blur(function() {
-    var elementRoot = $(this).parents('.datalist');
-    console.log("Click? "+click_in_process);
-    if(!click_in_process) {
-        buscaDatalist(elementRoot, elementRoot.find('.datalist-input').val());
-        
-        elementRoot.find('.icono').removeClass('ing-cancelar iconoAzul pointer').addClass('ing-buscar');
-        elementRoot.find('ul').children('li').show();
-        elementRoot.find('ul').hide();
-    }
-    click_in_process = false;
-});
-*/
-
-$('.datalist-text > ul li:not(.not-selectable)').click(function () {
-    console.log("Li click!" + $(this).html());
-    var elementRoot = $(this).parents('.datalist');
-    elementRoot.find('.datalist-input').val($(this).html().replace(/[\t\n]+/g, ' ').trim());
-
-    //$(this).parent('ul').siblings('input[type="text"]').blur();
-
-    var cid = $(this).data('id');
-    elementRoot.find("input[type=hidden]").val(cid);
-    elementRoot.find("li").removeClass("selected");
-    $(this).addClass("selected");
-    click_in_process = true;
-    //elementRoot.find('.datalist-input').blur();
-    ocultaList({ "data": { "padre": elementRoot } });
-});
-
-$('.datalist-text .datalist-input').click(function () {
-    var elementRoot = $(this).parents('.datalist');
-    limpiaInput({ "data": { "padre": elementRoot } });
-    elementRoot.find('ul').show();
-    elementRoot.find('input').focus();
-    elementRoot.find('ul').show();
-    elementRoot.find('.icono').removeClass('ing-buscar').addClass('ing-cancelar iconoAzul pointer');
-    elementRoot.find('.icono').on('click', { "padre": elementRoot }, limpiaInput);
-});
-
-
-function limpiaInput(e) {
-    (e.data.padre).find('.datalist-input').val('');
-    (e.data.padre).find('.datalist-input').parent().children('ul').children('li').show();
-    (e.data.padre).find('li:first').focus();
-    (e.data.padre).find('.datalist-input').focus();
-
-}
-
-// function make required
-function makeRequiredDatalist(selector, required = true) {
-    var elementRoot = $(selector).parents('.datalist');
-    if (required)
-        elementRoot.addClass("required");
-    else
-        elementRoot.removeClass("required");
-}
-
-// function validate
-function validateDatalist(selector) {
-    var elementRoot = $(selector).parents('.datalist');
-    if (elementRoot.hasClass("required") && $(selector).val() == "") {
-        invalidDatalist(selector, true);
-        return false;
-    }
-    invalidDatalist(selector, false);
-    return true;
-}
-
-export { makeRequiredDatalist, validateDatalist, disableDatalist, invalidDatalist, getDatalistText, ocultaList, limpiaInput, buscaDatalist, setDatalist };

+ 0 - 36
module/messages.js

@@ -1,36 +0,0 @@
-export function triggerMessage(message, header, color = "danger", selector = "message") {
-    const container = document.getElementById(selector);
-    container.innerHTML = '';
-    /* Template message_tmp */
-    var node = /* html */`
-    <article class="alert alert-${color} alert-dismissible fade show" role="alert" id="alert-color">
-    <h4 class="alert-heading"><span class="ing-${(color !== 'success') ? 'importante' : 'aceptar'}"></span> ${header}</h4>
-    <span id="message-alert">${message}</span>
-        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-            <span aria-hidden="true">&times;</span>
-        </button>
-    </article>
-    `
-    setTimeout(function () {
-        container.innerHTML = node;
-    }, 100);
-        
-
-    /* setTimeout(function () {
-        container.innerHTML = '';
-    }, 5000); */
-}
-
-export function messageMissingInputs(required) {
-    var message = 'Faltan los siguientes campos: ';
-    required.forEach(function (item, index) {
-        let last = required.length - 1;
-        if (index == last)
-            message += item;
-        else if (index == last - 1)
-            message += item + ' y ';
-        else
-            message += item + ', ';
-    });
-    triggerMessage(message, 'Error', 'danger');
-}

+ 1 - 1
permisos.php

@@ -9,7 +9,7 @@ if (!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Permisos');

+ 1 - 1
profesores.php

@@ -8,7 +8,7 @@ if (!isset($_SESSION['user'])) {
 } else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && $user->acceso == 'n') {
+if ($user->acceso == null) {
     header('Location: main.php?error=1');
 } else {
     $user->print_to_log('Profesores');

+ 1 - 1
reporte_de_asistencias.php

@@ -9,7 +9,7 @@ if (!isset($_SESSION['user']))
 
 $user = unserialize($_SESSION['user']);
 $user->access('reporte_de_asistencias');
-if (!$user->admin && in_array($user->acceso, ['n']))
+if (in_array($user->acceso, ['n']))
     die(header('Location: main.php?error=1'));
 
 $user->print_to_log('Consultar asistencia');

+ 1 - 1
reposiciones.php

@@ -6,7 +6,7 @@ if (!isset($_SESSION['user']))
 
 $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && in_array($user->acceso, ['n']))
+if (in_array($user->acceso, ['n']))
     die(header('Location: main.php?error=1'));
 
 $user->print_to_log('Consultar horario');

+ 767 - 0
reposiciones_autorizar.php

@@ -0,0 +1,767 @@
+<?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('Reposiciones');
+//$write = $user->admin || in_array($user->acceso, ['w']);
+$write = true; //
+
+function duracionMinutos($fechahora_i, $fechahora_f){
+    return round((strtotime($fechahora_f) - strtotime($fechahora_i)) / 60,2);
+}
+
+if(!empty($user->periodo)){
+    $en_fecha = $db->querySingle("SELECT ESTA_EN_PERIODO(NOW()::DATE, :periodo_id)", [':periodo_id' => $user->periodo])['esta_en_periodo'];
+
+
+    $profesores_rs = array();
+    $tab_inicial = 1;
+    if($user->jefe_carrera){
+        $carrera_rs = $db->query('SELECT * FROM fs_usuario_carrera(:usr, NULL)', [':usr'=>$user->user["id"]]);
+        foreach($carrera_rs as $carr){
+            $rs = $db->query('SELECT * FROM fs_profesor_carrera(:carr, :periodo)', [':carr' => $carr['carrera_id'], ':periodo' => $user->periodo]);
+            $profesores_rs = array_merge($profesores_rs, $rs);
+        }
+        
+    }else if($write){// $user->supervisor || $user->admin
+        $profesores_rs = $db->query('SELECT * FROM fs_profesor_carrera(NULL, :periodo)', [':periodo' => $user->periodo]);
+        $tab_inicial = 2;
+    }else{
+        die(header('Location: index.php'));
+    }
+
+
+    $salones_rs = $db->query('SELECT * FROM salon_view WHERE tiene_salones IS true');
+
+
+    //Periodo
+    $periodo_rs = $db->querySingle('SELECT periodo_fecha_inicio, periodo_fecha_fin FROM periodo WHERE periodo_id = :periodo_id', [':periodo_id' => $user->periodo]);
+    $periodo_fin = $periodo_rs["periodo_fecha_fin"];
+    if(strtotime($periodo_rs["periodo_fecha_inicio"])>strtotime(date("Y-m-d")) )
+        $fecha_man = date("d/m/Y", strtotime($periodo_rs["periodo_fecha_inicio"]));
+    else{
+        $dias = 3;
+        if( intval(date("w")) >=3 && intval(date("w"))<=5 )//Mie a Vie
+            $dias+=3;
+        else if( intval(date("w")) ==6 )//Sab
+            $dias+=2;
+        else if( intval(date("w")) ==0 )//Do
+            $dias+=1;
+        
+        $fecha_man = date("d/m/Y", strtotime("+".$dias." day"));
+    }
+
+    // Fechas filtro
+    if(isset($_POST["fecha_inicial"]))
+        $fecha_ini = $_POST["fecha_inicial"];
+    else
+        $fecha_ini = date("d/m/Y", strtotime($periodo_rs["periodo_fecha_inicio"]));
+
+    if(isset($_POST["fecha_final"]))
+        $fecha_fin = $_POST["fecha_final"];
+    else
+        $fecha_fin = date("d/m/Y", strtotime($periodo_rs["periodo_fecha_fin"]));
+
+
+    //Reposiciones
+    $repEdo_rs = $db->query('SELECT * FROM fs_estado_reposicion' );
+
+    $repoParams = array();
+    $query = "";//carrera, prof
+    if($user->jefe_carrera){
+        $query .= ":jefe, ";
+        $repoParams[":jefe"] = $user->user["id"];
+    }else{
+        $query .= "NULL, ";
+    }
+    if((isset($_POST["prof"]) && is_numeric($_POST["prof"])) ){
+        $query .= ":prof,";
+        $repoParams[":prof"] = filter_input(INPUT_POST, "prof", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+    }else{
+        $query .= "NULL,";
+    }
+    $query .= ":f_ini, :f_fin, :edo, ";
+    $repoParams[":f_ini"] = $fecha_ini;
+    $repoParams[":f_fin"] = $fecha_fin;
+    $repoParams[":edo"] = 1;//se sobreescribe
+}
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <title>Reposiciones autorizar | <?= $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>
+    <style>
+        .wizard { height: 20px; width: 80%; background: #D0D0D0; }
+        .wizard.full { background: #D0D0D0; }
+        .wizard.active > div:first-child { background: #00A6CE;  }
+        .wizard.active > div:last-child { width: 0px; height: 0px; border-style: solid; border-width: 10px 0 10px 6px; border-color: transparent transparent transparent #00a6ce; transform: rotate(0deg); }
+    </style>
+
+
+</head>
+<!--  -->
+
+<body style="display: block;">
+    <?php
+    include('include/constantes.php');
+    include("import/html_header.php");
+    html_header("Reposiciones de clase", "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') ?>
+        
+        <?php if(!empty($user->periodo)){ ?>
+        <form id="asistencia" method="post" onsubmit="return validaFechas()">
+            <div class="form-box">
+                <input type="hidden" name="facultad" value="5">
+                
+                <div class="form-group row">
+                    <label for="filtro_inicial" class="col-4 col-form-label">Fecha inicial</label>
+                    <div class="col-8 col-sm-4">
+                        <input id="filtro_inicial" name="fecha_inicial" type="text" class="form-control date-picker-filtro" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="" value="<?php echo $fecha_ini;?>">
+                        <div class="invalid-feedback">No es una fecha válida.</div>
+                    </div>
+                </div>
+                <div class="form-group row">
+                    <label for="filtro_final" class="col-4 col-form-label">Fecha final</label>
+                    <div class="col-8 col-sm-4">
+                        <input id="filtro_final" name="fecha_final" type="text" class="form-control date-picker-filtro" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="" value="<?php echo $fecha_fin;?>">
+                        <div class="invalid-feedback">El rango de fechas no es válido.</div>
+                    </div>
+                </div>
+                <div class="form-group row">
+                    <label for="filtro_prof" class="col-4 col-form-label">Profesor</label>
+                    <div class="col-8 col-sm-4">
+                        <input list="lista_profesores" name="dlProfesor" id="dlProfesor" class="form-control" placeholder="Profesor">
+                        <div class="valid-feedback">
+                            Profesor encontrado
+                        </div>
+                        <div class="invalid-feedback">
+                            Profesor no encontrado
+                        </div>
+                        <datalist id="lista_profesores">
+                            <?php
+                            foreach ($profesores_rs as $profesor) {
+                                extract($profesor);
+                            ?>
+                                <option data-clave="<?= $profesor_clave ?>" data-profesor="<?= $profesor_nombre ?>" data-id="<?= $profesor_id; ?>" value="<?= "$profesor_clave | $profesor_nombre" ?>"></option>
+                            <?php
+                            }
+                            ?>
+                        </datalist>
+                        <ul class="list-group" id="profesores"></ul>
+                        <input type="hidden" id="editor_profesor" name="profesor" value="">
+                    </div>
+                </div>
+            </div>
+            <div class="form-group row justify-content-center">
+                <button type="submit" class="btn btn-outline-primary mr-2" id="btn-buscar"><span class="ing-buscar ing-fw"></span> Buscar</button>
+                <button type="button" class="btn btn-outline-danger" onclick="window.location.href = window.location.href"><span class="ing-borrar ing-fw"></span> Limpiar</button>
+            </div>
+        </form>
+
+        <ul class="nav nav-tabs d-print-none mb-4" id="myTab" role="tablist">
+            <li class="nav-item">
+                <a class="nav-link" id="tab1-tab" data-toggle="tab" href="#tab1" role="tab" aria-controls="calendario" aria-selected="true">Nuevas reposiciones</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link" id="tab2-tab" data-toggle="tab" href="#tab2" role="tab" aria-controls="lista" aria-selected="false">Aprobadas por jefe</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link" id="tab3-tab" data-toggle="tab" href="#tab3" role="tab" aria-controls="lista" aria-selected="false">Autorizadas</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link" id="tab4-tab" data-toggle="tab" href="#tab4" role="tab" aria-controls="lista" aria-selected="false">Rechazadas</a>
+            </li>
+        </ul>
+        <div class="tab-content" id="TabContent">
+            <?php
+            $i=1;
+            foreach($repEdo_rs as $redo){ ?>
+            <div class="tab-pane fade" id="tab<?php echo $i;?>" role="tabpanel" aria-labelledby="tab<?php echo $i;?>-tab">
+                <?php
+                $repoParams[":edo"]=$redo["estado_reposicion_id"];
+                $reposiciones_rs = $db->query('SELECT * FROM fs_reposicion(NULL, '.$query.'0, NULL) ', $repoParams );
+                ?>
+                
+                <h4 class="mb-4" <?php echo "style='color:".$redo["estado_color"]."'>".$redo["estado_nombre"]; ?> </h4>
+
+                <table class="table table-sm table-striped table-white">
+                    <thead class="thead-dark">
+                        <tr>
+                            <th>Estado</th>
+                            <th>Tipo</th>
+                            <th>Profesor/Materia</th>
+                            <th style="width:160px">Fecha falta</th>
+                            <th style="width:160px">Fecha reposición</th>
+                            <th>Salón</th>
+                            <?php if($write){ ?><th>Acciones</th><?php } ?>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <?php
+                    if(isset($reposiciones_rs)){
+                        foreach($reposiciones_rs as $reposicion){
+                        ?>
+                        <tr data-id="<?php echo $reposicion["reposicion_id"]; ?>" data-edo="<?php echo $reposicion["estado_reposicion_id"];?>" id="id<?php echo $reposicion["reposicion_id"]; ?>">
+                            <td class="align-middle">
+                                <?php if($reposicion["estado_reposicion_id"]<3){ ?>
+                                <div class="wizard <?php if(intval($reposicion["estado_reposicion_id"])==2) echo "active";?> d-flex mx-auto">
+                                    <div class="w-50 h-100"></div>
+                                    <div class=""></div>
+                                </div>
+                                <?php } else if($reposicion["estado_reposicion_id"]==3){?>
+                                <div class="text-success text-center pt-1">
+                                    <span class="ing-autorizar ing-lg"></span>
+                                </div>
+                                <?php } else {?>
+                                <div class="text-danger text-center pt-1">
+                                    <span class="ing-negar ing-lg"></span>
+                                </div>
+                                <?php } ?>
+                            </td>
+                            <td class="align-middle">
+                                <?php if($reposicion["es_reposicion"]) echo "Resposición"; else echo "Cambio"; ?>
+                            </td>
+                            <td><?php
+                                echo $reposicion["profesor_clave"]." - ".$reposicion["profesor_nombre"];
+                                ?>
+                                <br>
+                                <small>
+                                <?php echo $reposicion["materia_nombre"]; ?>
+                                (<?php
+                                    echo $reposicion["horario_grupo"];
+                                ?>)
+                                </small>
+                            </td>
+                            <td class="text-center align-middle text-nowrap"><?php
+                                $fechaI = date("d/m/Y", strtotime($reposicion["fecha_clase"]));
+                                echo $fechaI."<br>".substr($reposicion["horario_hora"],0, 5);
+                                ?>
+                            </td>
+                            <td class="text-center align-middle text-nowrap"><?php
+                                $fechaF = date("d/m/Y", strtotime($reposicion["fecha_nueva"]));
+                                echo $fechaF."<br>".substr($reposicion["hora_nueva"],0, 5)." a ".substr($reposicion["hora_nueva_fin"],0, 5);
+                                ?>
+                            </td>
+                            <td class="text-center align-middle"><?php
+                                if($reposicion["salon_id"] != ""){
+                                    echo $reposicion["salon_id"];
+                                }else
+                                    echo "Pendiente";
+                                ?>
+                            </td>
+                            
+                            <?php if($write){ ?>
+                            <td class="text-center align-middle icono-acciones text-nowrap">
+                                <?php if (duracionMinutos($reposicion["fecha_nueva"], date("Y-m-d H:i:00")) < 0){ ?>
+                                    <?php //no se cumple la fecha de la reposicion, es jefe de carrera
+                                    if(($user->jefe_carrera || $user->admin) && $reposicion["estado_reposicion_id"] == 1){?>
+                                    <a href="#" data-toggle="modal" data-target="#modal_aprobar" data-tipo="2" title="Aprobar"><?php echo $ICO["ver"];?></a>
+                                    <?php } //no se cumple la fecha de la reposicion, no es jefe de carrera
+                                    else if((!$user->jefe_carrera || $user->admin) && $reposicion["estado_reposicion_id"] >= 2){?>
+                                    <a href="#" data-toggle="modal" data-target="#modal_aprobar" data-tipo="3" title="Autorizar" ><?php echo $ICO["ver"];?></a>
+                                    <?php } else { ?>
+                                        <a href="#" data-toggle="modal" data-target="#modal_aprobar" data-tipo="1" title="Aprobar"><?php echo $ICO["ver"];?></a>
+                                    <?php } ?>
+                                    <?php
+                                }else{ //fecha ya pasó?>
+                                    <a href="#" data-toggle="modal" data-target="#modal_aprobar" data-tipo="1" title="Ver detalle"><span class="text-danger"><?php echo $ICO["ver"];?></span></a>
+                                <?php } ?>
+
+                                <?php
+                                    if($reposicion["estado_reposicion_id"]<4){
+                                        if(
+                                            (($user->jefe_carrera || $user->admin) && $reposicion["estado_reposicion_id"] <= 2)
+                                            || ((!$user->jefe_carrera || $user->admin) && $reposicion["estado_reposicion_id"] >= 2 )){
+                                ?>
+                                <a href="#" data-toggle="modal" data-target="#modal_confirm" title="Cancelar"><span class="text-danger"><?php echo $ICO["cancelar"];?></span></a>
+                                <?php }
+                                    } //estado
+                                ?>
+                            </td>
+                            <?php }//edición ?>
+                        </tr>
+                        <?php 
+                        }//foreach
+                    }//if ?>
+                    </tbody>
+                </table>
+            </div>
+            <?php
+                $i++;
+            } ?>
+        </div>
+
+
+        <!-- Modal -->
+        <div class="modal fade" id="modal_aprobar" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">
+            <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h4 class="col-12 modal-title text-center"><span id="modalLabel">Aprobar Reposición</span>
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button></h4>
+                    </div>
+                    <div class="modal-body">
+                        <form action="./action/reposicion_autoriza.php" method="post" id="formaModal">
+                            <input type="hidden" name="id" id="id">
+                            <input type="hidden" name="edo" id="edo" value="">
+                            
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Profesor</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-prof"></p>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Materia</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-mat"></p>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Tipo</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-tipo"></p>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Fecha de falta</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-falta"></p>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Fecha de reposición</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-fecha"></p>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Alumnos aproximados</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-alumnos"></p>
+                                </div>
+                            </div>
+
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Tipo de aula</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-aula"></p>
+                                </div>
+                            </div>
+                            <div class="row" id="salon-ver">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Salón</p>
+                                </div>
+                                <div class="col-6">
+                                    <p class="rep-salon"></p>
+                                </div>
+                            </div>
+                            
+                            <div class="row" id="salon-editar" style="display: none;">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Salón *</p>
+                                </div>
+                                <div class="col-6">
+                                    <input list="lista_salones" name="dlSalon" id="dlSalon" class="form-control" placeholder="Salón">
+                                    <div class="valid-feedback">
+                                        Salón encontrado
+                                    </div>
+                                    <div class="invalid-feedback">
+                                        Salón no encontrado
+                                    </div>
+                                    <datalist id="lista_salones">
+                                        <?php
+                                        foreach ($salones_rs as $salon) {
+                                            extract($salon);
+                                        ?>
+                                            <option data-id="<?= $salon_id ?>" data-nombre="<?= $salon ?>" value="<?= $salon ?>"></option>
+                                        <?php
+                                        }
+                                        ?>
+                                    </datalist>
+                                    <ul class="list-group" id="salones"></ul>
+                                    <input type="hidden" id="salon" name="salon" value="">
+                                </div>
+                            </div>
+                            
+                            
+                            <div class="row mt-4">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Comentarios</p>
+                                </div>
+                                <div class="col-6 bg-light">
+                                    <p class="rep-comentarios"></p>
+                                </div>
+                            </div>
+                            
+
+                            <div class="form-group row mt-3">
+                                <div class="col-12 text-center">
+                                    
+                                    <p class="aprobar-block">Una vez realizada la acción no se puede deshacer.</p>
+                                    <p>
+                                        <button type="button" class="btn btn-primary btn-enviar aprobar-block" id="submitBtn" data-edo="<?php echo $edo_new; ?>" ><?php echo $ICO["aceptar"];?> Aprobar</button>
+                                        <button type="button" class="btn btn-outline-secondary" data-dismiss="modal" aria-label="Close">Cerrar</button>
+                                    </p>
+                                    
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+        </div>
+        
+        <div class="modal fade" id="modal_confirm" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">
+            <div class="modal-dialog modal-dialog-centered" role="document">
+                <div class="modal-content">
+                    <div class="modal-body">
+                        <div class="row">
+                            <div class="col">
+                                <p class="font-weight-bold">¿Estás seguro de que quieres declinar la reposición?</p>
+                                <p>Esta acción no se puede deshacer.</p>
+                            </div>
+                        </div>
+                    
+                        <form action="./action/reposicion_autoriza.php" method="post">
+                            <div class="row">
+                                <div class="col-6 col-sm-4 barra-right text-right">
+                                    <p class="font-weight-bold">Motivo</p>
+                                </div>
+                                <div class="col-6 col-sm-8">
+                                    <textarea name="motivo" id="motivo" rows="3" class="form-control"></textarea>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-12 mt-4 text-center">
+                                    <input type="hidden" id="id_borrar" name="id" value="">
+                                    <input type="hidden" name="edo" value="4">
+                                    <button type="submit" class="btn btn-outline-primary btn-borrar"><?php echo $ICO["aceptar"];?> Declinar</button>
+                                    <button type="button" class="btn btn-outline-danger" data-dismiss="modal" aria-label="Close"><?php echo $ICO["cancelar"];?> Cerrar</button>
+                                </div>
+                            </div>
+                        </form>
+
+                    </div>
+                </div>
+            </div>
+        </div>
+        <?php
+        }
+        ?>
+    </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/jquery-ui.js"></script>
+<script src="js/datepicker-es.js"></script>
+<script src="js/messages.js"></script>
+    <?php
+    //--Manejo de errores y mensajes de exito
+    if(isset($_GET["error"]) && is_numeric($_GET["error"])){
+        switch ($_GET["error"]){
+            case 0: $errorDesc = "No se reciberon los datos de la reposición."; break;
+            case 1: $errorDesc = "Ocurrió un error al insertar los datos de la reposición/cambio."; break;
+            case 2: $errorDesc = "Ocurrió un error al actualizar los datos de la reposición/cambio."; break;
+            case 3: $errorDesc = "No tienes permisos para realizar esa acción."; break;
+            case 4: $errorDesc = "Ocurrió un error al cargar los datos de la reposición/cambio."; break;
+            case 6: $errorDesc = "La reposición/cambio que buscas no existe. Consulta la lista de reopsiciones disponibles en esta sección."; break;
+            case 7: $errorDesc = "La reposición/cambio se empalma con el horario del grupo y no se puede guardar."; break;
+            case 8: $errorDesc = "El salón de la reposición está siendo utilizado ese día a esa hora y no se puede guardar."; break;
+            case 9: $errorDesc = "El profesor está asigndo a otra reposición/cambio el mismo día a la misma hora y no se puede guardar."; break;
+            case 10: $errorDesc = "El profesor está asigndo a una materia el mismo día a la misma hora y no se puede guardar."; break;
+            case 11: $errorDesc = "No hay clases asignadas para esa materia y grupo en la fecha de falta."; break;
+        }
+    }
+    if(isset($_GET["ok"]) && is_numeric($_GET["ok"])){
+        switch ($_GET["ok"]){
+            case 0: $successDesc = "La reposición se guardó correctamente."; break;
+            case 1: $successDesc = "La reposición se actualizó correctamente."; break;
+        }
+    }
+    require_once 'js/messages.php';
+    ?>
+<script>
+    <?php if(isset($errorDesc)){ ?>
+    triggerMessage("<?php echo $errorDesc;?>", "Error");
+    <?php } else if(isset($successDesc)){ ?>
+    triggerMessage("<?php echo $successDesc;?>", "Éxito", "success");
+    <?php } ?>
+
+    var _periodo_fecha_inicial = "<?php echo date("d/m/Y", strtotime($periodo_rs["periodo_fecha_inicio"])); ?>";
+    var _periodo_fecha_final = "<?php echo date("d/m/Y", strtotime($periodo_rs["periodo_fecha_fin"])); ?>";
+    var datepickerOptions = { dateFormat: "dd/mm/yy", minDate:_periodo_fecha_inicial, maxDate:_periodo_fecha_final };
+
+    $(document).ready(function(){
+        $(".date-picker-filtro" ).datepicker(datepickerOptions);
+        $(".date-picker-filtro" ).datepicker( $.datepicker.regional[ "es" ] );
+        $('#tab<?php echo $tab_inicial;?>-tab').tab('show');
+    });
+
+    function valida(){
+        <?php
+        if(!$user->jefe_carrera || $user->admin){ ?>
+        $("#salon").removeClass("is-invalid");
+        if($("#salon").val() === undefined || $("#salon").val() == 0 || $("#salon").val() == ''){
+            $("#salon").addClass("is-invalid");
+            return false;
+        }
+        <?php } ?>
+        return true;
+    }
+    
+    $(document).ready(function(){
+        
+        $('#modal_confirm').on('show.bs.modal', function (event) {
+            var button = $(event.relatedTarget); // Button that triggered the modal
+            var id = button.parents("tr").data("id");
+            $("#id_borrar").val(id);
+            $("#motivo").val("")
+        });
+
+        $('#modal_aprobar').on('show.bs.modal', function (event) {
+            var button = $(event.relatedTarget); // Button that triggered the modal
+            //console.log(button.data("tipo"));
+            var id = button.parents("tr").data("id");
+            var edo = button.data('tipo');
+
+            $("#edo").val(edo);
+            $("#id").val(id);
+            
+            
+
+            $.ajax({
+                url:  './action/reposicion_select.php',
+                type: 'POST', 
+                dataType: 'json',
+                data: { id: id},
+                success: function(result) {
+                    if(result["error"]!= "" &&  result["error"] !== undefined){
+                        triggerMessage(result["error"], "Error");
+                        $('#modal_aprobar').modal("hide");
+                    }else{
+
+                        $("#modal_aprobar .rep-prof").text(result["profesor_nombre"]);
+                        $("#modal_aprobar .rep-mat").text(result["materia_desc"]+" ["+result["grupo"]+"]" );
+                        if(result["tipo"])
+                            $("#modal_aprobar .rep-tipo").text("Reposición");
+                        else
+                            $("#modal_aprobar .rep-tipo").text("Cambio");
+                        $("#modal_aprobar .rep-aula").text(result["aula_desc"])
+                        $("#modal_aprobar .rep-aula").data("aula",result["aula"]);
+                        $("#modal_aprobar .rep-falta").text(result["fecha_clase"]);
+                        $("#modal_aprobar .rep-fecha").text(result["fecha_nueva"]+" de "+result["hora_ini"]+":"+result["min_ini"]+" a "+result["hora_fin"]+":"+result["min_fin"]);
+                        if(result["salon"] =="" || result["salon"] === undefined){
+                            $('#salon').prop("selectedIndex", 0);
+                        }else{
+                            $('#salon').val(result["salon"]);
+                        }
+                        $("#modal_aprobar .rep-salon").text(result["salon_desc"]);
+                        $("#modal_aprobar .rep-comentarios").text(result["comentario"]);
+                        $('#modal_aprobar .rep-alumnos').text(result["alumnos"]);
+
+                        if(button.data("tipo") == 1){//ver
+                            $("#modalLabel").text("Detalle de reposición");
+                            $(".aprobar-block").hide();
+                            $("#salon-ver").show();
+                            $("#salon-editar").hide();
+                            
+                        }else{
+                            if(parseInt($("#modal_aprobar .rep-aula").data("aula")) == 1){//ver
+                                $("#modalLabel").text("Detalle de reposición");
+                                $(".aprobar-block").hide();
+                                $("#salon-ver").show();
+                                $("#salon-editar").hide();
+                            }else{
+                                $("#modalLabel").text("Aprobar reposición");
+                                $(".aprobar-block").show();
+                                if(button.data("tipo") == 3){//aprobar (con salón)
+                                    $("#salon-ver").hide();
+                                    $("#salon-editar").show();
+                                    
+                                }
+                            }
+                        }
+
+                        if(result["aula_supervisor"]){//Solo supervisor
+                            <?php if($user->supervisor){ ?>
+                                $("#salon-editar").attr("disabled", false);
+                            <?php }else{?>
+                                $("#salon-editar").attr("disabled", true);
+                            <?php } ?>
+                        }else{// Facultad
+                            <?php if(!$user->supervisor){ ?>
+                                $("#salon-editar").attr("disabled", false);
+                            <?php }else{?>
+                                $("#salon-editar").attr("disabled", true);
+                            <?php } ?>
+                        }                        
+
+                    }
+                },
+                error: function(jqXHR, textStatus, errorThrown ){
+                    triggerMessage(errorThrown, "Error");
+                }
+            });//ajax
+        });
+        /*
+        $(".btn-borrar").click(function(){
+            var cid =  $("#id_borrar").val();
+            $.ajax({
+                url:  './action/reposicion_autoriza.php',
+                type: 'POST', 
+                dataType: 'json',
+                data: { id: cid, edo: 4},
+                success: function(result) {
+                    if(result["error"]!= "" &&  result["error"] !== undefined){
+                        $("#errorBox").collapse('show');
+                        $("#errorBox_text").html(result["error"]);
+                    }else{
+                        $("#successBox").collapse('show');
+                        $("#successBox_text").html(result["ok"]);
+                        $("#id"+cid).remove();
+                    }
+                    $('#messageBox')[0].scrollIntoView({ block: "end" });
+                },
+                error: function(jqXHR, textStatus, errorThrown ){
+                    $("#errorBox").collapse('show');
+                    $("#errorBox_text").html(errorThrown);
+                    $('#messageBox')[0].scrollIntoView({ block: "end" });
+                }
+            });//ajax
+            $('#modal_confirm').modal("hide");
+        });*/
+
+
+        $("#submitBtn").click(function(){
+            var edo =  parseInt($("#edo").val());
+            console.log(edo)
+            if((edo == 3 && valida()) || edo == 2){
+                $("#formaModal").submit();
+            }      
+        });
+
+    });
+
+    function validaFiltro(){
+        if($('#filter_fecha_ini').val() != "" && !validaFecha($('#filter_fecha_ini').val()) ){
+            $('#filter_fecha_ini').addClass("is-invalid");
+            return false;
+        }
+        if($('#filter_fecha_fin').val() != "" && !validaFecha($('#filter_fecha_fin').val())){
+            $('#filter_fecha_fin').addClass("is-invalid");
+            return false;
+        }
+        if($('#filter_fecha_ini').val() != "" &&  $('#filter_fecha_fin').val() != "" && fechaMayor($('#filter_fecha_ini').val(), $('#filter_fecha_fin').val()) >= 0){
+            $('#filter_fecha_fin').addClass("is-invalid");
+            return false;
+        }
+        return true;
+    }
+
+    $(function() {
+        $('[data-toggle="tooltip"]').tooltip()
+    });
+
+    /**@Auxiliary functions */
+    function listProfesor({
+        id,
+        grado,
+        profesor,
+        clave
+    }) {
+        const lista = document.getElementById("dlProfesor");
+        lista.innerHTML = "";
+        lista.classList.remove("is-invalid");
+        const li = document.createElement('li');
+        li.setAttribute('data-id', id);
+        li.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'align-items-center');
+        li.innerHTML = `${clave} | ${grado ?? ''} ${profesor}`
+
+        const btn = document.createElement('button');
+        btn.setAttribute('type', 'button');
+        btn.classList.add('badge', 'badge-danger', 'badge-pill', 'border-0');
+        btn.onclick = _ => li.remove();
+
+        const i = document.createElement('i');
+        i.classList.add('ing-cancelar');
+        btn.appendChild(i);
+        li.appendChild(btn);
+
+        document.getElementById("profesores").appendChild(li);
+    }
+
+    function listSalon({
+        id,
+        nombre
+    }) {
+        const lista = document.getElementById("dlSalon");
+        lista.innerHTML = "";
+        lista.classList.remove("is-invalid");
+        const li = document.createElement('li');
+        li.setAttribute('data-id', id);
+        li.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'align-items-center');
+        li.innerHTML = `${nombre}`
+        
+        const btn = document.createElement('button');
+        btn.setAttribute('type', 'button');
+        btn.classList.add('badge', 'badge-danger', 'badge-pill', 'border-0');
+        btn.onclick = _ => li.remove();
+
+        const i = document.createElement('i');
+        i.classList.add('ing-cancelar');
+        btn.appendChild(i);
+        li.appendChild(btn);
+
+        document.getElementById("salones").appendChild(li);
+    }
+</script>
+</html>

+ 933 - 0
reposiciones_crear.php

@@ -0,0 +1,933 @@
+<?php
+
+require_once 'class/c_login.php';
+if (!isset($_SESSION['user'])){
+    die(header('Location: index.php'));
+}
+
+$user = unserialize($_SESSION['user']);
+if (!$user->profesor && !$user->admin){
+    die(header('Location: index.php'));
+}
+$user->access();
+//if (!$user->admin && in_array($user->acceso, ['n']))
+    //die(header('Location: main.php?error=1'));
+$user->print_to_log('Reposiciones');
+
+//$write = $user->admin || in_array($user->acceso, ['w']);
+$write = true; //
+
+$en_fecha = $db->querySingle("SELECT ESTA_EN_PERIODO(NOW()::DATE, :periodo_id)", [':periodo_id' => $user->periodo_id])['esta_en_periodo'];
+
+$prof_rs = $db->query('SELECT * FROM profesor A WHERE EXISTS (
+    SELECT * FROM horario_view hv join HORARIO_PROFESOR ON hv.HORARIO_ID = HORARIO_PROFESOR.horario_id WHERE HORARIO_PROFESOR.profesor_id = A.profesor_id AND hv.periodo_id = :periodo_id
+) ORDER BY profesor_nombre', [':periodo_id' => $user->periodo_id]);
+
+
+//Periodo
+$periodo_rs = $db->querySingle('SELECT periodo_fecha_inicio, periodo_fecha_fin FROM periodo WHERE periodo_id = :periodo_id', [':periodo_id' => $user->periodo_id]);
+$periodo_fin = $periodo_rs["periodo_fecha_fin"];
+if(strtotime($periodo_rs["periodo_fecha_inicio"])>strtotime(date("Y-m-d")) )
+    $fecha_man = date("d/m/Y", strtotime($periodo_rs["periodo_fecha_inicio"]));
+else{
+    $dias = 3;
+    if( intval(date("w")) >=3 && intval(date("w"))<=5 )//Mie a Vie
+        $dias+=3;
+    else if( intval(date("w")) ==6 )//Sab
+        $dias+=2;
+    else if( intval(date("w")) ==0 )//Do
+        $dias+=1;
+    
+    $fecha_man = date("d/m/Y", strtotime("+".$dias." day"));
+}
+
+// Materias
+//$id_prof = $user->user["id"];
+$id_prof = 2142;
+//$facultad_id = 28;
+$materias_rs = $db->query('SELECT * FROM fs_materiasprofesor(:id)', [':id' => $id_prof]);
+
+if(isset($_POST["fecha_inicial"]))
+    $fecha_ini = $_POST["fecha_inicial"];
+else
+    $fecha_ini = date("d/m/Y", strtotime($periodo_rs["periodo_fecha_inicio"]));
+
+if(isset($_POST["fecha_final"]))
+    $fecha_fin = $_POST["fecha_final"];
+else
+    $fecha_fin = date("d/m/Y", strtotime($periodo_rs["periodo_fecha_fin"]));
+
+$fecha_ini_db= date("Y-m-d", strtotime($fecha_ini));
+$fecha_fin_db= date("Y-m-d", strtotime($fecha_fin));
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <title>Reposiciones crear | <?= $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>
+    <style>
+        .wizard { height: 20px; width: 80%; background: #D0D0D0; }
+        .wizard.full { background: #D0D0D0; }
+        .wizard.active > div:first-child { background: #00A6CE;  }
+        .wizard.active > div:last-child { width: 0px; height: 0px; border-style: solid; border-width: 10px 0 10px 6px; border-color: transparent transparent transparent #00a6ce; transform: rotate(0deg); }
+    </style>
+    <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/jquery-ui.js"></script>
+    <script src="js/datepicker-es.js"></script>
+
+</head>
+<!--  -->
+
+<body style="display: block;">
+    <?php
+    include('include/constantes.php');
+    include("import/html_header.php");
+    html_header("Reposiciones de clase", "Sistema de gestión de checador");
+    ?>
+
+    <main class="container content marco content-margin" id="local-app">
+        <?php if($write==true )  {?>
+        <!-- Botón para abrir el modal -->
+        <div class="row mb-4">
+            <div class="col-12 text-right">
+                <button type="button" class="btn btn-outline-secondary" data-tipo="1" data-toggle="modal" data-target="#modal" <?php if (!$en_fecha || (empty($materias_rs) || count($materias_rs)==0 ) ){ echo "disabled"; } ?>><span class="ing-mas ing-fw"></span>Crear reposición</button>
+            </div>
+        </div>
+        <?php }?>
+        <section id="message"></section>
+        <?php require('import/periodo.php') ?>
+        
+        <form id="asistencia" method="post" onsubmit="return validaFechas()">
+            <div class="form-box">
+                <input type="hidden" name="facultad" value="">
+                
+                <div class="form-group row">
+                    <label for="filtro_inicial" class="col-4 col-form-label">Fecha inicial</label>
+                    <div class="col-8 col-sm-4">
+                        <input id="filtro_inicial" name="fecha_inicial" type="text" class="form-control date-picker-filtro" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="" value="<?php echo $fecha_ini;?>">
+                        <div class="invalid-feedback">No es una fecha válida.</div>
+                    </div>
+                </div>
+                <div class="form-group row">
+                    <label for="filtro_final" class="col-4 col-form-label">Fecha final</label>
+                    <div class="col-8 col-sm-4">
+                        <input id="filtro_final" name="fecha_final" type="text" class="form-control date-picker-filtro" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="" value="<?php echo $fecha_fin;?>">
+                        <div class="invalid-feedback">El rango de fechas no es válido.</div>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group row justify-content-center">
+                <button type="submit" class="btn btn-outline-primary mr-2" id="btn-buscar"><span class="ing-buscar ing-fw"></span> Buscar</button>
+                <button type="button" class="btn btn-outline-danger" onclick="window.location.href = window.location.href"><span class="ing-borrar ing-fw"></span> Limpiar</button>
+            </div>
+        </form>
+
+        <?php
+        $reposiciones_rs = $db->query('SELECT * FROM fs_reposicionesprofesor(:f_ini, :f_fin, NULL, NULL)', [':f_ini' => $fecha_ini_db, ':f_fin' => $fecha_fin_db]);
+        ?>
+
+        <div class="row">
+            <?php
+            if(isset($reposiciones_rs) && count($reposiciones_rs)>0){ ?>
+            <h3 class="mb-3">Mis reposiciones</h3>
+            <div class="col-12 table-responsive px-0">
+                <table class="table table-sm table-striped table-white">
+                    <thead class="thead-dark">
+                        <tr >
+                            <th>Estado</th>
+                            <th>Materia</th>
+                            <th>Tipo</th>
+                            <th style="width:160px">Fecha falta</th>
+                            <th style="width:160px">Fecha reposición</th>
+                            <th>Salón</th>
+                            <?php if($write){ ?><th>Acciones</th><?php } ?>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <?php
+                        foreach($reposiciones_rs as $reposicion){
+                        ?>
+                        <tr data-id="<?php echo $reposicion["reposicion_id"]; ?>" id="id<?php echo $reposicion["reposicion_id"]; ?>">
+                            <td class="align-middle text-center" style="color:<?php echo $reposicion["estado_color"];?>" title="<?php echo $reposicion["estado_nombre"];?>">
+                                <?php if($reposicion["estado_reposicion_id"]<3){ ?>
+                                <div class="wizard <?php if(intval($reposicion["estado_reposicion_id"])==2) echo "active";?> d-flex mx-auto">
+                                    <div class="w-50 h-100"></div>
+                                    <div class=""></div>
+                                </div>
+                                <?php } else if($reposicion["estado_reposicion_id"]==3){?>
+                                <div class="text-success text-center pt-1">
+                                    <span class="ing-autorizar ing-lg"></span>
+                                </div>
+                                <?php } else {?>
+                                <div class="text-danger text-center pt-1">
+                                    <span class="ing-negar ing-lg"></span>
+                                </div>
+                                <?php } ?>
+                            </td>
+                            <td class="align-middle"><?php echo $reposicion["materia_nombre"]; ?></td>
+                            <td class="align-middle">
+                                <?php if($reposicion["es_reposicion"]) echo "Reposición"; else echo "Cambio"; ?>
+                            </td>
+                            <td class="align-middle text-center"><?php
+                                echo date("d/m/Y", strtotime($reposicion["fecha_clase"]));
+                                ?>
+                            </td>
+                            <td class="align-middle text-center"><?php
+                                
+                                echo date("d/m/Y", strtotime($reposicion["fecha_nueva"])) ."<br>".substr($reposicion["hora_nueva"],0,-3)." a ".substr($reposicion["hora_nueva_fin"],0,-3)." hrs.";
+                                ?>
+                            </td>
+                            <td class="align-middle text-center"><?php
+                                if($reposicion["salon_id"] != ""){
+                                    echo $reposicion["salon_id"];
+                                }else
+                                    echo "Pendiente";
+                                ?>
+                            </td>
+                            
+                            <?php if($write){ ?>
+                            <td class="align-middle text-center icono-acciones">
+                                <?php
+                                
+                                //no se ha aprobado
+                                if($reposicion["estado_reposicion_id"] == 1){?>
+                                <a href="#" data-tipo="2" title="Editar" data-toggle="modal" data-target="#modal"><?php echo $ICO["editar"];?></a>
+                                <a href="#" data-toggle="modal" data-target="#modal_confirm" title="Borrar"><?php echo $ICO["cancelar"];?></a>
+                                <?php } ?>
+                            </td>
+                            <?php } ?>
+                        </tr>
+                        <?php }
+                    ?>
+                    </tbody>
+                </table>
+            </div>
+            <?php } else { ?>
+            <div class="col-12 text-center">
+                <h4 class="mt-4 text-danger">No tienes reposiciones disponibles que cumplan con los filtros</h4>
+            </div>
+            <?php } ?>
+        </div>
+
+        <div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">
+            <div class="modal-dialog modal-dialog-centered modal-xl" role="document">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h4 class="col-12 modal-title text-center"><span id="modalLabel">Crear Reposición</span>
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button></h4>
+                    </div>
+                    <div class="modal-body">
+                        <form action="./action/reposicion_insert.php" method="post" id="formaModal" onsubmit="return submitForm()">
+                            <input type="hidden" name="id" id="id">
+                            <input type="hidden" name="estado" value="1">
+                            <div class="form-box">
+
+                                <div class="form-group row <?php if(true){ echo "d-none"; }?>" id="profBlock">
+                                    <label for="prof" class="col-4 col-form-label">Profesor *</label> 
+                                    <div class="col-8">
+                                        <div class="datalist datalist-select mb-1 w-100" id="dlProfesor">
+                                            <div class="datalist-input">Profesores del área</div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <?php foreach($prof_rs as $prof){?>
+                                                    <li data-id="<?php echo $prof["id"];?>" <?php if($prof["id"]==$_SESSION["usuario_id"]){ echo "class='selected'";} ?> ><?php echo $prof["profesor"];?></li>
+                                                <?php } ?>
+                                            </ul>
+                                            <input type="hidden" id="prof" name="prof" value="<?php echo $id_prof;?>">
+                                        </div>
+                                    </div>
+                                </div>
+                                
+                                
+                                <div class="form-group row" id="materiaBlock">
+                                    <label for="horario" class="col-4 col-form-label">Materia *</label> 
+                                    <div class="col-8">
+                                        <div class="datalist datalist-select mb-1 w-100" id="dlMateria">
+                                            <div class="datalist-input">Selecciona una materia</div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                            <?php foreach($materias_rs as $mat){ ?>
+                                                <li data-id="<?php echo $mat["horario_id"];?>" data-dia="<?php echo $mat["horario_dia"];?>" data-hr="<?php echo substr($mat["horario_hora"], 0, 2);?>" data-min="<?php echo substr($mat["horario_hora"], 3, 2);?>">
+                                                    <?php echo $mat["materia_nombre"].' - '.$mat["horario_dia_nombre"]." ".substr($mat["horario_hora"], 0, -3);?>
+                                                </li>
+                                            <?php } ?>
+                                            </ul>
+                                            <input type="hidden" id="horario" name="horario" value="">
+                                        </div>
+                                    </div>
+                                </div>
+                                
+                                <div class="form-group row">
+                                    <label for="tipo" class="col-4 col-form-label">Tipo *</label> 
+                                    <div class="col-8">
+                                        <div class="datalist datalist-select mb-1 w-100" id="dlTipo">
+                                            <div class="datalist-input">Reposición</div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <li data-id="1">Reposición</li>
+                                                <li data-id="2">Cambio de salón</li>
+                                            </ul>
+                                            <input type="hidden" id="tipo" name="tipo" value="1">
+                                        </div>
+                                    </div>
+                                </div>
+                                
+                                <div class="form-group row cambio_block materia-block" style="display: none;">
+                                    <label for="fecha_cambio" class="col-4 col-form-label">Fecha de cambio *</label> 
+                                    <div class="col-8">
+                                        <input id="fecha_cambio" name="fecha_cambio" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="readonly" value="">
+                                    </div>
+                                </div>
+
+                                <div class="form-group row repo_block materia-block">
+                                    <label for="fecha_falta" class="col-4 col-form-label">Fecha de falta *</label> 
+                                    <div class="col-8">
+                                        <input id="fecha_falta" name="fecha_falta" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="readonly" value="">
+                                    </div>
+                                </div>
+                                    <div class="form-group row repo_block materia-block">
+                                    <label for="fecha_inicial" class="col-4 col-form-label">Fecha de reposicion *</label> 
+                                    <div class="col-8">
+                                        <input id="fecha_inicial" name="fecha_inicial" type="text" class="form-control date-picker-future" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="readonly" value="">
+                                        <small class="form-text text-muted">Las reposiciones se deben solicitar con al menos 72hrs de anticipación.<br>
+                                                    Si repones en sábado, consulta los horarios con tu jefe de carrera.
+                                        </small>
+                                    </div>
+                                </div>
+                                <div class="form-group row materia-block">
+                                    <label for="hora_ini" class="col-4 col-form-label" id="hora_nombre">Hora reposición *</label>
+                                    <?php
+                                    //define("HORA_FINAL", 22);
+                                    //define("FRACCION_HORA", 15);
+                                    $default_h = 7; $default_m = 15;
+                                    ?>
+                                    <div class="col-4">
+                                        <select name="hora_ini" id="hora_ini" class="form-control" required="required">
+                                            <?php for($h = $default_h; $h < HORA_FINAL; $h++){?>
+                                            <option value="<?php echo sprintf( '%02d', $h );?>" <?php if($default_h == $h){ echo 'selected="selected"';}?>><?php echo sprintf( '%02d', $h );?></option>
+                                            <?php } ?>
+                                        </select>
+                                    </div>
+                                    <div class="col-4">
+                                        <select name="min_ini" id="min_ini" class="form-control" required="required">
+                                            <?php for($m = 0; $m < 60; $m+=(60/FRACCION_HORA)){?>
+                                            <option value="<?php echo sprintf( '%02d', $m );?>" <?php if($default_m == $m){ echo 'selected="selected"';}?>><?php echo sprintf( '%02d', $m );?></option>
+                                            <?php } ?>
+                                        </select>
+                                    </div>
+                                </div>
+
+                                
+                                <div class="form-group row materia-block">
+                                    <label for="salon" class="col-4 col-form-label">Alumnos aproximados *</label> 
+                                    <div class="col-8 col-md-4">
+                                        <input type="number" name="alumnos" id="alumnos" class="form-control" value="1" min="1" max="50">
+                                    </div>
+                                </div>
+                                
+                                <div class="form-group row materia-block">
+                                    <label for="aula" class="col-4 col-form-label">Tipo aula *</label> 
+                                    <div class="col-8">
+                                        <div class="datalist datalist-select mb-1 w-100" id="dlAula">
+                                            <div class="datalist-input">Salón</div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <li data-id="1">Salón</li>
+                                                <li data-id="2">Sala de cómputo</li>
+                                                <li data-id="3">Salón/Taller de la facultad</li>
+                                            </ul>
+                                            <input type="hidden" id="aula" name="aula" value="1">
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+
+                            <div class="form-box form-box-info materia-block">
+                                <div class="form-group row">
+                                    <label for="comentario" class="col-4 col-form-label">Comentarios</label> 
+                                    <div class="col-8">
+                                        <p><i>Requerimientos específicos del salón, software especializado, etc.</i></p>
+                                        <textarea rows="3" class="form-control" id="comentario" name="comentario"></textarea>
+                                    </div>
+                                </div>
+                            </div>
+                            
+                            <div class="form-group row mt-3">
+                                <div class="offset-4 col-8">
+                                    <button type="submit" class="btn btn-outline-primary  materia-block" id="submitBtn" data-tipo="1"><?php echo $ICO["aceptar"];?> Guardar</button>
+                                    <button type="reset" class="btn btn-outline-danger" data-dismiss="modal"><?php echo $ICO["cancelar"];?> Cancelar</button>
+                                </div>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+
+        <!-- Modal del formulario -->
+        <div class="modal fade" id="crearReposiciónOld" 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">&times;</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
+                                                
+                                                foreach ($prof_rs as $prof) {
+                                                ?>
+                                                    <option data-grado="<?= $prof["profesor_grado"] ?>" data-clave="<?= $prof["profesor_clave"] ?>" data-profesor="<?= $prof["profesor_nombre"] ?>" data-id="<?= $prof["profesor_id"]; ?>" value="<?= "{$prof["profesor_clave"]} | {$prof["profesor_grado"]} {$prof["profesor_nombre"]}" ?>"></option>
+                                                <?php
+                                                }
+                                                ?>
+                                            </datalist>
+                                            <ul class="list-group" id="profesores"></ul>
+                                            <input type="hidden" id="periodo_id" name="periodo_id" value="<?= $user->periodo_id ?>">
+                                            <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">&times;</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>
+
+        <div class="modal fade" id="modal_confirm" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">
+            <div class="modal-dialog modal-dialog-centered" role="document">
+                <div class="modal-content">
+                    <div class="modal-body">
+                        <div class="row">
+                            <div class="col">
+                                <p class="font-weight-bold">¿Estás seguro de que quieres borrar la reposición?</p>
+                                <p>Esta acción no se puede deshacer.</p>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="modal-footer">
+                        <input type="hidden" id="id_borrar" value="">
+                        <button type="button" class="btn btn-outline-primary btn-borrar"><?php echo $ICO["aceptar"];?> Borrar</button>
+                        <button type="button" class="btn btn-outline-danger" data-dismiss="modal" aria-label="Close"><?php echo $ICO["cancelar"];?> Cancelar</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </main>
+    
+    <?php
+    //--Manejo de errores y mensajes de exito
+    if(isset($_GET["error"]) && is_numeric($_GET["error"])){
+        switch ($_GET["error"]){
+            case 0: $errorDesc = "No se reciberon los datos de la reposición."; break;
+            case 1: $errorDesc = "Ocurrió un error al insertar los datos de la reposición/cambio."; break;
+            case 2: $errorDesc = "Ocurrió un error al actualizar los datos de la reposición/cambio."; break;
+            case 3: $errorDesc = "No tienes permisos para realizar esa acción."; break;
+            case 4: $errorDesc = "Ocurrió un error al cargar los datos de la reposición/cambio."; break;
+            case 6: $errorDesc = "La reposición/cambio que buscas no existe. Consulta la lista de reopsiciones disponibles en esta sección."; break;
+            case 7: $errorDesc = "La reposición/cambio se empalma con el horario del grupo y no se puede guardar."; break;
+            case 8: $errorDesc = "El salón de la reposición está siendo utilizado ese día a esa hora y no se puede guardar."; break;
+            case 9: $errorDesc = "El profesor está asigndo a otra reposición/cambio el mismo día a la misma hora y no se puede guardar."; break;
+            case 10: $errorDesc = "El profesor está asigndo a una materia el mismo día a la misma hora y no se puede guardar."; break;
+            case 11: $errorDesc = "No hay clases asignadas para esa materia y grupo en la fecha de falta."; break;
+        }
+    }
+    if(isset($_GET["ok"]) && is_numeric($_GET["ok"])){
+        switch ($_GET["ok"]){
+            case 0: $successDesc = "La reposición se guardó correctamente."; break;
+            case 1: $successDesc = "La reposición se actualizó correctamente."; break;
+        }
+    }
+    require_once 'js/messages.php';
+    ?>
+    <script>
+    <?php if(isset($errorDesc)){ ?>
+    triggerMessage("<?php echo $errorDesc;?>", "Error");
+    <?php } else if(isset($successDesc)){ ?>
+    triggerMessage("<?php echo $successDesc;?>", "Éxito", "success");
+    <?php } ?>
+
+    var vacaciones=[
+        <?php /*foreach($vacacionesArr as $v){ echo '"'.$v["fecha"].'",';}*/ ?>
+    ];
+    var _dias_asistencia = [];//ya registró asistencia, cambia con ajax
+    var _dia_valido = 0;
+    var _fecha_manhana = "<?php echo $fecha_man; ?>";
+    var _periodo_fecha_inicial = "<?php echo date("d/m/Y", strtotime($periodo_rs["periodo_fecha_inicio"])); ?>";
+    var _periodo_fecha_final = "<?php echo date("d/m/Y", strtotime($periodo_rs["periodo_fecha_fin"])); ?>";
+    var datepickerOptions_filtro = { dateFormat: "dd/mm/yy", minDate:_periodo_fecha_inicial, maxDate:_periodo_fecha_final};
+
+    var datepickerOptions = { dateFormat: "dd/mm/yy", minDate:_periodo_fecha_inicial, maxDate:_periodo_fecha_final,
+        beforeShowDay: function(date) {
+            var day = date.getDay();
+            var dateString = $.datepicker.formatDate("yy-mm-dd", date);
+            if (day === _dia_valido) {// 0 representa el domingo
+                if (vacaciones.indexOf(dateString) !== -1 || _dias_asistencia.indexOf(dateString) !== -1) 
+                    return [false];
+                else
+                    return [true];
+            }else{
+                return [false];
+            }
+            
+        }
+    };
+    var datepickerOptions_future = { dateFormat: "dd/mm/yy", minDate:_fecha_manhana, maxDate:_periodo_fecha_final,
+        beforeShowDay: function(date) {
+            var day = date.getDay();
+            var dateString = $.datepicker.formatDate("yy-mm-dd", date);
+            if (day === 0) {// 0 representa el domingo
+                return [false];
+            } else {
+                if (vacaciones.indexOf(dateString) !== -1) {
+                    return [false];
+                } else {
+                    return [true];
+                }
+            }
+            
+        }
+    };
+
+    function diaAAno(fecha_str){//de dd/mm/yyyy a yyyy-mm-dd 
+        if(fecha_str.charAt(2) == "/" && fecha_str.charAt(5) == "/"){//dd/mm/yyyy
+            var fecha_arr = fecha_str.split("/");
+            return fecha_arr[2]+"-"+fecha_arr[1]+"-"+fecha_arr[0];
+        }
+        return fecha_str;
+    }
+    function fechaMayor(fechaI, fechaF) {//cual es mayor >0 I mayor   <0 F mayor
+        return (Date.parse(diaAAno(fechaI)) - Date.parse(diaAAno(fechaF)));
+    }
+
+    function validaFechas(){
+        if(fechaMayor($('#filtro_inicial').val().trim(), $('#filtro_final').val().trim()) > 0){
+            $('#filtro_final').addClass("is-invalid");
+            return false;
+        }
+        return true;
+    }
+
+    function submitForm(){
+        var myBtn = $('#submitBtn');
+        var error = false;
+        
+        $("#gpo").removeClass("is-invalid");
+        invalidDatalist("#materia", false);
+        $("#fecha_inicial").removeClass("is-invalid");
+        $("#fecha_falta").removeClass("is-invalid");
+        $("#fecha_cambio").removeClass("is-invalid");
+
+        
+        if($("#tipo").val() == 1){//reposición
+            if($("#fecha_falta").val() == ""){
+                $("#fecha_falta").addClass("is-invalid");
+                error = true;
+            }
+            if($("#fecha_inicial").val() == ""){//fecha reposición
+                $("#fecha_inicial").addClass("is-invalid");
+                error = true;
+            }    
+        }else{
+            if($("#fecha_cambio").val() == ""){
+                $("#fecha_cambio").addClass("is-invalid");
+                error = true;
+            }
+        }
+        if($("#horario").val().trim() == "" || $("#horario").val() === null){
+            invalidDatalist("#horario", true);
+            error = true;
+        }
+        
+        if(myBtn.data("tipo") == 2 ){
+            $('#formaModal').prop("action", "./action/reposicion_update.php");
+        }else{
+            $('#formaModal').prop("action", "./action/reposicion_insert.php");
+        }
+        return !error;
+    }
+
+    function cambiaTipo(tipo){
+        if (tipo == 1){//reposición
+            $(".repo_block").show();
+            $(".cambio_block").hide();
+            $(".repo_block").find("input[type=text]").attr("required", true);
+            $(".cambio_block").find("input[type=text]").removeAttr("required");
+            $("#hora_nombre").text("Hora reposición *");
+        }else{//Cambio de salón
+            $(".repo_block").hide();
+            $(".cambio_block").show();
+            $(".repo_block").find("input[type=text]").removeAttr("required");
+            $(".cambio_block").find("input[type=text]").attr("required", true);
+            $("#hora_nombre").text("Hora cambio *");
+            var hora = $("#dlMateria ul li.selected").data("hr");
+            var min = $("#dlMateria ul li.selected").data("min");
+            $("#hora_ini").val(hora)
+            $("#min_ini").val(min)
+
+        }
+    }
+    
+    $(document).ready(function(){
+        //fecha de clase
+        $(".date-picker" ).datepicker(datepickerOptions);
+        $(".date-picker" ).datepicker( $.datepicker.regional[ "es" ] );
+
+        //fecha de clase
+        $(".date-picker-filtro" ).datepicker(datepickerOptions_filtro);
+        $(".date-picker-filtro" ).datepicker( $.datepicker.regional[ "es" ] );
+
+        //fecha nueva
+        $(".date-picker-future" ).datepicker(datepickerOptions_future);
+        $(".date-picker-future" ).datepicker( $.datepicker.regional[ "es" ] );
+        
+        <?php if(/*$_SESSION["jefe_carrera"] ||*/ $user->admin){ ?>
+        function creaOpcion(id_lista, nombre, gpo, dia, hora){
+            return '<li data-id="'+id_lista+'" >'+nombre+' ('+gpo+') - '+dia+' '+hora.substr(0, 5)+'</li>';
+        }
+
+        $('#filtro_final').focus(function(){
+            $("#filtro_final").removeClass("is-invalid");
+        });
+
+        $("#dlProfesor ul li").click(function(){//cambia datalist
+            var pid = $(this).data('id');
+            //busca materias del profesor
+            $.ajax({
+                url:  './action/materiasdiaprofesor_select.php',
+                type: 'POST', 
+                dataType: 'json',
+                data: { id: pid, },
+                success: function(result) {
+                    if(result["error"]!= "" &&  result["error"] !== undefined){
+                        triggerMessage(result["error"], "Error");
+                        $("#modal").modal('hide');
+                    }else{
+                        $("#dlMateria ul").html("");
+                        for(i=0; i<result["materias"].length; i++){
+                            var html = creaOpcion(result["materias"][i]["Horario_id"],
+                                result["materias"][i]["Materia_desc"],
+                                result["materias"][i]["Grupo_desc"]+" "+result["materias"][i]["Carrera_prefijo"],
+                                result["materias"][i]["Dia_desc"],
+                                result["materias"][i]["Hora_inicio"]
+                                );
+                            $("#dlMateria ul").append(html);
+                        }
+                        setDatalistFirst("#horario");
+                    }
+                },
+                error: function(jqXHR, textStatus, errorThrown ){
+                    triggerMessage(errorThrown, "Error");
+                }
+            });//ajax
+            
+        });
+        <?php } ?>
+        
+        //Actualiza días elegibles de calendario
+        $("#dlMateria ul li").click(function(){//cambia datalist
+            _dia_valido = $(this).data('dia');
+
+            $.ajax({
+                url:  './action/asistenciasprofesor_select.php',
+                type: 'POST', 
+                dataType: 'json',
+                data: { "id": $("#prof").val(), "hor": $(this).data("id") },
+                success: function(result) {
+                    if(result["error"]!= "" &&  result["error"] !== undefined){
+                        triggerMessage(result["error"], "Error");
+                        $('#modal').modal("hide");
+                    }else{
+                        _dias_asistencia = result["asistenciaArr"];
+                    }
+
+                },
+                error: function(jqXHR, textStatus, errorThrown ){
+                    triggerMessage(errorThrown, "Error");
+                }
+            });//ajax
+
+            $(".date-picker" ).datepicker(datepickerOptions);
+            var hora = $(this).data("hr");
+            var min = $(this).data("min");
+            $("#hora_ini").val(hora)
+            $("#min_ini").val(min)
+        });
+        
+        $("#dlTipo ul li").click(function(){//cambia datalist
+            cambiaTipo($(this).data('id'));
+            $(".date-picker" ).datepicker(datepickerOptions);
+        });
+
+        $('#modal_confirm').on('show.bs.modal', function (event) {
+            var button = $(event.relatedTarget); // Button that triggered the modal
+            var id = button.parents("tr").data("id");
+            $("#id_borrar").val(id);
+        });
+        
+        $(".btn-borrar").click(function(){
+            var r_id =  $("#id_borrar").val();
+            $.ajax({
+                url:  './action/reposicion_delete.php',
+                type: 'POST', 
+                dataType: 'json',
+                data: { id: r_id},
+                success: function(result) {
+                    if(result["error"]!= "" &&  result["error"] !== undefined){
+                        triggerMessage(result["error"], "Error");
+                    }else{
+                        triggerMessage(result["ok"], "Éxito");
+                        $("#id"+r_id).remove();
+                    }
+                },
+                error: function(jqXHR, textStatus, errorThrown ){
+                    triggerMessage(errorThrown, "Error");
+                }
+            });//ajax
+            $('#modal_confirm').modal("hide");
+        });
+
+        
+        $('#modal').on('show.bs.modal', function (event) {
+            var button = $(event.relatedTarget); // Button that triggered the modal
+            var tipo = button.data('tipo'); // 1 alta, 2 edicion
+            var modal = $(this);
+            
+            $("#modal .is-invalid").removeClass("is-invalid");
+            //$(this).find(".form-control:first-child").focus();
+            
+            $("#errorBox").collapse('hide');
+            $("#errorBox_text").html("");
+            if(tipo == 1){//alta
+                $("#submitBtn").data('tipo', 1);
+                $("#modalLabel").html("Crear Reposición");
+                modal.find("input[type=text]").val("");
+                modal.find("#alumnos").val("15");
+                $("#plan").attr("readonly", false);
+                $("#sem").attr("readonly", false);
+                $("#gpo").attr("readonly", false);
+                
+                //$("#prof").attr("readonly", false);
+                disableDatalist("#horario", false);
+                disableDatalist("#tipo", false);
+                if($("#prof").length>0)
+                    disableDatalist("#prof", false);
+                setDatalistFirst("#tipo");
+                setDatalistFirst("#aula");
+                setDatalistFirst("#horario");
+                $("#dlMateria ul li:first").click();
+                
+            }else{//editar
+                $("#submitBtn").data('tipo', 2);
+                $("#modalLabel").html("Editar Reposición");
+                $("#plan").attr("readonly", true);
+                $("#sem").attr("readonly", true);
+                $("#gpo").attr("readonly", true);
+                //$("#materia").attr("readonly", true);
+                disableDatalist("#horario");
+                disableDatalist("#tipo");
+                /*if($("#prof").length>0)
+                    disableDatalist("#prof");
+                $("#prof").attr("readonly", true);*/
+                var r_id = $(button).parents("tr").data("id");
+                $("#id").val(r_id);
+                $.ajax({
+                    url:  './action/reposicion_select.php',
+                    type: 'POST', 
+                    dataType: 'json',
+                    data: { id: r_id },
+                    success: function(result) {
+                        if(result["error"]!= "" &&  result["error"] !== undefined){
+                            triggerMessage(result["error"], "Error");
+                            $("#modal").modal('hide');
+                        }else{
+                            //setDatalist("#prof", result["profesor"]);
+                            $('#salon').val(result["salon"]);
+                            $("#fecha_falta").val(result["fecha_clase"]);
+                            $('#hora_ini').val(result["hora_ini"]);
+                            $('#min_ini').val(result["min_ini"]);
+                            $('#comentario').val(result["comentario"]);
+                            $('#alumnos').val(result["alumnos"]);
+                            setDatalist("#horario", result["horario"]);
+                            setDatalist("#profesor", result["profesor"]);
+                            if(result["tipo"]){
+                                setDatalist("#tipo", 1);
+                                cambiaTipo(1);
+                                $("#fecha_inicial").val(result["fecha_nueva"]);
+                            }else{
+                                setDatalist("#tipo", 2);
+                                cambiaTipo(2);
+                                $("#fecha_cambio").val(result["fecha_nueva"]);
+                            }
+                            _dia_valido = parseInt(result["dia"]);
+                            $(".date-picker" ).datepicker(datepickerOptions);
+                            $("#dlTipo ul li:selected").click();
+                            setDatalist("#aula", result["aula"]);
+                            modal.modal('show');
+                        }
+                    },
+                    error: function(jqXHR, textStatus, errorThrown ){
+                        triggerMessage(errorThrown, "Error");
+                        $("#modal").modal('hide');
+                        //$('#messageBox')[0].scrollIntoView({ block: "end" });
+                    }
+                });//ajax
+            }
+            
+        });//show
+        
+    });
+
+    $(function() {
+        $('[data-toggle="tooltip"]').tooltip()
+    })
+</script>
+<script src="js/messages.js"></script>
+<script type="module" src="js/reposiciones.js"></script>
+</body>
+
+</html>

+ 3 - 7
roles.php

@@ -9,7 +9,7 @@ if (!isset($_SESSION['user'])){
 else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if(!$user->admin && $user->acceso == 'n'){
+if($user->acceso == null){
     header('Location: main.php?error=1');
 }else{
     $user->print_to_log('Roles');
@@ -40,11 +40,7 @@ echo $fac;
         "Gestión de Checador "
     );
     $user->access();
-    $fs_roles = query(
-        "SELECT * FROM rol ORDER BY rol_titulo",
-        null,
-        false
-    );
+    $fs_roles = $db->orderBy('rol_titulo')->get('rol');
     ?>
     <main class="content marco">
         <?php if($user->acceso == 'w'){?>
@@ -60,7 +56,7 @@ echo $fac;
             <div class="col-12 table-responsive">
                 <table class="table table-sm table-striped table-white">
                     <thead class="thead-dark">
-                        <th>Titulo</th>
+                        <th>Título</th>
                         <?php if($user->acceso == 'w'){?>
                         <th>Acciones</th>
                         <?php }?>

+ 0 - 0
service/auto.php → service/_4564_periodo/auto.php


+ 0 - 0
service/backend/carreras.php → service/_4564_periodo/backend/carreras.php


+ 0 - 0
service/backend/periodos.php → service/_4564_periodo/backend/periodos.php


+ 0 - 0
service/client.html → service/_4564_periodo/client.html


+ 0 - 0
service/horarios.php → service/_4564_periodo/horarios.php


+ 0 - 0
service/periodos.v1.php → service/_4564_periodo/periodos.v1.php


+ 0 - 0
service/periodos.v2.php → service/_4564_periodo/periodos.v2.php


+ 0 - 0
service/auditoria/index.php


+ 1 - 1
supervisor.php

@@ -26,7 +26,7 @@
         "Sistema de gestión de checador",
     );
     ?>
-    <main class="container-fluid px-4" id="app" v-cloak @vue:mounted="mounted">
+    <main class="container-fluid px-4" id="app" v-cloak @vue:mounted="mounted" style="min-height: 60vh;">
         <!-- error messages -->
         <div class="container mb-4 mt-2">
             <div class="row">

+ 140 - 0
ts/horario.ts

@@ -0,0 +1,140 @@
+import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'
+type Profesor = {
+    profesor_clave: string;
+    profesor_correo: string;
+    profesor_grado: null | string;
+    profesor_id: number;
+    profesor_nombre: string;
+}
+
+type Horario = {
+    carrera: string;
+    carrera_id: number;
+    dia: string;
+    duracion: string;
+    duracion_id: number;
+    facultad: string;
+    facultad_id: number;
+    fecha_carga: Date;
+    horario_dia: number;
+    horario_fecha_fin: null;
+    horario_fecha_inicio: Date;
+    horario_fin: string;
+    horario_grupo: string;
+    horario_hora: string;
+    horario_id: number;
+    limite: null;
+    materia: string;
+    materia_id: number;
+    nivel: string;
+    nivel_id: number;
+    periodo: string;
+    periodo_fecha_fin: Date;
+    periodo_fecha_inicio: Date;
+    periodo_id: number;
+    profesor_id: number;
+    salon: string;
+    salon_id: number;
+    bloques: number;
+}
+
+
+const profesores = reactive({
+    data: [] as Profesor[],
+    search: null as null | string,
+    fetch: async function () {
+        const response = await fetch('action/action_profesor.php')
+        this.data = await response.json() as Profesor[]
+    },
+    get clave() {
+        const match = this.search.match(/^\((.+)\)/)
+        return match ? match[1] : ''
+    },
+    get current() {
+        return this.data.find((profesor: Profesor) => profesor.profesor_clave === profesores.clave)
+    },
+})
+
+type Structure = {
+    sábado: boolean;
+    hora_mínima: number;
+    hora_máxima: number;
+    horas_totales: number;
+}
+
+const horarios = reactive({
+    data: [] as Horario[],
+    fetch: async function () {
+        if (profesores.current) {
+            const response = await fetch(`action/action_horario.php?profesor_id=${profesores.current.profesor_id}`)
+            this.data = await response.json()
+        }
+    },
+    get structure() {
+        if (this.data.length === 0) return null;
+
+        const structure: Structure = {
+            sábado: this.data.some((horario: Horario) => horario.horario_dia === 6),
+            hora_mínima: Math.min(...this.data.map((horario: Horario) => parseInt(horario.horario_hora.split(':')[0]))),
+            hora_máxima: Math.max(...this.data.map((horario: Horario) => {
+                const [hour, minute] = horario.horario_fin.split(':').map(Number);
+                return hour + Math.ceil(minute / 60);
+            })),
+            horas_totales: 0
+        };
+
+        structure.horas_totales = structure.hora_máxima - structure.hora_mínima;
+
+        return structure;
+    },
+    get blocks() {
+        if (this.data.length === 0) return null;
+        return [...Array(this.structure.horas_totales).keys()].flatMap(hora => {
+            const baseHour = hora + this.structure.hora_mínima;
+            return [0, 15, 30, 45].map(block => ({ hour: baseHour, block }));
+        });
+    },
+    getHorarioData(hour: number, block: number, día: number) {
+        const foundHorario = this.data.find((horario: Horario) =>
+            parseInt(horario.horario_hora.split(':')[0]) === hour &&
+            parseInt(horario.horario_hora.split(':')[1]) === block &&
+            horario.horario_dia === día
+        );
+        return foundHorario;
+    },
+    isOccupied(hora: number, bloque: number, day: number) {
+        if (this.getHorarioData(hora, bloque, day)) {
+            return false;
+        }
+        const currentTimeInMinutes = hora * 60 + bloque;
+
+        for (const item of this.data) {
+            if (item.horario_dia !== day) {
+                continue; // Skip items that are not on the specified day
+            }
+
+            // Split the hour and minute from horario_hora
+            const [startHour, startMinute] = item.horario_hora.split(":").map(Number);
+            const startTimeInMinutes = startHour * 60 + startMinute;
+
+            // Calculate end time using duracion
+            const [durationHours, durationMinutes] = item.duracion.split(":").map(Number);
+            const endTimeInMinutes = startTimeInMinutes + (durationHours * 60) + durationMinutes;
+
+            if (currentTimeInMinutes >= startTimeInMinutes && currentTimeInMinutes < endTimeInMinutes) {
+                return true;  // The block is occupied
+            }
+        }
+
+        return false;  // The block is not occupied by any class
+    }
+
+})
+
+const app = createApp({
+    profesores,
+    horarios,
+    mounted: async function () {
+        await profesores.fetch()
+    }
+}).mount('#app')

+ 0 - 532
ts/horario_profesor.ts

@@ -1,532 +0,0 @@
-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 &#8230;</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;
-

+ 21 - 29
usuarios.php

@@ -1,22 +1,19 @@
 <?php
 require_once 'class/c_login.php';
 require_once 'include/bd_pdo.php';
-
 if (!isset($_SESSION['user'])) {
     header('Location: index.php');
     exit;
 } else
     $user = unserialize($_SESSION['user']);
 $user->access();
-if (!$user->admin && $user->acceso == 'n') {
+
+if ($user->acceso == null) {
     header('Location: main.php?error=1');
 } else {
     $user->print_to_log('Usuarios');
 }
 $fac = $user->facultad['facultad_id'] ?? null;
-if ($user->admin) {
-    $fac = null;
-}
 #echo $fac;
 ?>
 <!DOCTYPE html>
@@ -86,7 +83,7 @@ if ($user->admin) {
     $fs_roles = $db
         ->orderBy('rol_titulo', 'asc')
         ->get("rol");
-    if ($user->admin) {
+    if (!$user->facultad['facultad_id']) {
         $fs_facultades = $db
             ->orderBy('facultad_nombre', 'asc')
             ->get('facultad');
@@ -178,12 +175,12 @@ if ($user->admin) {
                             <th>Correo</th>
                             <th>Clave</th>
                             <th>Rol</th>
-                            <?php if ($user->admin) { ?>
+                            <? if (!$user->facultad['facultad_id']) { ?>
                                 <th>Facultad</th>
-                            <?php } ?>
-                            <?php if ($user->acceso == 'w') { ?>
+                            <? } ?>
+                            <? if ($user->acceso == 'w') { ?>
                                 <th>Acciones</th>
-                            <?php } ?>
+                            <? } ?>
                         </tr>
 
 
@@ -206,19 +203,19 @@ if ($user->admin) {
                                 <td class="text-primary">
                                     <?= $usuario['titulo'] ?>
                                 </td>
-                                <?php if ($user->admin) { ?>
+                                <? if (!$user->facultad['facultad_id']) { ?>
                                     <td class="text-primary">
                                         <?= $usuario['facultad_nombre'] ?>
                                     </td>
-                                <?php } ?>
-                                <?php if ($user->acceso == 'w') { ?>
+                                <? } ?>
+                                <? if ($user->acceso == 'w') { ?>
                                     <td class="text-center icono-acciones">
                                         <a href="#" data-toggle="modal" data-target="#modal" data-tipo="2" title="Editar"><span
                                                 class="ing-editar ing-fw"></span></a>
                                     </td>
-                                <?php } ?>
+                                <? } ?>
                             </tr>
-                        <?php } ?>
+                        <? } ?>
                     </tbody>
                 </table>
             </div>
@@ -291,7 +288,7 @@ if ($user->admin) {
                                     </div>
                                 </div>
                             </div>
-                            <?php if ($user->admin) { ?>
+                            <? if (!$user->facultad['facultad_id']) { ?>
                                 <div class="form-group row" id="mdatalist">
                                     <label for="dlfacultad" class="col-4 col-form-label">Facultad *</label>
                                     <div class="col-8">
@@ -299,7 +296,7 @@ if ($user->admin) {
                                             <div class="datalist-input">Mostrar todas</div>
                                             <span class="ing-buscar icono"></span>
                                             <ul style="display:none">
-                                            <li data-id="" class="pl-4">General</li>
+                                                <li data-id="" class="pl-4">General</li>
                                                 <?php foreach ($fs_facultades as $facultad) { ?>
                                                     <li data-id="<?= $facultad['facultad_id'] ?>" class="pl-4"><?= $facultad['facultad_nombre'] ?></li>
                                                 <?php } ?>
@@ -384,9 +381,9 @@ if ($user->admin) {
                 var button = $(event.relatedTarget);
                 var tipo = button.data('tipo');
                 var modal = $(this);
-                setDatalistFirst('#mrol');
+                // setDatalistFirst('#mrol');
                 <?php if ($user->admin) { ?>
-                    setDatalistFirst("#dlfacultad");
+                    // setDatalistFirst("#dlfacultad");
                 <?php } ?>
                 $("#mnombre").removeClass("is-invalid");
                 $("#mclave").removeClass("is-invalid");
@@ -398,7 +395,7 @@ if ($user->admin) {
                     $('#mnombre').val("");
                     $('#mcorreo').val("");
                     $('#id').val("");
-                    <?php if ($user->admin) { ?>
+                    <?php if (!$user->facultad['facultad_id']) { ?>
                         $('#mfacultad').val(<?= $fac ?>);
                     <?php } ?>
                 }
@@ -422,9 +419,9 @@ if ($user->admin) {
                             $('#mclave').val(result['usuario_clave']);
                             $('#mcorreo').val(result['usuario_correo']);
                             setDatalist('#mrol', result['rol_id']);
-                            <?php if ($user->admin) { ?>
+                            <? if (!$user->facultad['facultad_id']) { ?>
                                 setDatalist('#dlfacultad', result['facultad_id']);
-                            <?php } ?>
+                            <? } ?>
                         },
                         error: function () {
                             console.log("Error")
@@ -441,18 +438,13 @@ if ($user->admin) {
                     $('#desc-error').html("No puede tener espacios al inicio");
                     error = true;
                 }
-                if (error) {
-                    return false;
-                }
-                else {
-                    return true;
-                }
+                return !error;
             }
 
             $(document).on("click", ".btn-reset", function (event) {
                 var forma = $(this).parents("form");
                 forma.find("input[type=text]").val("");
-                setDatalistFirst("#filter_rol");
+                // setDatalistFirst("#filter_rol");
                 forma.submit();
             });
         </script>

+ 1 - 1
vista_profesor.php

@@ -9,7 +9,7 @@ if (!isset($_SESSION['user']))
 $user = unserialize($_SESSION['user']);
 
 $user->access('reporte_de_asistencias');
-if ( !$user->admin && $user->acceso == 'n' ) 
+if ( $user->acceso == null ) 
     die(header('Location: main.php?error=1'));
 
 $user->print_to_log('Consultar: Reporte de asistencias de profesor');