229 lines
8.0 KiB
PHP
229 lines
8.0 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="en" data-theme="corporate">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>CMOD</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
|
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/js/all.min.js"></script>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
|
font-size: 0.71rem;
|
|
}
|
|
.navbar {
|
|
padding: 0.2rem 1rem;
|
|
min-height: 0rem;
|
|
}
|
|
.card-body {
|
|
font-size: 0.71rem !important;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-base-200 min-h-screen flex flex-col">
|
|
|
|
<nav class="navbar bg-secondary shadow-sm text-white">
|
|
<div class='flex-1 font-bold'>
|
|
<a class=''>CMOD</a>
|
|
</div>
|
|
<div class="mr-2">
|
|
<a>Hi, lisfse</a>
|
|
</div>
|
|
<div class="dropdown dropdown-end p-0">
|
|
<div tabindex="0" role="button" class="btn btn-sm btn-secondary">Menu</div>
|
|
<ul tabindex="-1" class="dropdown-content menu bg-base-100 rounded-box z-1 w-46 p-2 shadow-sm text-black">
|
|
<li><a>Item 1</a></li>
|
|
<li><a>Item 2</a></li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="p-4 flex-1 flex flex-col gap-2" x-data="dashboard">
|
|
<div class="card bg-base-100">
|
|
<div class="card-body p-3 max-h-full overflow-y-auto">
|
|
<div class="flex gap-1">
|
|
<div class="flex-1 font-bold text-lg">Dashboard</div>
|
|
<div class="flex gap-1">
|
|
<button @click="filterKey = 'Pend'" :class="filterKey === 'Pend' ? 'btn-active' : ''" class="btn btn-outline btn-sm"><span x-text="counters.Pend"></span> Pending</button>
|
|
<button @click="filterKey = 'Coll'" :class="filterKey === 'Coll' ? 'btn-active' : ''" class="btn btn-outline btn-sm btn-secondary"><span x-text="counters.Coll"></span> Collected</button>
|
|
<button @click="filterKey = 'Recv'" :class="filterKey === 'Recv' ? 'btn-active' : ''" class="btn btn-outline btn-sm btn-primary"><span x-text="counters.Recv"></span> Received</button>
|
|
<button @click="filterKey = 'Inc'" :class="filterKey === 'Inc' ? 'btn-active' : ''" class="btn btn-outline btn-sm btn-warning"><span x-text="counters.Inc"></span> Incomplete</button>
|
|
<button @click="filterKey = 'Fin'" :class="filterKey === 'Fin' ? 'btn-active' : ''" class="btn btn-outline btn-sm btn-success"><span x-text="counters.Fin"></span> Final</button>
|
|
<button @click="filterKey = 'Total'" :class="filterKey === 'Total' ? 'btn-active' : ''" class="btn btn-outline btn-sm"><span x-text="counters.Total"></span> Total</button>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-3 mb-2">
|
|
<div class="flex-1 flex gap-2 items-center">
|
|
<div>Date</div>
|
|
<input type="date" class="input input-sm w-39" x-model="filter.date1"/>-
|
|
<input type="date" class="input input-sm w-39" x-model="filter.date2"/>
|
|
<button class="btn btn-sm btn-primary" @click='fetchList()'><i class='fa fa-search'></i>Search</button>
|
|
<button class="btn btn-sm btn-secondary" @click='reset()'><i class='fa fa-refresh'></i>Reset</button>
|
|
</div>
|
|
<div class="flex gap-2 items-center">
|
|
<div>Filter</div>
|
|
<input type="text" class="input input-sm w-39" x-model="filterTable" />
|
|
</div>
|
|
</div>
|
|
<template x-if="list.length">
|
|
<table class="table table-xs table-zebra w-full">
|
|
<thead class="bg-base-100 sticky top-0 z-10">
|
|
<tr>
|
|
<th style='width:7%;'>Order Datetime</th>
|
|
<th style='width:15%;'>Patient Name</th>
|
|
<th style='width:7%;'>No Lab</th>
|
|
<th style='width:7%;'>No Register</th>
|
|
<th style='width:8%;'>Reff</th>
|
|
<th style='width:8%;'>Doctor</th>
|
|
<th style='width:15%;'>Tests</th>
|
|
<th style='width:5%;'>Result To</th>
|
|
<th style='width:5%;'>Validation</th>
|
|
<th style='width:4%;'>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template x-for="req in filtered" :key="req.SP_ACCESSNUMBER">
|
|
<tr class="hover:bg-base-300">
|
|
<td x-text="req.REQDATE"></td>
|
|
<td x-text="req.Name"></td>
|
|
<td x-text="req.SP_ACCESSNUMBER"></td>
|
|
<td x-text="req.HOSTORDERNUMBER"></td>
|
|
<td x-text="req.REFF"></td>
|
|
<td x-text="req.DOC"></td>
|
|
<td x-text="req.TESTS"></td>
|
|
<td x-text="req.ODR_CRESULT_TO"></td>
|
|
<td>
|
|
<div>1: <span x-text="req.val1user"></span></div>
|
|
<div>2: <span x-text="req.val2user"></span></div>
|
|
</td>
|
|
<td><button x-text="req.STATS" class="btn btn-xs"
|
|
:class="statusColor[req.STATS]" @click="openSampleDialog(req.SP_ACCESSNUMBER)"></button></td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<?php echo $this->include('v2/dialog_sample'); ?>
|
|
</main>
|
|
|
|
<footer class='bg-base-100 p-1'>© 2025 - 5Panda</footer>
|
|
|
|
<script>
|
|
window.BASEURL = "<?=base_url();?>";
|
|
</script>
|
|
<script type="module">
|
|
import Alpine from '<?=base_url("js/app.js");?>';
|
|
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data("dashboard", ()=> ({
|
|
// dashboard
|
|
today: "",
|
|
filter: { date1: "", date2: "" },
|
|
list: [],
|
|
counters: { Pend: 0, Coll: 0, Recv: 0, Inc: 0, Fin: 0, Total: 0 },
|
|
statusColor: {
|
|
Pend: 'bg-white text-black font-bold',
|
|
PartColl: 'bg-orange-300 text-white font-bold',
|
|
Coll: 'bg-orange-500 text-white font-bold',
|
|
PartRecv: 'bg-blue-200 text-black font-bold',
|
|
Recv: 'bg-blue-500 text-white font-bold',
|
|
Inc: 'bg-yellow-500 text-white font-bold',
|
|
Fin: 'bg-green-500 text-white font-bold',
|
|
},
|
|
filterTable :"",
|
|
filterKey: 'Total',
|
|
statusMap: {
|
|
Total: [],
|
|
Pend: ['Pend'],
|
|
Coll: ['Coll', 'PartColl'],
|
|
Recv: ['Recv'],
|
|
Inc: ['Inc'],
|
|
Fin: ['Fin'],
|
|
},
|
|
|
|
init() {
|
|
this.today = new Date().toISOString().slice(0, 10);
|
|
this.filter.date1 = "2025-05-05";
|
|
this.filter.date2 = "2025-05-05";
|
|
//this.fetchList();
|
|
//this.filter.date1 = this.today;
|
|
//this.filter.date2 = this.today;
|
|
},
|
|
|
|
fetchList(){
|
|
this.list = [];
|
|
let statusOrder = { Pend: 1, PartColl: 2, Coll: 3, PartRecv: 4, Recv: 5, Inc: 6, Fin: 7 };
|
|
let param = new URLSearchParams(this.filter).toString();
|
|
// reset counters before processing
|
|
for (let k in this.counters) { this.counters[k] = 0; }
|
|
fetch(`${BASEURL}/api/request?${param}`, {
|
|
method: 'GET',
|
|
headers: {'Content-Type': 'application/json'},
|
|
}).then(res => res.json()).then(data => {
|
|
this.list = data.data ?? [];
|
|
this.filterKey = 'Total';
|
|
// count + sort in a single loop
|
|
this.list.forEach(item => {
|
|
if (this.counters[item.STATS] !== undefined) { this.counters[item.STATS]++; this.counters.Total++; }
|
|
else {
|
|
if(item.STATS == 'PartColl') { this.counters.Coll++; }
|
|
else if(item.STATS == 'PartRecv') { this.counters.Recv++; }
|
|
this.counters.Total++;
|
|
}
|
|
});
|
|
this.list.sort((a, b) => {
|
|
let codeA = statusOrder[a.STATS] ?? 0;
|
|
let codeB = statusOrder[b.STATS] ?? 0;
|
|
return codeA - codeB;
|
|
});
|
|
});
|
|
},
|
|
|
|
reset() {
|
|
this.filter.date1 = this.today;
|
|
this.filter.date2 = this.today;
|
|
this.fetchList();
|
|
},
|
|
|
|
get filtered() {
|
|
let data = this.list;
|
|
const valid = this.statusMap[this.filterKey]
|
|
if (valid.length > 0) {
|
|
data = data.filter(i => valid.includes(i.STATS));
|
|
}
|
|
if (this.filterTable) {
|
|
const s = this.filterTable.toLowerCase();
|
|
data = data.filter(i =>
|
|
Object.values(i).some(v =>
|
|
String(v).toLowerCase().includes(s)
|
|
)
|
|
);
|
|
}
|
|
|
|
return data;
|
|
},
|
|
|
|
// sample dialog
|
|
isDialogSampleOpen : false,
|
|
|
|
openSampleDialog (accessnumber) {
|
|
this.isDialogSampleOpen = true;
|
|
},
|
|
|
|
closeSampleDialog () {
|
|
this.isDialogSampleOpen = false;
|
|
},
|
|
|
|
}));
|
|
});
|
|
|
|
Alpine.start();
|
|
</script>
|
|
</body>
|
|
</html>
|