Add engineering result dialog and functionality across all roles

This commit is contained in:
mahdahar 2026-02-25 16:38:07 +07:00
parent 4fa33194a5
commit 5f6cf47689
12 changed files with 214 additions and 39 deletions

View File

@ -36,9 +36,10 @@ $routes->group('api', function ($routes) {
// Requests - All Roles (0,1,2,3,4)
$routes->group('requests', ['filter' => 'role:0,1,2,3,4'], function ($routes) {
$routes->get('', 'RequestsController::index');
$routes->get('(:any)/audit', 'ApiRequestsAuditController::show/$1');
$routes->post('validate/(:any)', 'RequestsController::val/$1');
$routes->delete('validate/(:any)', 'RequestsController::unval/$1');
$routes->get('(:any)/audit', 'ApiRequestsAuditController::show/$1');
$routes->post('validate/(:any)', 'RequestsController::val/$1');
$routes->delete('validate/(:any)', 'RequestsController::unval/$1');
$routes->post('(:any)/eng', 'RequestsController::setEngLanguage/$1');
});
// Validate API - Lab (2), Admin (1), Superuser (0)

View File

@ -16,14 +16,16 @@ class RequestsController extends BaseController
// Only allow Lab role (role 2)
if ($userroleid == 2) {
$sql = "SELECT * from GDC_CMOD.dbo.V_DASHBOARD_DEV where
COLLECTIONDATE between '$date1 00:00' and '$date2 23:59'
and ODR_DDATE between '$date1 00:00' and '$date2 23:59'
and (TESTS IS NOT NULL AND TESTS like '%[A-Za-z]%')";
$sql = "SELECT d.*, r.REPORT_LANG from GDC_CMOD.dbo.V_DASHBOARD_DEV d
LEFT JOIN GDC_CMOD.dbo.CM_REQUESTS r ON r.ACCESSNUMBER=d.SP_ACCESSNUMBER
where d.COLLECTIONDATE between '$date1 00:00' and '$date2 23:59'
and d.ODR_DDATE between '$date1 00:00' and '$date2 23:59'
and (d.TESTS IS NOT NULL AND d.TESTS like '%[A-Za-z]%')";
} else {
$sql = "SELECT * from GDC_CMOD.dbo.V_DASHBOARD_DEV where
COLLECTIONDATE between '$date1 00:00' and '$date2 23:59'
and ODR_DDATE between '$date1 00:00' and '$date2 23:59'";
$sql = "SELECT d.*, r.REPORT_LANG from GDC_CMOD.dbo.V_DASHBOARD_DEV d
LEFT JOIN GDC_CMOD.dbo.CM_REQUESTS r ON r.ACCESSNUMBER=d.SP_ACCESSNUMBER
where d.COLLECTIONDATE between '$date1 00:00' and '$date2 23:59'
and d.ODR_DDATE between '$date1 00:00' and '$date2 23:59'";
}
$rows = $db->query($sql)->getResultArray();
@ -132,4 +134,20 @@ class RequestsController extends BaseController
return $this->response->setJSON($data);
}
public function setEngLanguage($accessnumber)
{
$userid = session('userid');
$db = \Config\Database::connect();
// Set REPORT_LANG to 1 (English)
$sql = "UPDATE GDC_CMOD.dbo.CM_REQUESTS SET REPORT_LANG=1 WHERE ACCESSNUMBER='$accessnumber'";
$db->query($sql);
// Log the action
$logAudit = "INSERT INTO GDC_CMOD.dbo.AUDIT_EVENTS (ACCESSNUMBER, EVENT_TYPE, USERID, EVENT_AT) VALUES (?, 'SET_ENG', ?, GETDATE())";
$db->query($logAudit, [$accessnumber, $userid]);
return $this->response->setJSON(['status' => 'success', 'message' => 'English result created']);
}
}

View File

@ -11,6 +11,7 @@ $roleConfig = $config['admin'];
<?= $this->include('shared/dialog_unval'); ?>
<?= $this->include('shared/dialog_audit'); ?>
<?= $this->include('shared/dialog_results_generate'); ?>
<?= $this->include('shared/dialog_eng_result'); ?>
<?= $this->include('shared/dialog_preview'); ?>
</main>
<?= $this->endSection(); ?>

View File

@ -11,6 +11,7 @@ $roleConfig = $config['cs'];
<?= $this->include('shared/dialog_unval'); ?>
<?= $this->include('shared/dialog_audit'); ?>
<?= $this->include('shared/dialog_results_generate'); ?>
<?= $this->include('shared/dialog_eng_result'); ?>
</main>
<?= $this->endSection(); ?>

