go go alpine
This commit is contained in:
parent
46147de68b
commit
53a9a8db53
@ -52,6 +52,7 @@ $routes->get('api/request/validate/(:any)', 'Request::show/$1');
|
||||
|
||||
$routes->post('api/request/validate/(:any)', 'Request::val/$1');
|
||||
$routes->delete('api/request/validate/(:any)', 'Request::unval/$1');
|
||||
$routes->get('api/request', 'Request::index');
|
||||
|
||||
$routes->get('api/specimen/(:any)', 'Specimen::show/$1');
|
||||
$routes->post('api/specimen/collect/(:any)', 'Specimen::collect/$1');
|
||||
|
||||
@ -15,6 +15,11 @@ class Request extends BaseController {
|
||||
COLLECTIONDATE between '$date1 00:00' and '$date2 23:59'
|
||||
and ODR_DDATE between '$date1 00:00' and '$date2 23:59'";
|
||||
$rows = $db->query($sql)->getResultArray();
|
||||
foreach ($rows as &$row) {
|
||||
$row['COLLECTIONDATE'] = date('Y-m-d H:i', strtotime($row['COLLECTIONDATE']));
|
||||
$row['ODR_DDATE'] = date('Y-m-d H:i', strtotime($row['ODR_DDATE']));
|
||||
$row['REQDATE'] = date('Y-m-d H:i', strtotime($row['REQDATE']));
|
||||
}
|
||||
$data['data'] = $rows;
|
||||
return $this->response->setJSON($data);
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
<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;
|
||||
@ -19,25 +20,164 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar bg-secondary shadow-sm">
|
||||
<div class='flex-1'>
|
||||
<a>CMOD</a>
|
||||
<body class="bg-base-200 min-h-screen flex flex-col">
|
||||
|
||||
<nav class="navbar bg-secondary shadow-sm text-white">
|
||||
<div class='flex-1 '>
|
||||
<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">
|
||||
<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">
|
||||
<h3 class="text-lg font-bold mb-4">Dashboard</h3>
|
||||
<p>This is the main content area.</p>
|
||||
|
||||
<main class="p-4 flex-1" x-data="dashboard">
|
||||
<h3 class="text-lg font-bold mb-1">Dashboard</h3>
|
||||
<div class="card bg-base-200">
|
||||
<div class="card-body p-1">
|
||||
<div class="flex gap-3 mb-2">
|
||||
<div class="flex-1 flex gap-1 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-1 text-right">
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.Pend"></span> Pending</button>
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.PartColl"></span> P. Collected</button>
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.Coll"></span> Collected</button>
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.PartRecv"></span> P. Received</button>
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.Recv"></span> Received</button>
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.Inc"></span> Incomplete</button>
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.Fin"></span> Final</button>
|
||||
<button class="btn btn-outline btn-sm"><span x-text="counters.All"></span> Total</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template x-if="list.length">
|
||||
<div class="card bg-base-100 w-full">
|
||||
<div class="card-body p-1">
|
||||
<table class="table table-sm table-zebra w-full">
|
||||
<thead>
|
||||
<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 list" :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 x-text="req.STATS" :class="statusColor[req.STATS]"></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</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", ()=> ({
|
||||
today: "",
|
||||
filter: { date1: "", date2: "" },
|
||||
list: [],
|
||||
counters: {
|
||||
Pend: 0,
|
||||
PartColl: 0,
|
||||
Coll: 0,
|
||||
PartRecv: 0,
|
||||
Recv: 0,
|
||||
Inc: 0,
|
||||
Fin: 0,
|
||||
},
|
||||
statusColor: {
|
||||
Pend: 'bg-white text-black font-bold',
|
||||
PartColl: 'bg-orange-300 text-black 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',
|
||||
},
|
||||
|
||||
init() {
|
||||
this.today = new Date().toISOString().slice(0, 10);
|
||||
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 ?? [];
|
||||
// count + sort in a single loop
|
||||
this.list.forEach(item => {
|
||||
if (this.counters[item.STATS] !== undefined) { this.counters[item.STATS]++; }
|
||||
});
|
||||
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();
|
||||
}
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
Alpine.start();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
5
public/js/alpine.module.esm.min.js
vendored
Normal file
5
public/js/alpine.module.esm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
public/js/app.js
Normal file
11
public/js/app.js
Normal file
@ -0,0 +1,11 @@
|
||||
import Alpine from "./alpine.module.esm.min.js";
|
||||
import persist from './persist.module.esm.min.js';
|
||||
|
||||
Alpine.plugin(persist)
|
||||
window.Alpine = Alpine
|
||||
|
||||
Alpine.data("main", () => ({
|
||||
|
||||
}));
|
||||
|
||||
export default Alpine;
|
||||
1
public/js/persist.module.esm.min.js
vendored
Normal file
1
public/js/persist.module.esm.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
function m(t){let a=()=>{let r,l;try{l=localStorage}catch(i){console.error(i),console.warn("Alpine: $persist is using temporary storage since localStorage is unavailable.");let e=new Map;l={getItem:e.get.bind(e),setItem:e.set.bind(e)}}return t.interceptor((i,e,o,s,d)=>{let n=r||`_x_${s}`,u=g(n,l)?f(n,l):i;return o(u),t.effect(()=>{let c=e();p(n,c,l),o(c)}),u},i=>{i.as=e=>(r=e,i),i.using=e=>(l=e,i)})};Object.defineProperty(t,"$persist",{get:()=>a()}),t.magic("persist",a),t.persist=(r,{get:l,set:i},e=localStorage)=>{let o=g(r,e)?f(r,e):l();i(o),t.effect(()=>{let s=l();p(r,s,e),i(s)})}}function g(t,a){return a.getItem(t)!==null}function f(t,a){let r=a.getItem(t,a);if(r!==void 0)return JSON.parse(r)}function p(t,a,r){r.setItem(t,JSON.stringify(a))}var b=m;export{b as default,m as persist};
|
||||
Loading…
x
Reference in New Issue
Block a user