rbac done

This commit is contained in:
mahdahar 2024-12-04 11:11:02 +07:00
parent 306548909f
commit 8fdad44f80
37 changed files with 1514 additions and 141 deletions

1
.gitignore vendored
View File

@ -4,5 +4,4 @@
!cmod.7z
!.gitignore
!env
!cmod.sql
!cmod.bak

View File

@ -34,7 +34,7 @@ class Filters extends BaseFilters
'forcehttps' => ForceHTTPS::class,
'pagecache' => PageCache::class,
'performance' => PerformanceMetrics::class,
'auth' => \App\Filters\Auth::class,
'role' => \App\Filters\RoleFilter::class,
];
/**
@ -70,7 +70,7 @@ class Filters extends BaseFilters
*/
public array $globals = [
'before' => [
'auth' => [ 'except' => [
'role' => [ 'except' => [
'auth/*', 'setup', 'api/*'
]]
// 'honeypot',

View File

@ -5,47 +5,69 @@ use CodeIgniter\Router\RouteCollection;
/**
* @var RouteCollection $routes
*/
/*
// Pages
$routes->get('/', 'Pages::dashboard_index');
$routes->get('/userroles/', 'Pages::userroles_index');
$routes->get('/users/', 'Pages::users_index');
$routes->get('/changePass/', 'Pages::changePass');
$routes->get('/dictTests/', 'Pages::dictTests_index');
// Tubes
$routes->get('/tubes/collect/(:any)/(:any)', 'Tubes::collect/$1/$2');
$routes->get('/tubes/collectAll/(:any)', 'Tubes::collectAll/$1');
$routes->get('/tubes/uncollect/(:any)/(:any)', 'Tubes::uncollect/$1/$2');
$routes->get('/tubes/uncollectAll/(:any)', 'Tubes::uncollectAll/$1');
$routes->get('/tubes/unreceive/(:any)/(:any)', 'Tubes::unreceive/$1/$2');
$routes->get('/tubes/unreceiveAll/(:any)', 'Tubes::unreceiveAll/$1');
$routes->post('/tubes/comment/(:any)/(:any)', 'Tubes::comment/$1/$2');
$routes->get('/dashboard/viewAccess/(:any)', 'Dashboard::viewAccess/$1');
*/
// Auth
$routes->get('/auth/logout', 'Auth::logout');
$routes->get('/auth/loginTD', 'Auth::loginTD');
$routes->match(['get','post'], '/auth/login', 'Auth::login');
$routes->match(['get','post'], '/auth/setpass/(:any)', 'Auth::setpass/$1');
$routes->get('/', 'Auth::redirects');
$routes->get('changePass/', 'Auth::changePass');
// API - Dashboard
$routes->get('/api/dashboard/index', 'Dashboard::index');
$routes->get('/api/dashboard/index', 'API_Dashboard::index');
// API - Tubes
$routes->get('/tubes/collect/(:any)/(:any)', 'API_Tubes::collect/$1/$2');
$routes->get('/tubes/collectAll/(:any)', 'API_Tubes::collectAll/$1');
$routes->get('/tubes/uncollect/(:any)/(:any)', 'API_Tubes::uncollect/$1/$2');
$routes->get('/tubes/uncollectAll/(:any)', 'API_Tubes::uncollectAll/$1');
$routes->get('/tubes/unreceive/(:any)/(:any)', 'API_Tubes::unreceive/$1/$2');
$routes->get('/tubes/unreceiveAll/(:any)', 'API_Tubes::unreceiveAll/$1');
$routes->post('/tubes/comment/(:any)/(:any)', 'API_Tubes::comment/$1/$2');
// API - Userroles
$routes->get('/api/userroles/index', 'Userroles::index');
$routes->get('/api/userroles/detail/(:any)', 'Userroles::detail/$1');
$routes->post('/api/userroles/save/(:any)', 'Userroles::save/$1');
$routes->get('/api/userroles/index', 'API_Userroles::index');
$routes->get('/api/userroles/detail/(:any)', 'API_Userroles::detail/$1');
$routes->post('/api/userroles/save/(:any)', 'API_Userroles::save/$1');
// API - Users
$routes->get('/api/users/index', 'Users::index');
$routes->get('/api/users/detail/(:any)', 'Users::detail/$1');
$routes->post('/api/users/savePass/(:any)', 'Users::savePass/$1');
$routes->post('/api/users/saveRole/(:any)', 'Users::saveRole/$1');
$routes->get('/api/users/index', 'API_Users::index');
$routes->get('/api/users/detail/(:any)', 'API_Users::detail/$1');
$routes->post('/api/users/savePass/(:any)', 'API_Users::savePass/$1');
$routes->post('/api/users/saveRole/(:any)', 'API_Users::saveRole/$1');
// API - DictTests
$routes->POST('/api/dictTests/search', 'DictTests::search');
$routes->POST('/api/dictTests/save', 'DictTests::save');
$routes->get('/api/dictTests/index', 'DictTests::index');
$routes->get('/api/dictTests/detail/(:any)', 'DictTests::detail/$1');
$routes->POST('api/dictTests/search', 'API_DictTests::search');
$routes->POST('api/dictTests/save', 'API_DictTests::save');
$routes->get('api/dictTests/index', 'API_DictTests::index');
$routes->get('api/dictTests/detail/(:any)', 'API_DictTests::detail/$1');
// admin
$routes->group('admin', ['filter' => 'role:admin'], static function ($routes) {
$routes->get('', 'AdminController::index');
$routes->get('dashboard/viewAccess/(:any)', 'AdminController::viewAccess/$1');
$routes->get('userroles/', 'AdminController::userroles_index');
$routes->get('users/', 'AdminController::users_index');
$routes->get('dictTests/', 'AdminController::dictTests_index');
});
// user
$routes->group('user', ['filter' => 'role:user'], static function ($routes) {
$routes->get('', 'UserController::index');
$routes->get('dashboard/viewAccess/(:any)', 'UserController::viewAccess/$1');
});
// fo
$routes->group('fo', ['filter' => 'role:fo'], static function ($routes) {
$routes->get('', 'FoController::index');
$routes->get('dashboard/viewAccess/(:any)', 'FoController::viewAccess/$1');
});

View File

@ -3,7 +3,7 @@ namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
class Dashboard extends ResourceController {
class API_Dashboard extends ResourceController {
protected $format = 'json';
public function index() {
@ -57,19 +57,4 @@ end STATS
$data['count'] = array_count_values(array_column($results, 'STATS'));
return $this->respond($data,200);
}
public function viewAccess($accessnumber): string {
$db = \Config\Database::connect();
$sql = "select p.PATNUMBER, p.NAME, sr.HOSTORDERNUMBER, tu.SAMPLETYPE, ds.SHORTTEXT, tu.TUBESTATUS, ct.COLLSTATUS, ct.TUBECOMMENT from SP_TUBES tu
left join SP_REQUESTS sr on tu.SP_ACCESSNUMBER=sr.SP_ACCESSNUMBER
left join PATIENTS p on p.PATID=sr.PATID
left join DICT_SAMPLES_TYPES ds on ds.SAMPCODE= tu.SAMPLETYPE
left join cmod.dbo.CM_TUBES ct on ct.SAMPLETYPE=tu.SAMPLETYPE and ct.ACCESSNUMBER=tu.SP_ACCESSNUMBER
where tu.SP_ACCESSNUMBER='$accessnumber'";
$query = $db->query($sql);
$results = $query->getResultArray();
$data['data'] = $results;
$data['accessnumber'] = $accessnumber;
return view('dashboard_viewAccess', $data);
}
}

View File

@ -3,7 +3,7 @@ namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
class DictTests extends ResourceController {
class API_DictTests extends ResourceController {
protected $format = 'json';
public function index() {

View File

@ -2,7 +2,7 @@
namespace App\Controllers;
class Tubes extends BaseController {
class API_Tubes extends BaseController {
public function collect($access, $sample) {
$userid = session()->userid;

View File

@ -3,7 +3,7 @@ namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
class Userroles extends ResourceController {
class API_Userroles extends ResourceController {
protected $format = 'json';
public function index() {
@ -26,14 +26,13 @@ class Userroles extends ResourceController {
}
public function save($userroleid) {
$userrolecode = $this->request->getPost('userrolecode');
$userrolename = $this->request->getPost('userrolename');
$db = \Config\Database::connect();
if($userroleid == 0) { // new
$sql = "INSERT INTO cmod.dbo.CM_USERROLES(USERROLECODE, USERROLENAME, CREATEDATE) VALUES ('$userrolecode', '$userrolename', GETDATE())";
$sql = "INSERT INTO cmod.dbo.CM_USERROLES(USERROLENAME, CREATEDATE) VALUES ('$userrolename', GETDATE())";
} else { //update
$sql = "UPDATE cmod.dbo.CM_USERROLES set USERROLENAME='$userrolename', USERROLECODE='$userrolecode' where USERROLEID='$userroleid'";
$sql = "UPDATE cmod.dbo.CM_USERROLES set USERROLENAME='$userrolename' where USERROLEID='$userroleid'";
}
if( $db->query($sql) ) {

View File

@ -3,7 +3,7 @@ namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
class Users extends ResourceController {
class API_Users extends ResourceController {
protected $format = 'json';
public function index() {

View File

@ -0,0 +1,61 @@
<?php
namespace App\Controllers;
class AdminController extends BaseController {
public function index() {
return view('admin/dashboard');
}
public function viewAccess($accessnumber): string {
$db = \Config\Database::connect();
$sql = "select p.PATNUMBER, p.NAME, sr.HOSTORDERNUMBER, tu.SAMPLETYPE, ds.SHORTTEXT, tu.TUBESTATUS, ct.COLLSTATUS, ct.TUBECOMMENT from SP_TUBES tu
left join SP_REQUESTS sr on tu.SP_ACCESSNUMBER=sr.SP_ACCESSNUMBER
left join PATIENTS p on p.PATID=sr.PATID
left join DICT_SAMPLES_TYPES ds on ds.SAMPCODE= tu.SAMPLETYPE
left join cmod.dbo.CM_TUBES ct on ct.SAMPLETYPE=tu.SAMPLETYPE and ct.ACCESSNUMBER=tu.SP_ACCESSNUMBER
where tu.SP_ACCESSNUMBER='$accessnumber'";
$query = $db->query($sql);
$results = $query->getResultArray();
$data['data'] = $results;
$data['accessnumber'] = $accessnumber;
return view('admin/dashboard_viewAccess', $data);
}
public function userroles_index(): string {
return view('admin/userroles_index');
}
public function users_index(): string {
$db = \Config\Database::connect();
$sql = "select * from cmod.dbo.CM_USERROLES";
$query = $db->query($sql);
$results = $query->getResultArray();
$data['userroles'] = $results;
return view('admin/users_index', $data);
}
public function changePass() {
if ($this->request->getMethod() === 'POST') {
$password1 = $this->request->getVar('password1');
$password2 = $this->request->getVar('password2');
$data['password1'] = $password1;
$data['password2'] = $password2;
if($password1 == $password2) {
$password = password_hash($password1,PASSWORD_DEFAULT);
$db = \Config\Database::connect();
$sql = "update cmod.dbo.CM_USERS set PASSWORD='$password' where USERID='$userid'";
$db->query($sql);
return redirect()->to("/");
} else {
return redirect()->to("/auth/setpass/$userid")->with('flash_error', 'password is not the same.');
}
}
return view('changePass');
}
public function dictTests_index() {
return view('admin/dictTests_index');
}
}

View File

@ -18,7 +18,7 @@ class Auth extends BaseController {
$data['password'] = $password;
$db = \Config\Database::connect();
$sql = "SELECT u.USERID, u.USERNAME, u1.PASSWORD, ur.USERROLECODE
$sql = "SELECT u.USERID, u.USERNAME, u1.PASSWORD, ur.USERROLENAME
FROM USERS u
left join cmod.dbo.CM_USERS u1 on u.USERID=u1.USERID
left join cmod.dbo.CM_USERROLES ur on u1.USERROLEID=ur.USERROLEID
@ -30,7 +30,7 @@ WHERE u.USERID='$userid'";
$qpassword = $row['PASSWORD'];
$userid = $row['USERID'];
$username = $row['USERNAME'];
$userrole = $row['USERROLECODE'];
$userrole = $row['USERROLENAME'];
// if pass empty then first login / reset password
$data['password']=$password;
$data['qpassword']=$qpassword;
@ -65,7 +65,12 @@ WHERE u.USERID='$userid'";
public function logout() {
session()->destroy();
return redirect()->to('/auth/login');;
return redirect()->to('/auth/login');
}
public function redirects() {
$role = session()->get('userrole');
return redirect()->to("/$role/");;
}
public function setpass($userid) {
@ -93,4 +98,8 @@ WHERE u.USERID='$userid'";
return redirect()->to('/');
}
public function noAccess() {
return view('noAccess');
}
}

View File

@ -2,23 +2,25 @@
namespace App\Controllers;
class Pages extends BaseController {
class FoController extends BaseController {
public function dashboard_index() {
return view('dashboard');
public function index() {
return view('fo/dashboard');
}
public function userroles_index(): string {
return view('userroles_index');
}
public function users_index(): string {
public function viewAccess($accessnumber): string {
$db = \Config\Database::connect();
$sql = "select * from cmod.dbo.CM_USERROLES";
$sql = "select p.PATNUMBER, p.NAME, sr.HOSTORDERNUMBER, tu.SAMPLETYPE, ds.SHORTTEXT, tu.TUBESTATUS, ct.COLLSTATUS, ct.TUBECOMMENT from SP_TUBES tu
left join SP_REQUESTS sr on tu.SP_ACCESSNUMBER=sr.SP_ACCESSNUMBER
left join PATIENTS p on p.PATID=sr.PATID
left join DICT_SAMPLES_TYPES ds on ds.SAMPCODE= tu.SAMPLETYPE
left join cmod.dbo.CM_TUBES ct on ct.SAMPLETYPE=tu.SAMPLETYPE and ct.ACCESSNUMBER=tu.SP_ACCESSNUMBER
where tu.SP_ACCESSNUMBER='$accessnumber'";
$query = $db->query($sql);
$results = $query->getResultArray();
$data['userroles'] = $results;
return view('users_index', $data);
$data['data'] = $results;
$data['accessnumber'] = $accessnumber;
return view('fo/dashboard_viewAccess', $data);
}
public function changePass() {
@ -39,8 +41,4 @@ class Pages extends BaseController {
}
return view('changePass');
}
public function dictTests_index() {
return view('dictTests_index');
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Controllers;
class UserController extends BaseController {
public function index() {
return view('user/dashboard');
}
public function viewAccess($accessnumber): string {
$db = \Config\Database::connect();
$sql = "select p.PATNUMBER, p.NAME, sr.HOSTORDERNUMBER, tu.SAMPLETYPE, ds.SHORTTEXT, tu.TUBESTATUS, ct.COLLSTATUS, ct.TUBECOMMENT from SP_TUBES tu
left join SP_REQUESTS sr on tu.SP_ACCESSNUMBER=sr.SP_ACCESSNUMBER
left join PATIENTS p on p.PATID=sr.PATID
left join DICT_SAMPLES_TYPES ds on ds.SAMPCODE= tu.SAMPLETYPE
left join cmod.dbo.CM_TUBES ct on ct.SAMPLETYPE=tu.SAMPLETYPE and ct.ACCESSNUMBER=tu.SP_ACCESSNUMBER
where tu.SP_ACCESSNUMBER='$accessnumber'";
$query = $db->query($sql);
$results = $query->getResultArray();
$data['data'] = $results;
$data['accessnumber'] = $accessnumber;
return view('user/dashboard_viewAccess', $data);
}
public function changePass() {
if ($this->request->getMethod() === 'POST') {
$password1 = $this->request->getVar('password1');
$password2 = $this->request->getVar('password2');
$data['password1'] = $password1;
$data['password2'] = $password2;
if($password1 == $password2) {
$password = password_hash($password1,PASSWORD_DEFAULT);
$db = \Config\Database::connect();
$sql = "update cmod.dbo.CM_USERS set PASSWORD='$password' where USERID='$userid'";
$db->query($sql);
return redirect()->to("/");
} else {
return redirect()->to("/auth/setpass/$userid")->with('flash_error', 'password is not the same.');
}
}
return view('changePass');
}
}

View File

@ -5,12 +5,17 @@ use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class Auth implements FilterInterface {
class RoleFilter implements FilterInterface {
public function before(RequestInterface $request, $arguments = null) {
if (!session()->get('username')) {
return redirect()->to('/auth/login');
}
$userRole = session()->get('userrole');
if ($arguments && !in_array($userRole, $arguments)) {
return redirect()->to('/no-access');
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) {

View File

@ -0,0 +1,173 @@
<?= $this->extend('layouts/main.php') ?>
<?= $this->section('content') ?>
<div id='stats' class="d-flex justify-content-between p-0">
</div>
<div class="card border-0">
<div class="card-body">
<div class="table-responsive">
<table id="myTable" class="table">
<thead>
<th>Order</th>
<th>MR</th>
<th>Patient</th>
<th>Request</th>
<th>Hosp</th>
<th>Test</th>
<th>Status</th>
</thead>
<tbody id="table-body">
</tbody>
</table>
</div>
</div>
</div>
<div class="modal fade" id="modal" aria-hidden="true" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('script') ?>
<script>
index();
function index() {
let url = '<?=base_url('');?>api/dashboard/index';
$.ajax({
url: url,
method: 'GET',
success: function(response) {
/*
// counter
*/
$("#stats").html("");
var stats = ['Pend', 'PartColl', 'Coll', 'PartRecv', 'Recv', 'Inc', 'PartVal', 'Comp'];
var statcolor = ['text-orange', 'text-peach', 'text-pink', 'text-soft-blue', 'text-blue', 'text-grey', 'text-soft-green', 'text-green'];
var staticon = ['bi-clock-history','bi-tv', 'bi-collection', 'bi-file-medical', 'bi-journal-medical', 'bi-calendar3-week', 'bi-check2', 'bi-clipboard-check'];
var stattext = ['Pending', 'Part Collected', 'Collected', 'Part Received', 'Received', 'Incomplete', 'Part Validated', 'Validated'];
var count = response['count'];
var statcontent = '';
stats.forEach ( (item, index) => {
//console.log(item + ' ' + index);
if(!count[item]) { count[item] = 0; }
statcontent += '<div class="custom-card" data-filtertype="'+index+'">' +
'<div class="custom-card-content">' +
'<div class="row p-0 d-flex justify-content-between">' +
'<div class="col-3 text-start '+statcolor[index]+'"> <h5 class="m-0"><i class="bi '+staticon[index]+'"></i></h5> </div>' +
'<div class="col-9 text-end pe-3"> <h2 class="m-0 custom-card-title">'+count[item]+'</h2> </div>' +
'</div>' +
'<hr class="text-orange">' +
'<h3 class="custom-card-text m-0 p-0 '+statcolor[index]+'">'+ stattext[index] +'</h3>' +
'</div>' +
'</div>';
});
$("#stats").html(statcontent);
/*
// table
*/
$("#table-body").html("");
var data = response['data'];
for (var i = 0; i < data.length; i++) {
colldate = data[i].COLLECTIONDATE.substr(0,10);
patnumber = data[i].PATNUMBER.substr(-16,16);
accessnumber = data[i].SP_ACCESSNUMBER;
patname = data[i].NAME;
hon = data[i].HOSTORDERNUMBER;
tests = data[i].TESTS;
stat = data[i].STATS;
if(stat == 'Pend') {
bgcolor = 'bg-orange';
datafilter = "data-filterrow='0'";
stattext = 'Pending';
} else if(stat == 'PartColl') {
bgcolor = 'bg-peach';
datafilter = "data-filterrow='1'";
stattext = 'Part Collected';
} else if(stat == 'Coll') {
bgcolor = 'bg-pink';
datafilter = "data-filterrow='2'";
stattext = 'Collected';
} else if(stat == 'PartRecv') {
bgcolor = 'bg-soft-blue';
datafilter = "data-filterrow='2'";
stattext = 'Part Received';
} else if(stat == 'Recv') {
bgcolor = 'bg-blue';
datafilter = "data-filterrow='4'";
stattext = 'Received';
} else if(stat == 'Inc') {
bgcolor = 'bg-grey';
datafilter = "data-filterrow='5'";
stattext = 'Incomplete';
} else if(stat == 'PartVal') {
bgcolor = 'bg-soft-green';
datafilter = "data-filterrow='6'";
stattext = 'Part Validated';
} else if(stat == 'Comp') {
bgcolor = 'bg-green';
datafilter = "data-filterrow='7'";
stattext = 'Validated';
}
let datarow = '<tr class="align-middle" ' + datafilter + ' >' +
'<td>' + colldate + '</td> <td>' + patnumber + '</td> <td>' + accessnumber + '</td> <td>' + patname + '</td> <td>' + hon + '</td> <td>' + tests + '</td>' +
"<td role='button' class='"+bgcolor+" text-center align-middle' onclick='viewAccess("+accessnumber+")'>"+stattext+"</td>" + '</tr>';
$("#table-body").append(datarow);
}
$('#myTable').DataTable();
// datatable filter
const filterButton = document.querySelectorAll("[data-filtertype]");
const table = document.querySelector("#myTable");
const tr = table.getElementsByTagName("tr");
let activeButton = null;
filterButton.forEach((button) => {
button.addEventListener("click", () => {
const selectedButton = button.getAttribute("data-filtertype");
console.log(selectedButton);
if (activeButton === button) {
button.classList.remove("active", "border", "border-primary", "border-5");
activeButton = null;
for (let i = 1; i < tr.length; i++) {
tr[i].style.display = "";
}
} else {
filterButton.forEach((btn) => btn.classList.remove("active", "border", "border-info", "border-3"));
button.classList.add("active", "border", "border-info", "border-3");
activeButton = button;
for (let i = 1; i < tr.length; i++) {
const filterValue = tr[i].getAttribute("data-filterrow");
if (filterValue === selectedButton) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
});
});
},
error: function(response) { console.log(response.responseJSON); }
});
}
function viewAccess(access) {
let url = '<?=base_url();?>admin/dashboard/viewAccess/'+access;
$('.modal-content').load(url, function(){
$('#modal').modal('show');
});
}
</script>
<?= $this->endSection() ?>

View File

@ -9,7 +9,6 @@
<thead>
<tr>
<th>#</th>
<th>Code</th>
<th>Name</th>
<th>Action</th>
</tr>
@ -33,7 +32,6 @@
<div class="col-12">
<input type='hidden' id='userroleid' value='' />
<table class="table table-sm table-borderless">
<tr class="align-middle"> <th>User Role Code</th> <th>:</th> <td><input class='form-control' type='text' id='userrolecode' oninput='this.value = this.value.toUpperCase();'/></td> </tr>
<tr class="align-middle"> <th>User Role Name</th> <th>:</th> <td><input class='form-control' type='text' id='userrolename'/></td> </tr>
</table>
<button class='btn btn-sm btn-primary' onclick='save()'>Save</button>
@ -61,7 +59,7 @@ function index() {
let editBtn = '<button class="btn btn-sm btn-success" ' + ' onclick="edit(' + data[i].USERROLEID + ')">Edit' + '</button> ';
//let deleteBtn = '<button class="btn btn-sm btn-danger" ' + ' onclick="delete(' + data[i].USERROLEID + ')">Delete' + '</button>';
let datarow = '<tr class="align-middle">' +
'<td>' + data[i].USERROLEID + '</td>' + '<td>' + data[i].USERROLECODE+ '</td>' + '<td>' + data[i].USERROLENAME+ '</td>' + '<td>' + editBtn + '</td>' +
'<td>' + data[i].USERROLEID + '</td>' + '<td>' + data[i].USERROLENAME+ '</td>' + '<td>' + editBtn + '</td>' +
'</tr>';
$("#table-body").append(datarow);
}
@ -74,7 +72,6 @@ function create() {
$("#alert-div").html("");
$("#error-div").html("");
$("#userroleid").val("0");
$("#userrolecode").val("");
$("#userrolename").val("");
$("#modal_crud").modal('show');
}
@ -89,7 +86,6 @@ function edit(userroleid) {
$("#alert-div").html("");
$("#error-div").html("");
$("#userroleid").val(userroleid);
$("#userrolecode").val(data.USERROLECODE);
$("#userrolename").val(data.USERROLENAME);
$("#modal_crud").modal('show');
},
@ -101,11 +97,9 @@ function edit(userroleid) {
function save() {
var userroleid = $("#userroleid").val();
var userrolecode = $("#userrolecode").val();
var userrolename = $("#userrolename").val();
//console.log(userroleid+' '+userrolecode+' '+userrolename);
let url = '<?=base_url('');?>api/userroles/save/'+userroleid ;
let data = { userroleid: userroleid, userrolecode: userrolecode, userrolename: userrolename };
let data = { userroleid: userroleid, userrolename: userrolename };
$.ajax({
url: url,
method: "POST",
@ -115,7 +109,6 @@ function save() {
$("#alert-div").html("");
$("#error-div").html("");
$("#userroleid").val("");
$("#userrolecode").val("");
$("#userrolename").val("");
index();
$("#modal_crud").modal('hide');

View File

@ -32,13 +32,12 @@
<tr class="align-middle"> <th>Role</th> <th>:</th>
<td>
<select id='userroleid' class='form-control'>
<option value='0'></option>
<option value=''></option>
<?php
foreach($userroles as $data) {
$userroleid = $data['USERROLEID'];
$userrolecode = $data['USERROLECODE'];
$userrolename = $data['USERROLENAME'];
echo "<option value='$userroleid'>$userrolecode - $userrolename</option>";
echo "<option value='$userroleid'>$userrolename</option>";
}
?>
</select>
@ -92,6 +91,7 @@ function index() {
let userid = data[i].USERID;
let username = data[i].USERNAME;
let userroleid = data[i].USERROLEID;
if(userroleid === null) {userroleid = '';}
let userrolename = '';
if(data[i].USERROLENAME != null) { userrolename = data[i].USERROLENAME; }
let editBtn = '<button class="btn btn-sm btn-secondary" ' + ' onclick="editRole(\'' + userid + '\',\''+ userroleid +'\')">Edit Role' + '</button> ';

173
app/Views/fo/dashboard.php Normal file
View File

@ -0,0 +1,173 @@
<?= $this->extend('user/layout/main.php') ?>
<?= $this->section('content') ?>
<div id='stats' class="d-flex justify-content-between p-0">
</div>
<div class="card border-0">
<div class="card-body">
<div class="table-responsive">
<table id="myTable" class="table">
<thead>
<th>Order</th>
<th>MR</th>
<th>Patient</th>
<th>Request</th>
<th>Hosp</th>
<th>Test</th>
<th>Status</th>
</thead>
<tbody id="table-body">
</tbody>
</table>
</div>
</div>
</div>
<div class="modal fade" id="modal" aria-hidden="true" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('script') ?>
<script>
index();
function index() {
let url = '<?=base_url('');?>api/dashboard/index';
$.ajax({
url: url,
method: 'GET',
success: function(response) {
/*
// counter
*/
$("#stats").html("");
var stats = ['Pend', 'PartColl', 'Coll', 'PartRecv', 'Recv', 'Inc', 'PartVal', 'Comp'];
var statcolor = ['text-orange', 'text-peach', 'text-pink', 'text-soft-blue', 'text-blue', 'text-grey', 'text-soft-green', 'text-green'];
var staticon = ['bi-clock-history','bi-tv', 'bi-collection', 'bi-file-medical', 'bi-journal-medical', 'bi-calendar3-week', 'bi-check2', 'bi-clipboard-check'];
var stattext = ['Pending', 'Part Collected', 'Collected', 'Part Received', 'Received', 'Incomplete', 'Part Validated', 'Validated'];
var count = response['count'];
var statcontent = '';
stats.forEach ( (item, index) => {
//console.log(item + ' ' + index);
if(!count[item]) { count[item] = 0; }
statcontent += '<div class="custom-card" data-filtertype="'+index+'">' +
'<div class="custom-card-content">' +
'<div class="row p-0 d-flex justify-content-between">' +
'<div class="col-3 text-start '+statcolor[index]+'"> <h5 class="m-0"><i class="bi '+staticon[index]+'"></i></h5> </div>' +
'<div class="col-9 text-end pe-3"> <h2 class="m-0 custom-card-title">'+count[item]+'</h2> </div>' +
'</div>' +
'<hr class="text-orange">' +
'<h3 class="custom-card-text m-0 p-0 '+statcolor[index]+'">'+ stattext[index] +'</h3>' +
'</div>' +
'</div>';
});
$("#stats").html(statcontent);
/*
// table
*/
$("#table-body").html("");
var data = response['data'];
for (var i = 0; i < data.length; i++) {
colldate = data[i].COLLECTIONDATE.substr(0,10);
patnumber = data[i].PATNUMBER.substr(-16,16);
accessnumber = data[i].SP_ACCESSNUMBER;
patname = data[i].NAME;
hon = data[i].HOSTORDERNUMBER;
tests = data[i].TESTS;
stat = data[i].STATS;
if(stat == 'Pend') {
bgcolor = 'bg-orange';
datafilter = "data-filterrow='0'";
stattext = 'Pending';
} else if(stat == 'PartColl') {
bgcolor = 'bg-peach';
datafilter = "data-filterrow='1'";
stattext = 'Part Collected';
} else if(stat == 'Coll') {
bgcolor = 'bg-pink';
datafilter = "data-filterrow='2'";
stattext = 'Collected';
} else if(stat == 'PartRecv') {
bgcolor = 'bg-soft-blue';
datafilter = "data-filterrow='2'";
stattext = 'Part Received';
} else if(stat == 'Recv') {
bgcolor = 'bg-blue';
datafilter = "data-filterrow='4'";
stattext = 'Received';
} else if(stat == 'Inc') {
bgcolor = 'bg-grey';
datafilter = "data-filterrow='5'";
stattext = 'Incomplete';
} else if(stat == 'PartVal') {
bgcolor = 'bg-soft-green';
datafilter = "data-filterrow='6'";
stattext = 'Part Validated';
} else if(stat == 'Comp') {
bgcolor = 'bg-green';
datafilter = "data-filterrow='7'";
stattext = 'Validated';
}
let datarow = '<tr class="align-middle" ' + datafilter + ' >' +
'<td>' + colldate + '</td> <td>' + patnumber + '</td> <td>' + accessnumber + '</td> <td>' + patname + '</td> <td>' + hon + '</td> <td>' + tests + '</td>' +
"<td role='button' class='"+bgcolor+" text-center align-middle' onclick='viewAccess("+accessnumber+")'>"+stattext+"</td>" + '</tr>';
$("#table-body").append(datarow);
}
$('#myTable').DataTable();
// datatable filter
const filterButton = document.querySelectorAll("[data-filtertype]");
const table = document.querySelector("#myTable");
const tr = table.getElementsByTagName("tr");
let activeButton = null;
filterButton.forEach((button) => {
button.addEventListener("click", () => {
const selectedButton = button.getAttribute("data-filtertype");
console.log(selectedButton);
if (activeButton === button) {
button.classList.remove("active", "border", "border-primary", "border-5");
activeButton = null;
for (let i = 1; i < tr.length; i++) {
tr[i].style.display = "";
}
} else {
filterButton.forEach((btn) => btn.classList.remove("active", "border", "border-info", "border-3"));
button.classList.add("active", "border", "border-info", "border-3");
activeButton = button;
for (let i = 1; i < tr.length; i++) {
const filterValue = tr[i].getAttribute("data-filterrow");
if (filterValue === selectedButton) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
});
});
},
error: function(response) { console.log(response.responseJSON); }
});
}
function viewAccess(access) {
let url = '<?=base_url();?>fo/dashboard/viewAccess/'+access;
$('.modal-content').load(url, function(){
$('#modal').modal('show');
});
}
</script>
<?= $this->endSection() ?>

View File

@ -0,0 +1,178 @@
<?php
if(isset($data[0])) {
$row = $data[0];
$patnumber = $row['PATNUMBER'];
$host = $row['HOSTORDERNUMBER'];
$name = $row['NAME'];
?>
<div class="modal-header bg-soft-green text-white">
<h1 class="modal-title fs-5" id="exampleModalToggleLabel">Detail Request </h1>
<button type="button" class="btn-close text-white" data-bs-dismiss="modal" ></button>
</div>
<div class="modal-body" style='background-color:#F4F6FF'>
<div class="row">
<div class="col">
<table class="table table-sm table-borderless">
<tr> <th>Access#</th> <th>:</th> <td><?=$accessnumber;?></td> </tr>
<tr> <th>Patient</th> <th>:</th> <td><?=$patnumber;?> - <?=$name;?></td> </tr>
</table>
</div>
</div>
<div class='row'>
<div class="col-12">
<div class="card bg-white">
<div class="card-body">
<div class="card-title"><h3>Sample List</h3></div>
<table class='table'>
<tr> <th class='text-center'>Coll.</th> <th class='text-center'>Recv.</th> <th>Sample Name</th> <th>Action</th> <th>Comment</th> </tr>
<?php
foreach($data as $row) {
$sampletype = $row['SAMPLETYPE'];
$sampletext = $row['SHORTTEXT'];
$tubestatus = $row['TUBESTATUS'];
$collstatus = $row['COLLSTATUS'];
$comment = $row['TUBECOMMENT'];
echo "\r\n <tr>";
if($collstatus==1) {
echo " <td class='text-center'><input type='checkbox' class='form-check-input' id='coll$sampletype' checked disabled></td>";
} else {
echo " <td class='text-center'><input type='checkbox' class='form-check-input' id='coll$sampletype' disabled></td>";
}
if($tubestatus==4) {
echo "<td class='text-center'><input type='checkbox' class='form-check-input' id='recv$sampletype' checked disabled></td>";
} else {
echo "<td class='text-center'><input type='checkbox' class='form-check-input' id='recv$sampletype' disabled></td>";
}
echo "<td>$sampletext</td>";
echo "<td>
<button class='badge text-bg-dark'><i class='bi bi-printer'></i></button>
<button class='badge text-bg-success' onclick='collect($sampletype, $accessnumber)'>Coll.</button>
</td> ";
echo "<td id='comment$sampletype'>$comment <i class='bi bi-pencil-square' role='button' onclick='comment($sampletype, $accessnumber, \"$sampletext\", \"$comment\")'></i></td>";
echo " </tr>";
}
?>
<tr>
<td></td> <td></td> <td>Collection</td>
<td> <button class='badge badge-dark'><i class='bi bi-printer'></i></button> </td>
</tr>
<tr>
<td></td> <td></td> <td>All</td>
<td>
<button class='badge text-bg-dark'><i class='bi bi-printer'></i></button>
<button class='badge text-bg-success' onclick='collectAll(<?=$accessnumber;?>)'>Coll.</button>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
function collect(sample, access) {
sample = sample.toString().padStart(3,'0');
const url = '<?=base_url();?>tubes/collect/'+access+'/'+sample;
fetch(url)
.then(data => {
//console.log(data);
//$("#coll"+sample).prop("checked", true);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function collectAll(access) {
const url = '<?=base_url();?>tubes/collectAll/'+access;
fetch(url)
.then(data => {
//console.log(data);
//$('input[id^="coll"]').prop('checked', true);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function uncollect(sample, access) {
sample = sample.toString().padStart(3,'0');
const url = '<?=base_url();?>tubes/uncollect/'+access+'/'+sample;
fetch(url)
.then(data => {
//console.log(data);
//$("#coll"+sample).prop("checked", false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function uncollectAll(access) {
const url = '<?=base_url();?>tubes/uncollectAll/'+access;
fetch(url)
.then(data => {
//console.log(data);
//$('input[id^="coll"]').prop('checked', false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function unreceive(sample, access) {
sample = sample.toString().padStart(3,'0');
const url = '<?=base_url();?>tubes/unreceive/'+access+'/'+sample;
fetch(url)
.then(data => {
//console.log(data);
//$("#recv"+sample).prop("checked", false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function unreceiveAll(access) {
const url = '<?=base_url();?>tubes/unreceiveAll/'+access;
fetch(url)
.then(data => {
//console.log(data);
//$('input[id^="recv"]').prop('checked', false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function comment(sample, access, sampletext, comments) {
const url = '<?=base_url();?>tubes/comment/'+access+'/'+sample;
let comment = prompt('Comment for sample '+sampletext, comments);
//$('#comment'+sample).html(comment +"<i class='bi bi-pencil-square' onclick='comment("+ sample +", "+ access +', "'+sampletext+'", "'+comment+'")\'></i>');
fetch(url, {
method: "POST",
body: JSON.stringify({ comment : comment }),
headers: { "Content-type": "application/json; charset=UTF-8" }
}).then(data => {
//console.log(data);
viewAccess(access);
})
.catch(error => { console.error('Error:',error); });
}
</script>
<?php
} else {
?>
<div class="modal-header bg-black text-white">
<h1 class="modal-title fs-5" id="exampleModalToggleLabel">Detail Request </h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" ></button>
</div>
<div class="modal-body">
<h3>Data not found</h3>
</div>
<?php
}
?>

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" type="image/png" sizes="16x16" href="<?=base_url();?>/assets/favicon.png">
<title>Summit CRM</title>
<link href="<?=base_url();?>/assets/style.css" rel="stylesheet">
<link href="<?=base_url();?>/assets/select2/select2.min.css" rel="stylesheet">
<link href="<?=base_url();?>/assets/select2/select2-bootstrap-5-theme.min.css" rel="stylesheet">
<style>
.select2-results__option { font-size: 0.75rem !important; margin: 5px !important; padding: 5px !important; }
li.select2-selection__choice { background-color : #000 !important; padding:5px !improtant; }
.select2 {width:100%!important;}
</style>
<script src="<?=base_url();?>/assets/jquery/jquery.min.js"></script>
<script src="<?=base_url();?>/assets/select2/select2.min.js"></script>
<?= $this->renderSection('head'); ?>
</head>
<body class="skin-megna-dark fixed-layout">
<div class="preloader">
<div class="loader">
<div class="loader__figure"></div>
<p class="loader__label">Summit-CRM</p>
</div>
</div>
<div id="main-wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card mt-2">
<div class="card-body">
<?= $this->renderSection('content'); ?>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="<?=base_url();?>/assets/bootstrap.bundle.min.js"></script>
<script src="<?=base_url();?>/assets/perfect-scrollbar.jquery.min.js"></script>
<script src="<?=base_url();?>/assets/app.js"></script>
<?= $this->renderSection('script'); ?>
<script>
$('.select2').select2();
</script>
</body>
</html>

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Analis Dashboard</title>
<link rel="stylesheet" href="<?=base_url();?>assets/css/icons/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="<?=base_url();?>assets/datatables/datatables.min.css">
<link href="<?=base_url();?>assets/css/styles.css" rel="stylesheet" />
</head>
<!-- <body class="sb-nav-fixed sb-sidenav-toggled"> -->
<body class="sb-nav-fixed">
<?= $this->include('fo/layout/topbar'); ?>
<div id="layoutSidenav">
<?= $this->include('fo/layout/sidebar'); ?>
<div id="layoutSidenav_content">
<main>
<div class="container-fluid px-2 py-2">
<?php
if(isset($_SESSION['alertmsg'])) {
?>
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<?=$_SESSION['alertmsg'];?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php
}
?>
<?= $this->renderSection('content'); ?>
</div>
</main>
<footer class="py-3 bg-light mt-auto">
<div class="container-fluid px-4">
<div class="d-flex align-items-center justify-content-between small">
<div class="text-muted ms-auto ">Copyright &copy; 4SKAI 2024</div>
</div>
</div>
</footer>
</div>
</div>
<script src="<?=base_url();?>assets/jquery-3.6.0.min.js"></script>
<script src="<?=base_url();?>assets/js/bootstrap.bundle.min.js"></script>
<script src="<?=base_url();?>assets/datatables/datatables.min.js"></script>
<script src="<?=base_url();?>assets/js/scripts.js"></script>
<?= $this->renderSection('script'); ?>
</body>
</html>

View File

@ -0,0 +1,14 @@
<div id="layoutSidenav_nav">
<nav class="sb-sidenav accordion sb-sidenav-light" id="sidenavAccordion">
<div class="sb-sidenav-menu">
<div class="nav">
<div class="sb-sidenav-menu-heading">Main</div>
<a class="nav-link" href="<?=base_url();?>user/"><div class="sb-nav-link-icon"><i class="bi bi-speedometer"></i></div>Dashboard</a>
<a class="nav-link" href="<?=base_url();?>changePass/"><div class="sb-nav-link-icon"><i class="bi bi-key"></i></div>Change Password</a>
</div>
</div>
<div class="sb-sidenav-footer">
<div class="small">Logged in as: User</div>
</div>
</nav>
</div>

View File

@ -0,0 +1,23 @@
<nav class="sb-topnav navbar navbar-expand navbar-light bg-light shadow-sm text-luxury">
<a class="navbar-brand ps-3 d-none d-md-block" href="#"><img src='<?=base_url();?>assets/img/logo.png' width='30' /> CMOD</a>
<button class="btn btn-link me-2" id="sidebarToggle"><i class="bi bi-list "></i></button>
<div class="ms-auto">
<ul class="navbar-nav ms-md-0 me-3 me-lg-4">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDropdown" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle"></i>
<?=$_SESSION['username'];?>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#!">Profile</a></li>
<li><a class="dropdown-item" href="#!">Change Password</a></li>
<li><hr class="dropdown-divider" /></li>
<li><a class="dropdown-item" href="<?=base_url();?>auth/logout">Logout</a></li>
</ul>
</li>
</ul>
</div>
</nav>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" type="image/png" sizes="16x16" href="<?=base_url();?>/assets/favicon.png">
<title>Summit CRM</title>
<link href="<?=base_url();?>/assets/style.css" rel="stylesheet">
<link href="<?=base_url();?>/assets/font-awesome-640/css/all.min.css" rel="stylesheet">
<?= $this->renderSection('head'); ?>
</head>
<body class="skin-megna-dark fixed-layout">
<div class="preloader">
<div class="loader">
<div class="loader__figure"></div>
<p class="loader__label">Summit-CRM</p>
</div>
</div>
<div>
<?= $this->renderSection('content'); ?>
</div>
<script src="<?=base_url();?>/assets/jquery/jquery.min.js"></script>
<script src="<?=base_url();?>/assets/bootstrap.bundle.min.js"></script>
<script src="<?=base_url();?>/assets/app.js"></script>
<?= $this->renderSection('script'); ?>
<script>
$(document).ready(function() {
$('.select2').select2({
dropdownAutoWidth : true,
width: '100%',
});
});
</script>
</body>
</html>

View File

@ -3,12 +3,12 @@
<div class="sb-sidenav-menu">
<div class="nav">
<div class="sb-sidenav-menu-heading">Main</div>
<a class="nav-link" href="<?=base_url();?>"><div class="sb-nav-link-icon"><i class="bi bi-speedometer"></i></div>Dashboard</a>
<a class="nav-link" href="<?=base_url();?>admin/"><div class="sb-nav-link-icon"><i class="bi bi-speedometer"></i></div>Dashboard</a>
<a class="nav-link" href="<?=base_url();?>changePass/"><div class="sb-nav-link-icon"><i class="bi bi-key"></i></div>Change Password</a>
<div class="sb-sidenav-menu-heading">Administration</div>
<a class="nav-link" href="<?=base_url();?>dictTests/"> <div class="sb-nav-link-icon"><i class="bi bi-journal-album"></i></div> Dict. Test </a>
<a class="nav-link" href="<?=base_url();?>users/"> <div class="sb-nav-link-icon"><i class="bi bi-person-circle"></i></div> Users </a>
<a class="nav-link" href="<?=base_url();?>userroles/"> <div class="sb-nav-link-icon"><i class="bi bi-person-lock"></i></div> User Roles </a>
<a class="nav-link" href="<?=base_url();?>admin/dictTests/"> <div class="sb-nav-link-icon"><i class="bi bi-journal-album"></i></div> Dict. Test </a>
<a class="nav-link" href="<?=base_url();?>admin/users/"> <div class="sb-nav-link-icon"><i class="bi bi-person-circle"></i></div> Users </a>
<a class="nav-link" href="<?=base_url();?>admin/userroles/"> <div class="sb-nav-link-icon"><i class="bi bi-person-lock"></i></div> User Roles </a>
</div>
</div>
<div class="sb-sidenav-footer">

53
app/Views/noAccess.php Normal file
View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>No Access</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f8f9fa;
color: #343a40;
text-align: center;
}
.container {
max-width: 600px;
padding: 20px;
background: white;
border: 1px solid #dee2e6;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2.5rem;
color: #dc3545;
}
p {
font-size: 1rem;
margin-top: 10px;
margin-bottom: 20px;
}
a {
text-decoration: none;
color: #007bff;
font-weight: bold;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h1>Access Denied</h1>
<p>Sorry, you do not have permission to access this page.</p>
<p><a href="/">Return to Homepage</a></p>
</div>
</body>
</html>

View File

@ -0,0 +1,173 @@
<?= $this->extend('user/layout/main.php') ?>
<?= $this->section('content') ?>
<div id='stats' class="d-flex justify-content-between p-0">
</div>
<div class="card border-0">
<div class="card-body">
<div class="table-responsive">
<table id="myTable" class="table">
<thead>
<th>Order</th>
<th>MR</th>
<th>Patient</th>
<th>Request</th>
<th>Hosp</th>
<th>Test</th>
<th>Status</th>
</thead>
<tbody id="table-body">
</tbody>
</table>
</div>
</div>
</div>
<div class="modal fade" id="modal" aria-hidden="true" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('script') ?>
<script>
index();
function index() {
let url = '<?=base_url('');?>api/dashboard/index';
$.ajax({
url: url,
method: 'GET',
success: function(response) {
/*
// counter
*/
$("#stats").html("");
var stats = ['Pend', 'PartColl', 'Coll', 'PartRecv', 'Recv', 'Inc', 'PartVal', 'Comp'];
var statcolor = ['text-orange', 'text-peach', 'text-pink', 'text-soft-blue', 'text-blue', 'text-grey', 'text-soft-green', 'text-green'];
var staticon = ['bi-clock-history','bi-tv', 'bi-collection', 'bi-file-medical', 'bi-journal-medical', 'bi-calendar3-week', 'bi-check2', 'bi-clipboard-check'];
var stattext = ['Pending', 'Part Collected', 'Collected', 'Part Received', 'Received', 'Incomplete', 'Part Validated', 'Validated'];
var count = response['count'];
var statcontent = '';
stats.forEach ( (item, index) => {
//console.log(item + ' ' + index);
if(!count[item]) { count[item] = 0; }
statcontent += '<div class="custom-card" data-filtertype="'+index+'">' +
'<div class="custom-card-content">' +
'<div class="row p-0 d-flex justify-content-between">' +
'<div class="col-3 text-start '+statcolor[index]+'"> <h5 class="m-0"><i class="bi '+staticon[index]+'"></i></h5> </div>' +
'<div class="col-9 text-end pe-3"> <h2 class="m-0 custom-card-title">'+count[item]+'</h2> </div>' +
'</div>' +
'<hr class="text-orange">' +
'<h3 class="custom-card-text m-0 p-0 '+statcolor[index]+'">'+ stattext[index] +'</h3>' +
'</div>' +
'</div>';
});
$("#stats").html(statcontent);
/*
// table
*/
$("#table-body").html("");
var data = response['data'];
for (var i = 0; i < data.length; i++) {
colldate = data[i].COLLECTIONDATE.substr(0,10);
patnumber = data[i].PATNUMBER.substr(-16,16);
accessnumber = data[i].SP_ACCESSNUMBER;
patname = data[i].NAME;
hon = data[i].HOSTORDERNUMBER;
tests = data[i].TESTS;
stat = data[i].STATS;
if(stat == 'Pend') {
bgcolor = 'bg-orange';
datafilter = "data-filterrow='0'";
stattext = 'Pending';
} else if(stat == 'PartColl') {
bgcolor = 'bg-peach';
datafilter = "data-filterrow='1'";
stattext = 'Part Collected';
} else if(stat == 'Coll') {
bgcolor = 'bg-pink';
datafilter = "data-filterrow='2'";
stattext = 'Collected';
} else if(stat == 'PartRecv') {
bgcolor = 'bg-soft-blue';
datafilter = "data-filterrow='2'";
stattext = 'Part Received';
} else if(stat == 'Recv') {
bgcolor = 'bg-blue';
datafilter = "data-filterrow='4'";
stattext = 'Received';
} else if(stat == 'Inc') {
bgcolor = 'bg-grey';
datafilter = "data-filterrow='5'";
stattext = 'Incomplete';
} else if(stat == 'PartVal') {
bgcolor = 'bg-soft-green';
datafilter = "data-filterrow='6'";
stattext = 'Part Validated';
} else if(stat == 'Comp') {
bgcolor = 'bg-green';
datafilter = "data-filterrow='7'";
stattext = 'Validated';
}
let datarow = '<tr class="align-middle" ' + datafilter + ' >' +
'<td>' + colldate + '</td> <td>' + patnumber + '</td> <td>' + accessnumber + '</td> <td>' + patname + '</td> <td>' + hon + '</td> <td>' + tests + '</td>' +
"<td role='button' class='"+bgcolor+" text-center align-middle' onclick='viewAccess("+accessnumber+")'>"+stattext+"</td>" + '</tr>';
$("#table-body").append(datarow);
}
$('#myTable').DataTable();
// datatable filter
const filterButton = document.querySelectorAll("[data-filtertype]");
const table = document.querySelector("#myTable");
const tr = table.getElementsByTagName("tr");
let activeButton = null;
filterButton.forEach((button) => {
button.addEventListener("click", () => {
const selectedButton = button.getAttribute("data-filtertype");
console.log(selectedButton);
if (activeButton === button) {
button.classList.remove("active", "border", "border-primary", "border-5");
activeButton = null;
for (let i = 1; i < tr.length; i++) {
tr[i].style.display = "";
}
} else {
filterButton.forEach((btn) => btn.classList.remove("active", "border", "border-info", "border-3"));
button.classList.add("active", "border", "border-info", "border-3");
activeButton = button;
for (let i = 1; i < tr.length; i++) {
const filterValue = tr[i].getAttribute("data-filterrow");
if (filterValue === selectedButton) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
});
});
},
error: function(response) { console.log(response.responseJSON); }
});
}
function viewAccess(access) {
let url = '<?=base_url();?>user/dashboard/viewAccess/'+access;
$('.modal-content').load(url, function(){
$('#modal').modal('show');
});
}
</script>
<?= $this->endSection() ?>

View File

@ -0,0 +1,182 @@
<?php
if(isset($data[0])) {
$row = $data[0];
$patnumber = $row['PATNUMBER'];
$host = $row['HOSTORDERNUMBER'];
$name = $row['NAME'];
?>
<div class="modal-header bg-soft-green text-white">
<h1 class="modal-title fs-5" id="exampleModalToggleLabel">Detail Request </h1>
<button type="button" class="btn-close text-white" data-bs-dismiss="modal" ></button>
</div>
<div class="modal-body" style='background-color:#F4F6FF'>
<div class="row">
<div class="col">
<table class="table table-sm table-borderless">
<tr> <th>Access#</th> <th>:</th> <td><?=$accessnumber;?></td> </tr>
<tr> <th>Patient</th> <th>:</th> <td><?=$patnumber;?> - <?=$name;?></td> </tr>
</table>
</div>
</div>
<div class='row'>
<div class="col-12">
<div class="card bg-white">
<div class="card-body">
<div class="card-title"><h3>Sample List</h3></div>
<table class='table'>
<tr> <th class='text-center'>Coll.</th> <th class='text-center'>Recv.</th> <th>Sample Name</th> <th>Action</th> <th>Comment</th> </tr>
<?php
foreach($data as $row) {
$sampletype = $row['SAMPLETYPE'];
$sampletext = $row['SHORTTEXT'];
$tubestatus = $row['TUBESTATUS'];
$collstatus = $row['COLLSTATUS'];
$comment = $row['TUBECOMMENT'];
echo "\r\n <tr>";
if($collstatus==1) {
echo " <td class='text-center'><input type='checkbox' class='form-check-input' id='coll$sampletype' checked disabled></td>";
} else {
echo " <td class='text-center'><input type='checkbox' class='form-check-input' id='coll$sampletype' disabled></td>";
}
if($tubestatus==4) {
echo "<td class='text-center'><input type='checkbox' class='form-check-input' id='recv$sampletype' checked disabled></td>";
} else {
echo "<td class='text-center'><input type='checkbox' class='form-check-input' id='recv$sampletype' disabled></td>";
}
echo "<td>$sampletext</td>";
echo "<td>
<button class='badge text-bg-dark'><i class='bi bi-printer'></i></button>
<button class='badge text-bg-success' onclick='collect($sampletype, $accessnumber)'>Coll.</button>
<button class='badge text-bg-warning' onclick='uncollect($sampletype, $accessnumber)'>Un-Coll.</button>
<button class='badge text-bg-primary' onclick='unreceive($sampletype, $accessnumber)'>Un-Rec.</button>
</td> ";
echo "<td id='comment$sampletype'>$comment <i class='bi bi-pencil-square' role='button' onclick='comment($sampletype, $accessnumber, \"$sampletext\", \"$comment\")'></i></td>";
echo " </tr>";
}
?>
<tr>
<td></td> <td></td> <td>Collection</td>
<td> <button class='badge badge-dark'><i class='bi bi-printer'></i></button> </td>
</tr>
<tr>
<td></td> <td></td> <td>All</td>
<td>
<button class='badge text-bg-dark'><i class='bi bi-printer'></i></button>
<button class='badge text-bg-success' onclick='collectAll(<?=$accessnumber;?>)'>Coll.</button>
<!-- <button class='badge bg-black text-white' onclick='uncollectAll(<?=$accessnumber;?>)'>un-collect</button> -->
<button class='badge text-bg-primary' onclick='unreceiveAll(<?=$accessnumber;?>)'>Un-Rec.</button>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
function collect(sample, access) {
sample = sample.toString().padStart(3,'0');
const url = '<?=base_url();?>tubes/collect/'+access+'/'+sample;
fetch(url)
.then(data => {
//console.log(data);
//$("#coll"+sample).prop("checked", true);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function collectAll(access) {
const url = '<?=base_url();?>tubes/collectAll/'+access;
fetch(url)
.then(data => {
//console.log(data);
//$('input[id^="coll"]').prop('checked', true);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function uncollect(sample, access) {
sample = sample.toString().padStart(3,'0');
const url = '<?=base_url();?>tubes/uncollect/'+access+'/'+sample;
fetch(url)
.then(data => {
//console.log(data);
//$("#coll"+sample).prop("checked", false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function uncollectAll(access) {
const url = '<?=base_url();?>tubes/uncollectAll/'+access;
fetch(url)
.then(data => {
//console.log(data);
//$('input[id^="coll"]').prop('checked', false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function unreceive(sample, access) {
sample = sample.toString().padStart(3,'0');
const url = '<?=base_url();?>tubes/unreceive/'+access+'/'+sample;
fetch(url)
.then(data => {
//console.log(data);
//$("#recv"+sample).prop("checked", false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function unreceiveAll(access) {
const url = '<?=base_url();?>tubes/unreceiveAll/'+access;
fetch(url)
.then(data => {
//console.log(data);
//$('input[id^="recv"]').prop('checked', false);
viewAccess(access);
index();
})
.catch(error => { console.error('Error:',error); });
}
function comment(sample, access, sampletext, comments) {
const url = '<?=base_url();?>tubes/comment/'+access+'/'+sample;
let comment = prompt('Comment for sample '+sampletext, comments);
//$('#comment'+sample).html(comment +"<i class='bi bi-pencil-square' onclick='comment("+ sample +", "+ access +', "'+sampletext+'", "'+comment+'")\'></i>');
fetch(url, {
method: "POST",
body: JSON.stringify({ comment : comment }),
headers: { "Content-type": "application/json; charset=UTF-8" }
}).then(data => {
//console.log(data);
viewAccess(access);
})
.catch(error => { console.error('Error:',error); });
}
</script>
<?php
} else {
?>
<div class="modal-header bg-black text-white">
<h1 class="modal-title fs-5" id="exampleModalToggleLabel">Detail Request </h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" ></button>
</div>
<div class="modal-body">
<h3>Data not found</h3>
</div>
<?php
}
?>

View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" type="image/png" sizes="16x16" href="<?=base_url();?>/assets/favicon.png">
<title>Summit CRM</title>
<link href="<?=base_url();?>/assets/style.css" rel="stylesheet">
<link href="<?=base_url();?>/assets/select2/select2.min.css" rel="stylesheet">
<link href="<?=base_url();?>/assets/select2/select2-bootstrap-5-theme.min.css" rel="stylesheet">
<style>
.select2-results__option { font-size: 0.75rem !important; margin: 5px !important; padding: 5px !important; }
li.select2-selection__choice { background-color : #000 !important; padding:5px !improtant; }
.select2 {width:100%!important;}
</style>
<script src="<?=base_url();?>/assets/jquery/jquery.min.js"></script>
<script src="<?=base_url();?>/assets/select2/select2.min.js"></script>
<?= $this->renderSection('head'); ?>
</head>
<body class="skin-megna-dark fixed-layout">
<div class="preloader">
<div class="loader">
<div class="loader__figure"></div>
<p class="loader__label">Summit-CRM</p>
</div>
</div>
<div id="main-wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card mt-2">
<div class="card-body">
<?= $this->renderSection('content'); ?>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="<?=base_url();?>/assets/bootstrap.bundle.min.js"></script>
<script src="<?=base_url();?>/assets/perfect-scrollbar.jquery.min.js"></script>
<script src="<?=base_url();?>/assets/app.js"></script>
<?= $this->renderSection('script'); ?>
<script>
$('.select2').select2();
</script>
</body>
</html>

View File

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Analis Dashboard</title>
<link rel="stylesheet" href="<?=base_url();?>assets/css/icons/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="<?=base_url();?>assets/datatables/datatables.min.css">
<link href="<?=base_url();?>assets/css/styles.css" rel="stylesheet" />
</head>
<!-- <body class="sb-nav-fixed sb-sidenav-toggled"> -->
<body class="sb-nav-fixed">
<?= $this->include('user/layout/topbar'); ?>
<div id="layoutSidenav">
<?= $this->include('user/layout/sidebar'); ?>
<div id="layoutSidenav_content">
<main>
<div class="container-fluid px-2 py-2">
<?php
if(isset($_SESSION['alertmsg'])) {
?>
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<?=$_SESSION['alertmsg'];?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php
}
?>
<?= $this->renderSection('content'); ?>
</div>
</main>
<footer class="py-3 bg-light mt-auto">
<div class="container-fluid px-4">
<div class="d-flex align-items-center justify-content-between small">
<div class="text-muted ms-auto ">Copyright &copy; 4SKAI 2024</div>
</div>
</div>
</footer>
</div>
</div>
<script src="<?=base_url();?>assets/jquery-3.6.0.min.js"></script>
<script src="<?=base_url();?>assets/js/bootstrap.bundle.min.js"></script>
<script src="<?=base_url();?>assets/datatables/datatables.min.js"></script>
<script src="<?=base_url();?>assets/js/scripts.js"></script>
<?= $this->renderSection('script'); ?>
</body>
</html>

View File

@ -0,0 +1,14 @@
<div id="layoutSidenav_nav">
<nav class="sb-sidenav accordion sb-sidenav-light" id="sidenavAccordion">
<div class="sb-sidenav-menu">
<div class="nav">
<div class="sb-sidenav-menu-heading">Main</div>
<a class="nav-link" href="<?=base_url();?>user/"><div class="sb-nav-link-icon"><i class="bi bi-speedometer"></i></div>Dashboard</a>
<a class="nav-link" href="<?=base_url();?>changePass/"><div class="sb-nav-link-icon"><i class="bi bi-key"></i></div>Change Password</a>
</div>
</div>
<div class="sb-sidenav-footer">
<div class="small">Logged in as: User</div>
</div>
</nav>
</div>

View File

@ -0,0 +1,23 @@
<nav class="sb-topnav navbar navbar-expand navbar-light bg-light shadow-sm text-luxury">
<a class="navbar-brand ps-3 d-none d-md-block" href="#"><img src='<?=base_url();?>assets/img/logo.png' width='30' /> CMOD</a>
<button class="btn btn-link me-2" id="sidebarToggle"><i class="bi bi-list "></i></button>
<div class="ms-auto">
<ul class="navbar-nav ms-md-0 me-3 me-lg-4">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDropdown" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle"></i>
<?=$_SESSION['username'];?>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="#!">Profile</a></li>
<li><a class="dropdown-item" href="#!">Change Password</a></li>
<li><hr class="dropdown-divider" /></li>
<li><a class="dropdown-item" href="<?=base_url();?>auth/logout">Logout</a></li>
</ul>
</li>
</ul>
</div>
</nav>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" type="image/png" sizes="16x16" href="<?=base_url();?>/assets/favicon.png">
<title>Summit CRM</title>
<link href="<?=base_url();?>/assets/style.css" rel="stylesheet">
<link href="<?=base_url();?>/assets/font-awesome-640/css/all.min.css" rel="stylesheet">
<?= $this->renderSection('head'); ?>
</head>
<body class="skin-megna-dark fixed-layout">
<div class="preloader">
<div class="loader">
<div class="loader__figure"></div>
<p class="loader__label">Summit-CRM</p>
</div>
</div>
<div>
<?= $this->renderSection('content'); ?>
</div>
<script src="<?=base_url();?>/assets/jquery/jquery.min.js"></script>
<script src="<?=base_url();?>/assets/bootstrap.bundle.min.js"></script>
<script src="<?=base_url();?>/assets/app.js"></script>
<?= $this->renderSection('script'); ?>
<script>
$(document).ready(function() {
$('.select2').select2({
dropdownAutoWidth : true,
width: '100%',
});
});
</script>
</body>
</html>

BIN
cmod.7z

Binary file not shown.

BIN
cmod.bak

Binary file not shown.

View File

@ -1,56 +0,0 @@
USE [cmod]
GO
/****** Object: Table [dbo].[CM_TUBES] Script Date: 19/11/2024 16:26:14 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CM_TUBES](
[TUBEID] [int] IDENTITY(1,1) NOT NULL,
[ACCESSNUMBER] [varchar](10) NULL,
[SAMPLETYPE] [varchar](10) NULL,
[COLLECTIONDATE] [datetime] NULL,
[COLL_USERID] [varchar](25) NULL,
[COLLSTATUS] [varchar](1) NULL,
[CREATEDATE] [datetime] NULL,
[TUBECOMMENT] [varchar](150) NULL,
CONSTRAINT [PK_TUBES] PRIMARY KEY CLUSTERED
(
[TUBEID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[CM_USERROLES] Script Date: 19/11/2024 16:26:14 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CM_USERROLES](
[USERROLEID] [int] IDENTITY(1,1) NOT NULL,
[USERROLECODE] [varchar](5) NOT NULL,
[USERROLENAME] [varchar](50) NULL,
[CREATEDATE] [datetime] NULL,
CONSTRAINT [PK_CM_USERROLES] PRIMARY KEY CLUSTERED
(
[USERROLEID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[CM_USERS] Script Date: 19/11/2024 16:26:14 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CM_USERS](
[USERID] [varchar](25) NOT NULL,
[PASSWORD] [varchar](100) NULL,
[USERROLEID] [int] NULL,
[CREATEDATE] [datetime] NULL,
CONSTRAINT [PK_USERS] PRIMARY KEY CLUSTERED
(
[USERID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[CM_TUBES] ADD CONSTRAINT [DF_TUBES_STATUS] DEFAULT ((0)) FOR [COLLSTATUS]
GO