View File

@ -11,6 +11,7 @@ $roleConfig = $config['lab'];
<?= $this->include('shared/dialog_unval'); ?>
<?= $this->include('shared/dialog_audit'); ?>
<?= $this->include('shared/dialog_results_generate'); ?>
<?= $this->include('shared/dialog_eng_result'); ?>
<?= $this->include('shared/dialog_preview'); ?>
</main>
<?= $this->endSection(); ?>

View File

@ -11,6 +11,7 @@ $roleConfig = $config['phlebo'];
<?= $this->include('shared/dialog_unval'); ?>
<?= $this->include('shared/dialog_audit'); ?>
<?= $this->include('shared/dialog_results_generate'); ?>
<?= $this->include('shared/dialog_eng_result'); ?>
</main>
<?= $this->endSection(); ?>

View File

@ -1,5 +1,5 @@
<div class="card bg-base-100 shadow-xl h-full border border-base-200 overflow-hidden">
<div class="card-body p-0 h-full flex flex-col">
<div class="card bg-base-100 shadow-xl border border-base-200 h-full">
<div class="card-body p-0 flex flex-col h-full">
<!-- Header & Filters -->
<div class="p-4 border-b border-base-200 bg-base-50">
@ -76,7 +76,7 @@
</div>
</div>
<div class="flex-1 overflow-y-auto px-4 pb-4">
<div class="flex-1 px-4 pb-4">
<template x-if="isLoading">
<table class="table table-xs table-zebra w-full">
<thead class="bg-base-100 sticky top-0 z-10">
@ -229,16 +229,16 @@ $previewEnabled = $configFile[$configKey]['previewEnabled'] ?? false;
</button>
</template>
<template x-if="req.VAL1USER && req.VAL2USER">
<div class="dropdown dropdown-end dropdown-hover">
<div class="dropdown dropdown-top dropdown-end dropdown-hover">
<div tabindex="0" role="button"
class="btn btn-xs w-full btn-success text-white">
<i class="fa fa-clipboard-check mr-1"></i>
<span class="text-xs">Ready</span>
</div>
<ul tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-40 p-2 shadow-lg border border-base-300">
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-40 p-2 shadow-lg border border-base-300 text-xs">
<?php else: ?>
<div class="dropdown dropdown-end dropdown-hover">
<div class="dropdown dropdown-top dropdown-end dropdown-hover">
<div tabindex="0" role="button"
class="btn btn-xs w-full"
:class="req.VAL1USER && req.VAL2USER ? 'btn-success text-white' : 'btn-warning'">
@ -246,20 +246,30 @@ $previewEnabled = $configFile[$configKey]['previewEnabled'] ?? false;
<span class="text-xs" x-text="req.VAL1USER && req.VAL2USER ? 'Ready' : 'Pending'"></span>
</div>
<ul tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-40 p-2 shadow-lg border border-base-300">
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-40 p-2 shadow-lg border border-base-300 text-xs">
<?php endif; ?>
<template x-if="req.VAL1USER && req.VAL2USER">
<div>
<li>
<a :href="'<?=base_url('report/');?>' + req.SP_ACCESSNUMBER" target="_blank">
<i class="fa fa-print mr-2"></i> Print
</a>
</li>
<li>
<a :href="'http://glenlis/pdf/' + req.REQDATE.split(' ')[0].split('-').join('/') + '/' + req.SP_ACCESSNUMBER + '.pdf'" target="_blank">
<i class="fa fa-file-pdf mr-2"></i> PDF
</a>
</li>
<div>
<li>
<a :href="'<?=base_url('report/');?>' + req.SP_ACCESSNUMBER" target="_blank">
<i class="fa fa-print mr-2"></i> Print
</a>
</li>
<li>
<a :href="'http://glenlis/pdf/' + req.REQDATE.split(' ')[0].split('-').join('/') + '/' + req.SP_ACCESSNUMBER + '.pdf'" target="_blank">
<i class="fa fa-file-pdf mr-2"></i> PDF
</a>
</li>
<li x-show="req.REPORT_LANG == 1">
<a :href="'<?=base_url('report/');?>' + req.SP_ACCESSNUMBER + '/print/eng'" target="_blank">
<i class="fa fa-print mr-2"></i> Print Eng
</a>
</li>
<li x-show="req.REPORT_LANG == 1">
<a :href="'http://glenlis/pdf/' + req.REQDATE.split(' ')[0].split('-').join('/') + '/' + req.SP_ACCESSNUMBER + '_eng.pdf'" target="_blank">
<i class="fa fa-file-pdf mr-2"></i> PDF Eng
</a>
</li>
<?php if (!in_array(session('userroleid'), [3, 4])): ?>
<li>
<a @click="openGenerateDialog(req.SP_ACCESSNUMBER)">
@ -268,7 +278,13 @@ $previewEnabled = $configFile[$configKey]['previewEnabled'] ?? false;
</a>
</li>
<?php endif; ?>
</div>
<li x-show="!req.REPORT_LANG || req.REPORT_LANG != 1">
<a @click="openEngResultDialog(req)">
<i class="fa fa-language mr-2"></i>
<span>Create Eng Result</span>
</a>
</li>
</div>
</template>
<?php if (!$previewEnabled): ?>
<template x-if="!req.VAL1USER || !req.VAL2USER">
@ -289,12 +305,12 @@ $previewEnabled = $configFile[$configKey]['previewEnabled'] ?? false;
<?php endif; ?>
</td>
<td>
<div class="dropdown dropdown-end dropdown-hover">
<div class="dropdown dropdown-top dropdown-end dropdown-hover">
<div tabindex="0" role="button" class="btn btn-xs btn-primary w-full">
<i class="fa fa-cog mr-1"></i> Actions
</div>
<ul tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-48 p-2 shadow-lg border border-base-300">
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-48 p-2 shadow-lg border border-base-300 text-xs">
<li x-show="req.ISVAL == 1 && req.ISPENDING != 1 && (req.VAL1USER == '<?= session('userid'); ?>' || req.VAL2USER == '<?= session('userid'); ?>')">
<?php if (session()->get('userlevel') <= 1): ?>
<a @click="openUnvalDialog(req.SP_ACCESSNUMBER)" class="text-error hover:bg-error/10">

