399 lines
16 KiB
PHP
399 lines
16 KiB
PHP
<?php
|
|
$configFile = include __DIR__ . '/config.php';
|
|
$roleMap = ['superuser' => 'superuser', 'admin' => 'admin', 'lab analyst' => 'lab', 'phlebotomist' => 'phlebo', 'customer service' => 'cs'];
|
|
$userRole = strtolower(session('userrole') ?? '');
|
|
$configKey = $roleMap[$userRole] ?? '';
|
|
$previewEnabled = $configFile[$configKey]['previewEnabled'] ?? false;
|
|
$userRoleId = (int) session('userroleid');
|
|
$userId = (string) session('userid');
|
|
$userLevel = (int) session()->get('userlevel');
|
|
$isPhlebo = $userRoleId === 3;
|
|
$isAdminSuper = !in_array($userRoleId, [3, 4]);
|
|
$canUnval = $userLevel <= 1;
|
|
?>
|
|
<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">
|
|
<div class="flex flex-col md:flex-row justify-between items-center gap-4 mb-4">
|
|
<div class="flex-1">
|
|
<h2 class="text-2xl font-bold flex items-center gap-2 text-base-content">
|
|
<i class="fa fa-chart-bar text-primary"></i> Requests Overview
|
|
</h2>
|
|
</div>
|
|
|
|
<!-- Status Filters -->
|
|
<div class="join shadow-sm bg-base-100 rounded-lg">
|
|
<button @click="setFilterKey('Total')"
|
|
:class="filterKey === 'Total' ? 'btn-active btn-neutral text-white' : 'btn-ghost'"
|
|
class="btn btn-sm join-item">
|
|
All <span class="badge badge-sm badge-ghost ml-1" x-text="counters.Total"></span>
|
|
</button>
|
|
<button @click="setFilterKey('Pend')"
|
|
:class="filterKey === 'Pend' ? 'btn-active btn-status-pend' : 'btn-ghost'"
|
|
class="btn btn-sm join-item">
|
|
Pending <span class="badge badge-sm badge-status-pend ml-1" x-text="counters.Pend"></span>
|
|
</button>
|
|
<button @click="setFilterKey('Coll')"
|
|
:class="filterKey === 'Coll' ? 'btn-active btn-status-coll' : 'btn-ghost'"
|
|
class="btn btn-sm join-item">
|
|
Coll <span class="badge badge-sm badge-status-coll ml-1" x-text="counters.Coll"></span>
|
|
</button>
|
|
<button @click="setFilterKey('Recv')"
|
|
:class="filterKey === 'Recv' ? 'btn-active btn-status-recv' : 'btn-ghost'" class="btn btn-sm join-item">
|
|
Recv <span class="badge badge-sm badge-status-recv ml-1" x-text="counters.Recv"></span>
|
|
</button>
|
|
<button @click="setFilterKey('Inc')"
|
|
:class="filterKey === 'Inc' ? 'btn-active btn-status-inc' : 'btn-ghost'" class="btn btn-sm join-item">
|
|
Inc <span class="badge badge-sm badge-status-inc ml-1" x-text="counters.Inc"></span>
|
|
</button>
|
|
<button @click="setFilterKey('Fin')"
|
|
:class="filterKey === 'Fin' ? 'btn-active btn-status-fin' : 'btn-ghost'"
|
|
class="btn btn-sm join-item">
|
|
Fin <span class="badge badge-sm badge-status-fin ml-1" x-text="counters.Fin"></span>
|
|
</button>
|
|
<button @click="setFilterKey('Validated')"
|
|
:class="filterKey === 'Validated' ? 'btn-active btn-primary text-white' : 'btn-ghost'"
|
|
class="btn btn-sm join-item">
|
|
Val <span class="badge badge-sm badge-primary ml-1" x-text="validatedCount"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search & Date Filter -->
|
|
<div
|
|
class="flex flex-col md:flex-row gap-3 items-end bg-base-100 p-3 rounded-lg border border-base-200 shadow-sm">
|
|
<div class="form-control">
|
|
<label class="label text-xs font-bold py-1 text-base-content/60">Date Range</label>
|
|
<div class="join">
|
|
<input type="date" class="input input-sm input-bordered join-item" x-model="filter.date1" />
|
|
<span class="join-item btn btn-sm btn-ghost no-animation bg-base-200 font-normal px-2">-</span>
|
|
<input type="date" class="input input-sm input-bordered join-item" x-model="filter.date2" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex gap-2">
|
|
<button class="btn btn-sm btn-primary" @click='currentPage = 1; fetchList()'><i class='fa fa-search'></i> Search</button>
|
|
<button class="btn btn-sm btn-neutral" @click='reset()'><i class='fa fa-sync-alt'></i> Reset</button>
|
|
</div>
|
|
|
|
<span class="flex-1"></span>
|
|
|
|
<div class="form-control w-full md:w-auto">
|
|
<label class='input input-sm input-bordered'>
|
|
<i class="fa fa-filter"></i>
|
|
<input type="text" placeholder="Type to filter..." x-model="filterTable" @input.debounce.300ms="setFilterTable(filterTable)" />
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<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">
|
|
<tr>
|
|
<th style='width:9%;'>
|
|
<div class="skeleton h-4 w-20"></div>
|
|
</th>
|
|
<th style='width:7%;'>
|
|
<div class="skeleton h-4 w-16"></div>
|
|
</th>
|
|
<th style='width:18%;'>
|
|
<div class="skeleton h-4 w-32"></div>
|
|
</th>
|
|
<th style='width:9%;'>
|
|
<div class="skeleton h-4 w-16"></div>
|
|
</th>
|
|
<th style='width:9%;'>
|
|
<div class="skeleton h-4 w-16"></div>
|
|
</th>
|
|
<th style='width:9%;'>
|
|
<div class="skeleton h-4 w-16"></div>
|
|
</th>
|
|
<th style='width:9%;'>
|
|
<div class="skeleton h-4 w-20"></div>
|
|
</th>
|
|
<th style='width:20%;'>
|
|
<div class="skeleton h-4 w-32"></div>
|
|
</th>
|
|
<th style='width:4%;'>
|
|
<div class="skeleton h-4 w-12"></div>
|
|
</th>
|
|
<th style='width:8%;'>
|
|
<div class="skeleton h-4 w-16"></div>
|
|
</th>
|
|
<th style='width:5%;'>
|
|
<div class="skeleton h-4 w-12"></div>
|
|
</th>
|
|
<th style='width:5%;'>
|
|
<div class="skeleton h-4 w-12"></div>
|
|
</th>
|
|
<th style='width:5%;'>
|
|
<div class="skeleton h-4 w-12"></div>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template x-for="i in 5" :key="i">
|
|
<tr>
|
|
<td colspan="13">
|
|
<div class="skeleton h-4 w-full"></div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</template>
|
|
<template x-if="!isLoading && !filteredRows.length">
|
|
<div class="text-center py-10">
|
|
<i class="fa fa-inbox text-4xl mb-2 opacity-50"></i>
|
|
<p>No records found</p>
|
|
</div>
|
|
</template>
|
|
<template x-if="!isLoading && filteredRows.length">
|
|
<table class="table table-xs table-zebra w-full">
|
|
<thead class="bg-base-100 sticky top-0 z-10">
|
|
<tr>
|
|
<th style='width:9%;' @click="sort('REQDATE')"
|
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
|
<div class="flex items-center gap-1">
|
|
Order Datetime
|
|
<i class="fa text-xs"
|
|
:class="sortCol === 'REQDATE' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
|
</div>
|
|
</th>
|
|
<th style='width:7%;' @click="sort('STATS')"
|
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
|
<div class="flex items-center gap-1">
|
|
Status
|
|
<i class="fa text-xs"
|
|
:class="sortCol === 'STATS' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
|
</div>
|
|
</th>
|
|
<th style='width:18%;' @click="sort('Name')"
|
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
|
<div class="flex items-center gap-1">
|
|
Patient Name
|
|
<i class="fa text-xs"
|
|
:class="sortCol === 'Name' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
|
</div>
|
|
</th>
|
|
<th style='width:9%;' @click="sort('SP_ACCESSNUMBER')"
|
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
|
<div class="flex items-center gap-1">
|
|
No Lab
|
|
<i class="fa text-xs"
|
|
:class="sortCol === 'SP_ACCESSNUMBER' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
|
</div>
|
|
</th>
|
|
<th style='width:9%;' @click="sort('HOSTORDERNUMBER')"
|
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
|
<div class="flex items-center gap-1">
|
|
No Register
|
|
<i class="fa text-xs"
|
|
:class="sortCol === 'HOSTORDERNUMBER' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
|
</div>
|
|
</th>
|
|
<th style='width:9%;' @click="sort('REFF')"
|
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
|
<div class="flex items-center gap-1">
|
|
Reff
|
|
<i class="fa text-xs"
|
|
:class="sortCol === 'REFF' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
|
</div>
|
|
</th>
|
|
<th style='width:9%;' @click="sort('DOC')"
|
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
|
<div class="flex items-center gap-1">
|
|
Doctor
|
|
<i class="fa text-xs"
|
|
:class="sortCol === 'DOC' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
|
</div>
|
|
</th>
|
|
<th style='width:20%;'>Tests</th>
|
|
<th style='width:4%;'>ResTo</th>
|
|
<th style='width:5%;'>Val</th>
|
|
<th style='width:3%;'>PDF</th>
|
|
<th style='width:5%;'>Result</th>
|
|
<th style='width:5%;'></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template x-for="req in filteredRows" :key="req.SP_ACCESSNUMBER">
|
|
<tr class="hover:bg-base-300">
|
|
<td x-text="req.REQDATE" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.STATS" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.Name" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.SP_ACCESSNUMBER" class="font-bold" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.HOSTORDERNUMBER" class="font-bold" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.REFF" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.DOC" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.TESTS" :class="statusRowBg[req.STATS]"></td>
|
|
<td x-text="req.ODR_CRESULT_TO"></td>
|
|
<td>
|
|
<div class='text-xs'>
|
|
<p>1: <span x-text="req.VAL1USER"></span></p>
|
|
<p>2: <span x-text="req.VAL2USER"></span></p>
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<i class="fa fa-file-pdf" :class="req.ISPDF ? 'text-success' : 'text-base-300'"></i>
|
|
</td>
|
|
<td>
|
|
<?php if ($isPhlebo): ?>
|
|
<span class="text-xs font-bold" :class="req.VAL1USER && req.VAL2USER ? 'text-success' : 'text-warning'" x-text="req.VAL1USER && req.VAL2USER ? 'Ready' : 'Pending'"></span>
|
|
<?php else: ?>
|
|
<?php if ($previewEnabled): ?>
|
|
<template x-if="['Pend', 'PartColl', 'Coll'].includes(req.STATS)">
|
|
<button disabled class="btn btn-xs w-full btn-warning opacity-70 cursor-not-allowed">
|
|
<i class="fa fa-clock mr-1"></i>
|
|
<span class="text-xs">Pending</span>
|
|
</button>
|
|
</template>
|
|
<template x-if="!['Pend', 'PartColl', 'Coll'].includes(req.STATS) && (!req.VAL1USER || !req.VAL2USER)">
|
|
<button @click="openPreviewDialog(req)"
|
|
class="btn btn-xs w-full btn-warning">
|
|
<i class="fa fa-eye mr-1"></i>
|
|
<span class="text-xs">Preview</span>
|
|
</button>
|
|
</template>
|
|
<template x-if="req.VAL1USER && req.VAL2USER">
|
|
<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-50 w-40 p-2 shadow-lg border border-base-300 text-xs">
|
|
<?php else: ?>
|
|
<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'">
|
|
<i class="fa fa-clipboard-check mr-1"></i>
|
|
<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-50 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://192.168.0.254/actlab/pdf/view.php?regno=' + req.HOSTORDERNUMBER" 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://192.168.0.254/actlab/pdf-en/view.php?regno=' + req.HOSTORDERNUMBER" target="_blank">
|
|
<i class="fa fa-file-pdf mr-2"></i> PDF Eng
|
|
</a>
|
|
</li>
|
|
<?php if ($isAdminSuper): ?>
|
|
<li>
|
|
<a @click="openGenerateDialog(req.SP_ACCESSNUMBER)">
|
|
<i class="fa fa-file-pdf mr-2"></i>
|
|
<span>Generate Result</span>
|
|
</a>
|
|
</li>
|
|
<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>
|
|
<?php endif; ?>
|
|
</div>
|
|
</template>
|
|
<?php if (!$previewEnabled): ?>
|
|
<template x-if="!req.VAL1USER || !req.VAL2USER">
|
|
<div>
|
|
<li class="disabled opacity-50 cursor-not-allowed">
|
|
<span class="flex items-center">
|
|
<i class="fa fa-clock mr-2"></i> Result Pending
|
|
</span>
|
|
</li>
|
|
</div>
|
|
</template>
|
|
<?php endif; ?>
|
|
</ul>
|
|
<?php if ($previewEnabled): ?>
|
|
</template>
|
|
<?php else: ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<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-50 w-48 p-2 shadow-lg border border-base-300 text-xs">
|
|
<?php if ($isAdminSuper): ?>
|
|
<li x-show="(req.VAL1USER && req.VAL2USER) && req.ISPENDING != 1">
|
|
<?php if ($canUnval): ?>
|
|
<a @click="openUnvalDialog(req.SP_ACCESSNUMBER)" class="text-error hover:bg-error/10">
|
|
<i class="fa fa-times-circle mr-2"></i> UnVal
|
|
</a>
|
|
<?php endif; ?>
|
|
</li>
|
|
<?php endif; ?>
|
|
<li>
|
|
<a @click="openSampleDialog(req.SP_ACCESSNUMBER)">
|
|
<i class="fa fa-vial mr-2 text-success"></i> View Samples
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a @click="openAuditDialog(req.SP_ACCESSNUMBER)">
|
|
<i class="fa fa-history mr-2 text-info"></i> View Audit Trail
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Pagination Control -->
|
|
<div class="p-2 border-t border-base-200 bg-base-50 flex justify-between items-center"
|
|
x-show="!isLoading && totalRows > 0">
|
|
<div class="text-xs text-base-content/60">
|
|
Showing <span class="font-bold" x-text="((currentPage - 1) * pageSize) + 1"></span> to
|
|
<span class="font-bold" x-text="Math.min(currentPage * pageSize, totalRows)"></span> of
|
|
<span class="font-bold" x-text="totalRows"></span> entries
|
|
</div>
|
|
<div class="join">
|
|
<button class="join-item btn btn-sm" @click="prevPage()" :disabled="currentPage === 1">
|
|
<i class="fa fa-chevron-left"></i>
|
|
</button>
|
|
<button class="join-item btn btn-sm no-animation bg-base-100 cursor-default">
|
|
Page <span x-text="currentPage"></span> / <span x-text="totalPages"></span>
|
|
</button>
|
|
<button class="join-item btn btn-sm" @click="nextPage()" :disabled="currentPage === totalPages">
|
|
<i class="fa fa-chevron-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|