Herramientas Informaticas

Categoría: MedicalSoft

CI4MedicalSoft lanzado

Me complace avisar que ya tenemos las primeras versiones del software medico en el que hemos estado trabajando, aun puede que salgan detalles pero se van corrigiendo

¿Qué es CodeIgniter 4 medicalSoft?

CodeIgniter 4 medicalSoft es un software básico para la correcta administración/manejo del catálogo de pacientes, historial médico, control de fechas, impresión de recetas.

Características

  1. Altas, Bajas y cambios de Pacientes
  2. Altas, bajas y cambios Medicamentos
  3. altas bajas y cambios de diagnósticos / enfermedades
  4. Registro de Citas Medicas
  5. Registrar consulta médica
  6. Imprimir en PDF prescripción médica
  7. Configuraciones Generales del Hospital

Instalación y actualizaciones

composer create-project julio101290/ci4medical-soft y luego composer update cada vez que haya una nueva versión del marco.

Al actualizar, consulte las notas de la versión para ver si hay algún cambio que deba aplicar a la carpeta de su aplicación. Los archivos afectados se pueden copiar o combinar desde el vendor/codeigniter4/framework/app.

Copie env a .env y personalícelo para su aplicación, específicamente la baseURL y cualquier configuración de la base de datos.

Base de datos

Puede encontrar el archivo de base de datos en la base de datos en la carpeta base de datos / medicalsoft2022.sql

Pronto crearemos los archivos de migración para construir las tablas sin tener que ejecutar el archivo .sql

Además conforme se vayan haciendose cambios vamos a generar una aplicación instalable para windows para que la instalación sea lo más fácil posible.

Primero ejecutamos el instalador

En la ruta la dejamos tal como está y le damos click en el botón siguiente

Activamos la casilla para que se genere el acceso directo al escritorio

Verificamos que la información esté correcta

Una vez que se le da click se empezará a instalar

Una vez terminado nos sale la ventana de login, en caso de salir la pantalla en blanco cerrar la ventana y volverla abrir, el usuario es admin y la contraseña es super-admin

Una vez ingresado al sistema nos mostrará la pantalla principal y podremos empezar a usar el sistema MedicalSoft

Cambio importante con index.php

index.php ya no está en la raíz del proyecto! Se ha movido dentro de la carpeta, public para una mejor seguridad y separación de componentes.

Esto significa que debe configurar su servidor web para que “se dirija” a su proyecto carpeta public y no a la raíz del proyecto. Una mejor práctica sería configurar un host virtual para que apunte allí. Una mala práctica sería apuntar su servidor web a la raíz del proyecto y esperar ingresar public/…, como el resto de tu lógica y el El marco está expuesto.

Por favor lea la guía del usuario para obtener una mejor explicación de cómo funciona CI4!

Requisitos del servidor

Se requiere PHP versión 7.4 o superior, con las siguientes extensiones instaladas:

  • intl
  • libcurl si planea usar la biblioteca HTTP \ CURLRequest

Además, asegúrese de que las siguientes extensiones estén habilitadas en su PHP:

  • json ( habilitado de forma predeterminada – no lo apague )
  • mbstring
  • mysqlnd
  • xml ( habilitado de forma predeterminada: no lo apague )

Demo en https://medicalsoft.cesarsystems.com.mx/ usuario:user contraseña:super-user

Screenshots

CI 4.0 MedicalSoft CRUD Para Medicamentos

Ya creamos el CRUD para pacientes, ahora será necesario crear un CRUD para los medicamentos.

Primero creamos la tabla medicamentos, les dejo la exportación desde phpmyadmin

-- phpMyAdmin SQL Dump
-- version 5.0.4
-- https://www.phpmyadmin.net/
--
-- Servidor: 127.0.0.1
-- Tiempo de generación: 06-01-2023 a las 20:37:36
-- Versión del servidor: 10.4.17-MariaDB
-- Versión de PHP: 7.4.15

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Base de datos: `medicalsoft`
--

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `medicamentos`
--