View File

@ -0,0 +1,72 @@
<dialog class="modal" :open="isDialogEngResultOpen" @keydown.escape="closeEngResultDialog()">
<div class="modal-box w-2/3 max-w-5xl h-[85vh] flex flex-col p-0 overflow-hidden bg-base-100" x-trap.noreturn="isDialogEngResultOpen">
<!-- Header -->
<div class="bg-base-200 p-3 border-b border-base-300">
<div class="flex justify-between items-center">
<div>
<h3 class="font-bold text-lg">Create Eng Result</h3>
<div class="text-sm text-base-content/70 mt-1">
Access#: <span x-text="engResultItem?.SP_ACCESSNUMBER" class="font-mono font-bold"></span>
<span class="mx-2">|</span>
Patient: <span x-text="engResultItem?.PATNAME || engResultItem?.Name"></span>
</div>
</div>
<button class="btn btn-sm btn-ghost" @click="closeEngResultDialog()" aria-label="Close">
<i class="fa fa-times"></i>
</button>
</div>
</div>
<!-- Content -->
<div class="flex-1 flex flex-col min-h-0 p-4">
<p class="text-sm text-base-content/70 mb-3">
Review the English version below. If it looks correct, click Confirm to enable Print Eng and PDF Eng options.
<span x-show="!engResultItem?.VAL1USER || !engResultItem?.VAL2USER" class="text-error ml-2">
<i class="fa fa-exclamation-circle"></i> Request must be validated first
</span>
</p>
<div class="flex-1 relative">
<!-- Loading spinner -->
<template x-if="!isEngResultIframeLoaded">
<div class="absolute inset-0 flex items-center justify-center bg-base-100 z-10">
<span class="loading loading-spinner loading-lg text-primary"></span>
</div>
</template>
<!-- English report iframe -->
<iframe id="eng-result-iframe"
x-ref="engResultIframe"
:src="getEngResultUrl()"
@load="onEngResultIframeLoad()"
class="w-full h-full border border-base-300 rounded"
frameborder="0"></iframe>
</div>
</div>
<!-- Footer -->
<div class="bg-base-200 p-4 border-t border-base-300 flex justify-between items-center">
<div class="text-xs text-base-content/60">
<span x-show="!isEngResultIframeLoaded">Loading preview...</span>
<span x-show="isEngResultIframeLoaded" class="text-success">
<i class="fa fa-check-circle"></i> Preview loaded
</span>
</div>
<div class="flex gap-2">
<button class="btn btn-sm" @click="closeEngResultDialog()">
Cancel
</button>
<button class="btn btn-sm btn-primary"
@click="confirmCreateEngResult()"
:disabled="!isEngResultIframeLoaded || isCreatingEngResult || !engResultItem?.VAL1USER || !engResultItem?.VAL2USER">
<span x-show="!isCreatingEngResult">Confirm</span>
<span x-show="isCreatingEngResult" class="flex items-center gap-2">
<span class="loading loading-spinner loading-xs"></span>
Creating...
</span>
</button>
</div>
</div>
</div>
<div class="modal-backdrop bg-black/30" @click="closeEngResultDialog()"></div>
</dialog>

