Update Merge p0_zaka dan Haris
This commit is contained in:
commit
a81a919d68
86
CHECKLIST.md
Normal file
86
CHECKLIST.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# Project Checklist: Glen RME & Lab Management System
|
||||||
|
|
||||||
|
**Last Updated:** January 19, 2026
|
||||||
|
**Source:** PROJECT_BACKLOG.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0 - Critical (Access Control & Security)
|
||||||
|
|
||||||
|
_Must be completed first to ensure basic process flow is correct._
|
||||||
|
|
||||||
|
- [ ] **T-002:** Hide/Disable 'Validation' button after 2nd validation
|
||||||
|
- Prevent redundant validation actions
|
||||||
|
- [ ] **T-003:** Restrict Print/Save-to-PDF to CS Role only
|
||||||
|
- Lab can only preview, CS can print/save
|
||||||
|
- [ ] **T-004:** Update User Role levels
|
||||||
|
- Standardize roles: Superuser, Admin, Lab, Phlebo, CS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P1 - High (Dashboard & UI Improvements)
|
||||||
|
|
||||||
|
_Features that improve speed and correctness of lab operations._
|
||||||
|
|
||||||
|
- [ ] **T-005:** Role-Based Dashboard Filtering
|
||||||
|
- Filter by patient_status or service_type (Klinik+Lab vs Lab Only)
|
||||||
|
- [ ] **T-006:** Create Clinical Patients Dashboard
|
||||||
|
- Hide "No Lab" column for clinical workflows
|
||||||
|
- [X] **T-007:** Fix Table Sorting
|
||||||
|
- Enable sorting by "No Register" and "Patient Name"
|
||||||
|
- [X] **T-008:** Fix Language Toggle (ID/EN)
|
||||||
|
- Toggle lab result preview between Indonesian and English
|
||||||
|
- [X] **T-009:** Apply Row Color-Coding
|
||||||
|
- Color-code "No Register" column (Yellow/Blue/Green)
|
||||||
|
- [X] **T-011:** Initialize RME Sidebar Menu
|
||||||
|
- Create menu items: Dashboard, Patient, Hasil Lab, Validation, Unreceived, Report, Sample Collection, User Management, Unvalidate
|
||||||
|
- [ ] **T-012:** Create 'Detail Unvalidated' History Log/View
|
||||||
|
- Log unvalidation actions with timestamp, user ID, and reason
|
||||||
|
- [ ] **T-013:** Enhanced Patient Detail Logging
|
||||||
|
- Track: Sample Collection Time, Sample Received Time, Print History
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P2 - Medium (Maintenance & UX)
|
||||||
|
|
||||||
|
_UI improvements and backend optimizations._
|
||||||
|
|
||||||
|
- [ ] **T-014:** Add Dedicated Print Button
|
||||||
|
- Trigger browser/system print dialog
|
||||||
|
- [ ] **T-015:** Add Error Handling for Preview Button
|
||||||
|
- Handle empty data gracefully
|
||||||
|
- [ ] **T-016:** Ensure 'Uncollect' Feature Functional
|
||||||
|
- Maintain Uncollect feature functionality
|
||||||
|
- [ ] **T-017:** Backend Performance & Connectivity
|
||||||
|
- Investigate intermittent connection issues with Server 253
|
||||||
|
- Plan SSD upgrade for database server
|
||||||
|
- Verify API integration: GDC_cmod, GDC_CS2, Report2
|
||||||
|
- [X] **T-018: Delayed** Dashboard Performance
|
||||||
|
- When getting data more than 100 rows, it load too slow.
|
||||||
|
- Answer : Its Alpine Limitation, later will create pagination for dashboard.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P3 - Onsite / Onprem Tasks
|
||||||
|
|
||||||
|
- [ ] **T-010:** Update PDF Report Metadata
|
||||||
|
- Replace 'Printed By' with validating user's name
|
||||||
|
- Add 'Finish Validation' status per sample
|
||||||
|
|
||||||
|
---
|
||||||
|
## Quick Progress Summary
|
||||||
|
|
||||||
|
| Priority | Total | Completed |
|
||||||
|
|----------|-------|-----------|
|
||||||
|
| P0 - Critical | 4 | 0 |
|
||||||
|
| P1 - High | 9 | 0 |
|
||||||
|
| P2 - Medium | 4 | 0 |
|
||||||
|
| **Total** | **17** | **0** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Legend
|
||||||
|
|
||||||
|
- Tasks are ordered by priority (P0 → P1 → P2)
|
||||||
|
- Check items as you complete them
|
||||||
|
- Refer to PROJECT_BACKLOG.md for detailed technical specifications
|
||||||
@ -1,120 +0,0 @@
|
|||||||
# Project Backlog: Glen RME & Lab Management System
|
|
||||||
|
|
||||||
**Last Updated:** January 19, 2026
|
|
||||||
**Sources:** TODO.md, TODO.json
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P0 - Critical (Access Control & Security)
|
|
||||||
|
|
||||||
_Must be completed first to ensure basic process flow is correct._
|
|
||||||
|
|
||||||
| ID | Task | Source | Status |
|
|
||||||
|----|------|--------|--------|
|
|
||||||
| T-001 | **Restrict 'Unvalidate' button to specific User IDs**<br><br>User Story: As an Admin, I want an "Unvalidate" menu to revert finalized records in case of data entry errors.<br><br>Technical: Only Doctors and Bu Yani should have access. | Both | Pending |
|
|
||||||
| T-002 | **Hide/Disable 'Validation' button after 2nd validation**<br><br>User Story: As a Lab Validator, I want the "Validate" button to disappear or disable once 2-level validation is complete to prevent redundant actions. | Both | Pending |
|
|
||||||
| T-003 | **Restrict Print/Save-to-PDF to CS Role only**<br><br>User Story: As a Manager, I want to restrict the "Print Result" permission to the CS Role only, so that the Lab team cannot bypass the official release process.<br><br>Technical: Lab can only preview, CS can print/save. | Both | Pending |
|
|
||||||
| T-004 | **Update User Role levels**<br><br>Technical: Standardize roles to: Superuser, Admin, Lab, Phlebo, and CS. | JSON | Pending |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P1 - High (Dashboard & UI Improvements)
|
|
||||||
|
|
||||||
_Features that improve speed and correctness of lab operations._
|
|
||||||
|
|
||||||
| ID | Task | Source | Status |
|
|
||||||
|----|------|--------|--------|
|
|
||||||
| T-005 | **Role-Based Dashboard Filtering**<br><br>User Story: As a Lab Staff, I want the dashboard to only show "Klinik+Lab" or "Lab Only" patients so I can focus on relevant tasks.<br><br>User Story: As a CS Staff, I want to see all patients to monitor the entire facility flow.<br><br>Technical: Implement filter logic based on patient_status or service_type field. | MD | Pending |
|
|
||||||
| T-006 | **Create Clinical Patients Dashboard**<br><br>Technical: Dedicated dashboard that hides the "No Lab" column for clinical workflows. | JSON | Pending |
|
|
||||||
| T-007 | **Fix Table Sorting**<br><br>User Story: As a User, I want to sort dashboard tables by "No Register" and "Patient Name" to find specific records quickly.<br><br>Technical: Fix sorting functionality on all table headers. | Both | Pending |
|
|
||||||
| T-008 | **Fix Language Toggle (ID/EN)**<br><br>User Story: As a Lab Staff, I want to toggle the lab result preview between Indonesian and English so I can provide reports for international requirements. | Both | Pending |
|
|
||||||
| T-009 | **Apply Row Color-Coding**<br><br>User Story: As a User, I want the "No Register" column to be color-coded (Yellow/Blue/Green) based on legacy system logic for quick status recognition. | Both | Pending |
|
|
||||||
| T-010 | **Update PDF Report Metadata**<br><br>Technical: Replace 'Printed By' with name of validating user. Add 'Finish Validation' status per sample in PDF output. | JSON | Pending |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P1 - High (RME Module Development)
|
|
||||||
|
|
||||||
| ID | Task | Source | Status |
|
|
||||||
|----|------|--------|--------|
|
|
||||||
| T-011 | **Initialize RME Sidebar Menu**<br><br>Technical: Create menu items for Dashboard, Patient, Hasil Lab, Validation, Unreceived, Report, Sample Collection, User Management, and Unvalidate. | JSON | Pending |
|
|
||||||
| T-012 | **Create 'Detail Unvalidated' History Log/View**<br><br>User Story: As a User, I want the "Reason for Unvalidation" to be visible in the Patient Detail view so I know why a record was reopened.<br><br>Technical: Log all unvalidation actions with timestamp, user ID, and reason. | Both | Pending |
|
|
||||||
| T-013 | **Enhanced Patient Detail Logging**<br><br>User Story: As a Staff member, I want to see a detailed history in the patient profile including:<br><br>- Sample Collection Time (categorized by type: EDTA, Serum, etc.)<br>- Sample Received Time<br>- Print History (Who printed and when) | MD | Pending |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P2 - Medium (Maintenance & UX)
|
|
||||||
|
|
||||||
_UI improvements and backend optimizations._
|
|
||||||
|
|
||||||
| ID | Task | Source | Status |
|
|
||||||
|----|------|--------|--------|
|
|
||||||
| T-014 | **Add Dedicated Print Button**<br><br>User Story: As a User, I want a clear "Print" button that triggers the browser/system print dialog, as standard shortcuts (Ctrl+P) are currently unreliable in the app. | MD | Pending |
|
|
||||||
| T-015 | **Add Error Handling for Preview Button**<br><br>Technical: Ensure Preview doesn't crash when data is empty. | JSON | Pending |
|
|
||||||
| T-016 | **Ensure 'Uncollect' Feature Functional**<br><br>Technical: Maintain Uncollect feature functionality in current phase. | JSON | Pending |
|
|
||||||
| T-017 | **Backend Performance & Connectivity**<br><br>Technical Tasks:<br>- Investigate intermittent connection issues with Server 253<br>- Plan and execute SSD Upgrade for database server<br>- Verify API integration between GDC_cmod, GDC_CS2, and Report2 for sample reception module | MD | Pending |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Management Notes
|
|
||||||
|
|
||||||
| ID | Note | Source |
|
|
||||||
|----|------|--------|
|
|
||||||
| NOTE-001 | Mas Rizqi is the IT person in charge. | JSON |
|
|
||||||
| NOTE-002 | Merging of 'Hasil' and 'Validation' menus is pending management approval. | JSON |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Acceptance Criteria Summary
|
|
||||||
|
|
||||||
### Code Consistency
|
|
||||||
- All new UI elements (buttons/toggles) must match the existing design system.
|
|
||||||
|
|
||||||
### Audit Trail
|
|
||||||
- Every status change (Validate/Unvalidate) must be logged with a timestamp and user ID.
|
|
||||||
|
|
||||||
### Cross-Browser
|
|
||||||
- The "Print" functionality must work across Chrome and Edge browsers.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Source Mapping Appendix
|
|
||||||
|
|
||||||
| Task ID | Source | Original Description |
|
|
||||||
|---------|--------|---------------------|
|
|
||||||
| T-001 | TODO.json | Restrict 'Unvalidate' button to specific User IDs |
|
|
||||||
| T-002 | Both | Hide/disable Validation button after 2nd validation |
|
|
||||||
| T-003 | Both | Restrict Print/Save-to-PDF to CS Role only |
|
|
||||||
| T-004 | TODO.json | Update User Role levels |
|
|
||||||
| T-005 | TODO.md | Role-Based Dashboard Filtering |
|
|
||||||
| T-006 | TODO.json | Create Clinical Patients Dashboard |
|
|
||||||
| T-007 | Both | Fix Table Sorting |
|
|
||||||
| T-008 | Both | Fix Language Toggle (ID/EN) |
|
|
||||||
| T-009 | Both | Apply row color-coding |
|
|
||||||
| T-010 | TODO.json | Update PDF reports metadata |
|
|
||||||
| T-011 | TODO.json | Initialize RME Sidebar Menu |
|
|
||||||
| T-012 | Both | Detail Unvalidated history log/view |
|
|
||||||
| T-013 | TODO.md | Enhanced Patient Detail Logging |
|
|
||||||
| T-014 | TODO.md | Dedicated Print Button |
|
|
||||||
| T-015 | TODO.json | Error handling for Preview button |
|
|
||||||
| T-016 | TODO.json | Ensure Uncollect feature functional |
|
|
||||||
| T-017 | TODO.md | Backend Performance & Connectivity |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Reference: Task Count by Priority
|
|
||||||
|
|
||||||
| Priority | Count | Items |
|
|
||||||
|----------|-------|-------|
|
|
||||||
| P0 - Critical | 4 | T-001 through T-004 |
|
|
||||||
| P1 - High | 9 | T-005 through T-013 |
|
|
||||||
| P2 - Medium | 4 | T-014 through T-017 |
|
|
||||||
| **Total** | **17** | |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Legend
|
|
||||||
|
|
||||||
- **MD** = TODO.md (User story focused)
|
|
||||||
- **JSON** = TODO.json (Developer task focused)
|
|
||||||
- **Both** = Content merged from both sources
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace App\Controllers;
|
|
||||||
use CodeIgniter\API\ResponseTrait;
|
|
||||||
|
|
||||||
class Request extends BaseController {
|
|
||||||
use ResponseTrait;
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
$db = db_connect();
|
|
||||||
$date1 = $this->request->getGet('date1');
|
|
||||||
$date2 = $this->request->getGet('date2');
|
|
||||||
|
|
||||||
$db = \Config\Database::connect();
|
|
||||||
$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'";
|
|
||||||
$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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function show($accessnumber) {
|
|
||||||
$db = db_connect();
|
|
||||||
$data['accessnumber'] = $accessnumber;
|
|
||||||
$sql = "SELECT d.STATS, r.* FROM GDC_CMOD.dbo.V_DASHBOARD_DEV d
|
|
||||||
left join GDC_CMOD.dbo.CM_REQUESTS r ON r.ACCESSNUMBER=d.SP_ACCESSNUMBER
|
|
||||||
WHERE d.SP_ACCESSNUMBER='$accessnumber'";
|
|
||||||
$result = $db
|
|
||||||
->query($sql)
|
|
||||||
->getResultArray();
|
|
||||||
$data['val1'] = $result[0]['ISVAL1'];
|
|
||||||
$data['val1user'] = $result[0]['VAL1USER'];
|
|
||||||
$data['val2'] = $result[0]['ISVAL2'];
|
|
||||||
$data['val2user'] = $result[0]['VAL2USER'];
|
|
||||||
return view('admin/modal_request',$data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function showUnval($accessnumber) {
|
|
||||||
$data['accessnumber'] = $accessnumber;
|
|
||||||
return view('admin/modal_unvalidate',$data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unval($accessnumber) {
|
|
||||||
$input = $this->request->getJSON(true);
|
|
||||||
$userid = $input['userid'];
|
|
||||||
$comment = $input['comment'];
|
|
||||||
$db = db_connect();
|
|
||||||
$sql = "update GDC_CMOD.dbo.CM_REQUESTS set ISVAL1=null, VAL1USER=null, VAL1DATE=null, ISVAL2=null, VAL2USER=null, VAL2DATE=null,
|
|
||||||
ISPENDING=1, PENDINGTEXT='$comment', PENDINGUSER='$userid', PENDINGDATE=GETDATE() where ACCESSNUMBER='$accessnumber'";
|
|
||||||
$db->query($sql);
|
|
||||||
$data = ['status' => 'success', 'message' => 'Data updated successfully', 'data' => "$accessnumber" ];
|
|
||||||
|
|
||||||
return $this->response->setJSON($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function val($accessnumber) {
|
|
||||||
$input = $this->request->getJSON(true);
|
|
||||||
$userid = $input['userid'];
|
|
||||||
$db = db_connect();
|
|
||||||
//cek val
|
|
||||||
$sql = "select * from GDC_CMOD.dbo.CM_REQUESTS where ACCESSNUMBER='$accessnumber'";
|
|
||||||
$result = $db->query($sql)->getResultArray();
|
|
||||||
//$data['data'] = $result;
|
|
||||||
if(!isset($result[0])) {
|
|
||||||
$sql = "insert into GDC_CMOD.dbo.CM_REQUESTS(ACCESSNUMBER, ISVAL1, VAL1USER, VAL1DATE) VALUES ('$accessnumber', 1, '$userid', GETDATE())";
|
|
||||||
$db->query($sql);
|
|
||||||
$data['val'] = 1;
|
|
||||||
$data['userid'] = $userid;
|
|
||||||
} else {
|
|
||||||
$row = $result[0];
|
|
||||||
$isval1 = $row['ISVAL1'];
|
|
||||||
$isval2 = $row['ISVAL2'];
|
|
||||||
$val1user = $row['VAL1USER'];
|
|
||||||
if( $isval1 == 1 ) {
|
|
||||||
// val done
|
|
||||||
if ( $isval2 == 1 ) { return $this->response->setJSON(['message'=> 'validation done, not updating anything']); }
|
|
||||||
else {
|
|
||||||
// val2 if user val1 != userid
|
|
||||||
if($val1user != $userid) {
|
|
||||||
$sql = "update GDC_CMOD.dbo.CM_REQUESTS set ISVAL2=1, VAL2USER='$userid', VAL2DATE=GETDATE() where ACCESSNUMBER='$accessnumber'";
|
|
||||||
$data['val'] = 2;
|
|
||||||
$data['userid'] = $userid;
|
|
||||||
} else {
|
|
||||||
$this->response->setStatusCode(500);
|
|
||||||
return $this->response->setJSON([ 'message'=> 'user already validate this request' ]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// val1
|
|
||||||
$sql = "update GDC_CMOD.dbo.CM_REQUESTS set ISVAL1=1, VAL1USER='$userid', VAL1DATE=GETDATE() where ACCESSNUMBER='$accessnumber'";
|
|
||||||
$data['val'] = 1;
|
|
||||||
$data['userid'] = $userid;
|
|
||||||
}
|
|
||||||
$db->query($sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->response->setJSON($data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,11 +9,19 @@
|
|||||||
<span class="badge badge-ghost text-xs" x-text="previewAccessnumber"></span>
|
<span class="badge badge-ghost text-xs" x-text="previewAccessnumber"></span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="join shadow-sm">
|
<div class="join shadow-sm" x-show="previewItem && previewItem.VAL1USER && previewItem.VAL2USER">
|
||||||
<button @click="previewType = 'preview'" :class="previewType === 'preview' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">Preview</button>
|
<button @click="setPreviewType('preview')"
|
||||||
<button @click="setPreviewType('ind')" :class="previewType === 'ind' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">IND</button>
|
:class="previewType === 'preview' ? 'btn-active btn-neutral text-white' : 'btn-ghost'"
|
||||||
<button @click="setPreviewType('eng')" :class="previewType === 'eng' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">ENG</button>
|
class="btn btn-sm join-item">Default</button>
|
||||||
<button @click="setPreviewType('pdf')" :class="previewType === 'pdf' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">PDF</button>
|
<button @click="setPreviewType('ind')"
|
||||||
|
:class="previewType === 'ind' ? 'btn-active btn-neutral text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">ID</button>
|
||||||
|
<button @click="setPreviewType('eng')"
|
||||||
|
:class="previewType === 'eng' ? 'btn-active btn-neutral text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">EN</button>
|
||||||
|
<button @click="setPreviewType('pdf')"
|
||||||
|
:class="previewType === 'pdf' ? 'btn-active btn-neutral text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">PDF</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-circle btn-ghost" @click="closePreviewDialog()">
|
<button class="btn btn-sm btn-circle btn-ghost" @click="closePreviewDialog()">
|
||||||
<i class="fa fa-times"></i>
|
<i class="fa fa-times"></i>
|
||||||
@ -23,7 +31,8 @@
|
|||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="flex-1 bg-base-300 relative p-1">
|
<div class="flex-1 bg-base-300 relative p-1">
|
||||||
<iframe id="preview-iframe" x-ref="previewIframe" :src="getPreviewUrl()" class="w-full h-full rounded shadow-sm bg-white"></iframe>
|
<iframe id="preview-iframe" x-ref="previewIframe" :src="getPreviewUrl()"
|
||||||
|
class="w-full h-full rounded shadow-sm bg-white"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
|
|||||||
@ -16,32 +16,47 @@
|
|||||||
|
|
||||||
<!-- Status Filters -->
|
<!-- Status Filters -->
|
||||||
<div class="join shadow-sm bg-base-100 rounded-lg">
|
<div class="join shadow-sm bg-base-100 rounded-lg">
|
||||||
<button @click="filterKey = 'Total'" :class="filterKey === 'Total' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = '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>
|
All <span class="badge badge-sm badge-ghost ml-1" x-text="counters.Total"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Pend'" :class="filterKey === 'Pend' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Pend'"
|
||||||
|
:class="filterKey === 'Pend' ? 'btn-active btn-neutral text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Pending <span class="badge badge-sm ml-1" x-text="counters.Pend"></span>
|
Pending <span class="badge badge-sm ml-1" x-text="counters.Pend"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Coll'" :class="filterKey === 'Coll' ? 'btn-active btn-warning text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Coll'"
|
||||||
|
:class="filterKey === 'Coll' ? 'btn-active btn-warning text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Coll <span class="badge badge-sm badge-warning ml-1" x-text="counters.Coll"></span>
|
Coll <span class="badge badge-sm badge-warning ml-1" x-text="counters.Coll"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Recv'" :class="filterKey === 'Recv' ? 'btn-active btn-info text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Recv'"
|
||||||
|
:class="filterKey === 'Recv' ? 'btn-active btn-info text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Recv <span class="badge badge-sm badge-info ml-1" x-text="counters.Recv"></span>
|
Recv <span class="badge badge-sm badge-info ml-1" x-text="counters.Recv"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Inc'" :class="filterKey === 'Inc' ? 'btn-active btn-error text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Inc'"
|
||||||
|
:class="filterKey === 'Inc' ? 'btn-active btn-error text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Inc <span class="badge badge-sm badge-error ml-1" x-text="counters.Inc"></span>
|
Inc <span class="badge badge-sm badge-error ml-1" x-text="counters.Inc"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Fin'" :class="filterKey === 'Fin' ? 'btn-active btn-success text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Fin'"
|
||||||
|
:class="filterKey === 'Fin' ? 'btn-active btn-success text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Fin <span class="badge badge-sm badge-success ml-1" x-text="counters.Fin"></span>
|
Fin <span class="badge badge-sm badge-success ml-1" x-text="counters.Fin"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Validated'" :class="filterKey === 'Validated' ? 'btn-active btn-primary text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = '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>
|
Val <span class="badge badge-sm badge-primary ml-1" x-text="validatedCount"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search & Date Filter -->
|
<!-- 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="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">
|
<div class="form-control">
|
||||||
<label class="label text-xs font-bold py-1 text-base-content/60">Date Range</label>
|
<label class="label text-xs font-bold py-1 text-base-content/60">Date Range</label>
|
||||||
<div class="join">
|
<div class="join">
|
||||||
@ -72,23 +87,47 @@
|
|||||||
<table class="table table-xs table-zebra w-full">
|
<table class="table table-xs table-zebra w-full">
|
||||||
<thead class="bg-base-100 sticky top-0 z-10">
|
<thead class="bg-base-100 sticky top-0 z-10">
|
||||||
<tr>
|
<tr>
|
||||||
<th style='width:7%;'><div class="skeleton h-4 w-20"></div></th>
|
<th style='width:7%;'>
|
||||||
<th style='width:15%;'><div class="skeleton h-4 w-32"></div></th>
|
<div class="skeleton h-4 w-20"></div>
|
||||||
<th style='width:7%;'><div class="skeleton h-4 w-16"></div></th>
|
</th>
|
||||||
<th style='width:7%;'><div class="skeleton h-4 w-16"></div></th>
|
<th style='width:15%;'>
|
||||||
<th style='width:8%;'><div class="skeleton h-4 w-16"></div></th>
|
<div class="skeleton h-4 w-32"></div>
|
||||||
<th style='width:8%;'><div class="skeleton h-4 w-20"></div></th>
|
</th>
|
||||||
<th style='width:15%;'><div class="skeleton h-4 w-32"></div></th>
|
<th style='width:7%;'>
|
||||||
<th style='width:3%;'><div class="skeleton h-4 w-12"></div></th>
|
<div class="skeleton h-4 w-16"></div>
|
||||||
<th style='width:5%;'><div class="skeleton h-4 w-16"></div></th>
|
</th>
|
||||||
<th style='width:5%;'><div class="skeleton h-4 w-16"></div></th>
|
<th style='width:7%;'>
|
||||||
<th style='width:4%;'><div class="skeleton h-4 w-12"></div></th>
|
<div class="skeleton h-4 w-16"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:8%;'>
|
||||||
|
<div class="skeleton h-4 w-16"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:8%;'>
|
||||||
|
<div class="skeleton h-4 w-20"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:15%;'>
|
||||||
|
<div class="skeleton h-4 w-32"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:3%;'>
|
||||||
|
<div class="skeleton h-4 w-12"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:5%;'>
|
||||||
|
<div class="skeleton h-4 w-16"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:5%;'>
|
||||||
|
<div class="skeleton h-4 w-16"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:4%;'>
|
||||||
|
<div class="skeleton h-4 w-12"></div>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template x-for="i in 5" :key="i">
|
<template x-for="i in 5" :key="i">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="11"><div class="skeleton h-4 w-full"></div></td>
|
<td colspan="11">
|
||||||
|
<div class="skeleton h-4 w-full"></div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -104,26 +143,78 @@
|
|||||||
<table class="table table-xs table-zebra w-full">
|
<table class="table table-xs table-zebra w-full">
|
||||||
<thead class="bg-base-100 sticky top-0 z-10">
|
<thead class="bg-base-100 sticky top-0 z-10">
|
||||||
<tr>
|
<tr>
|
||||||
<th style='width:7%;'>Order Datetime</th>
|
<th style='width:7%;' @click="sort('REQDATE')"
|
||||||
<th style='width:15%;'>Patient Name</th>
|
class="cursor-pointer hover:bg-base-200 transition-colors select-none">
|
||||||
<th style='width:7%;'>No Lab</th>
|
<div class="flex items-center gap-1">
|
||||||
<th style='width:7%;'>No Register</th>
|
Order Datetime
|
||||||
<th style='width:8%;'>Reff</th>
|
<i class="fa text-xs"
|
||||||
<th style='width:8%;'>Doctor</th>
|
:class="sortCol === 'REQDATE' ? (sortAsc ? 'fa-sort-up' : 'fa-sort-down') : 'fa-sort opacity-20'"></i>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th style='width:15%;' @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:7%;' @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:7%;' @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:8%;' @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:8%;' @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:15%;'>Tests</th>
|
<th style='width:15%;'>Tests</th>
|
||||||
<th style='width:3%;'>ResTo</th>
|
<th style='width:3%;'>ResTo</th>
|
||||||
<th style='width:5%;'>Val</th>
|
<th style='width:5%;'>Val</th>
|
||||||
<th style='width:5%;'>Result</th>
|
<th style='width:5%;'>Result</th>
|
||||||
<th style='width:4%;'>Status</th>
|
<th style='width:4%;' @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>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template x-for="req in filtered" :key="req.SP_ACCESSNUMBER">
|
<tbody>
|
||||||
|
<template x-for="req in paginated" :key="req.SP_ACCESSNUMBER">
|
||||||
<tr class="hover:bg-base-300">
|
<tr class="hover:bg-base-300">
|
||||||
<td x-text="req.REQDATE"></td>
|
<td x-text="req.REQDATE"></td>
|
||||||
<td x-text="req.Name"></td>
|
<td x-text="req.Name"></td>
|
||||||
<td x-text="req.SP_ACCESSNUMBER"></td>
|
<td x-text="req.SP_ACCESSNUMBER" class="font-bold cursor-pointer" :class="statusColor[req.STATS]"
|
||||||
<td x-text="req.HOSTORDERNUMBER"></td>
|
@click="openSampleDialog(req.SP_ACCESSNUMBER)"></td>
|
||||||
|
<td x-text="req.HOSTORDERNUMBER" class="font-bold cursor-pointer" :class="statusColor[req.STATS]"
|
||||||
|
@click="openSampleDialog(req.SP_ACCESSNUMBER)"></td>
|
||||||
<td x-text="req.REFF"></td>
|
<td x-text="req.REFF"></td>
|
||||||
<td x-text="req.DOC"></td>
|
<td x-text="req.DOC"></td>
|
||||||
<td x-text="req.TESTS"></td>
|
<td x-text="req.TESTS"></td>
|
||||||
@ -136,8 +227,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
||||||
<div class='text-center'>
|
<div class='text-center'>
|
||||||
<template x-if="req.VAL1USER == '<?=session('userid');?>' || req.VAL2USER == '<?=session('userid');?>'">
|
<template
|
||||||
<button class="btn btn-xs btn-outline btn-secondary" @click="openUnvalDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-rotate-right"></i></button>
|
x-if="req.VAL1USER == '<?= session('userid'); ?>' || req.VAL2USER == '<?= session('userid'); ?>'">
|
||||||
|
<button class="btn btn-xs btn-outline btn-secondary"
|
||||||
|
@click="openUnvalDialog(req.SP_ACCESSNUMBER)"><i
|
||||||
|
class="fa-solid fa-rotate-right"></i></button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -145,11 +239,12 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<template x-if="req.STATS !== 'PartColl' && req.STATS !== 'Coll' && req.STATS !== 'Pend'">
|
<template x-if="req.STATS !== 'PartColl' && req.STATS !== 'Coll' && req.STATS !== 'Pend'">
|
||||||
<button class="btn btn-xs btn-outline btn-primary" @click="openPreviewDialog(req.SP_ACCESSNUMBER, 'preview')">Preview</button>
|
<button class="btn btn-xs btn-outline btn-primary"
|
||||||
|
@click="openPreviewDialog(req.SP_ACCESSNUMBER, 'preview', req)">Preview</button>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td><button x-text="req.STATS === 'Fin' ? 'Final' : req.STATS" class="btn btn-xs"
|
<td x-text="req.STATS === 'Fin' ? 'Final' : req.STATS" class="font-bold cursor-pointer"
|
||||||
:class="statusColor[req.STATS]" @click="openSampleDialog(req.SP_ACCESSNUMBER)"></button></td>
|
:class="statusColor[req.STATS]" @click="openSampleDialog(req.SP_ACCESSNUMBER)"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -157,6 +252,27 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination Control -->
|
||||||
|
<div class="p-2 border-t border-base-200 bg-base-50 flex justify-between items-center"
|
||||||
|
x-show="!isLoading && list.length > 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, filtered.length)"></span> of
|
||||||
|
<span class="font-bold" x-text="filtered.length"></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>
|
||||||
|
|
||||||
<?php echo $this->include('admin/dialog_sample'); ?>
|
<?php echo $this->include('admin/dialog_sample'); ?>
|
||||||
@ -189,6 +305,7 @@
|
|||||||
},
|
},
|
||||||
filterTable: "",
|
filterTable: "",
|
||||||
filterKey: 'Total',
|
filterKey: 'Total',
|
||||||
|
filterKey: 'Total',
|
||||||
statusMap: {
|
statusMap: {
|
||||||
Total: [],
|
Total: [],
|
||||||
Pend: ['Pend'],
|
Pend: ['Pend'],
|
||||||
@ -198,10 +315,55 @@
|
|||||||
Fin: ['Fin'],
|
Fin: ['Fin'],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Sorting & Pagination
|
||||||
|
sortCol: 'REQDATE',
|
||||||
|
sortAsc: false,
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 15,
|
||||||
|
|
||||||
|
sort(col) {
|
||||||
|
if (this.sortCol === col) {
|
||||||
|
this.sortAsc = !this.sortAsc;
|
||||||
|
} else {
|
||||||
|
this.sortCol = col;
|
||||||
|
this.sortAsc = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
nextPage() {
|
||||||
|
if (this.currentPage < this.totalPages) this.currentPage++;
|
||||||
|
},
|
||||||
|
|
||||||
|
prevPage() {
|
||||||
|
if (this.currentPage > 1) this.currentPage--;
|
||||||
|
},
|
||||||
|
|
||||||
|
get totalPages() {
|
||||||
|
return Math.ceil(this.filtered.length / this.pageSize) || 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
get sorted() {
|
||||||
|
return this.filtered.slice().sort((a, b) => {
|
||||||
|
let modifier = this.sortAsc ? 1 : -1;
|
||||||
|
if (a[this.sortCol] < b[this.sortCol]) return -1 * modifier;
|
||||||
|
if (a[this.sortCol] > b[this.sortCol]) return 1 * modifier;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get paginated() {
|
||||||
|
const start = (this.currentPage - 1) * this.pageSize;
|
||||||
|
const end = start + this.pageSize;
|
||||||
|
return this.sorted.slice(start, end);
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.today = new Date().toISOString().slice(0, 10);
|
this.today = new Date().toISOString().slice(0, 10);
|
||||||
this.filter.date1 = this.today;
|
this.filter.date1 = this.today;
|
||||||
this.filter.date2 = this.today;
|
this.filter.date2 = this.today;
|
||||||
|
this.$watch('filterTable', () => {
|
||||||
|
this.currentPage = 1;
|
||||||
|
});
|
||||||
this.fetchList();
|
this.fetchList();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -245,6 +407,10 @@
|
|||||||
return item.ISVAL == 1 && item.ISPENDING != 1;
|
return item.ISVAL == 1 && item.ISPENDING != 1;
|
||||||
},
|
},
|
||||||
get filtered() {
|
get filtered() {
|
||||||
|
// Reset pagination when filter changes (implied by this getter being accessed if dependencies change)
|
||||||
|
// However, side-effects in getters are tricky.
|
||||||
|
// Better to just let the user navigate back, or watch variables.
|
||||||
|
// For now, let's keep it pure.
|
||||||
let filteredList = this.list;
|
let filteredList = this.list;
|
||||||
if (this.filterKey === 'Validated') {
|
if (this.filterKey === 'Validated') {
|
||||||
filteredList = filteredList.filter(item => this.isValidated(item));
|
filteredList = filteredList.filter(item => this.isValidated(item));
|
||||||
@ -333,16 +499,17 @@
|
|||||||
*/
|
*/
|
||||||
isDialogPreviewOpen: false,
|
isDialogPreviewOpen: false,
|
||||||
reviewed: false,
|
reviewed: false,
|
||||||
previewAccessnumber : null,
|
previewItem: null,
|
||||||
previewType : 'preview',
|
openPreviewDialog(accessnumber, type, item) {
|
||||||
openPreviewDialog (accessnumber, type) {
|
|
||||||
this.previewAccessnumber = accessnumber;
|
this.previewAccessnumber = accessnumber;
|
||||||
|
this.previewItem = item;
|
||||||
this.previewType = type;
|
this.previewType = type;
|
||||||
this.isDialogPreviewOpen = true;
|
this.isDialogPreviewOpen = true;
|
||||||
this.reviewed = false;
|
this.reviewed = false;
|
||||||
},
|
},
|
||||||
closePreviewDialog() {
|
closePreviewDialog() {
|
||||||
this.isDialogPreviewOpen = false;
|
this.isDialogPreviewOpen = false;
|
||||||
|
this.previewItem = null;
|
||||||
},
|
},
|
||||||
setPreviewType(type) {
|
setPreviewType(type) {
|
||||||
this.previewType = type;
|
this.previewType = type;
|
||||||
@ -353,6 +520,9 @@
|
|||||||
if (this.previewType === 'ind') url += '&lang=ID';
|
if (this.previewType === 'ind') url += '&lang=ID';
|
||||||
if (this.previewType === 'eng') url += '&lang=EN';
|
if (this.previewType === 'eng') url += '&lang=EN';
|
||||||
if (this.previewType === 'pdf') url += '&output=pdf';
|
if (this.previewType === 'pdf') url += '&output=pdf';
|
||||||
|
|
||||||
|
// Keep fallback for local dev if needed, but the above is the expected logic
|
||||||
|
// return "http://localhost/application.html";
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
validate(accessnumber, userid) {
|
validate(accessnumber, userid) {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" data-theme="corporate">
|
<html lang="en" data-theme="corporate">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
@ -14,21 +15,33 @@
|
|||||||
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-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;
|
font-size: 0.71rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
padding: 0.2rem 1rem;
|
padding: 0.2rem 1rem;
|
||||||
min-height: 0rem;
|
min-height: 0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
font-size: 0.71rem !important;
|
font-size: 0.71rem !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-base-200 min-h-screen flex flex-col" x-data="main">
|
|
||||||
|
|
||||||
|
<body class="bg-base-200 min-h-screen" x-data="main">
|
||||||
|
<div class="drawer">
|
||||||
|
<input id="main-drawer" type="checkbox" class="drawer-toggle" />
|
||||||
|
<div class="drawer-content flex flex-col min-h-screen">
|
||||||
|
<!-- Navbar -->
|
||||||
<nav class="navbar bg-base-100 shadow-md px-6 z-20">
|
<nav class="navbar bg-base-100 shadow-md px-6 z-20">
|
||||||
|
<div class="flex-none">
|
||||||
|
<label for="main-drawer" aria-label="open sidebar" class="btn btn-square btn-ghost">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class='flex-1'>
|
<div class='flex-1'>
|
||||||
<a class='text-xl text-primary font-bold tracking-wide flex items-center gap-2'>
|
<a class='text-xl text-primary font-bold tracking-wide flex items-center gap-2 ml-2'>
|
||||||
<i class="fa fa-cube"></i> CMOD <span class="text-base-content/40 font-light text-sm hidden sm:inline-block">| Admin Dashboard</span>
|
<i class="fa fa-cube"></i> CMOD <span
|
||||||
|
class="text-base-content/40 font-light text-sm hidden sm:inline-block">| Admin Dashboard</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@ -40,23 +53,45 @@
|
|||||||
<div tabindex="0" role="button" class="btn btn-ghost avatar placeholder px-2">
|
<div tabindex="0" role="button" class="btn btn-ghost avatar placeholder px-2">
|
||||||
<span class="text-xl"><i class="fa fa-user"></i></span>
|
<span class="text-xl"><i class="fa fa-user"></i></span>
|
||||||
</div>
|
</div>
|
||||||
<ul tabindex="-1" class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2 shadow-xl border border-base-200 mt-2">
|
|
||||||
<li><a class="active:bg-primary" href="<?=base_url('admin') ?>"><i class="fa fa-chart-bar mr-2"></i> Dashboard</a></li>
|
|
||||||
<li><a class="active:bg-primary" href="<?=base_url('admin/users') ?>"><i class="fa fa-users mr-2"></i> Users </a></li>
|
|
||||||
<li><a @click.prevent="openDialogSetPassword()" class="active:bg-primary"><i class="fa fa-key mr-2"></i> Change Password</a></li>
|
|
||||||
<li class="divider my-1"></li>
|
|
||||||
<li><a href="<?=base_url('logout')?>" class="text-error hover:bg-error/10"><i class="fa fa-sign-out-alt mr-2"></i> Logout</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<!-- Page Content -->
|
||||||
<?= $this->renderSection('content'); ?>
|
<?= $this->renderSection('content'); ?>
|
||||||
<?= $this->include('admin/dialog_setPassword'); ?>
|
<?= $this->include('admin/dialog_setPassword'); ?>
|
||||||
<footer class='bg-base-100 p-1'>© <?=date('Y');?> - 5Panda</footer>
|
|
||||||
|
<footer class='bg-base-100 p-1 mt-auto'>© <?= date('Y'); ?> - 5Panda</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="drawer-side z-50">
|
||||||
|
<label for="main-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
|
<ul class="menu bg-base-100 text-base-content min-h-full w-80 p-4 flex flex-col">
|
||||||
|
<!-- Sidebar content here -->
|
||||||
|
<li class="mb-4">
|
||||||
|
<a class='text-xl text-primary font-bold tracking-wide flex items-center gap-2'>
|
||||||
|
<i class="fa fa-cube"></i> CMOD
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><a href="<?= base_url('admin') ?>"><i class="fa fa-chart-bar mr-2"></i> Dashboard</a></li>
|
||||||
|
<li><a href="<?= base_url('admin/users') ?>"><i class="fa fa-users mr-2"></i> Users </a></li>
|
||||||
|
|
||||||
|
<div class="mt-auto">
|
||||||
|
<li class="menu-title">Account</li>
|
||||||
|
<li><a @click.prevent="openDialogSetPassword()"><i class="fa fa-key mr-2"></i> Change Password</a></li>
|
||||||
|
<li><a href="<?= base_url('logout') ?>" class="text-error hover:bg-error/10"><i
|
||||||
|
class="fa fa-sign-out-alt mr-2"></i> Logout</a></li>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.BASEURL = "<?= base_url("admin"); ?>";
|
window.BASEURL = "<?= base_url("admin"); ?>";
|
||||||
</script>
|
</script>
|
||||||
<?= $this->renderSection('script'); ?>
|
<?= $this->renderSection('script'); ?>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -16,32 +16,47 @@
|
|||||||
|
|
||||||
<!-- Status Filters -->
|
<!-- Status Filters -->
|
||||||
<div class="join shadow-sm bg-base-100 rounded-lg">
|
<div class="join shadow-sm bg-base-100 rounded-lg">
|
||||||
<button @click="filterKey = 'Total'" :class="filterKey === 'Total' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = '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>
|
All <span class="badge badge-sm badge-ghost ml-1" x-text="counters.Total"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Pend'" :class="filterKey === 'Pend' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Pend'"
|
||||||
|
:class="filterKey === 'Pend' ? 'btn-active btn-neutral text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Pending <span class="badge badge-sm ml-1" x-text="counters.Pend"></span>
|
Pending <span class="badge badge-sm ml-1" x-text="counters.Pend"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Coll'" :class="filterKey === 'Coll' ? 'btn-active btn-warning text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Coll'"
|
||||||
|
:class="filterKey === 'Coll' ? 'btn-active btn-warning text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Coll <span class="badge badge-sm badge-warning ml-1" x-text="counters.Coll"></span>
|
Coll <span class="badge badge-sm badge-warning ml-1" x-text="counters.Coll"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Recv'" :class="filterKey === 'Recv' ? 'btn-active btn-info text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Recv'"
|
||||||
|
:class="filterKey === 'Recv' ? 'btn-active btn-info text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Recv <span class="badge badge-sm badge-info ml-1" x-text="counters.Recv"></span>
|
Recv <span class="badge badge-sm badge-info ml-1" x-text="counters.Recv"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Inc'" :class="filterKey === 'Inc' ? 'btn-active btn-error text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Inc'"
|
||||||
|
:class="filterKey === 'Inc' ? 'btn-active btn-error text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Inc <span class="badge badge-sm badge-error ml-1" x-text="counters.Inc"></span>
|
Inc <span class="badge badge-sm badge-error ml-1" x-text="counters.Inc"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Fin'" :class="filterKey === 'Fin' ? 'btn-active btn-success text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = 'Fin'"
|
||||||
|
:class="filterKey === 'Fin' ? 'btn-active btn-success text-white' : 'btn-ghost'"
|
||||||
|
class="btn btn-sm join-item">
|
||||||
Fin <span class="badge badge-sm badge-success ml-1" x-text="counters.Fin"></span>
|
Fin <span class="badge badge-sm badge-success ml-1" x-text="counters.Fin"></span>
|
||||||
</button>
|
</button>
|
||||||
<button @click="filterKey = 'Validated'" :class="filterKey === 'Validated' ? 'btn-active btn-primary text-white' : 'btn-ghost'" class="btn btn-sm join-item">
|
<button @click="filterKey = '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>
|
Val <span class="badge badge-sm badge-primary ml-1" x-text="validatedCount"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search & Date Filter -->
|
<!-- 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="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">
|
<div class="form-control">
|
||||||
<label class="label text-xs font-bold py-1 text-base-content/60">Date Range</label>
|
<label class="label text-xs font-bold py-1 text-base-content/60">Date Range</label>
|
||||||
<div class="join">
|
<div class="join">
|
||||||
@ -61,7 +76,8 @@
|
|||||||
<div class="form-control w-full md:w-auto">
|
<div class="form-control w-full md:w-auto">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<i class="fa fa-filter absolute left-3 top-2.5 text-base-content/30 text-xs"></i>
|
<i class="fa fa-filter absolute left-3 top-2.5 text-base-content/30 text-xs"></i>
|
||||||
<input type="text" class="input input-sm input-bordered w-full md:w-64 pl-8" placeholder="Type to filter..." x-model="filterTable" />
|
<input type="text" class="input input-sm input-bordered w-full md:w-64 pl-8"
|
||||||
|
placeholder="Type to filter..." x-model="filterTable" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,21 +88,41 @@
|
|||||||
<table class="table table-xs table-zebra w-full">
|
<table class="table table-xs table-zebra w-full">
|
||||||
<thead class="bg-base-100 sticky top-0 z-10">
|
<thead class="bg-base-100 sticky top-0 z-10">
|
||||||
<tr>
|
<tr>
|
||||||
<th style='width:7%;'><div class="skeleton h-4 w-20"></div></th>
|
<th style='width:7%;'>
|
||||||
<th style='width:15%;'><div class="skeleton h-4 w-32"></div></th>
|
<div class="skeleton h-4 w-20"></div>
|
||||||
<th style='width:7%;'><div class="skeleton h-4 w-16"></div></th>
|
</th>
|
||||||
<th style='width:8%;'><div class="skeleton h-4 w-16"></div></th>
|
<th style='width:15%;'>
|
||||||
<th style='width:8%;'><div class="skeleton h-4 w-20"></div></th>
|
<div class="skeleton h-4 w-32"></div>
|
||||||
<th style='width:15%;'><div class="skeleton h-4 w-32"></div></th>
|
</th>
|
||||||
<th style='width:5%;'><div class="skeleton h-4 w-12"></div></th>
|
<th style='width:7%;'>
|
||||||
<th style='width:5%;'><div class="skeleton h-4 w-16"></div></th>
|
<div class="skeleton h-4 w-16"></div>
|
||||||
<th style='width:4%;'><div class="skeleton h-4 w-12"></div></th>
|
</th>
|
||||||
|
<th style='width:8%;'>
|
||||||
|
<div class="skeleton h-4 w-16"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:8%;'>
|
||||||
|
<div class="skeleton h-4 w-20"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:15%;'>
|
||||||
|
<div class="skeleton h-4 w-32"></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-16"></div>
|
||||||
|
</th>
|
||||||
|
<th style='width:4%;'>
|
||||||
|
<div class="skeleton h-4 w-12"></div>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template x-for="i in 5" :key="i">
|
<template x-for="i in 5" :key="i">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="9"><div class="skeleton h-4 w-full"></div></td>
|
<td colspan="9">
|
||||||
|
<div class="skeleton h-4 w-full"></div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -131,11 +167,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
||||||
<div class='text-center'>
|
<div class='text-center'>
|
||||||
<template x-if="req.VAL1USER == '<?=session('userid');?>' || req.VAL2USER == '<?=session('userid');?>'">
|
<template
|
||||||
<button class="btn btn-xs btn-outline btn-secondary" @click="openUnvalDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-rotate-right"></i></button>
|
x-if="req.VAL1USER == '<?= session('userid'); ?>' || req.VAL2USER == '<?= session('userid'); ?>'">
|
||||||
|
<button class="btn btn-xs btn-outline btn-secondary"
|
||||||
|
@click="openUnvalDialog(req.SP_ACCESSNUMBER)"><i
|
||||||
|
class="fa-solid fa-rotate-right"></i></button>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="req.VAL1USER != '<?=session('userid');?>' && req.VAL2USER != '<?=session('userid');?>'">
|
<template
|
||||||
<button class="btn btn-xs btn-outline btn-success" @click="openValDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-check"></i></button>
|
x-if="(req.VAL1USER != '<?= session('userid'); ?>' && req.VAL2USER != '<?= session('userid'); ?>') && (!req.VAL1USER || !req.VAL2USER)">
|
||||||
|
<button class="btn btn-xs btn-outline btn-success"
|
||||||
|
@click="openValDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-check"></i></button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" data-theme="corporate">
|
<html lang="en" data-theme="corporate">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
@ -14,21 +15,33 @@
|
|||||||
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-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;
|
font-size: 0.71rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
padding: 0.2rem 1rem;
|
padding: 0.2rem 1rem;
|
||||||
min-height: 0rem;
|
min-height: 0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
font-size: 0.71rem !important;
|
font-size: 0.71rem !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-base-200 min-h-screen flex flex-col">
|
|
||||||
|
|
||||||
|
<body class="bg-base-200 min-h-screen">
|
||||||
|
<div class="drawer">
|
||||||
|
<input id="main-drawer" type="checkbox" class="drawer-toggle" />
|
||||||
|
<div class="drawer-content flex flex-col min-h-screen">
|
||||||
|
<!-- Navbar -->
|
||||||
<nav class="navbar bg-base-100 shadow-md px-6 z-20">
|
<nav class="navbar bg-base-100 shadow-md px-6 z-20">
|
||||||
|
<div class="flex-none">
|
||||||
|
<label for="main-drawer" aria-label="open sidebar" class="btn btn-square btn-ghost">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class='flex-1'>
|
<div class='flex-1'>
|
||||||
<a class='text-xl text-primary font-bold tracking-wide flex items-center gap-2'>
|
<a class='text-xl text-primary font-bold tracking-wide flex items-center gap-2 ml-2'>
|
||||||
<i class="fa fa-cube"></i> CMOD <span class="text-base-content/40 font-light text-sm hidden sm:inline-block">| Lab Analyst Dashboard</span>
|
<i class="fa fa-cube"></i> CMOD <span
|
||||||
|
class="text-base-content/40 font-light text-sm hidden sm:inline-block">| Lab Analyst Dashboard</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@ -40,23 +53,43 @@
|
|||||||
<div tabindex="0" role="button" class="btn btn-ghost avatar placeholder px-2">
|
<div tabindex="0" role="button" class="btn btn-ghost avatar placeholder px-2">
|
||||||
<span class="text-xl"><i class="fa fa-user"></i></span>
|
<span class="text-xl"><i class="fa fa-user"></i></span>
|
||||||
</div>
|
</div>
|
||||||
<ul tabindex="-1" class="dropdown-content menu bg-base-100 rounded-box z-50 w-52 p-2 shadow-xl border border-base-200 mt-2">
|
|
||||||
<li><a class="active:bg-primary" href="<?=base_url('lab') ?>"><i class="fa fa-chart-bar mr-2"></i> Dashboard</a></li>
|
|
||||||
<li><a class="active:bg-primary" href="<?=base_url('setPassword') ?>"><i class="fa fa-key mr-2"></i> Set Password</a></li>
|
|
||||||
<li class="divider my-1"></li>
|
|
||||||
<li><a href="<?=base_url('logout')?>" class="text-error hover:bg-error/10"><i class="fa fa-sign-out-alt mr-2"></i> Logout</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<!-- Page Content -->
|
||||||
<?= $this->renderSection('content'); ?>
|
<?= $this->renderSection('content'); ?>
|
||||||
|
|
||||||
<footer class='bg-base-100 p-1'>© <?=date('Y');?> - 5Panda</footer>
|
<footer class='bg-base-100 p-1 mt-auto'>© <?= date('Y'); ?> - 5Panda</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="drawer-side z-50">
|
||||||
|
<label for="main-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||||
|
<ul class="menu bg-base-100 text-base-content min-h-full w-80 p-4 flex flex-col">
|
||||||
|
<!-- Sidebar content here -->
|
||||||
|
<li class="mb-4">
|
||||||
|
<a class='text-xl text-primary font-bold tracking-wide flex items-center gap-2'>
|
||||||
|
<i class="fa fa-cube"></i> CMOD
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><a href="<?= base_url('lab') ?>"><i class="fa fa-chart-bar mr-2"></i> Dashboard</a></li>
|
||||||
|
|
||||||
|
<div class="mt-auto">
|
||||||
|
<li class="menu-title">Account</li>
|
||||||
|
<li><a href="<?= base_url('setPassword') ?>"><i class="fa fa-key mr-2"></i> Set Password</a></li>
|
||||||
|
<li><a href="<?= base_url('logout') ?>" class="text-error hover:bg-error/10"><i
|
||||||
|
class="fa fa-sign-out-alt mr-2"></i> Logout</a></li>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.BASEURL = "<?= base_url("lab"); ?>";
|
window.BASEURL = "<?= base_url("lab"); ?>";
|
||||||
</script>
|
</script>
|
||||||
<?= $this->renderSection('script'); ?>
|
<?= $this->renderSection('script'); ?>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user