CREATE TABLE `medicamentos` (
  `id` int(11) NOT NULL,
  `descripcion` varchar(256) COLLATE utf8_spanish2_ci NOT NULL,
  `created_at` datetime DEFAULT NULL,
  `deleted_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;

--
-- Índices para tablas volcadas
--

--
-- Indices de la tabla `medicamentos`
--
ALTER TABLE `medicamentos`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT de las tablas volcadas
--

--
-- AUTO_INCREMENT de la tabla `medicamentos`
--
ALTER TABLE `medicamentos`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Creamos el archivo MedicamentosModel.php en app/models/

<?php

namespace App\Models;

use CodeIgniter\Model;

class MedicamentosModel extends Model
{
    protected $table      = 'medicamentos';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','descripcion', 'created_at','updated_at'];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    =  [
        'descripcion'     => 'required|alpha_numeric_space|min_length[3]'
        
    ];
    protected $validationMessages = [];
    protected $skipValidation     = false;
    
   
}

Creamos el archivo MedicamentosController.php en app/controller

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use App\Models\MedicamentosModel;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class MedicamentosController extends BaseController {

    use ResponseTrait;

    protected $bitacora;
    protected $medicamentos;

    public function __construct() {
        $this->medicamentos = new MedicamentosModel();
        $this->bitacora = new BitacoraModel();
        helper('menu');
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
              $start = $this->request->getGet('start');
              $length = $this->request->getGet('length');
              $search = $this->request->getGet('search[value]');
              $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
              $dir = $this->request->getGet('order[0][dir]');
             */

            $datos = $this->medicamentos->select('id,descripcion,created_at,updated_at')->where('deleted_at', null);
            // $resultado = $this->bitacora->findAll();
            // $this->bitacora->getResource()->countAllResults(),
            // $this->bitacora->getResource($search)->countAllResults()
            //      var_dump($datos);


            return \Hermawan\DataTables\DataTable::of($datos)->add('action', function ($row) {
                                return " <div class=\"btn-group\">
                          
                  <button class=\"btn btn-warning btnEditarMedicamento\" data-toggle=\"modal\" idMedicamento=\"$row->id\" data-target=\"#modalAgregarMedicamentos\">  <i class=\" fa fa-edit \"></i></button>
                  <button class=\"btn btn-danger btnEliminarMedicamento\" idMedicamento=\"$row->id\"><i class=\"fa fa-times\"></i></button></div>";
                            }, 'last')
                            ->toJson();
        }

        $titulos["title"] = lang('medicamentos.title');
        $titulos["subtitle"] = lang('medicamentos.subtitle');

        return view('medicamentos', $titulos);
    }

    /*
     * Lee paciente
     */

    public function traeMedicamento() {


        $idMedicamento = $this->request->getPost("idMedicamento");
        $datosMedicamento = $this->medicamentos->find($idMedicamento);

        echo json_encode($datosMedicamento);
    }

    /*
     * Guarda o actualiza paciente
     */

    public function guardar() {


        helper('auth');
        $userName = user()->username;
        $idUser = user()->id;

        $datos = $this->request->getPost();

        if ($datos["idMedicamento"] == 0) {


            try {


                if ($this->medicamentos->save($datos) === false) {

                    $errores = $this->medicamentos->errors();

                    foreach ($errores as $field => $error) {

                        echo $error . " ";
                    }

                    return;
                }

                $datosBitacora["descripcion"] = "Se guardo la medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;

                $this->bitacora->save($datosBitacora);

                echo "Guardado Correctamente";
            } catch (\PHPUnit\Framework\Exception $ex) {


                echo "Error al guardar " . $ex->getMessage();
            }
        } else {


            if ($this->medicamentos->update($datos["idMedicamento"], $datos) == false) {
                
                $errores = $this->medicamentos->errors();
                foreach ($errores as $field => $error) {

                    echo $error . " ";
                }
                
                return;
               
            } else {

                $datosBitacora["descripcion"] = "Se actualizo el medicamento con los siguientes datos: " . json_encode($datos);
                $datosBitacora["usuario"] = $userName;
                $this->bitacora->save($datosBitacora);
                echo "Actualizado Correctamente";

                return;
            }
        }

        return;


    }
    
    
    
     public function delete($id)
    {
        if (!$found = $this->medicamentos->delete($id)) {
            return $this->failNotFound(lang('medicamentos.msg.msg_get_fail'));
        }
        
        $infoMedicamento = $this->medicamentos->find($id);
        
        $datosBitacora["descripcion"] = "Se elimino la medicamento que contenia los siguientes datos ". json_encode($infoMedicamento);
        
        $this->bitacora->save($datosBitacora);
        return $this->respondDeleted($found, lang('medicamentos.msg.msg_delete'));
    }

}

Creamos el archivo de la vista medicamentos.php en app/views

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\nestable') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

<!-- Section content -->
<?= $this->section('content') ?>

<?= $this->include('medicamentosModulos\modalCaptura') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="float-right">
            <div class="btn-group">

                <button class="btn btn-primary btnAgregarMedicamento" data-toggle="modal" data-target="#modalAgregarMedicamentos"><i class="fa fa-plus"></i>

                    <?= lang('medicamentos.add') ?>

                </button>

            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaMedicamentos" class="table table-striped table-hover va-middle tablaMedicamentos">
                        <thead>
                            <tr>



                                <th>#</th>
                                <th><?= lang('medicamentos.description') ?></th>
                        
                                <th><?= lang('medicamentos.createdAt') ?></th>
                                <th><?= lang('medicamentos.updateAt') ?></th>
                                <th>Acciones </th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->

<?= $this->endSection() ?>




<?= $this->section('js') ?>
<script>

    /**
     * Cargamos la tabla
     */


    function cargaTabla() {



         $('.tablaMedicamentos').DataTable({
         "ajax": "<?= route_to('admin/medicamentos') ?>",
         "deferRender": true,
         "serverSide": true,
         "retrieve": true,
         "processing": true
         
         });
        
    }


    cargaTabla();



    /**
     * Carga datos actualizar
     */


    /*=============================================
     EDITAR PACIENTE
     =============================================*/
    $(".tablaMedicamentos").on("click", ".btnEditarMedicamento", function () {

        var idMedicamento = $(this).attr("idMedicamento");


        console.log("idMedicamento ", idMedicamento);

        var datos = new FormData();
        datos.append("idMedicamento", idMedicamento);

        $.ajax({

            url: "<?= route_to('admin/medicamentos/traerMedicamento') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            dataType: "json",
            success: function (respuesta) {
                console.log(respuesta);
                $("#idMedicamento").val(respuesta["id"]);
                $("#descripcion").val(respuesta["descripcion"]);
            
            }

        })

    })


    /*=============================================
     ELIMINAR PACIENTE
     =============================================*/
    $(".tablaMedicamentos").on("click", ".btnEliminarMedicamento", function () {

        var idMedicamento = $(this).attr("idMedicamento");


 

        Swal.fire({
            title: '<?= lang('boilerplate.global.sweet.title') ?>',
            text: "<?= lang('boilerplate.global.sweet.text') ?>",
            icon: 'warning',
            showCancelButton: true,
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: '<?= lang('boilerplate.global.sweet.confirm_delete') ?>'
        })
                .then((result) => {
                    if (result.value) {
                        $.ajax({
                            url: `<?= route_to('admin/medicamentos') ?>/` + idMedicamento,
                            method: 'DELETE',
                        }).done((data, textStatus, jqXHR) => {
                            Toast.fire({
                                icon: 'success',
                                title: jqXHR.statusText,
                            });

                           $(".tablaMedicamentos").DataTable().destroy();
                            cargaTabla();
                            //tableUser.ajax.reload();
                        }).fail((error) => {
                            Toast.fire({
                                icon: 'error',
                                title: error.responseJSON.messages.error,
                            });
                        })
                    }
                })
    })




</script>
<?= $this->endSection() ?>

Creamos la carpeta en views medicamentosModulos y dentro de ella creamos el archivo modelCaptura.php con el siguiente código

<!-- Modal Medicamentos -->
<div class="modal fade" id="modalAgregarMedicamentos" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"><?= lang('medicamentos.createEdit') ?></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-enfermedad" class="form-horizontal">
                    <input type="hidden" id="idMedicamento" name="idMedicamento" value="0">



                    <div class="form-group row">
                        <label for="inputName" class="col-sm-2 col-form-label"><?= lang('medicamentos.description') ?></label>
                        <div class="col-sm-10">
                            <div class="input-group">
                                <div class="input-group-prepend">
                                    <span class="input-group-text"><i class="fas fa-pencil-alt"></i></span>
                                </div>
                                <input type="text" name="descripcion" id="descripcion" class="form-control <?= session('error.descripcion ') ? 'is-invalid' : '' ?>" value="<?= old('descripcion') ?>" placeholder="<?= lang('medicamentos.description') ?>" autocomplete="off">
                            </div>
                        </div>
                    </div>

                  

         

   
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal"><?= lang('boilerplate.global.close') ?></button>
                <button type="button" class="btn btn-primary btn-sm" id="btnGuardarMedicamento"><?= lang('boilerplate.global.save') ?></button>
            </div>
        </div>
    </div>
</div>

<?= $this->section('js') ?>


<script>

    $(document).on('click', '.btnAgregarMedicamento', function (e) {

        console.log("asdasd");
        $(".form-control").val("");

        $("#idMedicamento").val("0");
        
           $("#btnGuardarMedicamento").removeAttr("disabled");

    })

    /* 
     * AL hacer click al editar
     */



    $(document).on('click', '.btnGuardarMedicamento', function (e) {


        var idMedicamento = $(this).attr("idMedicamento");

        //LIMPIAMOS CONTROLES
        $(".form-control").val("");

        $("#idMedicamento").val(idMedicamento);
        $("#btnGuardarMedicamento").removeAttr("disabled");

    })

    /**
     * Guardar paciente
     */

    $(document).on('click', '#btnGuardarMedicamento', function (e) {

        var idMedicamento = $("#idMedicamento").val();
        var descripcion = $("#descripcion").val();
      


        $("#btnGuardarMedicamento").attr("disabled", true);


        var datos = new FormData();
        datos.append("idMedicamento", idMedicamento);
        datos.append("descripcion", descripcion);


        $.ajax({

            url: "<?= route_to('admin/medicamentos/guardar') ?>",
            method: "POST",
            data: datos,
            cache: false,
            contentType: false,
            processData: false,
            //dataType:"json",
            success: function (respuesta) {


                if (respuesta.match(/Correctamente.*/)) {


                    Toast.fire({
                        icon: 'success',
                        title: "Guardado Correctamente"
                    });


                    $('.tablaMedicamentos').DataTable().destroy();
                    cargaTabla();
                    $("#btnGuardarMedicamento").removeAttr("disabled");

                   
                     $('#modalAgregarMedicamentos').modal('hide');
                } else {

                    Toast.fire({
                        icon: 'error',
                        title: respuesta
                    });

                    $("#btnGuardarMedicamento").removeAttr("disabled");
                  //  $('#modalAgregarMedicamento').modal('hide');

                }

            }

        }

        )




    });
</script>


<?= $this->endSection() ?>

en app/Languaje/en creamos el archivo medicamentos.php con el siguiente código

<?php

$medicamentos["descrption"] = "Description";

$medicamentos["createdAt"] = "Date Creation";
$medicamentos["updateAt"] = "Date Update";
$medicamentos["add"] = "Add medicine";
$medicamentos["actions"] = "Actions";
$medicamentos["createEdit"] = "Create  / Edit medicine";
$medicamentos["title"] = "Medicine";
$medicamentos["subtitle"] = "List of medicines";

$medicamentos["msg_delete"] = "medicine has deleted .";
$medicamentos["msg_get_fail"] = "The medicine not exist or has deleted.";





return $medicamentos;

en app/Languaje/escreamos el archivo medicamentos.php con el siguiente código

<?php

$medicamentos["description"] = "Descripcion";

$medicamentos["createdAt"] = "Fecha Creación";
$medicamentos["updateAt"] = "Fecha de Modificación";
$medicamentos["add"] = "Agregar Medicamentos";
$medicamentos["actions"] = "Acciones";
$medicamentos["createEdit"] = "Crear  / Editar Medicamentos";
$medicamentos["title"] = "Medicamentos";
$medicamentos["subtitle"] = "Lista de Medicamentos";

$medicamentos["msg_delete"] = "El medicamento ha sido eliminada .";
$medicamentos["msg_get_fail"] = "La medicamento no existe o fue eliminada.";





return $medicamentos;

en el archivo app/config/routes.php agregamos dentro del grupo admin la siguiente rutas

    $routes->resource('medicamentos', [
        'filter' => 'permission:medicamentos-permiso',
        'controller' => 'MedicamentosController',
        'except' => 'show'
    ]);

    $routes->post('medicamentos/guardar', 'MedicamentosController::guardar');

    $routes->post('medicamentos/traerMedicamento', 'MedicamentosController::traeMedicamento');
Y listo ya tenemos nuestro CRUD de medicamentos

CI 4.0 MedicalSoft Datatable para tabla de Bitacora

Ya vimos como guardar los datos de la bitácora, ahora veremos como usar el datatable con server side para mostrar los datos de guardados en la bitácora

Primero creamos el archivo BitacoraModel.php de la siguiente forma en la carpeta app/model/ aunque a estas altura ya debemos tenerlo

Leer Mas: CI 4.0 MedicalSoft Datatable para tabla de Bitacora
<?php

namespace App\Models;

use CodeIgniter\Model;

class BitacoraModel extends Model
{
    protected $table      = 'bitacora';
    protected $primaryKey = 'id';

    protected $useAutoIncrement = true;

    protected $returnType     = 'array';
    protected $useSoftDeletes = true;

    protected $allowedFields = ['id','descripcion', 'usuario'];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $deletedField  = 'deleted_at';

    protected $validationRules    = [];
    protected $validationMessages = [];
    protected $skipValidation     = false;
    
    
    
    const ORDERABLE = [
        1 => 'id',
        2 => 'descripcion',
        3 => 'usuario',
        4 => 'created_at',
        
    ];

    /**
     * Get resource data.
     *
     * @param string $search
     *
     * @return \CodeIgniter\Database\BaseBuilder
     */
   public function getResource(string $search = '')
    {
        $builder = $this->builder()
            ->select('id, descripcion, usuario, created_at');

        $condition = empty($search)
            ? $builder
            : $builder->groupStart()
                ->like('descripcion', $search)
                ->orLike('Usuario', $search)
                ->orLike('created_at', $search)
            ->groupEnd();

        return $condition->where('deleted_at', null);
    }
}

Para convertir fácilmente el resultado de la consulta SQL del QUERYBUILDER a formato json que usa datatable instalamos en la terminal el siguiente componente de hermawan usando composer

composer require hermawan/codeigniter4-datatables

Creamos el archivo BitacoraController.php con los siguientes, si observamos en la función index vemos que solo con una linea se crea el json

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use \App\Models\BitacoraModel;
use CodeIgniter\API\ResponseTrait;

class BitacoraController extends BaseController {

    use ResponseTrait;

    protected $bitacora;

    public function __construct() {
        $this->bitacora = new BitacoraModel();
        helper('menu');
    }

    public function index() {


        if ($this->request->isAJAX()) {

            /*
            $start = $this->request->getGet('start');
            $length = $this->request->getGet('length');
            $search = $this->request->getGet('search[value]');
            $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
            $dir = $this->request->getGet('order[0][dir]');
*/

           $datos = $this->bitacora->select('id,descripcion,usuario,created_at');
           // $resultado = $this->bitacora->findAll();
                                   // $this->bitacora->getResource()->countAllResults(),
                                   // $this->bitacora->getResource($search)->countAllResults()
           return \Hermawan\DataTables\DataTable::of($datos)->toJson();
        }
        
        $titulos["title"] = lang('log.title');
        $titulos["subtitle"] = lang('log.subtitle');
        //$data["data"] = $datos;
        return view('bitacora',$titulos);
    }

    public function tabla() {


        if ($this->request->isAJAX()) {
            $start = $this->request->getGet('start');
            $length = $this->request->getGet('length');
            $search = $this->request->getGet('search[value]');
            $order = BitacoraModel::ORDERABLE[$this->request->getGet('order[0][column]')];
            $dir = $this->request->getGet('order[0][dir]');

            return $this->respond(Collection::datatable(
                                    $this->bitacora->getResource($search)->orderBy($order, $dir)->limit($length, $start)->get()->getResultObject(),
                                    $this->bitacora->getResource()->countAllResults(),
                                    $this->bitacora->getResource($search)->countAllResults()
            ));
        }
    }

}

Creamos la vista en app/views/bitacora.php

<?= $this->include('agungsugiarto\boilerplate\Views\load\select2') ?>
<?= $this->include('agungsugiarto\boilerplate\Views\load\datatables') ?>
<!-- Extend from layout index -->
<?= $this->extend('agungsugiarto\boilerplate\Views\layout\index') ?>

<!-- Section content -->
<?= $this->section('content') ?>
<!-- SELECT2 EXAMPLE -->
<div class="card card-default">
    <div class="card-header">
        <div class="card-tools">
            <div class="btn-group">


            </div>
        </div>
    </div>
    <div class="card-body">
        <div class="row">
            <div class="col-md-12">
                <div class="table-responsive">
                    <table id="tablaBitacora" class="table table-striped table-hover va-middle tablaBitacora">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th><?= lang('log.description') ?></th>
                                <th><?= lang('log.user') ?></th>
                                <th><?= lang('log.date') ?></th>


                            </tr>
                        </thead>
                        <tbody>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- /.card -->
<?= $this->endSection() ?>

<?= $this->section('js') ?>
<script>

/**
 * Cargamos la tabla
 */
$('.tablaBitacora').DataTable( {
    "ajax": "<?= route_to('admin/bitacora/manage') ?>",
    "deferRender": true,
     "serverSide" : true,
  "retrieve": true,
  "processing": true

} )

    
</script>
<?= $this->endSection() ?>

En las rutas app/config/routes.php agregamos la siguiente linea

    /*
     * RUTA BITACORA
     */

    $routes->resource('bitacora', [
        'filter' => 'permission:bitacora-permiso',
        'controller' => 'BitacoraController',
        'except' => 'show'
    ]);
Al final este es el resultado

Creado con WordPress & Tema de Anders Norén