View File

@ -101,12 +101,14 @@
<h6 class="p-0 m-0">Coll.</h6>
</button>
</template>
<template x-if="sample.tubestatus != 0">
<button class="btn btn-sm btn-error px-2 py-1"
@click="unreceive(sample.sampcode, item.accessnumber)">
<h6 class="p-0 m-0">Un-Recv.</h6>
</button>
</template>
<?php if (in_array(session('userroleid'), [0, 1])): ?>
<template x-if="sample.tubestatus != 0">
<button class="btn btn-sm btn-error px-2 py-1"
@click="unreceive(sample.sampcode, item.accessnumber)">
<h6 class="p-0 m-0">Un-Recv.</h6>
</button>
</template>
<?php endif; ?>
<?php endif; ?>
</td>
<td></td>

View File

@ -36,7 +36,7 @@
</head>
<body class="bg-base-200 h-screen overflow-hidden" x-data="main">
<div class="flex flex-col h-full">
<div class="flex flex-col h-screen">
<!-- Navbar -->
<nav class="navbar bg-base-100 shadow-md px-6 z-20">
<div class='flex-1'>
@ -73,7 +73,9 @@
</nav>
<!-- Page Content -->
<?= $this->renderSection('content'); ?>
<div class="flex-1 overflow-y-auto">
<?= $this->renderSection('content'); ?>
</div>
<?= $this->include('shared/dialog_setPassword'); ?>
<footer class='bg-base-100 p-1 mt-auto'>&copy; <?= date('Y'); ?> - 5Panda</footer>

View File

@ -532,6 +532,65 @@ document.addEventListener('alpine:init', () => {
}
},
// English Result Dialog
isDialogEngResultOpen: false,
engResultAccessnumber: null,
engResultItem: null,
isCreatingEngResult: false,
isEngResultIframeLoaded: false,
openEngResultDialog(item) {
this.engResultItem = item;
this.engResultAccessnumber = item.SP_ACCESSNUMBER;
this.isEngResultIframeLoaded = false;
this.isCreatingEngResult = false;
this.isDialogEngResultOpen = true;
},
closeEngResultDialog() {
this.isDialogEngResultOpen = false;
this.engResultAccessnumber = null;
this.engResultItem = null;
this.isEngResultIframeLoaded = false;
this.isCreatingEngResult = false;
},
getEngResultUrl() {
if (this.engResultAccessnumber) {
return `${BASEURL}report/${this.engResultAccessnumber}?eng=1`;
}
return '';
},
onEngResultIframeLoad() {
this.isEngResultIframeLoaded = true;
},
async confirmCreateEngResult() {
if (!this.engResultAccessnumber || this.isCreatingEngResult) return;
this.isCreatingEngResult = true;
try {
const res = await fetch(`${BASEURL}/api/requests/${this.engResultAccessnumber}/eng`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const data = await res.json();
this.isCreatingEngResult = false;
if (data.status === 'success') {
this.showToast('English result created successfully', 'success');
this.closeEngResultDialog();
this.fetchList();
} else {
this.showToast(data.message || 'Failed to create English result', 'error');
}
} catch (e) {
this.isCreatingEngResult = false;
this.showToast('Failed to create English result', 'error');
}
},
showToast(message, type = 'success') {
const toast = document.createElement('div');
toast.className = `alert alert-${type} fixed top-4 right-4 z-50`;

View File

@ -11,6 +11,7 @@ $roleConfig = $config['superuser'];
<?= $this->include('shared/dialog_unval'); ?>
<?= $this->include('shared/dialog_audit'); ?>
<?= $this->include('shared/dialog_results_generate'); ?>
<?= $this->include('shared/dialog_eng_result'); ?>
<?= $this->include('shared/dialog_preview'); ?>
</main>
<?= $this->endSection(); ?>