Refactor: Remove V2 namespace and consolidate role-based architecture
- Moved all V2 controllers (Lab, Requests, Samples, Users) to App\Controllers - Removed deprecated role controllers (Admin, Doctor, Analyst, CustomerService) - Simplified routes by removing /v2 prefix - Added AGENTS.md with project conventions and TODO.md with task tracking - Updated README.md with RBAC documentation - Fixed hardcoded dates, status color mappings, and duplicate database calls
This commit is contained in:
parent
3b9c3dec10
commit
b29f807295
1
.gitignore
vendored
1
.gitignore
vendored
@ -125,3 +125,4 @@ _modules/*
|
||||
/results/
|
||||
/phpunit*.xml
|
||||
|
||||
.roo/
|
||||
267
AGENTS.md
Normal file
267
AGENTS.md
Normal file
@ -0,0 +1,267 @@
|
||||
# AGENTS.md
|
||||
|
||||
This file provides guidance to agents when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a CodeIgniter 4 PHP application for a laboratory management system (CMOD). It uses SQL Server database with role-based access control for different user types (admin, doctor/analyst, customer service).
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
./vendor/bin/phpunit
|
||||
composer test
|
||||
|
||||
# Run single test file
|
||||
./vendor/bin/phpunit tests/unit/HealthTest.php
|
||||
|
||||
# Run single test method
|
||||
./vendor/bin/phpunit tests/unit/HealthTest.php --filter testIsDefinedAppPath
|
||||
|
||||
# Development server (Linux/Mac)
|
||||
php spark serve
|
||||
|
||||
# List all routes
|
||||
php spark list
|
||||
|
||||
# Create controller
|
||||
php spark make:controller Admin
|
||||
|
||||
# Create model
|
||||
php spark make:model User
|
||||
```
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### PHP Standards
|
||||
- Use PHP 8.1+ features (typed properties, match expressions where appropriate)
|
||||
- Always declare return types for public methods
|
||||
- Use `strict_types=1` not required (CodeIgniter doesn't use it)
|
||||
- No comments unless explaining complex logic (per project convention)
|
||||
|
||||
### Naming Conventions
|
||||
- **Classes**: PascalCase (e.g., `Admin`, `UserController`)
|
||||
- **Methods**: camelCase (e.g., `index()`, `getUsers()`)
|
||||
- **Variables**: camelCase (e.g., `$userId`, `$dataList`)
|
||||
- **Constants**: UPPER_SNAKE_CASE (e.g., `DB_HOST`)
|
||||
- **Database tables**: UPPER_SNAKE_CASE (e.g., `GDC_CMOD.dbo.USERS`)
|
||||
- **Views**: lowercase with underscores (e.g., `admin/index.php`)
|
||||
|
||||
### Controller Patterns
|
||||
|
||||
#### Base Controllers
|
||||
- All controllers extend `App\Controllers\BaseController`
|
||||
- BaseController extends CodeIgniter\Controller
|
||||
|
||||
```php
|
||||
namespace App\Controllers;
|
||||
|
||||
class Admin extends BaseController {
|
||||
public function index() {
|
||||
// Method body
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### API Controllers
|
||||
- Controllers use `ResponseTrait` for API responses
|
||||
- Use `$this->respond()` or `$this->response->setJSON()` for responses
|
||||
|
||||
```php
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
class Users extends BaseController {
|
||||
use ResponseTrait;
|
||||
|
||||
protected $db;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = \Config\Database::connect();
|
||||
helper(['url', 'form', 'text']);
|
||||
}
|
||||
|
||||
public function index() {
|
||||
$query = $this->db->query("SELECT * FROM table");
|
||||
return $this->respond(['data' => $query->getResultArray()]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Database Operations
|
||||
|
||||
#### Connection Pattern
|
||||
```php
|
||||
$this->db = \Config\Database::connect();
|
||||
```
|
||||
|
||||
#### Query Methods
|
||||
- `getRowArray()` - returns single row as associative array
|
||||
- `getResultArray()` - returns multiple rows as array of arrays
|
||||
- Use parameterized queries to prevent SQL injection
|
||||
|
||||
```php
|
||||
$query = $this->db->query("SELECT * FROM table WHERE id = ?", [$id]);
|
||||
$row = $query->getRowArray();
|
||||
$results = $query->getResultArray();
|
||||
```
|
||||
|
||||
#### Transactions
|
||||
```php
|
||||
$this->db->transBegin();
|
||||
try {
|
||||
$this->db->query("INSERT INTO ...", [$data]);
|
||||
$this->db->transCommit();
|
||||
} catch (\Throwable $e) {
|
||||
$this->db->transRollback();
|
||||
return $this->response->setJSON(['message' => 'Error']);
|
||||
}
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
#### Session Structure
|
||||
```php
|
||||
$session->set([
|
||||
'isLoggedIn' => true,
|
||||
'userid' => (string) $user['USERID'],
|
||||
'userlevel' => (int) $user['USERLEVEL'],
|
||||
'userrole' => (string) $role, // 'admin', 'doctor', 'analyst', 'cs'
|
||||
]);
|
||||
```
|
||||
|
||||
#### Session Values
|
||||
- `isLoggedIn`: bool
|
||||
- `userid`: string
|
||||
- `userlevel`: int
|
||||
- `userrole`: string
|
||||
|
||||
### Role-Based Access Control
|
||||
|
||||
#### Role Values
|
||||
- `1` = admin
|
||||
- `2` = doctor (or lab/analyst)
|
||||
- `3` = analyst
|
||||
- `4` = cs (customer service)
|
||||
|
||||
#### Route Filter Syntax
|
||||
```php
|
||||
// Single role
|
||||
['filter' => 'role:1']
|
||||
|
||||
// Multiple roles
|
||||
['filter' => 'role:1,2']
|
||||
```
|
||||
|
||||
### Request/Response Patterns
|
||||
|
||||
#### Getting Input
|
||||
```php
|
||||
// POST data
|
||||
$input = $this->request->getJSON(true);
|
||||
$userid = $input['userid'];
|
||||
|
||||
// Query parameters
|
||||
$date1 = $this->request->getVar('date1') ?? date('Y-m-d');
|
||||
```
|
||||
|
||||
#### JSON Response (V2 API)
|
||||
```php
|
||||
return $this->respond(['data' => $results]);
|
||||
return $this->response->setJSON(['message' => 'Success']);
|
||||
```
|
||||
|
||||
#### View Response (Traditional)
|
||||
```php
|
||||
return view('admin/index', $data);
|
||||
```
|
||||
|
||||
#### Redirect with Errors
|
||||
```php
|
||||
return redirect()->back()->with('errors', ['key' => 'message']);
|
||||
```
|
||||
|
||||
### API Endpoint Patterns
|
||||
|
||||
#### Validation Endpoints
|
||||
- `POST /api/{resource}/validate/{id}` - validate a record
|
||||
- `DELETE /api/{resource}/validate/{id}` - unvalidate a record
|
||||
|
||||
#### Route Examples
|
||||
```php
|
||||
// Admin routes
|
||||
$routes->group('admin', ['filter' => 'role:1'], function($routes) {
|
||||
$routes->get('/', 'Admin::index');
|
||||
$routes->get('users', 'Admin::users');
|
||||
$routes->get('api/users', 'Users::index');
|
||||
$routes->post('api/users', 'Users::create');
|
||||
});
|
||||
|
||||
// Lab routes
|
||||
$routes->group('lab', ['filter' => 'role:2'], function($routes) {
|
||||
$routes->get('/', 'Lab::index');
|
||||
$routes->get('api/requests', 'Requests::index');
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
#### Database Operations
|
||||
```php
|
||||
try {
|
||||
// DB operations
|
||||
} catch (\Throwable $e) {
|
||||
// Handle error
|
||||
return $this->response->setJSON(['message' => 'Server error']);
|
||||
}
|
||||
```
|
||||
|
||||
#### Validation Errors
|
||||
```php
|
||||
if ($condition) {
|
||||
return $this->response->setJSON(['message' => 'Error message']);
|
||||
}
|
||||
```
|
||||
|
||||
### Views
|
||||
|
||||
#### View Pattern
|
||||
- Views are plain PHP files in `app/Views/`
|
||||
- Use `<?php echo $variable; ?>` syntax
|
||||
- Pass data as associative array
|
||||
|
||||
```php
|
||||
// Controller
|
||||
$data['dataList'] = $results;
|
||||
return view('admin/index', $data);
|
||||
|
||||
// View
|
||||
<?php foreach ($dataList as $item): ?>
|
||||
<tr><td><?= esc($item['name']) ?></td></tr>
|
||||
<?php endforeach; ?>
|
||||
```
|
||||
|
||||
### Security
|
||||
|
||||
- Always use parameterized queries (never interpolate directly)
|
||||
- Use `esc()` when outputting user data in views
|
||||
- Hash passwords with `password_hash()` and verify with `password_verify()`
|
||||
- Validate and sanitize all input before use
|
||||
|
||||
### Helper Functions
|
||||
|
||||
Available helpers loaded via `helper(['name', 'name2'])`:
|
||||
- `url` - URL helpers
|
||||
- `form` - Form helpers
|
||||
- `text` - Text formatting
|
||||
|
||||
### Database Schema
|
||||
|
||||
- Database: SQL Server
|
||||
- Schema: `dbo`
|
||||
- Main database: `GDC_CMOD`
|
||||
- Reference database: `glendb`
|
||||
- Table naming: `GDC_CMOD.dbo.TABLENAME`
|
||||
41
README.md
41
README.md
@ -28,6 +28,47 @@ to your `app` folder. The affected files can be copied or merged from
|
||||
Copy `env` to `.env` and tailor for your app, specifically the baseURL
|
||||
and any database settings.
|
||||
|
||||
## Role-Based Access Control
|
||||
|
||||
This application uses role-based access control with four user roles.
|
||||
|
||||
### User Roles
|
||||
|
||||
| Role | Level | Access |
|
||||
|------|-------|--------|
|
||||
| Admin | 1 | All functions |
|
||||
| Lab | 2 | All functions |
|
||||
| Phlebo | 3 | Specimen collection, Dashboard |
|
||||
| CS | 4 | Dashboard |
|
||||
|
||||
### Feature Categories by Role
|
||||
|
||||
#### Admin
|
||||
- **Dashboard** - View all requests with status filters (Pend, Coll, Recv, Inc, Fin, Val)
|
||||
- **User Management** - Create, edit, delete users; assign roles
|
||||
- **Request Management** - View, validate, unvalidate all requests
|
||||
- **Sample Management** - Collect, uncollect, receive, unreceive samples
|
||||
- **Result Management** - Preview and print results
|
||||
|
||||
#### Lab
|
||||
- **Dashboard** - View requests with status filters
|
||||
- **Request Validation** - Validate/unvalidate requests (2-level validation)
|
||||
- **Sample Management** - Collect samples, mark received
|
||||
- **Result Preview** - Preview and print results
|
||||
|
||||
#### Phlebo
|
||||
- **Dashboard** - View pending collections
|
||||
- **Specimen Collection** - Log collected specimens
|
||||
|
||||
#### CS (Customer Service)
|
||||
- **Dashboard** - View-only request tracking
|
||||
- **Status Monitoring** - Filter by request status
|
||||
- **Patient Inquiry** - View request details
|
||||
|
||||
### Route Prefixes
|
||||
- Admin: `/admin`
|
||||
- Lab: `/lab`
|
||||
|
||||
## Important Change with index.php
|
||||
|
||||
`index.php` is no longer in the root of the project! It has been moved inside the *public* folder,
|
||||
|
||||
129
TODO.md
Normal file
129
TODO.md
Normal file
@ -0,0 +1,129 @@
|
||||
# CMOD Project TODO
|
||||
|
||||
## In Progress
|
||||
|
||||
### V2 Namespace Removal
|
||||
- [ ] Complete migration of Doctor role to new architecture
|
||||
- [ ] Complete migration of Analyst role to new architecture
|
||||
- [ ] Complete migration of CS (Customer Service) role to new architecture
|
||||
|
||||
## Pending
|
||||
|
||||
### Print Functionality
|
||||
- [ ] Refactor print functionality from external URL (`http://glenlis/spooler_db/main_dev.php`) to internal solution
|
||||
- [ ] Add print preview capability to Admin views
|
||||
- [ ] Add print preview capability to Lab views
|
||||
- [ ] Implement server-side PDF generation for print jobs
|
||||
|
||||
### API Improvements
|
||||
- [ ] Add pagination to Users API endpoint
|
||||
- [ ] Add pagination to Requests API endpoint
|
||||
- [ ] Add search/filter capability to list endpoints
|
||||
- [ ] Add audit logging for critical operations (create/update/delete user, validate/unvalidate request)
|
||||
|
||||
### Frontend Improvements
|
||||
- [ ] Add loading states to all API calls
|
||||
- [ ] Add toast notifications for success/error feedback
|
||||
- [ ] Implement form validation with clear error messages
|
||||
- [ ] Add confirmation dialogs for destructive actions (delete, unvalidate)
|
||||
|
||||
### Role-Based Access Control
|
||||
- [ ] Document current permission matrix for each role
|
||||
- [ ] Add permission checks to API endpoints
|
||||
- [ ] Create shared permission constants in a central location
|
||||
|
||||
### Testing
|
||||
- [ ] Set up automated tests for API endpoints
|
||||
- [ ] Add unit tests for controller logic
|
||||
- [ ] Add integration tests for critical workflows
|
||||
|
||||
### Documentation
|
||||
- [ ] Update README with current architecture overview
|
||||
- [ ] Document API endpoints with examples
|
||||
- [ ] Document database schema changes
|
||||
|
||||
## Completed
|
||||
|
||||
### V2 Namespace Removal
|
||||
- [x] Created AGENTS.md with project conventions
|
||||
- [x] Moved v2/admin views to views/admin
|
||||
- [x] Moved v2/lab views to views/lab
|
||||
- [x] Renamed V2.php controller to Auth.php
|
||||
- [x] Renamed V2/Admin.php controller to Admin.php
|
||||
- [x] Renamed V2/Lab.php controller to Lab.php
|
||||
- [x] Renamed V2/Users.php controller to Users.php
|
||||
- [x] Renamed V2/Samples.php controller to Samples.php
|
||||
- [x] Renamed V2/Requests.php controller to Requests.php
|
||||
- [x] Updated routes to remove v2 prefix
|
||||
- [x] Updated view paths in all controllers
|
||||
- [x] Fixed hardcoded date bug in views (changed to dynamic date)
|
||||
- [x] Fixed status color mappings (added PartColl, PartRecv, partial statuses)
|
||||
- [x] Fixed missing variables in Samples controller
|
||||
- [x] Fixed duplicate db_connect() calls in Requests controller
|
||||
- [x] Fixed id parameter in Users::update()
|
||||
- [x] Cleaned up V2 namespace directory
|
||||
- [x] Cleaned up old controller files (Admin.php, Doctor.php, Analyst.php, CustomerService.php)
|
||||
- [x] Backed up old views to views/backup/
|
||||
- [x] Updated AGENTS.md with new code examples
|
||||
|
||||
## Backlog
|
||||
|
||||
### Features
|
||||
- [ ] Add user profile page with activity history
|
||||
- [ ] Add bulk import for users
|
||||
- [ ] Add bulk operations for sample management
|
||||
- [ ] Add export to CSV/Excel functionality
|
||||
- [ ] Add dashboard analytics and statistics
|
||||
|
||||
### Technical Debt
|
||||
- [ ] Remove unused dependencies from composer.json
|
||||
- [ ] Clean up unused view files in views/backup/
|
||||
- [ ] Add type hints to all controller methods
|
||||
- [ ] Add return type declarations to all controller methods
|
||||
- [ ] Consolidate duplicate code in dialog components
|
||||
|
||||
### Security
|
||||
- [ ] Add rate limiting to login endpoint
|
||||
- [ ] Add CSRF protection to forms
|
||||
- [ ] Implement password strength requirements
|
||||
- [ ] Add session timeout configuration
|
||||
- [ ] Audit all SQL queries for potential injection vulnerabilities
|
||||
|
||||
## Notes
|
||||
|
||||
### Print Functionality Current State
|
||||
The current print implementation uses an external URL that opens a separate window:
|
||||
```javascript
|
||||
BASEURL + 'http://glenlis/spooler_db/main_dev.php?req_id=' + req_id
|
||||
```
|
||||
This should be replaced with:
|
||||
1. Server-side PDF generation using a library like TCPDF or Dompdf
|
||||
2. Display PDF in an iframe for preview before print
|
||||
3. Send directly to printer using browser print API or WebSocket to print server
|
||||
|
||||
### Role Permissions
|
||||
- **Admin (1)**: Full access to all features including user management
|
||||
- **Doctor/Lab (2)**: Sample collection, validation
|
||||
- **Analyst (3)**: Sample validation, report generation
|
||||
- **CS (4)**: Read-only access to requests and samples
|
||||
|
||||
### Database Tables
|
||||
- `GDC_CMOD.dbo.USERS` - User accounts
|
||||
- `GDC_CMOD.dbo.REQUESTS` - Test requests
|
||||
- `GDC_CMOD.dbo.SAMPLES` - Sample records
|
||||
- `glendb.dbo.*` - Reference data (tests, panels, etc.)
|
||||
|
||||
### API Endpoints
|
||||
All API endpoints return JSON responses and should follow consistent format:
|
||||
```json
|
||||
{
|
||||
"data": [...] // or single object for GET by ID
|
||||
}
|
||||
```
|
||||
|
||||
Error responses:
|
||||
```json
|
||||
{
|
||||
"message": "Error description"
|
||||
}
|
||||
```
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
use CodeIgniter\Router\RouteCollection;
|
||||
|
||||
// ------------------------------------------------------For Error Handling------------------------------------------------------ //
|
||||
$routes->set404Override(function() {
|
||||
$response = service('response');
|
||||
$response->setStatusCode(404);
|
||||
@ -11,96 +10,39 @@ $routes->set404Override(function() {
|
||||
$routes->get('/unauthorized', 'ErrorPage::unauthorized');
|
||||
|
||||
|
||||
// ------------------------------------------------------Basic Page and Login/Logout------------------------------------------------------ //
|
||||
$routes->get('/', 'Home::index');
|
||||
$routes->match(['get','post'],'/login', 'Auth::login', ['filter' => 'guest']);
|
||||
$routes->get('/login', 'Auth::loginPage', ['filter' => 'guest']);
|
||||
$routes->post('/login', 'Auth::login', ['filter' => 'guest']);
|
||||
$routes->get('/logout', 'Auth::logout');
|
||||
$routes->patch('/setPassword', 'Auth::setPassword');
|
||||
|
||||
$routes->get('label/coll/(:any)', 'Label::coll/$1');
|
||||
$routes->get('label/dispatch/(:any)/(:any)', 'Label::dispatch/$1/$2');
|
||||
$routes->get('label/all/(:any)', 'Label::print_all/$1');
|
||||
|
||||
// ------------------------------------------------------Page Based on Role------------------------------------------------------ //
|
||||
$routes->group('admin', ['filter' => 'role:1'], function($routes) {
|
||||
$routes->get('/', 'Admin::index');
|
||||
$routes->get('modal_specimen', 'Admin::modal_specimen');
|
||||
$routes->get('user', 'User::index');
|
||||
$routes->post('user/create', 'User::create');
|
||||
$routes->post('user/update', 'User::update');
|
||||
$routes->post('user/delete', 'User::delete');
|
||||
$routes->get('', 'Admin::index');
|
||||
$routes->get('users', 'Admin::users');
|
||||
$routes->get('api/users', 'Users::index');
|
||||
$routes->post('api/users', 'Users::create');
|
||||
$routes->patch('api/users/(:any)', 'Users::update/$1');
|
||||
$routes->delete('api/users/(:any)', 'Users::delete/$1');
|
||||
$routes->get('api/requests', 'Requests::index');
|
||||
$routes->post('api/requests/validate/(:any)', 'Requests::val/$1');
|
||||
$routes->delete('api/requests/validate/(:any)', 'Requests::unval/$1');
|
||||
$routes->post('api/samples/collect/(:any)', 'Samples::collect/$1');
|
||||
$routes->delete('api/samples/collect/(:any)', 'Samples::uncollect/$1');
|
||||
$routes->delete('api/samples/receive/(:any)', 'Samples::unreceive/$1');
|
||||
$routes->get('api/samples/(:any)', 'Samples::show/$1');
|
||||
});
|
||||
|
||||
$routes->group('doctor', ['filter' => 'role:2'], function($routes) {
|
||||
$routes->get('/', 'Doctor::index');
|
||||
$routes->get('modal_specimen', 'Doctor::modal_specimen');
|
||||
$routes->group('lab', ['filter' => 'role:2'], function($routes) {
|
||||
$routes->get('', 'Lab::index');
|
||||
$routes->get('api/requests', 'Requests::index');
|
||||
$routes->post('api/requests/validate/(:any)', 'Requests::val/$1');
|
||||
$routes->delete('api/requests/validate/(:any)', 'Requests::unval/$1');
|
||||
$routes->post('api/samples/collect/(:any)', 'Samples::collect/$1');
|
||||
$routes->get('api/samples/(:any)', 'Samples::show/$1');
|
||||
});
|
||||
|
||||
$routes->group('analyst', ['filter' => 'role:3'], function($routes) {
|
||||
$routes->get('/', 'Analyst::index');
|
||||
$routes->get('modal_specimen', 'Analyst::modal_specimen');
|
||||
});
|
||||
|
||||
$routes->group('cs', ['filter' => 'role:4'], function($routes) {
|
||||
$routes->get('/', 'CustomerService::index');
|
||||
});
|
||||
|
||||
// dummy long page
|
||||
$routes->get('/dummypage', 'Home::dummyPage');
|
||||
|
||||
// ------------------------------------------------------For API------------------------------------------------------ //
|
||||
// $routes->get('/api/dashboard', 'ApiDashboard::index');
|
||||
$routes->get('api/request/unvalidate/(:any)', 'Request::showUnval/$1');
|
||||
$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/sample/(:any)', 'Sample::show/$1');
|
||||
$routes->post('api/sample/collect/(:any)', 'Sample::collect/$1');
|
||||
$routes->delete('api/sample/collect/(:any)', 'Sample::uncollect/$1');
|
||||
$routes->delete('api/sample/receive/(:any)', 'Sample::unreceive/$1');
|
||||
|
||||
|
||||
$routes->get('api/specimen/(:any)', 'Specimen::show/$1');
|
||||
$routes->post('api/specimen/collect/(:any)', 'Specimen::collect/$1');
|
||||
$routes->delete('api/specimen/receive/(:any)', 'Specimen::unreceive/$1');
|
||||
|
||||
/*- lets go alpine -*/
|
||||
|
||||
$routes->group('v2', function($routes) {
|
||||
$routes->get('', 'V2::index');
|
||||
$routes->get('login', 'V2::loginPage');
|
||||
$routes->post('login', 'V2::login');
|
||||
$routes->get('logout', 'V2::logout');
|
||||
$routes->patch('setPassword', 'V2::setPassword');
|
||||
// Admin
|
||||
$routes->group('admin', ['filter' => 'role:1'], function($routes) {
|
||||
$routes->get('', 'V2\Admin::index');
|
||||
$routes->get('users', 'V2\Admin::users');
|
||||
// Users
|
||||
$routes->get('api/users', 'V2\Users::index');
|
||||
$routes->post('api/users', 'V2\Users::create');
|
||||
$routes->patch('api/users/(:any)', 'V2\Users::update/$1');
|
||||
$routes->delete('api/users/(:any)', 'V2\Users::delete/$1');
|
||||
// Request
|
||||
$routes->get('api/requests', 'V2\Requests::index');
|
||||
$routes->post('api/requests/validate/(:any)', 'V2\Requests::val/$1');
|
||||
$routes->delete('api/requests/validate/(:any)', 'V2\Requests::unval/$1');
|
||||
// Samples
|
||||
$routes->post('api/samples/collect/(:any)', 'V2\Samples::collect/$1');
|
||||
$routes->delete('api/samples/collect/(:any)', 'V2\Samples::uncollect/$1');
|
||||
$routes->delete('api/samples/receive/(:any)', 'V2\Samples::unreceive/$1');
|
||||
$routes->get('api/samples/(:any)', 'V2\Samples::show/$1');
|
||||
});
|
||||
// Lab
|
||||
$routes->group('lab', ['filter' => 'role:2'], function($routes) {
|
||||
$routes->get('', 'V2\Lab::index');
|
||||
// Request
|
||||
$routes->get('api/requests', 'V2\Requests::index');
|
||||
$routes->post('api/requests/validate/(:any)', 'V2\Requests::val/$1');
|
||||
$routes->delete('api/requests/validate/(:any)', 'V2\Requests::unval/$1');
|
||||
// Samples
|
||||
$routes->post('api/samples/collect/(:any)', 'V2\Samples::collect/$1');
|
||||
$routes->get('api/samples/(:any)', 'V2\Samples::show/$1');
|
||||
});
|
||||
});
|
||||
@ -1,116 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use CodeIgniter\Validation\CreditCardRules;
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
class Admin extends BaseController {
|
||||
|
||||
public function __construct() {
|
||||
helper(['url', 'form', 'text']);
|
||||
}
|
||||
|
||||
public function index() {
|
||||
$today = date('Y-m-d');
|
||||
$date1 = $this->request->getVar('date1') ?? $today;
|
||||
$date2 = $this->request->getVar('date2') ?? $today;
|
||||
|
||||
$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'";
|
||||
$query = $db->query($sql);
|
||||
$results = $query->getResultArray();
|
||||
|
||||
// === Variabel counter ===
|
||||
$CPending = 0; $CPColl = 0; $CColl = 0; $CPRecv = 0;
|
||||
$CRecv = 0; $CInc = 0; $CPenV = 0; $CFin = 0; $CFinV = 0;
|
||||
$CTotal = 0;
|
||||
|
||||
$dataList = [];
|
||||
|
||||
foreach ($results as $row) {
|
||||
$reqdate = '';
|
||||
if (!empty($row['REQDATE'])) {
|
||||
$reqdate = date('Y-m-d H:i', strtotime($row['REQDATE']));
|
||||
}
|
||||
$patname = $row['Name'] ?? '';
|
||||
$sp_accessnumber = $row['SP_ACCESSNUMBER'] ?? '';
|
||||
$hostordernumber = $row['HOSTORDERNUMBER'] ?? '';
|
||||
$stats = $row['STATS'] ?? '';
|
||||
$tests = $row['TESTS'] ?? '';
|
||||
$isDelete = $row['ISDELETE'] ?? 0;
|
||||
|
||||
// Bersihkan test string
|
||||
$test = str_replace(['(', ')', ',', 'FA'], '', $tests);
|
||||
|
||||
if (!is_numeric($test) && $isDelete == 0) {
|
||||
switch ($stats) {
|
||||
case 'Pend': $statscode = 1; $CPending++; break;
|
||||
case 'PartColl':$statscode = 2; $CPColl++; break;
|
||||
case 'Coll': $statscode = 3; $CColl++; break;
|
||||
case 'PartRecv':$statscode = 4; $CPRecv++; break;
|
||||
case 'Recv': $statscode = 5; $CRecv++; break;
|
||||
case 'Inc': $statscode = 6; $CInc++; break;
|
||||
case 'PenV': $statscode = 7; $CPenV++; break;
|
||||
case 'Fin': $statscode = 8; $CFin++; break;
|
||||
case 'FinV': $statscode = 9; $CFinV++; break;
|
||||
default: $statscode = 0; break;
|
||||
}
|
||||
$CTotal++;
|
||||
// Simpan ke array
|
||||
$dataList[] = [
|
||||
'statscode' => $statscode,
|
||||
'reqdate' => $reqdate,
|
||||
'patname' => $patname,
|
||||
'sp_accessnumber' => $sp_accessnumber,
|
||||
'hostordernumber' => $hostordernumber,
|
||||
'reff' => $row['REFF'] ?? '',
|
||||
'doc' => $row['DOC'] ?? '',
|
||||
'tests' => $row['TESTS'] ?? '',
|
||||
'stats' => $stats,
|
||||
'odr_cresult_to' => $row['ODR_CRESULT_TO'],
|
||||
'isprinted'=> $row['ODR_ISPRINTED'] ?? 0,
|
||||
'ispending'=> $row['ODR_ISPENDING'] ?? 0,
|
||||
'ishardcopy'=> $row['ODR_NFLAGHARDCOPY'] ?? 0,
|
||||
'isvaltd' => $row['ISVAL'] ?? 0,
|
||||
// 'isval1' => $row['ISVAL1'] ?? 0,
|
||||
// 'isval2' => $row['ISVAL2'] ?? 0,
|
||||
'isdelete' => $isDelete,
|
||||
'valcounter' => $row['VAL'] ?? 0,
|
||||
'val1user' => $row['VAL1USER'] ?? '-',
|
||||
'val2user' => $row['VAL2USER'] ?? '-',
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// === Total counter ===
|
||||
$counter = [
|
||||
'pending' => $CPending,
|
||||
'collect' => $CPColl+$CColl,
|
||||
'recv' => $CPRecv+$CRecv,
|
||||
'incomplete' => $CInc+$CPenV,
|
||||
'complete' => $CFin+$CFinV,
|
||||
'total' => $CTotal,
|
||||
/*
|
||||
'partialCollected' => $CPColl,
|
||||
'collected' => $CColl,
|
||||
'partialReceived' => $CPRecv,
|
||||
'received' => $CRecv,
|
||||
'incomplete' => $CInc,
|
||||
'pendingValidation' => $CPenV,
|
||||
'final' => $CFin,
|
||||
'finalValidation' => $CFinV,
|
||||
*/
|
||||
];
|
||||
|
||||
$data['dataList'] = $dataList;
|
||||
$data['counter'] = $counter;
|
||||
$data['date1'] = $date1;
|
||||
$data['date2'] = $date2;
|
||||
|
||||
// dd($results);
|
||||
|
||||
|
||||
return view('admin/index', $data);
|
||||
return view('admin/index');
|
||||
}
|
||||
|
||||
public function modal_specimen() {
|
||||
return view('admin/modal_specimen');
|
||||
public function users() {
|
||||
return view('admin/users');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
class Analyst extends BaseController
|
||||
{
|
||||
public function index() {
|
||||
$today = date('Y-m-d');
|
||||
$date1 = $this->request->getVar('date1') ?? $today;
|
||||
$date2 = $this->request->getVar('date2') ?? $today;
|
||||
|
||||
$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'";
|
||||
$query = $db->query($sql);
|
||||
$results = $query->getResultArray();
|
||||
|
||||
// === Variabel counter ===
|
||||
$CPending = 0; $CPColl = 0; $CColl = 0; $CPRecv = 0;
|
||||
$CRecv = 0; $CInc = 0; $CPenV = 0; $CFin = 0; $CFinV = 0;
|
||||
$CTotal = 0;
|
||||
|
||||
$dataList = [];
|
||||
|
||||
foreach ($results as $row) {
|
||||
$reqdate = '';
|
||||
if (!empty($row['REQDATE'])) {
|
||||
$reqdate = date('Y-m-d H:i', strtotime($row['REQDATE']));
|
||||
}
|
||||
$patname = $row['Name'] ?? '';
|
||||
$sp_accessnumber = $row['SP_ACCESSNUMBER'] ?? '';
|
||||
$hostordernumber = $row['HOSTORDERNUMBER'] ?? '';
|
||||
$stats = $row['STATS'] ?? '';
|
||||
$tests = $row['TESTS'] ?? '';
|
||||
$isDelete = $row['ISDELETE'] ?? 0;
|
||||
|
||||
// Bersihkan test string
|
||||
$test = str_replace(['(', ')', ',', 'FA'], '', $tests);
|
||||
|
||||
if (!is_numeric($test) && $isDelete == 0) {
|
||||
switch ($stats) {
|
||||
case 'Pend': $statscode = 1; $CPending++; break;
|
||||
case 'PartColl':$statscode = 2; $CPColl++; break;
|
||||
case 'Coll': $statscode = 3; $CColl++; break;
|
||||
case 'PartRecv':$statscode = 4; $CPRecv++; break;
|
||||
case 'Recv': $statscode = 5; $CRecv++; break;
|
||||
case 'Inc': $statscode = 6; $CInc++; break;
|
||||
case 'PenV': $statscode = 7; $CPenV++; break;
|
||||
case 'Fin': $statscode = 8; $CFin++; break;
|
||||
case 'FinV': $statscode = 9; $CFinV++; break;
|
||||
default: $statscode = 0; break;
|
||||
}
|
||||
$CTotal++;
|
||||
// Simpan ke array
|
||||
$dataList[] = [
|
||||
'statscode' => $statscode,
|
||||
'reqdate' => $reqdate,
|
||||
'patname' => $patname,
|
||||
'sp_accessnumber' => $sp_accessnumber,
|
||||
'hostordernumber' => $hostordernumber,
|
||||
'reff' => $row['REFF'] ?? '',
|
||||
'doc' => $row['DOC'] ?? '',
|
||||
'tests' => $row['TESTS'] ?? '',
|
||||
'stats' => $stats,
|
||||
'odr_cresult_to' => $row['ODR_CRESULT_TO'],
|
||||
'isprinted'=> $row['ODR_ISPRINTED'] ?? 0,
|
||||
'ispending'=> $row['ODR_ISPENDING'] ?? 0,
|
||||
'ishardcopy'=> $row['ODR_NFLAGHARDCOPY'] ?? 0,
|
||||
'isvaltd' => $row['ISVAL'] ?? 0,
|
||||
// 'isval1' => $row['ISVAL1'] ?? 0,
|
||||
// 'isval2' => $row['ISVAL2'] ?? 0,
|
||||
'isdelete' => $isDelete,
|
||||
'valcounter' => $row['VAL'] ?? 0,
|
||||
'val1user' => $row['VAL1USER'] ?? '-',
|
||||
'val2user' => $row['VAL2USER'] ?? '-',
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// === Total counter ===
|
||||
$counter = [
|
||||
'pending' => $CPending,
|
||||
'collect' => $CPColl+$CColl,
|
||||
'recv' => $CPRecv+$CRecv,
|
||||
'incomplete' => $CInc+$CPenV,
|
||||
'complete' => $CFin+$CFinV,
|
||||
'total' => $CTotal,
|
||||
/*
|
||||
'partialCollected' => $CPColl,
|
||||
'collected' => $CColl,
|
||||
'partialReceived' => $CPRecv,
|
||||
'received' => $CRecv,
|
||||
'incomplete' => $CInc,
|
||||
'pendingValidation' => $CPenV,
|
||||
'final' => $CFin,
|
||||
'finalValidation' => $CFinV,
|
||||
*/
|
||||
];
|
||||
|
||||
$data['dataList'] = $dataList;
|
||||
$data['counter'] = $counter;
|
||||
$data['date1'] = $date1;
|
||||
$data['date2'] = $date2;
|
||||
|
||||
// dd($results);
|
||||
|
||||
|
||||
return view('analyst/index', $data);
|
||||
}
|
||||
|
||||
public function modal_specimen() {
|
||||
return view('analyst/modal_specimen');
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,60 +2,85 @@
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
class Auth extends BaseController {
|
||||
|
||||
public function loginPage() {
|
||||
return view("login");
|
||||
}
|
||||
|
||||
public function login() {
|
||||
if ($this->request->getMethod() === 'GET') {
|
||||
return view('login');
|
||||
} else if ($this->request->getMethod() === 'POST') {
|
||||
helper(['form', 'url']);
|
||||
$session = session();
|
||||
$db = \Config\Database::connect();
|
||||
helper(['form', 'url']);
|
||||
$session = session();
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
$userid = strtoupper(trim($this->request->getPost('userid')));
|
||||
$password = $this->request->getPost('password');
|
||||
$userid = strtoupper(trim($this->request->getPost('userid')));
|
||||
$password = $this->request->getPost('password');
|
||||
|
||||
// Gunakan raw SQL sesuai kolom di tabel kamu
|
||||
$query = $db->query("SELECT * FROM gdc_cmod.dbo.USERS WHERE USERID = ?", [$userid]);
|
||||
$user = $query->getRowArray();
|
||||
$query = $db->query("SELECT * FROM gdc_cmod.dbo.USERS WHERE USERID = ?", [$userid]);
|
||||
$user = $query->getRowArray();
|
||||
|
||||
if ($user && !empty($user['PASSWORD']) && password_verify($password, $user['PASSWORD'])) {
|
||||
if ($user && !empty($user['PASSWORD']) && password_verify($password, $user['PASSWORD'])) {
|
||||
|
||||
// Role untuk url
|
||||
switch ((int)$user['USERLEVEL']) {
|
||||
case 1: $role = 'admin'; break;
|
||||
case 2: $role = 'doctor'; break;
|
||||
case 3: $role = 'analyst'; break;
|
||||
case 4: $role = 'cs'; break;
|
||||
default: $role = ''; break;
|
||||
}
|
||||
|
||||
// Simpan session
|
||||
$session->set([
|
||||
'isLoggedIn' => true,
|
||||
'userid' => (string) $user['USERID'],
|
||||
'userlevel' => (int) $user['USERLEVEL'],
|
||||
'userrole' => (string) $role,
|
||||
]);
|
||||
|
||||
// Redirect sesuai level dari data didatabase
|
||||
switch ((int)$user['USERLEVEL']) {
|
||||
case 1: return redirect()->to('/admin');
|
||||
case 2: return redirect()->to('/doctor');
|
||||
case 3: return redirect()->to('/analyst');
|
||||
case 4: return redirect()->to('/cs');
|
||||
default: return redirect()->to('/login');
|
||||
}
|
||||
} else {
|
||||
$session->setFlashdata('error', 'USERID atau PASSWORD salah.');
|
||||
return redirect()->back();
|
||||
switch ((int)$user['USERLEVEL']) {
|
||||
case 1:
|
||||
$role = 'admin';
|
||||
break;
|
||||
case 2:
|
||||
$role = 'analyst';
|
||||
break;
|
||||
case 3:
|
||||
$role = 'phlebotomist';
|
||||
break;
|
||||
case 4:
|
||||
$role = 'cs';
|
||||
break;
|
||||
default:
|
||||
$role = '';
|
||||
break;
|
||||
}
|
||||
|
||||
$session->set([
|
||||
'isLoggedIn' => true,
|
||||
'userid' => (string) $user['USERID'],
|
||||
'userlevel' => (int) $user['USERLEVEL'],
|
||||
'userrole' => (string) $role,
|
||||
]);
|
||||
|
||||
switch ((int)$user['USERLEVEL']) {
|
||||
case 1:
|
||||
return redirect()->to('admin');
|
||||
case 2:
|
||||
return redirect()->to('lab');
|
||||
case 3:
|
||||
return redirect()->to('analyst');
|
||||
case 4:
|
||||
return redirect()->to('cs');
|
||||
default:
|
||||
return redirect()->to('login');
|
||||
}
|
||||
} else {
|
||||
$session->setFlashdata('error', 'USERID atau PASSWORD salah.');
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
session()->destroy();
|
||||
return redirect()->to('/login');
|
||||
$session = session();
|
||||
$session->destroy();
|
||||
return redirect()->to('login');
|
||||
}
|
||||
|
||||
public function setPassword() {
|
||||
$input = $this->request->getJSON(true);
|
||||
$userid = $input['userid'];
|
||||
$password = $input['password'];
|
||||
$password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$db = db_connect();
|
||||
$sql = "update GDC_CMOD.dbo.USERS set PASSWORD='$password' where USERID='$userid'";
|
||||
$db->query($sql);
|
||||
$data = ['status' => 'success', 'message' => 'Password updated successfully', 'data' => "$userid" ];
|
||||
return $this->response->setJSON($data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,111 +0,0 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
class CustomerService extends BaseController
|
||||
{
|
||||
public function index() {
|
||||
$today = date('Y-m-d');
|
||||
$date1 = $this->request->getVar('date1') ?? $today;
|
||||
$date2 = $this->request->getVar('date2') ?? $today;
|
||||
|
||||
$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'";
|
||||
$query = $db->query($sql);
|
||||
$results = $query->getResultArray();
|
||||
|
||||
// === Variabel counter ===
|
||||
$CPending = 0; $CPColl = 0; $CColl = 0; $CPRecv = 0;
|
||||
$CRecv = 0; $CInc = 0; $CPenV = 0; $CFin = 0; $CFinV = 0;
|
||||
$CTotal = 0;
|
||||
|
||||
$dataList = [];
|
||||
|
||||
foreach ($results as $row) {
|
||||
$reqdate = '';
|
||||
if (!empty($row['REQDATE'])) {
|
||||
$reqdate = date('Y-m-d H:i', strtotime($row['REQDATE']));
|
||||
}
|
||||
$patname = $row['Name'] ?? '';
|
||||
$sp_accessnumber = $row['SP_ACCESSNUMBER'] ?? '';
|
||||
$hostordernumber = $row['HOSTORDERNUMBER'] ?? '';
|
||||
$stats = $row['STATS'] ?? '';
|
||||
$tests = $row['TESTS'] ?? '';
|
||||
$isDelete = $row['ISDELETE'] ?? 0;
|
||||
|
||||
// Bersihkan test string
|
||||
$test = str_replace(['(', ')', ',', 'FA'], '', $tests);
|
||||
|
||||
if (!is_numeric($test) && $isDelete == 0) {
|
||||
switch ($stats) {
|
||||
case 'Pend': $statscode = 1; $CPending++; break;
|
||||
case 'PartColl':$statscode = 2; $CPColl++; break;
|
||||
case 'Coll': $statscode = 3; $CColl++; break;
|
||||
case 'PartRecv':$statscode = 4; $CPRecv++; break;
|
||||
case 'Recv': $statscode = 5; $CRecv++; break;
|
||||
case 'Inc': $statscode = 6; $CInc++; break;
|
||||
case 'PenV': $statscode = 7; $CPenV++; break;
|
||||
case 'Fin': $statscode = 8; $CFin++; break;
|
||||
case 'FinV': $statscode = 9; $CFinV++; break;
|
||||
default: $statscode = 0; break;
|
||||
}
|
||||
$CTotal++;
|
||||
// Simpan ke array
|
||||
$dataList[] = [
|
||||
'statscode' => $statscode,
|
||||
'reqdate' => $reqdate,
|
||||
'patname' => $patname,
|
||||
'sp_accessnumber' => $sp_accessnumber,
|
||||
'hostordernumber' => $hostordernumber,
|
||||
'reff' => $row['REFF'] ?? '',
|
||||
'doc' => $row['DOC'] ?? '',
|
||||
'tests' => $row['TESTS'] ?? '',
|
||||
'stats' => $stats,
|
||||
'odr_cresult_to' => $row['ODR_CRESULT_TO'],
|
||||
'isprinted'=> $row['ODR_ISPRINTED'] ?? 0,
|
||||
'ispending'=> $row['ODR_ISPENDING'] ?? 0,
|
||||
'ishardcopy'=> $row['ODR_NFLAGHARDCOPY'] ?? 0,
|
||||
'isvaltd' => $row['ISVAL'] ?? 0,
|
||||
// 'isval1' => $row['ISVAL1'] ?? 0,
|
||||
// 'isval2' => $row['ISVAL2'] ?? 0,
|
||||
'isdelete' => $isDelete,
|
||||
'valcounter' => $row['VAL'] ?? 0,
|
||||
'val1user' => $row['VAL1USER'] ?? '-',
|
||||
'val2user' => $row['VAL2USER'] ?? '-',
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// === Total counter ===
|
||||
$counter = [
|
||||
'pending' => $CPending,
|
||||
'collect' => $CPColl+$CColl,
|
||||
'recv' => $CPRecv+$CRecv,
|
||||
'incomplete' => $CInc+$CPenV,
|
||||
'complete' => $CFin+$CFinV,
|
||||
'total' => $CTotal,
|
||||
/*
|
||||
'partialCollected' => $CPColl,
|
||||
'collected' => $CColl,
|
||||
'partialReceived' => $CPRecv,
|
||||
'received' => $CRecv,
|
||||
'incomplete' => $CInc,
|
||||
'pendingValidation' => $CPenV,
|
||||
'final' => $CFin,
|
||||
'finalValidation' => $CFinV,
|
||||
*/
|
||||
];
|
||||
|
||||
$data['dataList'] = $dataList;
|
||||
$data['counter'] = $counter;
|
||||
$data['date1'] = $date1;
|
||||
$data['date2'] = $date2;
|
||||
|
||||
// dd($results);
|
||||
|
||||
|
||||
return view('cs/index', $data);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,115 +0,0 @@
|
||||
<?php
|
||||
namespace App\Controllers;
|
||||
|
||||
class Doctor extends BaseController
|
||||
{
|
||||
public function index() {
|
||||
$today = date('Y-m-d');
|
||||
$date1 = $this->request->getVar('date1') ?? $today;
|
||||
$date2 = $this->request->getVar('date2') ?? $today;
|
||||
|
||||
$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'";
|
||||
$query = $db->query($sql);
|
||||
$results = $query->getResultArray();
|
||||
|
||||
// === Variabel counter ===
|
||||
$CPending = 0; $CPColl = 0; $CColl = 0; $CPRecv = 0;
|
||||
$CRecv = 0; $CInc = 0; $CPenV = 0; $CFin = 0; $CFinV = 0;
|
||||
$CTotal = 0;
|
||||
|
||||
$dataList = [];
|
||||
|
||||
foreach ($results as $row) {
|
||||
$reqdate = '';
|
||||
if (!empty($row['REQDATE'])) {
|
||||
$reqdate = date('Y-m-d H:i', strtotime($row['REQDATE']));
|
||||
}
|
||||
$patname = $row['Name'] ?? '';
|
||||
$sp_accessnumber = $row['SP_ACCESSNUMBER'] ?? '';
|
||||
$hostordernumber = $row['HOSTORDERNUMBER'] ?? '';
|
||||
$stats = $row['STATS'] ?? '';
|
||||
$tests = $row['TESTS'] ?? '';
|
||||
$isDelete = $row['ISDELETE'] ?? 0;
|
||||
|
||||
// Bersihkan test string
|
||||
$test = str_replace(['(', ')', ',', 'FA'], '', $tests);
|
||||
|
||||
if (!is_numeric($test) && $isDelete == 0) {
|
||||
switch ($stats) {
|
||||
case 'Pend': $statscode = 1; $CPending++; break;
|
||||
case 'PartColl':$statscode = 2; $CPColl++; break;
|
||||
case 'Coll': $statscode = 3; $CColl++; break;
|
||||
case 'PartRecv':$statscode = 4; $CPRecv++; break;
|
||||
case 'Recv': $statscode = 5; $CRecv++; break;
|
||||
case 'Inc': $statscode = 6; $CInc++; break;
|
||||
case 'PenV': $statscode = 7; $CPenV++; break;
|
||||
case 'Fin': $statscode = 8; $CFin++; break;
|
||||
case 'FinV': $statscode = 9; $CFinV++; break;
|
||||
default: $statscode = 0; break;
|
||||
}
|
||||
$CTotal++;
|
||||
// Simpan ke array
|
||||
$dataList[] = [
|
||||
'statscode' => $statscode,
|
||||
'reqdate' => $reqdate,
|
||||
'patname' => $patname,
|
||||
'sp_accessnumber' => $sp_accessnumber,
|
||||
'hostordernumber' => $hostordernumber,
|
||||
'reff' => $row['REFF'] ?? '',
|
||||
'doc' => $row['DOC'] ?? '',
|
||||
'tests' => $row['TESTS'] ?? '',
|
||||
'stats' => $stats,
|
||||
'odr_cresult_to' => $row['ODR_CRESULT_TO'],
|
||||
'isprinted'=> $row['ODR_ISPRINTED'] ?? 0,
|
||||
'ispending'=> $row['ODR_ISPENDING'] ?? 0,
|
||||
'ishardcopy'=> $row['ODR_NFLAGHARDCOPY'] ?? 0,
|
||||
'isvaltd' => $row['ISVAL'] ?? 0,
|
||||
// 'isval1' => $row['ISVAL1'] ?? 0,
|
||||
// 'isval2' => $row['ISVAL2'] ?? 0,
|
||||
'isdelete' => $isDelete,
|
||||
'valcounter' => $row['VAL'] ?? 0,
|
||||
'val1user' => $row['VAL1USER'] ?? '-',
|
||||
'val2user' => $row['VAL2USER'] ?? '-',
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// === Total counter ===
|
||||
$counter = [
|
||||
'pending' => $CPending,
|
||||
'collect' => $CPColl+$CColl,
|
||||
'recv' => $CPRecv+$CRecv,
|
||||
'incomplete' => $CInc+$CPenV,
|
||||
'complete' => $CFin+$CFinV,
|
||||
'total' => $CTotal,
|
||||
/*
|
||||
'partialCollected' => $CPColl,
|
||||
'collected' => $CColl,
|
||||
'partialReceived' => $CPRecv,
|
||||
'received' => $CRecv,
|
||||
'incomplete' => $CInc,
|
||||
'pendingValidation' => $CPenV,
|
||||
'final' => $CFin,
|
||||
'finalValidation' => $CFinV,
|
||||
*/
|
||||
];
|
||||
|
||||
$data['dataList'] = $dataList;
|
||||
$data['counter'] = $counter;
|
||||
$data['date1'] = $date1;
|
||||
$data['date2'] = $date2;
|
||||
|
||||
// dd($results);
|
||||
|
||||
|
||||
return view('doctor/index', $data);
|
||||
}
|
||||
|
||||
public function modal_specimen() {
|
||||
return view('doctor/modal_specimen');
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,12 +14,12 @@ class Home extends BaseController {
|
||||
}
|
||||
|
||||
// Jika sudah login, arahkan sesuai level
|
||||
switch ($session->get('level')) {
|
||||
case 1: return redirect()->to('/admin');
|
||||
case 2: return redirect()->to('/dokter');
|
||||
case 3: return redirect()->to('/analis');
|
||||
case 4: return redirect()->to('/cs');
|
||||
default: return redirect()->to('/login');
|
||||
switch ($session->get('userlevel')) {
|
||||
case 1: return redirect()->to('admin');
|
||||
case 2: return redirect()->to('lab');
|
||||
case 3: return redirect()->to('analyst');
|
||||
case 4: return redirect()->to('cs');
|
||||
default: return redirect()->to('login');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
namespace App\Controllers\V2;
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
@ -10,7 +11,7 @@ class Lab extends BaseController {
|
||||
}
|
||||
|
||||
public function index() {
|
||||
return view('v2/lab/index');
|
||||
return view('lab/index');
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace App\Controllers\V2;
|
||||
namespace App\Controllers;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
@ -7,11 +7,10 @@ class Requests extends BaseController {
|
||||
use ResponseTrait;
|
||||
|
||||
public function index() {
|
||||
$db = db_connect();
|
||||
$db = \Config\Database::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'";
|
||||
@ -27,7 +26,7 @@ class Requests extends BaseController {
|
||||
|
||||
|
||||
public function show($accessnumber) {
|
||||
$db = db_connect();
|
||||
$db = \Config\Database::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
|
||||
@ -51,7 +50,7 @@ class Requests extends BaseController {
|
||||
$input = $this->request->getJSON(true);
|
||||
$userid = $input['userid'];
|
||||
$comment = $input['comment'];
|
||||
$db = db_connect();
|
||||
$db = \Config\Database::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);
|
||||
@ -63,11 +62,9 @@ class Requests extends BaseController {
|
||||
public function val($accessnumber) {
|
||||
$input = $this->request->getJSON(true);
|
||||
$userid = $input['userid'];
|
||||
$db = db_connect();
|
||||
//cek val
|
||||
$db = \Config\Database::connect();
|
||||
$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);
|
||||
@ -79,10 +76,8 @@ class Requests extends BaseController {
|
||||
$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;
|
||||
@ -93,7 +88,6 @@ class Requests extends BaseController {
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace App\Controllers\V2;
|
||||
namespace App\Controllers;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
@ -59,7 +59,7 @@ class Samples extends BaseController {
|
||||
|
||||
return $this->response->setJSON($resp);
|
||||
}
|
||||
|
||||
|
||||
public function collect($accessnumber) {
|
||||
$db = \Config\Database::connect();
|
||||
$input = $this->request->getJSON(true);
|
||||
@ -90,7 +90,6 @@ class Samples extends BaseController {
|
||||
$db = \Config\Database::connect();
|
||||
$input = $this->request->getJSON(true);
|
||||
$samplenumber = $input['samplenumber'];
|
||||
// update firebird
|
||||
$sql = "select r.EXTERNALORDERNUMBER, dt.TESTCODE, do.HISCODE from glendb.dbo.TESTS t
|
||||
left join glendb.dbo.DICT_TESTS dt on dt.TESTID=t.TESTID
|
||||
left join glendb.dbo.REQUESTS r on r.REQUESTID=t.REQUESTID
|
||||
@ -101,6 +100,7 @@ class Samples extends BaseController {
|
||||
and r.ACCESSNUMBER='$accessnumber' and ds.SAMPCODE='$samplenumber'";
|
||||
$rows = $db->query($sql)->getResultArray();
|
||||
$his_test = '';
|
||||
$lis_test = '';
|
||||
foreach( $rows as $row ) {
|
||||
$hon = $row['EXTERNALORDERNUMBER'];
|
||||
$testcode = $row['TESTCODE'];
|
||||
@ -1,5 +1,5 @@
|
||||
<?php
|
||||
namespace App\Controllers\V2;
|
||||
namespace App\Controllers;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
@ -9,7 +9,6 @@ class Users extends BaseController {
|
||||
protected $db;
|
||||
|
||||
public function __construct() {
|
||||
// Koneksi database dan validation service
|
||||
$this->db = \Config\Database::connect();
|
||||
}
|
||||
|
||||
@ -17,22 +16,20 @@ class Users extends BaseController {
|
||||
$sql = "select u.USERID, u.USERLEVEL from GDC_CMOD.dbo.USERS u
|
||||
left join glendb.dbo.USERS u1 on u1.USERID=u.USERID
|
||||
where u1.LOCKEDACCOUNT is null";
|
||||
$query = $this->db->query($sql);
|
||||
$results = $query->getResultArray();
|
||||
$data['data'] = $results;
|
||||
$query = $this->db->query($sql);
|
||||
$results = $query->getResultArray();
|
||||
$data['data'] = $results;
|
||||
|
||||
return $this->respond(['data' => $results]);
|
||||
}
|
||||
|
||||
public function create() {
|
||||
$input = $this->request->getJSON(true);
|
||||
// ambil input
|
||||
$input = $this->request->getJSON(true);
|
||||
$userid = $input['userid'];
|
||||
$userlevel = $input['userlevel'];
|
||||
$password = $input['password'];
|
||||
$password_2 = $input['password_2'];
|
||||
|
||||
// Cek Password Apakah Sama
|
||||
if ($password != $password_2) {
|
||||
return $this->response->setJSON(['message'=> 'Password not the same']);
|
||||
}
|
||||
@ -40,17 +37,14 @@ class Users extends BaseController {
|
||||
return $this->response->setJSON(['message'=> 'Password must be more than 2 characters']);
|
||||
}
|
||||
|
||||
// Cek Apakah USERID Sama
|
||||
$sql = $this->db->query("SELECT USERID FROM gdc_cmod.dbo.USERS WHERE USERID = ?", [$userid]);
|
||||
$query = $sql->getRowArray();
|
||||
if ($query != null) {
|
||||
return $this->response->setJSON(['message'=> 'Userid already exists']);
|
||||
}
|
||||
|
||||
// Hash Password
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Insert
|
||||
$this->db->transBegin();
|
||||
try {
|
||||
$sqlInsert = "
|
||||
@ -62,32 +56,25 @@ class Users extends BaseController {
|
||||
$this->db->transCommit();
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
// Kalau ada error, rollback semua perubahan
|
||||
$this->db->transRollback();
|
||||
|
||||
// (Opsional) tampilkan atau log error
|
||||
return $this->response->setJSON(['message'=> 'Server error']);
|
||||
}
|
||||
|
||||
return $this->response->setJSON(['message'=> 'User '.$userid.' Berhasil ditambahkan!']);
|
||||
}
|
||||
|
||||
public function update() {
|
||||
public function update($id = null) {
|
||||
$input = $this->request->getJSON(true);
|
||||
$userid = $input['userid'];
|
||||
$userlevel = $input['userlevel'];
|
||||
$password = $input['password'];
|
||||
$password_2 = $input['password_2'];
|
||||
|
||||
// Jika password tidak kosong - Lakukan Full Update
|
||||
if ( $password != '' || $password_2 != '') {
|
||||
|
||||
// Cek Password Apakah Sama
|
||||
if ($password != $password_2) {
|
||||
return $this->response->setJSON(['message'=> 'Password not the same']);
|
||||
}
|
||||
|
||||
// Hash Password
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
$sqlUpdate ="
|
||||
UPDATE gdc_cmod.dbo.USERS
|
||||
@ -97,7 +84,6 @@ class Users extends BaseController {
|
||||
";
|
||||
$fullUpdate = true;
|
||||
|
||||
// Jika password kosong - Lakukan Partial Update Saja
|
||||
} else {
|
||||
$sqlUpdate ="
|
||||
UPDATE gdc_cmod.dbo.USERS
|
||||
@ -107,7 +93,6 @@ class Users extends BaseController {
|
||||
$fullUpdate = false;
|
||||
}
|
||||
|
||||
// Insert
|
||||
$this->db->transBegin();
|
||||
try {
|
||||
|
||||
@ -120,10 +105,7 @@ class Users extends BaseController {
|
||||
$this->db->transCommit();
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
// Kalau ada error, rollback semua perubahan
|
||||
$this->db->transRollback();
|
||||
|
||||
// (Opsional) tampilkan atau log error
|
||||
return $this->response->setJSON(['message'=> 'Terjadi kesalahan pada server.']);
|
||||
}
|
||||
|
||||
@ -1,112 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
|
||||
class V2 extends BaseController {
|
||||
|
||||
public function index() {
|
||||
$session = session();
|
||||
|
||||
if (! $session->get('isLoggedIn')) {
|
||||
return redirect()->to('v2/login');
|
||||
}
|
||||
|
||||
// Jika sudah login, arahkan sesuai level
|
||||
switch ($session->get('level')) {
|
||||
case 1:
|
||||
return redirect()->to('v2/admin');
|
||||
case 2:
|
||||
return redirect()->to('v2/analyst');
|
||||
case 3:
|
||||
return redirect()->to('v2/phlebotomist');
|
||||
case 4:
|
||||
return redirect()->to('v2/cs');
|
||||
default:
|
||||
return redirect()->to('v2/login');
|
||||
}
|
||||
}
|
||||
|
||||
public function loginPage() {
|
||||
return view("v2/login");
|
||||
}
|
||||
|
||||
public function login() {
|
||||
helper(['form', 'url']);
|
||||
$session = session();
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
$userid = strtoupper(trim($this->request->getPost('userid')));
|
||||
$password = $this->request->getPost('password');
|
||||
|
||||
// Gunakan raw SQL sesuai kolom di tabel kamu
|
||||
$query = $db->query("SELECT * FROM gdc_cmod.dbo.USERS WHERE USERID = ?", [$userid]);
|
||||
$user = $query->getRowArray();
|
||||
|
||||
if ($user && !empty($user['PASSWORD']) && password_verify($password, $user['PASSWORD'])) {
|
||||
|
||||
// Role untuk url
|
||||
switch ((int)$user['USERLEVEL']) {
|
||||
case 1:
|
||||
$role = 'admin';
|
||||
break;
|
||||
case 2:
|
||||
$role = 'analyst';
|
||||
break;
|
||||
case 3:
|
||||
$role = 'phlebotomist';
|
||||
break;
|
||||
case 4:
|
||||
$role = 'cs';
|
||||
break;
|
||||
default:
|
||||
$role = '';
|
||||
break;
|
||||
}
|
||||
|
||||
// Simpan session
|
||||
$session->set([
|
||||
'isLoggedIn' => true,
|
||||
'userid' => (string) $user['USERID'],
|
||||
'userlevel' => (int) $user['USERLEVEL'],
|
||||
'userrole' => (string) $role,
|
||||
]);
|
||||
|
||||
// Redirect sesuai level dari data didatabase
|
||||
switch ((int)$user['USERLEVEL']) {
|
||||
case 1:
|
||||
return redirect()->to('v2/admin');
|
||||
case 2:
|
||||
return redirect()->to('v2/lab');
|
||||
case 3:
|
||||
return redirect()->to('v2/phlebotomist');
|
||||
case 4:
|
||||
return redirect()->to('v2/cs');
|
||||
default:
|
||||
return redirect()->to('v2/login');
|
||||
}
|
||||
} else {
|
||||
$session->setFlashdata('error', 'USERID atau PASSWORD salah.');
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
$session = session();
|
||||
$session->destroy();
|
||||
return redirect()->to('v2/login');
|
||||
}
|
||||
|
||||
public function setPassword() {
|
||||
$input = $this->request->getJSON(true);
|
||||
$userid = $input['userid'];
|
||||
$password = $input['password'];
|
||||
$password = password_hash($password, PASSWORD_DEFAULT);
|
||||
$db = db_connect();
|
||||
$sql = "update GDC_CMOD.dbo.USERS set PASSWORD='$password' where USERID='$userid'";
|
||||
$db->query($sql);
|
||||
$data = ['status' => 'success', 'message' => 'Password updated successfully', 'data' => "$userid" ];
|
||||
return $this->response->setJSON($data);
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
namespace App\Controllers\V2;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
|
||||
class Admin extends BaseController {
|
||||
use ResponseTrait;
|
||||
|
||||
protected $db;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = \Config\Database::connect();
|
||||
helper(['url', 'form', 'text']);
|
||||
}
|
||||
|
||||
public function index() {
|
||||
return view('v2/admin/index');
|
||||
}
|
||||
|
||||
public function users() {
|
||||
return view('v2/admin/users');
|
||||
}
|
||||
}
|
||||
@ -1,157 +0,0 @@
|
||||
<?php
|
||||
// 1_Digunakan Untuk Nav Link Pada Side Bar Yg Aktif dan Tidak
|
||||
$uri = service('uri');
|
||||
$path = $uri->getPath();
|
||||
$activeUrls = ['/admin', '/cs', '/analyst', '/doctor'];
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<?= $this->renderSection('title'); ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css">
|
||||
<link href="https://cdn.datatables.net/v/bs5/dt-2.3.4/r-3.0.7/datatables.min.css" rel="stylesheet" integrity="sha384-RaJlMsTv+nhuWA/3SQzc3dPVUOKfEb08YW4YZsaNK3UNFUhjvLkn/SwJEfKSavGD" crossorigin="anonymous">
|
||||
<style>
|
||||
table {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Untuk Card Status Counter */
|
||||
.status-card {
|
||||
width: 123px;
|
||||
/* lebar tetap */
|
||||
height: 60px;
|
||||
/* tinggi seragam */
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
border : solid 1px #ddd ;
|
||||
/* box-shadow: 0 3px 5px rgba(0, 0, 0, 0.08); */
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
display: flex;
|
||||
/* flex untuk isi card */
|
||||
align-items: center;
|
||||
/* tengah vertikal */
|
||||
justify-content: center;
|
||||
/* tengah horizontal */
|
||||
flex-direction: column;
|
||||
/* urut atas ke bawah */
|
||||
}
|
||||
|
||||
.status-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.status-count {
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
/* biar teks tidak turun ke bawah */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* kalau terlalu panjang, potong dengan "..." */
|
||||
max-width: 100%;
|
||||
/* biar rapih dalam box */
|
||||
}
|
||||
|
||||
|
||||
.dot {
|
||||
height: 7px;
|
||||
width: 7px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Untuk Cursor di Tabel */
|
||||
.pointercol {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 1 */
|
||||
.Pend {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* 2 */
|
||||
.PartColl {
|
||||
background-color: #ff99aaaf !important;
|
||||
}
|
||||
|
||||
/* 3 */
|
||||
.Collected {
|
||||
background-color: #d6303086 !important;
|
||||
}
|
||||
|
||||
/* 4 */
|
||||
.PartRecv {
|
||||
background-color: #a0c0d9af !important;
|
||||
}
|
||||
|
||||
/* 5 */
|
||||
.Received {
|
||||
background-color: #0985e39d !important;
|
||||
}
|
||||
|
||||
/* 6 */
|
||||
.Incomplete {
|
||||
background-color: #fbff009a !important;
|
||||
}
|
||||
|
||||
/* 7 */
|
||||
.Final {
|
||||
background-color: #90EE90 !important;
|
||||
}
|
||||
|
||||
/* 8 */
|
||||
.FinVerification {
|
||||
background-color: #09ff00ab !important;
|
||||
}
|
||||
|
||||
/* 9 */
|
||||
.PenVerification {
|
||||
background-color: #ee5a24c7 !important;
|
||||
}
|
||||
|
||||
/* 10 */
|
||||
.Suspend {
|
||||
background-color: purple;
|
||||
}
|
||||
|
||||
.btn-xs { font-size : 0.7rem; }
|
||||
.text-xs { font-size : 0.7rem; }
|
||||
</style>
|
||||
|
||||
<?= $this->renderSection('css'); ?>
|
||||
</head>
|
||||
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
<?= $this->renderSection('content'); ?>
|
||||
<footer class="bg-dark text-white mt-auto py-1 px-3 fs-6">
|
||||
<small>© 2025 - 5Panda</small>
|
||||
</footer>
|
||||
|
||||
|
||||
<script>
|
||||
console.log("salam 5panda" );
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/v/bs5/dt-2.3.4/r-3.0.7/datatables.min.js" integrity="sha384-O4V7rOTTcSRflQBTMk8URAYWhGGEMgmmLFrqu3e83FQtze3vmndvrH3GcRdrfXRu" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.datatables.net/2.3.4/js/dataTables.bootstrap5.js"></script>
|
||||
<script src="https://unpkg.com/mustache@4.2.0/mustache.min.js"></script>
|
||||
|
||||
<?= $this->renderSection('script'); ?>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
45
app/Views/admin/dialog_preview.php
Normal file
45
app/Views/admin/dialog_preview.php
Normal file
@ -0,0 +1,45 @@
|
||||
<dialog class="modal" :open="isDialogPreviewOpen">
|
||||
<template x-if="previewAccessnumber">
|
||||
<div class="modal-box w-11/12 max-w-7xl h-[90vh] flex flex-col p-0 overflow-hidden bg-base-100">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center p-3 bg-base-200 border-b border-base-300">
|
||||
<h3 class="font-bold text-lg flex items-center gap-2">
|
||||
<i class="fa fa-eye text-primary"></i>
|
||||
Preview
|
||||
<span class="badge badge-ghost text-xs" x-text="previewAccessnumber"></span>
|
||||
</h3>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="join shadow-sm">
|
||||
<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('ind')" :class="previewType === 'ind' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">IND</button>
|
||||
<button @click="setPreviewType('eng')" :class="previewType === 'eng' ? 'btn-active btn-neutral text-white' : 'btn-ghost'" class="btn btn-sm join-item">ENG</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>
|
||||
<button class="btn btn-sm btn-circle btn-ghost" @click="closePreviewDialog()">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="p-3 bg-base-200 border-t border-base-300 flex justify-end items-center gap-4">
|
||||
<label class="label cursor-pointer gap-2 mb-0">
|
||||
<input type="checkbox" x-model="reviewed" class="checkbox checkbox-sm checkbox-primary" />
|
||||
<span class="label-text text-sm">I have reviewed the results</span>
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-sm btn-ghost" @click="closePreviewDialog()">Cancel</button>
|
||||
<button id="validate-btn" x-ref="validateBtn" class="btn btn-sm btn-success"
|
||||
@click="validate(previewAccessnumber, '<?=session('userid');?>')" :disabled="!reviewed">
|
||||
<i class="fa fa-check mr-1"></i> Validate
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</dialog>
|
||||
@ -2,6 +2,15 @@
|
||||
<div class="modal-box w-2/3 max-w-5xl">
|
||||
<p class='text-right'><button class="btn btn-xs btn-neutral" @click="closeSampleDialog()">X</button></p>
|
||||
|
||||
<template x-if="isSampleLoading">
|
||||
<div class="text-center py-10">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
<p class="mt-2">Loading data...</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-if="!isSampleLoading">
|
||||
<div>
|
||||
<table class="table table-xs table-compact w-full mb-4">
|
||||
<tr>
|
||||
<td>MR# </td> <td x-text="': '+item.patnumber"></td>
|
||||
@ -13,10 +22,10 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Note</td>
|
||||
<td colspan='3'>
|
||||
<textarea x-text="item.comment" class="textarea textarea-bordered w-full"></textarea>
|
||||
<button class="btn btn-sm btn-primary mt-2" @click="saveComment(item.accessnumber)">Save</button>
|
||||
</td>
|
||||
<td colspan='3'>
|
||||
<textarea x-text="item.comment" class="textarea textarea-bordered w-full"></textarea>
|
||||
<button class="btn btn-sm btn-primary mt-2" @click="saveComment(item.accessnumber)">Save</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@ -44,8 +53,8 @@
|
||||
<button class="btn btn-sm btn-success px-2 py-1" onclick=""><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<template x-for="sample in item.samples">
|
||||
<tr>
|
||||
<td x-text="sample.sampcode"></td>
|
||||
@ -75,7 +84,7 @@
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
@ -84,5 +93,7 @@
|
||||
</table>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</dialog>
|
||||
28
app/Views/admin/dialog_setPassword.php
Normal file
28
app/Views/admin/dialog_setPassword.php
Normal file
@ -0,0 +1,28 @@
|
||||
<dialog class="modal" :open="isDialogSetPasswordOpen">
|
||||
<div class="modal-box w-96">
|
||||
<h3 class="font-bold text-lg mb-4">Change Password</h3>
|
||||
<div class="form-control w-full">
|
||||
<label class="label">
|
||||
<span class="label-text">New Password</span>
|
||||
</label>
|
||||
<input type="password" x-model="password" class="input input-bordered w-full" placeholder="Enter new password" />
|
||||
</div>
|
||||
<div class="form-control w-full mt-3">
|
||||
<label class="label">
|
||||
<span class="label-text">Confirm Password</span>
|
||||
</label>
|
||||
<input type="password" x-model="confirm_password" class="input input-bordered w-full" placeholder="Confirm new password" />
|
||||
</div>
|
||||
<div x-show="error" class="alert alert-error mt-3 text-sm">
|
||||
<span x-text="error"></span>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<button class="btn btn-ghost" @click="closeDialogSetPassword()">Cancel</button>
|
||||
<button class="btn btn-primary" @click="savePassword('<?=session('userid'); ?>')" :disabled="isLoading">
|
||||
<span x-show="isLoading" class="loading loading-spinner loading-sm"></span>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop bg-black/30" @click="closeDialogSetPassword()"></div>
|
||||
</dialog>
|
||||
20
app/Views/admin/dialog_unval.php
Normal file
20
app/Views/admin/dialog_unval.php
Normal file
@ -0,0 +1,20 @@
|
||||
<dialog class="modal" :open="isDialogUnvalOpen">
|
||||
<template x-if="unvalAccessnumber">
|
||||
<div class="modal-box w-96">
|
||||
<h3 class="font-bold text-lg mb-4 text-warning">
|
||||
<i class="fa fa-exclamation-triangle mr-2"></i>Unvalidate Request
|
||||
</h3>
|
||||
<p class="text-sm mb-3" x-text="'Access Number: ' + unvalAccessnumber"></p>
|
||||
<textarea class="textarea textarea-bordered w-full" rows="4"
|
||||
x-model="unvalReason" placeholder="Enter reason for unvalidation..."></textarea>
|
||||
<p class='text-right mt-3'>
|
||||
<button class="btn btn-sm btn-ghost" @click="closeUnvalDialog()">Cancel</button>
|
||||
<button id="unvalidate-btn" class="btn btn-sm btn-warning"
|
||||
@click="unvalidate(unvalAccessnumber, '<?=session('userid');?>')"
|
||||
:disabled="!unvalReason.trim()">
|
||||
<i class="fa fa-undo mr-1"></i>Unvalidate
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</dialog>
|
||||
@ -1,287 +1,401 @@
|
||||
<?= $this->extend('_layouts/main.php') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<title>Admin Glenlis</title>
|
||||
<?= $this->endSection() ?>
|
||||
<?= $this->extend('admin/main'); ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div class="container-fluid px-3">
|
||||
<div class='row p-1'>
|
||||
<div class='col fw-bold'>Dashboard, <?=session('userrole');?></div>
|
||||
<div class='col text-end'>
|
||||
Hi, <?=session('userid');?>
|
||||
<button class="btn btn-sm btn-secondary" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-list"></i></button>
|
||||
<div class="dropdown">
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li><a class="dropdown-item" href="<?=base_url('admin/');?>">Dashboard</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('');?>">Report</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('admin/user');?>">User</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('logout');?>">Log Out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main class="p-4 flex-1 flex flex-col gap-2" x-data="dashboard">
|
||||
<div class="card bg-base-100 shadow-xl h-full border border-base-200 overflow-hidden">
|
||||
<div class="card-body p-0 h-full flex flex-col">
|
||||
|
||||
<!-- 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="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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='col'>
|
||||
<form method="GET" action='<?= base_url('/admin') ?>'>
|
||||
<div class="row align-items-center g-1">
|
||||
<div class="col-auto fw-semibold text-muted">
|
||||
Date :
|
||||
</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='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>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date1"
|
||||
value="<?php echo $date1; ?>" />
|
||||
</div>
|
||||
<span class="flex-1"></span>
|
||||
|
||||
<div class="col-auto">-</div>
|
||||
<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" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date2"
|
||||
value="<?php echo $date2; ?>" />
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto 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:7%;'><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:7%;'><div class="skeleton h-4 w-16"></div></th>
|
||||
<th style='width:7%;'><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>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="i in 5" :key="i">
|
||||
<tr>
|
||||
<td colspan="11"><div class="skeleton h-4 w-full"></div></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<template x-if="!isLoading && !list.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 && 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:3%;'>ResTo</th>
|
||||
<th style='width:5%;'>Val</th>
|
||||
<th style='width:5%;'>Result</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 class='flex gap-1 items-center'>
|
||||
<div class='w-15'>
|
||||
<p>1: <span x-text="req.VAL1USER"></span></p>
|
||||
<p>2: <span x-text="req.VAL2USER"></span></p>
|
||||
</div>
|
||||
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
||||
<div class='text-center'>
|
||||
<template 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>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<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>
|
||||
</template>
|
||||
</td>
|
||||
<td><button x-text="req.STATS === 'Fin' ? 'Final' : req.STATS" class="btn btn-xs"
|
||||
:class="statusColor[req.STATS]" @click="openSampleDialog(req.SP_ACCESSNUMBER)"></button></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="col-auto"> <button type="button" class="btn btn-sm btn-outline-dark" onclick="window.location.href='<?=base_url();?>';"> RESET </button> </div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="filterGroup" class="col text-end">
|
||||
<button class='btn btn-sm btn-outline-secondary' data-type='secondary'><?=$counter['pending'];?> <small>Pending</small></button>
|
||||
<button class='btn btn-sm btn-outline-info' data-type='info'><?=$counter['collect'];?> <small>Collect</small></button>
|
||||
<button class='btn btn-sm btn-outline-primary' data-type='primary'><?=$counter['recv'];?> <small>Receive</small></button>
|
||||
<button class='btn btn-sm btn-outline-warning' data-type='warning'><?=$counter['incomplete'];?> <small>Incomplete</small></button>
|
||||
<button class='btn btn-sm btn-outline-success' data-type='success'><?=$counter['complete'];?> <small>Complete</small></button>
|
||||
<button class='btn btn-sm btn-dark' data-type='dark'><?=$counter['total'];?> <small>Total</small></button>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button id="filterValBtn" class='btn btn-sm btn-outline-primary' onclick="filterVal('<?=session('userid');?>')"> <small>Validate</small></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table table-responsive-scroll">
|
||||
<table id="datatables" class="table table-sm table-hover table-bordered align-middle table-striped">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th style="width:1%;">S</th>
|
||||
<th style='width:7%;'>Order Datetime</th>
|
||||
<th>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>
|
||||
<?php foreach($dataList as $row) : ?>
|
||||
<?php
|
||||
switch ($row['statscode']) {
|
||||
case 1: $class = 'BackPend'; break;
|
||||
case 2: $class = 'PartColl'; break;
|
||||
case 3: $class = 'Collected'; break;
|
||||
case 4: $class = 'PartRecv'; break;
|
||||
case 5: $class = 'Received'; break;
|
||||
case 6: $class = 'Incomplete'; break;
|
||||
case 7: $class = 'Final'; break;
|
||||
case 8: $class = 'FinVerification'; break;
|
||||
case 9: $class = 'PenVerification'; break;
|
||||
default: $class = ''; break;
|
||||
}
|
||||
$tests = esc($row['tests']);
|
||||
$tests = str_replace(",", ", ", $tests);
|
||||
$accessnumber = $row['sp_accessnumber'];
|
||||
?>
|
||||
<tr>
|
||||
<!--
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['statscode']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reqdate']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['patname']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['sp_accessnumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['hostordernumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reff']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['doc']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['tests']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['odr_cresult_to']) ?> </td>
|
||||
-->
|
||||
<td> <?= esc($row['statscode']) ?> </td> <td> <?= esc($row['reqdate']) ?> </td> <td> <?= esc($row['patname']) ?> </td>
|
||||
<td> <?= esc($row['sp_accessnumber']) ?> </td> <td> <?= esc($row['hostordernumber']) ?> </td> <td> <?= esc($row['reff']) ?> </td>
|
||||
<td> <?= esc($row['doc']) ?> </td> <td> <?= $tests; ?> </td>
|
||||
|
||||
<td>
|
||||
<?= esc($row['odr_cresult_to']) ?> <br/>
|
||||
<?php if($row['valcounter'] == 2) { ?>
|
||||
<input type="checkbox" name="printed" id='printed'> Printed <br/>
|
||||
<input type="checkbox" name="eng" id='eng'> Eng
|
||||
<?php } ?>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<table class='mx-auto'>
|
||||
<tr>
|
||||
<td id='val1user-<?=$accessnumber;?>'>1 : <?=$row['val1user'];?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id='val2user-<?=$accessnumber;?>'>2 : <?=$row['val2user'];?></td>
|
||||
</tr>
|
||||
|
||||
<?php if($row['isvaltd'] != 0) { ?>
|
||||
<?php
|
||||
$user = session()->get('userid');
|
||||
$val1 = $row['val1user'];
|
||||
$val2 = $row['val2user'];
|
||||
|
||||
$valButton = (($val1 === '-') || ($val2 === '-')) && ($user !== $val1 && $user !== $val2);
|
||||
$unvalButton = ($val1 !== '-') || ($val2 !== '-');
|
||||
?>
|
||||
<tr>
|
||||
|
||||
<td id="btnContainer-<?=$accessnumber;?>">
|
||||
<?php if ($unvalButton): ?>
|
||||
<button id='unvalBtn-<?=$accessnumber;?>' class='btn btn-xs btn-outline-secondary px-1 py-0 mb-1' onclick="unvalidateShow(<?=$row['sp_accessnumber'];?>)" data-bs-toggle="modal" data-bs-target="#unvalidateModal"><i class="bi bi-arrow-counterclockwise"></i></button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($valButton): ?>
|
||||
<button id='valBtn-<?= $accessnumber; ?>' class='btn btn-xs btn-outline-success px-1 py-0 mb-1' onclick="validateShow(<?= $row['sp_accessnumber']; ?>)" data-bs-toggle="modal" data-bs-target="#validateModal"> <i class="bi bi-check-lg"></i></button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
|
||||
|
||||
<td class="pointercol <?= esc($class) ?>" data-access="<?= $row['sp_accessnumber'] ?>" onclick="detailShow('<?=session('userrole');?>')" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['stats']) ?> </td>
|
||||
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Specimen Collection-->
|
||||
<div class="modal fade" id="detailModal" aria-hidden="true" aria-labelledby="detailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fw-bold fs-4" id="detailModal">Specimen Collection</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="specimenModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="validateModal" aria-hidden="true" aria-labelledby="validateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="validateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="unvalidateModal" aria-hidden="true" aria-labelledby="unvalidateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="unvalidateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
</div>
|
||||
|
||||
<?php echo $this->include('admin/dialog_sample'); ?>
|
||||
<?php echo $this->include('admin/dialog_unval'); ?>
|
||||
<?php echo $this->include('admin/dialog_preview'); ?>
|
||||
|
||||
</main>
|
||||
<?= $this->endSection(); ?>
|
||||
|
||||
<?= $this->section('script') ?>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
table = $('#datatables').DataTable({
|
||||
layout: {
|
||||
topEnd: 'search',
|
||||
bottomStart: null, bottomEnd: null
|
||||
},
|
||||
order: [[0, 'asc'], [3, 'asc']],
|
||||
paging: false,
|
||||
scrollCollapse: true,
|
||||
scrollY: '70vh',
|
||||
columnDefs: [{
|
||||
className: 'text-center',
|
||||
targets: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
}, // semua kolom di tengah
|
||||
{
|
||||
className: 'text-start',
|
||||
targets: [10]
|
||||
}
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
<script type="module">
|
||||
import Alpine from '<?=base_url("js/app.js");?>';
|
||||
|
||||
$('#filterGroup .btn').on('click', function () {
|
||||
$('#filterGroup .btn').each(function () {
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-${type}`).addClass(`btn-outline-${type}`);
|
||||
});
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data("dashboard", ()=> ({
|
||||
// dashboard
|
||||
today: "",
|
||||
filter: { date1: "", date2: "" },
|
||||
list: [],
|
||||
isLoading: false,
|
||||
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', 'PartRecv'],
|
||||
Inc: ['Inc'],
|
||||
Fin: ['Fin'],
|
||||
},
|
||||
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-outline-${type}`).addClass(`btn-${type}`);
|
||||
let vals;
|
||||
if(type=='secondary') { vals = [0]; }
|
||||
if(type=='info') { vals = ['1','2']; }
|
||||
if(type=='primary') { vals = ['3','4','5']; }
|
||||
if(type=='warning') { vals = [6,7]; }
|
||||
if(type=='success') { vals = [8]; }
|
||||
if(type=='dark') {vals=[];}
|
||||
regex = vals.join('|');
|
||||
table.column(0).search(regex, true, false).draw();
|
||||
});
|
||||
});
|
||||
|
||||
init() {
|
||||
this.today = new Date().toISOString().slice(0, 10);
|
||||
this.filter.date1 = this.today;
|
||||
this.filter.date2 = this.today;
|
||||
this.fetchList();
|
||||
},
|
||||
|
||||
fetchList(){
|
||||
this.isLoading = true;
|
||||
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();
|
||||
for (let k in this.counters) { this.counters[k] = 0; }
|
||||
fetch(`${BASEURL}/api/requests?${param}`, {
|
||||
method: 'GET',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
}).then(res => res.json()).then(data => {
|
||||
this.list = data.data ?? [];
|
||||
this.filterKey = 'Total';
|
||||
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;
|
||||
});
|
||||
}).finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
// Select the column by index (1) and apply a search that excludes "Test"
|
||||
// The regex: ^((?!Test).)*$
|
||||
// - ^...$ anchors the start and end
|
||||
// - (?!Test) is a negative lookahead, failing the match if "Test" is found
|
||||
function filterVal(userid) {
|
||||
var currentSearch = table.column(9).search();
|
||||
if (currentSearch.includes(userid)) {
|
||||
table.column(9).search( '' ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-primary`).addClass(`btn-outline-primary`);
|
||||
} else {
|
||||
table.column(9).search( `^((?!${userid}).)*$`, true, false ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-outline-primary`).addClass(`btn-primary`);
|
||||
}
|
||||
}
|
||||
reset() {
|
||||
this.filter.date1 = this.today;
|
||||
this.filter.date2 = this.today;
|
||||
this.fetchList();
|
||||
},
|
||||
|
||||
function unvalidate(accessnumber,userid) {
|
||||
if(confirm("Are you sure?")) {
|
||||
fetch(`${BASE_URL}/api/request/validate/${accessnumber}`, {
|
||||
method: "DELETE",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}` })
|
||||
}).then(response => {
|
||||
console.log("Response:", response);
|
||||
$(`#val1user-${accessnumber}`).text("1 : ");
|
||||
$(`#val2user-${accessnumber}`).text("2 : ");
|
||||
$(`#valBtn-${accessnumber}`).prop('disabled', false);
|
||||
$(`#unvalBtn-${accessnumber}`).prop('disabled', false);
|
||||
}).catch(error => {
|
||||
console.log("Error:", error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_URL = "<?= base_url(); ?>";
|
||||
</script>
|
||||
<script src="<?=base_url('js/admin/specimen.js');?>"></script>
|
||||
<script src="<?=base_url('js/admin/request.js');?>"></script>
|
||||
<?= $this->endSection() ?>
|
||||
isValidated (item) {
|
||||
return item.ISVAL == 1 && item.ISPENDING != 1;
|
||||
},
|
||||
get filtered() {
|
||||
let filteredList = this.list;
|
||||
if (this.filterKey === 'Validated') {
|
||||
filteredList = filteredList.filter(item => this.isValidated(item));
|
||||
} else {
|
||||
const validStatuses = this.statusMap[this.filterKey];
|
||||
if (validStatuses.length > 0) {
|
||||
filteredList = filteredList.filter(item => validStatuses.includes(item.STATS));
|
||||
}
|
||||
}
|
||||
if (this.filterTable) {
|
||||
const searchTerm = this.filterTable.toLowerCase();
|
||||
filteredList = filteredList.filter(item =>
|
||||
Object.values(item).some(value =>
|
||||
String(value).toLowerCase().includes(searchTerm)
|
||||
)
|
||||
);
|
||||
}
|
||||
return filteredList;
|
||||
},
|
||||
get validatedCount() {
|
||||
return this.list.filter(r => this.isValidated(r)).length;
|
||||
},
|
||||
|
||||
/*
|
||||
sample dialog
|
||||
*/
|
||||
item : '',
|
||||
isDialogSampleOpen : false,
|
||||
isSampleLoading: false,
|
||||
|
||||
openSampleDialog (accessnumber) {
|
||||
this.isDialogSampleOpen = true;
|
||||
this.fetchItem(accessnumber)
|
||||
},
|
||||
|
||||
closeSampleDialog () {
|
||||
this.isDialogSampleOpen = false;
|
||||
},
|
||||
|
||||
fetchItem(accessnumber){
|
||||
this.isSampleLoading = true;
|
||||
this.item = [];
|
||||
fetch(`${BASEURL}/api/samples/${accessnumber}`, { method: 'GET', headers: {'Content-Type': 'application/json'}})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.item = data.data ?? {};
|
||||
if (!Array.isArray(this.item.samples)) this.item.samples = [];
|
||||
}).finally(() => {
|
||||
this.isSampleLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
collect(sampcode, accessnumber) {
|
||||
fetch(`${BASEURL}/api/samples/collect/${accessnumber}`, {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid: '<?= session('userid'); ?>'})
|
||||
})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.fetchItem(accessnumber);
|
||||
});
|
||||
},
|
||||
|
||||
uncollect(sampcode, accessnumber) {
|
||||
if(!confirm(`Uncollect sample ${sampcode} from request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/api/samples/collect/${accessnumber}`, {
|
||||
method: 'DELETE', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid: '<?= session('userid'); ?>'})
|
||||
})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.fetchItem(accessnumber);
|
||||
});
|
||||
},
|
||||
|
||||
unreceive(sampcode, accessnumber) {
|
||||
if(!confirm(`Unreceive sample ${sampcode} from request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/api/samples/unreceive/${accessnumber}`, {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid : '<?= session('userid'); ?>'})
|
||||
})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.fetchItem(accessnumber);
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
preview dialog
|
||||
*/
|
||||
isDialogPreviewOpen : false,
|
||||
reviewed: false,
|
||||
previewAccessnumber : null,
|
||||
previewType : 'preview',
|
||||
openPreviewDialog (accessnumber, type) {
|
||||
this.previewAccessnumber = accessnumber;
|
||||
this.previewType = type;
|
||||
this.isDialogPreviewOpen = true;
|
||||
this.reviewed = false;
|
||||
},
|
||||
closePreviewDialog () {
|
||||
this.isDialogPreviewOpen = false;
|
||||
},
|
||||
setPreviewType(type) {
|
||||
this.previewType = type;
|
||||
},
|
||||
getPreviewUrl() {
|
||||
let base = 'http://glenlis/spooler_db/main_dev.php';
|
||||
let url = `${base}?acc=${this.previewAccessnumber}`;
|
||||
if (this.previewType === 'ind') url += '&lang=ID';
|
||||
if (this.previewType === 'eng') url += '&lang=EN';
|
||||
if (this.previewType === 'pdf') url += '&output=pdf';
|
||||
return url;
|
||||
},
|
||||
validate(accessnumber, userid) {
|
||||
fetch(`${BASEURL}/api/requests/validate/${accessnumber}`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}` })
|
||||
}).then(response => {
|
||||
this.closePreviewDialog();
|
||||
this.fetchList();
|
||||
console.log('Validate clicked for', this.previewAccessnumber, 'by user', userid);
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
unvalidate dialog
|
||||
*/
|
||||
isDialogUnvalOpen : false,
|
||||
unvalReason : '',
|
||||
unvalAccessnumber : null,
|
||||
openUnvalDialog (accessnumber) {
|
||||
this.unvalReason = '';
|
||||
this.isDialogUnvalOpen = true;
|
||||
this.unvalAccessnumber = accessnumber;
|
||||
},
|
||||
unvalidate(accessnumber, userid) {
|
||||
if(!confirm(`Unvalidate request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/api/requests/validate/${accessnumber}`, {
|
||||
method: "DELETE",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}`, comment: this.unvalReason.trim() })
|
||||
}).then(response => {
|
||||
this.closeUnvalDialog();
|
||||
this.fetchList();
|
||||
console.log(`Unvalidate clicked for ${accessnumber}, by user ${userid}`);
|
||||
});
|
||||
},
|
||||
closeUnvalDialog () {
|
||||
this.isDialogUnvalOpen = false;
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
Alpine.start();
|
||||
</script>
|
||||
<?= $this->endSection(); ?>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
}
|
||||
.card-body {
|
||||
font-size: 0.71rem !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-base-200 min-h-screen flex flex-col" x-data="main">
|
||||
@ -41,22 +41,22 @@
|
||||
<span class="text-xl"><i class="fa fa-user"></i></span>
|
||||
</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('v2/admin') ?>"><i class="fa fa-chart-bar mr-2"></i> Dashboard</a></li>
|
||||
<li><a class="active:bg-primary" href="<?=base_url('v2/admin/users') ?>"><i class="fa fa-users mr-2"></i> Users </a></li>
|
||||
<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('v2/logout')?>" class="text-error hover:bg-error/10"><i class="fa fa-sign-out-alt mr-2"></i> Logout</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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?=$this->renderSection('content');?>
|
||||
<?=$this->include('v2/dialog_setPassword');?>
|
||||
<?=$this->include('admin/dialog_setPassword');?>
|
||||
<footer class='bg-base-100 p-1'>© <?=date('Y');?> - 5Panda</footer>
|
||||
<script>
|
||||
window.BASEURL = "<?=base_url("v2");?>";
|
||||
window.BASEURL = "<?=base_url("admin");?>";
|
||||
</script>
|
||||
<?=$this->renderSection('script');?>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@ -1,40 +0,0 @@
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class="col">val : <?=$val1user;?> - <?=$val2user;?></div>
|
||||
<div class='col text-end'>
|
||||
<button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button>
|
||||
<!-- <button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mx-auto">
|
||||
<iframe id="result-iframe" src="http://glenlis/spooler_db/main_dev.php?acc=<?=$accessnumber;?>" width="750px" height="600px"> -->
|
||||
<!-- <iframe id="result-iframe" src="<?=base_url('dummypage');?>" width="750px" height="600px"> -->
|
||||
|
||||
<p>Your browser does not support iframes. Please contact the technical support.</p>
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{
|
||||
// verifymodal
|
||||
let iframe = document.getElementById('result-iframe');
|
||||
let button = document.getElementById('result-button');
|
||||
|
||||
iframe.addEventListener('load', () => {
|
||||
//console.log('loaded');
|
||||
let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
let scrollElement = iframeDoc.scrollingElement || iframeDoc.documentElement;
|
||||
|
||||
iframeDoc.addEventListener('scroll', () => {
|
||||
let scrollTop = scrollElement.scrollTop;
|
||||
let scrollHeight = scrollElement.scrollHeight;
|
||||
let clientHeight = scrollElement.clientHeight;
|
||||
|
||||
// Check if scrolled to bottom
|
||||
if (scrollTop + clientHeight >= scrollHeight - 2) {
|
||||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -1,106 +0,0 @@
|
||||
<div class="row mb-4 p-3">
|
||||
|
||||
<div class="col-8">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Patient</div>
|
||||
<div class="info-value">: {{patientIdentity.name}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Age</div>
|
||||
<div class="info-value">: {{patientIdentity.age}} years</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Gender</div>
|
||||
<div class="info-value">: {{patientIdentity.gender}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">Lab#</div>
|
||||
<div class="info-value">: {{accessnumber}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">MR#</div>
|
||||
<div class="info-value">: {{patientIdentity.rm}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">KTP</div>
|
||||
<div class="info-value">: {{patientIdentity.ktp}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fs-4 mb-2">
|
||||
<i class="bi bi-flask-florence-fill"></i> Tube
|
||||
</h5>
|
||||
|
||||
<table class="table fs-6 text-start table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sample Code</th>
|
||||
<th>Sample Name</th>
|
||||
<th class='text-center'>Collected</th>
|
||||
<th class='text-center'>Received</th>
|
||||
<th>Action</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td> <td>Collection</td> <td></td> <td></td>
|
||||
<td><button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td> <td>All</td> <td></td> <td></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick=""><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
</tr>
|
||||
{{#samples}}
|
||||
<tr id='row-{{accessnumber}}-{{sampcode}}'>
|
||||
<td>{{sampcode}}</td>
|
||||
<td>{{name}}</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input coll-checkbox"
|
||||
{{#is_coll}}checked{{/is_coll}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input recv-checkbox"
|
||||
{{#is_recv}}checked{{/is_recv}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 1)"><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 0)"><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="unreceive({{accessnumber}}, {{sampcode}})"><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{{/samples}}
|
||||
|
||||
<tr class="text-center">
|
||||
<td colspan="6">
|
||||
<h6 class="p-0 m-0">
|
||||
<i class="bi bi-pencil-square" role="button" onclick="commentValue()"></i>
|
||||
</h6>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,16 +0,0 @@
|
||||
<form id='unValForm'>
|
||||
<div class="row mb-2">
|
||||
<h4>Unvalidate <?=$accessnumber;?></h4>
|
||||
</div>
|
||||
<div class="row m-2">
|
||||
<input type='hidden' name='accessnumber' value='<?=$accessnumber;?>'>
|
||||
<input type='hidden' name='userid' value='<?=session('userid');?>'>
|
||||
<textarea class='form-control' name='comment' placeholder="Note ..." rows='5'></textarea>
|
||||
</div>
|
||||
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class='col text-end'>
|
||||
<button class='btn btn-xs btn-warning' onclick="event.preventDefault();unvalidateRequest('unValForm', <?=$accessnumber;?>);">Un-Validate</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -1,4 +1,4 @@
|
||||
<?= $this->extend('v2/admin/main'); ?>
|
||||
<?= $this->extend('admin/main'); ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div x-data="users" class="contents">
|
||||
@ -15,39 +15,63 @@
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra w-full" x-show="list.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User ID</th>
|
||||
<th>Role/Level</th>
|
||||
<th class="text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="user in list" :key="user.USERID">
|
||||
<template x-if="isLoading">
|
||||
<table class="table table-zebra w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="font-bold" x-text="user.USERID"></td>
|
||||
<td>
|
||||
<span class="badge"
|
||||
:class="getRoleClass(user.USERLEVEL)"
|
||||
x-text="getRoleName(user.USERLEVEL)"></span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<button class="btn btn-square btn-ghost btn-xs text-info" @click="openUserModal('edit', user)">
|
||||
<i class="fa fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-square btn-ghost btn-xs text-error" @click="deleteUser(user.USERID)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
<th><div class="skeleton h-4 w-24"></div></th>
|
||||
<th><div class="skeleton h-4 w-24"></div></th>
|
||||
<th class="text-right"><div class="skeleton h-4 w-16 ml-auto"></div></th>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<div x-show="list.length === 0" class="text-center py-10 opacity-50">
|
||||
<i class="fa fa-spinner fa-spin text-4xl mb-2"></i>
|
||||
<p>Loading users...</p>
|
||||
</div>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="i in 5" :key="i">
|
||||
<tr>
|
||||
<td><div class="skeleton h-4 w-20"></div></td>
|
||||
<td><div class="skeleton h-4 w-24"></div></td>
|
||||
<td class="text-right"><div class="skeleton h-4 w-16 ml-auto"></div></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<template x-if="!isLoading && list.length">
|
||||
<table class="table table-zebra w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User ID</th>
|
||||
<th>Role/Level</th>
|
||||
<th class="text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="user in list" :key="user.USERID">
|
||||
<tr>
|
||||
<td class="font-bold" x-text="user.USERID"></td>
|
||||
<td>
|
||||
<span class="badge"
|
||||
:class="getRoleClass(user.USERLEVEL)"
|
||||
x-text="getRoleName(user.USERLEVEL)"></span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<button class="btn btn-square btn-ghost btn-xs text-info" @click="openUserModal('edit', user)">
|
||||
<i class="fa fa-edit"></i>
|
||||
</button>
|
||||
<button class="btn btn-square btn-ghost btn-xs text-error" @click="deleteUser(user.USERID)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<template x-if="!isLoading && !list.length">
|
||||
<div class="text-center py-10">
|
||||
<i class="fa fa-inbox text-4xl mb-2 opacity-50"></i>
|
||||
<p>No users found</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -150,7 +174,7 @@
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.data("users", () => ({
|
||||
list: [],
|
||||
mode: 'create', // create | edit
|
||||
mode: 'create',
|
||||
isLoading: false,
|
||||
errorMsg: '',
|
||||
form: {
|
||||
@ -165,10 +189,13 @@
|
||||
},
|
||||
|
||||
fetchUsers() {
|
||||
fetch(`${BASEURL}/admin/api/users`)
|
||||
this.isLoading = true;
|
||||
fetch(`${BASEURL}/api/users`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
this.list = data.data ?? [];
|
||||
}).finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
@ -182,9 +209,6 @@
|
||||
return map[level] || 'badge-ghost';
|
||||
},
|
||||
|
||||
/*
|
||||
User Modal
|
||||
*/
|
||||
openUserModal(targetMode, user = null) {
|
||||
this.mode = targetMode;
|
||||
this.errorMsg = '';
|
||||
@ -213,13 +237,13 @@
|
||||
try {
|
||||
let res;
|
||||
if(this.mode == 'create') {
|
||||
res = await fetch(`${BASEURL}/admin/api/users`, {
|
||||
res = await fetch(`${BASEURL}/api/users`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(this.form)
|
||||
});
|
||||
} else {
|
||||
res = await fetch(`${BASEURL}/admin/api/users/${this.form.userid}`, {
|
||||
res = await fetch(`${BASEURL}/api/users/${this.form.userid}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(this.form)
|
||||
@ -1,285 +0,0 @@
|
||||
<?= $this->extend('_layouts/main.php') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<title>Analyst Glenlis</title>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div class="container-fluid px-3">
|
||||
<div class='row p-1'>
|
||||
<div class='col fw-bold'>Dashboard, <?=session('userrole');?></div>
|
||||
<div class='col text-end'>
|
||||
Hi, <?=session('userid');?>
|
||||
<button class="btn btn-sm btn-secondary" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-list"></i></button>
|
||||
<div class="dropdown">
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li><a class="dropdown-item" href="<?=base_url('analyst/');?>">Dashboard</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('');?>">Report</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('logout');?>">Log Out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='col'>
|
||||
<form method="GET" action='<?= base_url('/analyst') ?>'>
|
||||
<div class="row align-items-center g-1">
|
||||
<div class="col-auto fw-semibold text-muted">
|
||||
Date :
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date1"
|
||||
value="<?php echo $date1; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="col-auto">-</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date2"
|
||||
value="<?php echo $date2; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="col-auto"> <button type="button" class="btn btn-sm btn-outline-dark" onclick="window.location.href='<?=base_url();?>';"> RESET </button> </div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="filterGroup" class="col text-end">
|
||||
<button class='btn btn-sm btn-outline-secondary' data-type='secondary'><?=$counter['pending'];?> <small>Pending</small></button>
|
||||
<button class='btn btn-sm btn-outline-info' data-type='info'><?=$counter['collect'];?> <small>Collect</small></button>
|
||||
<button class='btn btn-sm btn-outline-primary' data-type='primary'><?=$counter['recv'];?> <small>Receive</small></button>
|
||||
<button class='btn btn-sm btn-outline-warning' data-type='warning'><?=$counter['incomplete'];?> <small>Incomplete</small></button>
|
||||
<button class='btn btn-sm btn-outline-success' data-type='success'><?=$counter['complete'];?> <small>Complete</small></button>
|
||||
<button class='btn btn-sm btn-dark' data-type='dark'><?=$counter['total'];?> <small>Total</small></button>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button id="filterValBtn" class='btn btn-sm btn-outline-primary' onclick="filterVal('<?=session('userid');?>')"> <small>Validate</small></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table table-responsive-scroll">
|
||||
<table id="datatables" class="table table-sm table-hover table-bordered align-middle table-striped">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th style="width:1%;">S</th>
|
||||
<th style='width:7%;'>Order Datetime</th>
|
||||
<th>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>
|
||||
<?php foreach($dataList as $row) : ?>
|
||||
<?php
|
||||
switch ($row['statscode']) {
|
||||
case 1: $class = 'BackPend'; break;
|
||||
case 2: $class = 'PartColl'; break;
|
||||
case 3: $class = 'Collected'; break;
|
||||
case 4: $class = 'PartRecv'; break;
|
||||
case 5: $class = 'Received'; break;
|
||||
case 6: $class = 'Incomplete'; break;
|
||||
case 7: $class = 'Final'; break;
|
||||
case 8: $class = 'FinVerification'; break;
|
||||
case 9: $class = 'PenVerification'; break;
|
||||
default: $class = ''; break;
|
||||
}
|
||||
$tests = esc($row['tests']);
|
||||
$tests = str_replace(",", ", ", $tests);
|
||||
$accessnumber = $row['sp_accessnumber'];
|
||||
?>
|
||||
<tr>
|
||||
<!--
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['statscode']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reqdate']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['patname']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['sp_accessnumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['hostordernumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reff']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['doc']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['tests']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['odr_cresult_to']) ?> </td>
|
||||
-->
|
||||
<td> <?= esc($row['statscode']) ?> </td> <td> <?= esc($row['reqdate']) ?> </td> <td> <?= esc($row['patname']) ?> </td>
|
||||
<td> <?= esc($row['sp_accessnumber']) ?> </td> <td> <?= esc($row['hostordernumber']) ?> </td> <td> <?= esc($row['reff']) ?> </td>
|
||||
<td> <?= esc($row['doc']) ?> </td> <td> <?= $tests; ?> </td>
|
||||
|
||||
<td>
|
||||
<?= esc($row['odr_cresult_to']) ?> <br/>
|
||||
<?php if($row['valcounter'] == 2) { ?>
|
||||
<input type="checkbox" name="printed" id='printed'> Printed <br/>
|
||||
<input type="checkbox" name="eng" id='eng'> Eng
|
||||
<?php } ?>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<table class='mx-auto'>
|
||||
<tr>
|
||||
<td id='val1user-<?=$accessnumber;?>'>1 : <?=$row['val1user'];?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id='val2user-<?=$accessnumber;?>'>2 : <?=$row['val2user'];?></td>
|
||||
</tr>
|
||||
|
||||
<?php if($row['isvaltd'] != 0) { ?>
|
||||
<?php
|
||||
$user = session()->get('userid');
|
||||
$val1 = $row['val1user'];
|
||||
$val2 = $row['val2user'];
|
||||
|
||||
$valButton = (($val1 === '-') || ($val2 === '-')) && ($user !== $val1 && $user !== $val2);
|
||||
$unvalButton = ($val1 !== '-') || ($val2 !== '-');
|
||||
?>
|
||||
<tr>
|
||||
|
||||
<td id="btnContainer-<?=$accessnumber;?>">
|
||||
<?php if ($unvalButton): ?>
|
||||
<button id='unvalBtn-<?=$accessnumber;?>' class='btn btn-xs btn-outline-secondary px-1 py-0 mb-1' onclick="unvalidateShow(<?=$row['sp_accessnumber'];?>)" data-bs-toggle="modal" data-bs-target="#unvalidateModal"><i class="bi bi-arrow-counterclockwise"></i></button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($valButton): ?>
|
||||
<button id='valBtn-<?= $accessnumber; ?>' class='btn btn-xs btn-outline-success px-1 py-0 mb-1' onclick="validateShow(<?= $row['sp_accessnumber']; ?>)" data-bs-toggle="modal" data-bs-target="#validateModal"> <i class="bi bi-check-lg"></i></button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
|
||||
|
||||
<td class="pointercol <?= esc($class) ?>" data-access="<?= $row['sp_accessnumber'] ?>" onclick="detailShow('<?=session('userrole');?>')" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['stats']) ?> </td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Specimen Collection-->
|
||||
<div class="modal fade" id="detailModal" aria-hidden="true" aria-labelledby="detailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fw-bold fs-4" id="detailModal">Specimen Collection</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="specimenModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="validateModal" aria-hidden="true" aria-labelledby="validateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="validateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="unvalidateModal" aria-hidden="true" aria-labelledby="unvalidateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="unvalidateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('script') ?>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
table = $('#datatables').DataTable({
|
||||
layout: {
|
||||
topEnd: 'search',
|
||||
bottomStart: null, bottomEnd: null
|
||||
},
|
||||
order: [[0, 'asc'], [3, 'asc']],
|
||||
paging: false,
|
||||
scrollCollapse: true,
|
||||
scrollY: '70vh',
|
||||
columnDefs: [{
|
||||
className: 'text-center',
|
||||
targets: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
}, // semua kolom di tengah
|
||||
{
|
||||
className: 'text-start',
|
||||
targets: [10]
|
||||
}
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
$('#filterGroup .btn').on('click', function () {
|
||||
$('#filterGroup .btn').each(function () {
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-${type}`).addClass(`btn-outline-${type}`);
|
||||
});
|
||||
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-outline-${type}`).addClass(`btn-${type}`);
|
||||
let vals;
|
||||
if(type=='secondary') { vals = [0]; }
|
||||
if(type=='info') { vals = ['1','2']; }
|
||||
if(type=='primary') { vals = ['3','4','5']; }
|
||||
if(type=='warning') { vals = [6,7]; }
|
||||
if(type=='success') { vals = [8]; }
|
||||
if(type=='dark') {vals=[];}
|
||||
regex = vals.join('|');
|
||||
table.column(0).search(regex, true, false).draw();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Select the column by index (1) and apply a search that excludes "Test"
|
||||
// The regex: ^((?!Test).)*$
|
||||
// - ^...$ anchors the start and end
|
||||
// - (?!Test) is a negative lookahead, failing the match if "Test" is found
|
||||
function filterVal(userid) {
|
||||
var currentSearch = table.column(9).search();
|
||||
if (currentSearch.includes(userid)) {
|
||||
table.column(9).search( '' ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-primary`).addClass(`btn-outline-primary`);
|
||||
} else {
|
||||
table.column(9).search( `^((?!${userid}).)*$`, true, false ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-outline-primary`).addClass(`btn-primary`);
|
||||
}
|
||||
}
|
||||
|
||||
function unvalidate(accessnumber,userid) {
|
||||
if(confirm("Are you sure?")) {
|
||||
fetch(`${BASE_URL}/api/request/validate/${accessnumber}`, {
|
||||
method: "DELETE",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}` })
|
||||
}).then(response => {
|
||||
console.log("Response:", response);
|
||||
$(`#val1user-${accessnumber}`).text("1 : ");
|
||||
$(`#val2user-${accessnumber}`).text("2 : ");
|
||||
$(`#valBtn-${accessnumber}`).prop('disabled', false);
|
||||
$(`#unvalBtn-${accessnumber}`).prop('disabled', false);
|
||||
}).catch(error => {
|
||||
console.log("Error:", error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_URL = "<?= base_url(); ?>";
|
||||
</script>
|
||||
<script src="<?=base_url('js/admin/specimen.js');?>"></script>
|
||||
<script src="<?=base_url('js/admin/request.js');?>"></script>
|
||||
<?= $this->endSection() ?>
|
||||
@ -1,40 +0,0 @@
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class="col">val : <?=$val1user;?> - <?=$val2user;?></div>
|
||||
<div class='col text-end'>
|
||||
<button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button>
|
||||
<!-- <button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mx-auto">
|
||||
<iframe id="result-iframe" src="http://glenlis/spooler_db/main_dev.php?acc=<?=$accessnumber;?>" width="750px" height="600px"> -->
|
||||
<!-- <iframe id="result-iframe" src="<?=base_url('dummypage');?>" width="750px" height="600px"> -->
|
||||
|
||||
<p>Your browser does not support iframes. Please contact the technical support.</p>
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{
|
||||
// verifymodal
|
||||
let iframe = document.getElementById('result-iframe');
|
||||
let button = document.getElementById('result-button');
|
||||
|
||||
iframe.addEventListener('load', () => {
|
||||
//console.log('loaded');
|
||||
let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
let scrollElement = iframeDoc.scrollingElement || iframeDoc.documentElement;
|
||||
|
||||
iframeDoc.addEventListener('scroll', () => {
|
||||
let scrollTop = scrollElement.scrollTop;
|
||||
let scrollHeight = scrollElement.scrollHeight;
|
||||
let clientHeight = scrollElement.clientHeight;
|
||||
|
||||
// Check if scrolled to bottom
|
||||
if (scrollTop + clientHeight >= scrollHeight - 2) {
|
||||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -1,106 +0,0 @@
|
||||
<div class="row mb-4 p-3">
|
||||
|
||||
<div class="col-8">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Patient</div>
|
||||
<div class="info-value">: {{patientIdentity.name}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Age</div>
|
||||
<div class="info-value">: {{patientIdentity.age}} years</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Gender</div>
|
||||
<div class="info-value">: {{patientIdentity.gender}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">Lab#</div>
|
||||
<div class="info-value">: {{accessnumber}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">MR#</div>
|
||||
<div class="info-value">: {{patientIdentity.rm}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">KTP</div>
|
||||
<div class="info-value">: {{patientIdentity.ktp}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fs-4 mb-2">
|
||||
<i class="bi bi-flask-florence-fill"></i> Tube
|
||||
</h5>
|
||||
|
||||
<table class="table fs-6 text-start table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sample Code</th>
|
||||
<th>Sample Name</th>
|
||||
<th class='text-center'>Collected</th>
|
||||
<th class='text-center'>Received</th>
|
||||
<th>Action</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td> <td>Collection</td> <td></td> <td></td>
|
||||
<td><button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td> <td>All</td> <td></td> <td></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick=""><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
</tr>
|
||||
{{#samples}}
|
||||
<tr id='row-{{accessnumber}}-{{sampcode}}'>
|
||||
<td>{{sampcode}}</td>
|
||||
<td>{{name}}</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input coll-checkbox"
|
||||
{{#is_coll}}checked{{/is_coll}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input recv-checkbox"
|
||||
{{#is_recv}}checked{{/is_recv}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 1)"><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 0)"><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="unreceive({{accessnumber}}, {{sampcode}})"><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{{/samples}}
|
||||
|
||||
<tr class="text-center">
|
||||
<td colspan="6">
|
||||
<h6 class="p-0 m-0">
|
||||
<i class="bi bi-pencil-square" role="button" onclick="commentValue()"></i>
|
||||
</h6>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,16 +0,0 @@
|
||||
<form id='unValForm'>
|
||||
<div class="row mb-2">
|
||||
<h4>Unvalidate <?=$accessnumber;?></h4>
|
||||
</div>
|
||||
<div class="row m-2">
|
||||
<input type='hidden' name='accessnumber' value='<?=$accessnumber;?>'>
|
||||
<input type='hidden' name='userid' value='<?=session('userid');?>'>
|
||||
<textarea class='form-control' name='comment' placeholder="Note ..." rows='5'></textarea>
|
||||
</div>
|
||||
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class='col text-end'>
|
||||
<button class='btn btn-xs btn-warning' onclick="event.preventDefault();unvalidateRequest('unValForm', <?=$accessnumber;?>);">Un-Validate</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -1,261 +0,0 @@
|
||||
<?= $this->extend('_layouts/main.php') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<title>Customer Service Glenlis</title>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div class="container-fluid px-3">
|
||||
<div class='row p-1'>
|
||||
<div class='col fw-bold'>Dashboard, <?=session('userrole');?></div>
|
||||
<div class='col text-end'>
|
||||
Hi, <?=session('userid');?>
|
||||
<button class="btn btn-sm btn-secondary" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-list"></i></button>
|
||||
<div class="dropdown">
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li><a class="dropdown-item" href="<?=base_url('cs/');?>">Dashboard</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('');?>">Report</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('logout');?>">Log Out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='col'>
|
||||
<form method="GET" action='<?= base_url('/cs') ?>'>
|
||||
<div class="row align-items-center g-1">
|
||||
<div class="col-auto fw-semibold text-muted">
|
||||
Date :
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date1"
|
||||
value="<?php echo $date1; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="col-auto">-</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date2"
|
||||
value="<?php echo $date2; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="col-auto"> <button type="button" class="btn btn-sm btn-outline-dark" onclick="window.location.href='<?=base_url();?>';"> RESET </button> </div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="filterGroup" class="col text-end">
|
||||
<button class='btn btn-sm btn-outline-secondary' data-type='secondary'><?=$counter['pending'];?> <small>Pending</small></button>
|
||||
<button class='btn btn-sm btn-outline-info' data-type='info'><?=$counter['collect'];?> <small>Collect</small></button>
|
||||
<button class='btn btn-sm btn-outline-primary' data-type='primary'><?=$counter['recv'];?> <small>Receive</small></button>
|
||||
<button class='btn btn-sm btn-outline-warning' data-type='warning'><?=$counter['incomplete'];?> <small>Incomplete</small></button>
|
||||
<button class='btn btn-sm btn-outline-success' data-type='success'><?=$counter['complete'];?> <small>Complete</small></button>
|
||||
<button class='btn btn-sm btn-dark' data-type='dark'><?=$counter['total'];?> <small>Total</small></button>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button id="filterValBtn" class='btn btn-sm btn-outline-primary' onclick="filterVal('<?=session('userid');?>')"> <small>Validate</small></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table table-responsive-scroll">
|
||||
<table id="datatables" class="table table-sm table-hover table-bordered align-middle table-striped">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th style="width:1%;">S</th>
|
||||
<th style='width:7%;'>Order Datetime</th>
|
||||
<th>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>
|
||||
<?php foreach($dataList as $row) : ?>
|
||||
<?php
|
||||
switch ($row['statscode']) {
|
||||
case 1: $class = 'BackPend'; break;
|
||||
case 2: $class = 'PartColl'; break;
|
||||
case 3: $class = 'Collected'; break;
|
||||
case 4: $class = 'PartRecv'; break;
|
||||
case 5: $class = 'Received'; break;
|
||||
case 6: $class = 'Incomplete'; break;
|
||||
case 7: $class = 'Final'; break;
|
||||
case 8: $class = 'FinVerification'; break;
|
||||
case 9: $class = 'PenVerification'; break;
|
||||
default: $class = ''; break;
|
||||
}
|
||||
$tests = esc($row['tests']);
|
||||
$tests = str_replace(",", ", ", $tests);
|
||||
$accessnumber = $row['sp_accessnumber'];
|
||||
?>
|
||||
<tr>
|
||||
<!--
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['statscode']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reqdate']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['patname']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['sp_accessnumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['hostordernumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reff']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['doc']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['tests']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['odr_cresult_to']) ?> </td>
|
||||
-->
|
||||
<td> <?= esc($row['statscode']) ?> </td> <td> <?= esc($row['reqdate']) ?> </td> <td> <?= esc($row['patname']) ?> </td>
|
||||
<td> <?= esc($row['sp_accessnumber']) ?> </td> <td> <?= esc($row['hostordernumber']) ?> </td> <td> <?= esc($row['reff']) ?> </td>
|
||||
<td> <?= esc($row['doc']) ?> </td> <td> <?= $tests; ?> </td>
|
||||
|
||||
<td>
|
||||
<?= esc($row['odr_cresult_to']) ?> <br/>
|
||||
<?php if($row['valcounter'] == 2) { ?>
|
||||
<input type="checkbox" name="printed" id='printed'> Printed <br/>
|
||||
<input type="checkbox" name="eng" id='eng'> Eng
|
||||
<?php } ?>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<table class='mx-auto'>
|
||||
<tr>
|
||||
<td id='val1user-<?=$accessnumber;?>'>1 : <?=$row['val1user'];?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id='val2user-<?=$accessnumber;?>'>2 : <?=$row['val2user'];?></td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
|
||||
|
||||
<td class="pointercol <?= esc($class) ?>" > <?= esc($row['stats']) ?> </td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Specimen Collection-->
|
||||
<div class="modal fade" id="detailModal" aria-hidden="true" aria-labelledby="detailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fw-bold fs-4" id="detailModal">Specimen Collection</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="specimenModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="validateModal" aria-hidden="true" aria-labelledby="validateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="validateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="unvalidateModal" aria-hidden="true" aria-labelledby="unvalidateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="unvalidateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('script') ?>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
table = $('#datatables').DataTable({
|
||||
layout: {
|
||||
topEnd: 'search',
|
||||
bottomStart: null, bottomEnd: null
|
||||
},
|
||||
order: [[0, 'asc'], [3, 'asc']],
|
||||
paging: false,
|
||||
scrollCollapse: true,
|
||||
scrollY: '70vh',
|
||||
columnDefs: [{
|
||||
className: 'text-center',
|
||||
targets: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
}, // semua kolom di tengah
|
||||
{
|
||||
className: 'text-start',
|
||||
targets: [10]
|
||||
}
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
$('#filterGroup .btn').on('click', function () {
|
||||
$('#filterGroup .btn').each(function () {
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-${type}`).addClass(`btn-outline-${type}`);
|
||||
});
|
||||
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-outline-${type}`).addClass(`btn-${type}`);
|
||||
let vals;
|
||||
if(type=='secondary') { vals = [0]; }
|
||||
if(type=='info') { vals = ['1','2']; }
|
||||
if(type=='primary') { vals = ['3','4','5']; }
|
||||
if(type=='warning') { vals = [6,7]; }
|
||||
if(type=='success') { vals = [8]; }
|
||||
if(type=='dark') {vals=[];}
|
||||
regex = vals.join('|');
|
||||
table.column(0).search(regex, true, false).draw();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Select the column by index (1) and apply a search that excludes "Test"
|
||||
// The regex: ^((?!Test).)*$
|
||||
// - ^...$ anchors the start and end
|
||||
// - (?!Test) is a negative lookahead, failing the match if "Test" is found
|
||||
function filterVal(userid) {
|
||||
var currentSearch = table.column(9).search();
|
||||
if (currentSearch.includes(userid)) {
|
||||
table.column(9).search( '' ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-primary`).addClass(`btn-outline-primary`);
|
||||
} else {
|
||||
table.column(9).search( `^((?!${userid}).)*$`, true, false ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-outline-primary`).addClass(`btn-primary`);
|
||||
}
|
||||
}
|
||||
|
||||
function unvalidate(accessnumber,userid) {
|
||||
if(confirm("Are you sure?")) {
|
||||
fetch(`${BASE_URL}/api/request/validate/${accessnumber}`, {
|
||||
method: "DELETE",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}` })
|
||||
}).then(response => {
|
||||
console.log("Response:", response);
|
||||
$(`#val1user-${accessnumber}`).text("1 : ");
|
||||
$(`#val2user-${accessnumber}`).text("2 : ");
|
||||
$(`#valBtn-${accessnumber}`).prop('disabled', false);
|
||||
$(`#unvalBtn-${accessnumber}`).prop('disabled', false);
|
||||
}).catch(error => {
|
||||
console.log("Error:", error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_URL = "<?= base_url(); ?>";
|
||||
</script>
|
||||
<script src="<?=base_url('js/admin/specimen.js');?>"></script>
|
||||
<script src="<?=base_url('js/admin/request.js');?>"></script>
|
||||
<?= $this->endSection() ?>
|
||||
@ -1,40 +0,0 @@
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class="col">val : <?=$val1user;?> - <?=$val2user;?></div>
|
||||
<div class='col text-end'>
|
||||
<button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button>
|
||||
<!-- <button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mx-auto">
|
||||
<iframe id="result-iframe" src="http://glenlis/spooler_db/main_dev.php?acc=<?=$accessnumber;?>" width="750px" height="600px"> -->
|
||||
<!-- <iframe id="result-iframe" src="<?=base_url('dummypage');?>" width="750px" height="600px"> -->
|
||||
|
||||
<p>Your browser does not support iframes. Please contact the technical support.</p>
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{
|
||||
// verifymodal
|
||||
let iframe = document.getElementById('result-iframe');
|
||||
let button = document.getElementById('result-button');
|
||||
|
||||
iframe.addEventListener('load', () => {
|
||||
//console.log('loaded');
|
||||
let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
let scrollElement = iframeDoc.scrollingElement || iframeDoc.documentElement;
|
||||
|
||||
iframeDoc.addEventListener('scroll', () => {
|
||||
let scrollTop = scrollElement.scrollTop;
|
||||
let scrollHeight = scrollElement.scrollHeight;
|
||||
let clientHeight = scrollElement.clientHeight;
|
||||
|
||||
// Check if scrolled to bottom
|
||||
if (scrollTop + clientHeight >= scrollHeight - 2) {
|
||||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -1,106 +0,0 @@
|
||||
<div class="row mb-4 p-3">
|
||||
|
||||
<div class="col-8">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Patient</div>
|
||||
<div class="info-value">: {{patientIdentity.name}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Age</div>
|
||||
<div class="info-value">: {{patientIdentity.age}} years</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Gender</div>
|
||||
<div class="info-value">: {{patientIdentity.gender}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">Lab#</div>
|
||||
<div class="info-value">: {{accessnumber}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">MR#</div>
|
||||
<div class="info-value">: {{patientIdentity.rm}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">KTP</div>
|
||||
<div class="info-value">: {{patientIdentity.ktp}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fs-4 mb-2">
|
||||
<i class="bi bi-flask-florence-fill"></i> Tube
|
||||
</h5>
|
||||
|
||||
<table class="table fs-6 text-start table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sample Code</th>
|
||||
<th>Sample Name</th>
|
||||
<th class='text-center'>Collected</th>
|
||||
<th class='text-center'>Received</th>
|
||||
<th>Action</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td> <td>Collection</td> <td></td> <td></td>
|
||||
<td><button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td> <td>All</td> <td></td> <td></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick=""><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
</tr>
|
||||
{{#samples}}
|
||||
<tr id='row-{{accessnumber}}-{{sampcode}}'>
|
||||
<td>{{sampcode}}</td>
|
||||
<td>{{name}}</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input coll-checkbox"
|
||||
{{#is_coll}}checked{{/is_coll}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input recv-checkbox"
|
||||
{{#is_recv}}checked{{/is_recv}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 1)"><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 0)"><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="unreceive({{accessnumber}}, {{sampcode}})"><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{{/samples}}
|
||||
|
||||
<tr class="text-center">
|
||||
<td colspan="6">
|
||||
<h6 class="p-0 m-0">
|
||||
<i class="bi bi-pencil-square" role="button" onclick="commentValue()"></i>
|
||||
</h6>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,16 +0,0 @@
|
||||
<form id='unValForm'>
|
||||
<div class="row mb-2">
|
||||
<h4>Unvalidate <?=$accessnumber;?></h4>
|
||||
</div>
|
||||
<div class="row m-2">
|
||||
<input type='hidden' name='accessnumber' value='<?=$accessnumber;?>'>
|
||||
<input type='hidden' name='userid' value='<?=session('userid');?>'>
|
||||
<textarea class='form-control' name='comment' placeholder="Note ..." rows='5'></textarea>
|
||||
</div>
|
||||
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class='col text-end'>
|
||||
<button class='btn btn-xs btn-warning' onclick="event.preventDefault();unvalidateRequest('unValForm', <?=$accessnumber;?>);">Un-Validate</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -1,285 +0,0 @@
|
||||
<?= $this->extend('_layouts/main.php') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<title>Doctor Glenlis</title>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div class="container-fluid px-3">
|
||||
<div class='row p-1'>
|
||||
<div class='col fw-bold'>Dashboard, <?=session('userrole');?></div>
|
||||
<div class='col text-end'>
|
||||
Hi, <?=session('userid');?>
|
||||
<button class="btn btn-sm btn-secondary" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false"><i class="bi bi-list"></i></button>
|
||||
<div class="dropdown">
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li><a class="dropdown-item" href="<?=base_url('doctor/');?>">Dashboard</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('');?>">Report</a></li>
|
||||
<li><a class="dropdown-item" href="<?=base_url('logout');?>">Log Out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='col'>
|
||||
<form method="GET" action='<?= base_url('/doctor') ?>'>
|
||||
<div class="row align-items-center g-1">
|
||||
<div class="col-auto fw-semibold text-muted">
|
||||
Date :
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date1"
|
||||
value="<?php echo $date1; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="col-auto">-</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="date" class="form-control form-control-sm" name="date2"
|
||||
value="<?php echo $date2; ?>" />
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="col-auto"> <button type="button" class="btn btn-sm btn-outline-dark" onclick="window.location.href='<?=base_url();?>';"> RESET </button> </div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="filterGroup" class="col text-end">
|
||||
<button class='btn btn-sm btn-outline-secondary' data-type='secondary'><?=$counter['pending'];?> <small>Pending</small></button>
|
||||
<button class='btn btn-sm btn-outline-info' data-type='info'><?=$counter['collect'];?> <small>Collect</small></button>
|
||||
<button class='btn btn-sm btn-outline-primary' data-type='primary'><?=$counter['recv'];?> <small>Receive</small></button>
|
||||
<button class='btn btn-sm btn-outline-warning' data-type='warning'><?=$counter['incomplete'];?> <small>Incomplete</small></button>
|
||||
<button class='btn btn-sm btn-outline-success' data-type='success'><?=$counter['complete'];?> <small>Complete</small></button>
|
||||
<button class='btn btn-sm btn-dark' data-type='dark'><?=$counter['total'];?> <small>Total</small></button>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button id="filterValBtn" class='btn btn-sm btn-outline-primary' onclick="filterVal('<?=session('userid');?>')"> <small>Validate</small></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table table-responsive-scroll">
|
||||
<table id="datatables" class="table table-sm table-hover table-bordered align-middle table-striped">
|
||||
<thead class="table-primary">
|
||||
<tr>
|
||||
<th style="width:1%;">S</th>
|
||||
<th style='width:7%;'>Order Datetime</th>
|
||||
<th>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>
|
||||
<?php foreach($dataList as $row) : ?>
|
||||
<?php
|
||||
switch ($row['statscode']) {
|
||||
case 1: $class = 'BackPend'; break;
|
||||
case 2: $class = 'PartColl'; break;
|
||||
case 3: $class = 'Collected'; break;
|
||||
case 4: $class = 'PartRecv'; break;
|
||||
case 5: $class = 'Received'; break;
|
||||
case 6: $class = 'Incomplete'; break;
|
||||
case 7: $class = 'Final'; break;
|
||||
case 8: $class = 'FinVerification'; break;
|
||||
case 9: $class = 'PenVerification'; break;
|
||||
default: $class = ''; break;
|
||||
}
|
||||
$tests = esc($row['tests']);
|
||||
$tests = str_replace(",", ", ", $tests);
|
||||
$accessnumber = $row['sp_accessnumber'];
|
||||
?>
|
||||
<tr>
|
||||
<!--
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['statscode']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reqdate']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['patname']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['sp_accessnumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['hostordernumber']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['reff']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['doc']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['tests']) ?> </td>
|
||||
<td class="pointercol" data-access="<?= $row['sp_accessnumber'] ?>" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['odr_cresult_to']) ?> </td>
|
||||
-->
|
||||
<td> <?= esc($row['statscode']) ?> </td> <td> <?= esc($row['reqdate']) ?> </td> <td> <?= esc($row['patname']) ?> </td>
|
||||
<td> <?= esc($row['sp_accessnumber']) ?> </td> <td> <?= esc($row['hostordernumber']) ?> </td> <td> <?= esc($row['reff']) ?> </td>
|
||||
<td> <?= esc($row['doc']) ?> </td> <td> <?= $tests; ?> </td>
|
||||
|
||||
<td>
|
||||
<?= esc($row['odr_cresult_to']) ?> <br/>
|
||||
<?php if($row['valcounter'] == 2) { ?>
|
||||
<input type="checkbox" name="printed" id='printed'> Printed <br/>
|
||||
<input type="checkbox" name="eng" id='eng'> Eng
|
||||
<?php } ?>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<table class='mx-auto'>
|
||||
<tr>
|
||||
<td id='val1user-<?=$accessnumber;?>'>1 : <?=$row['val1user'];?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id='val2user-<?=$accessnumber;?>'>2 : <?=$row['val2user'];?></td>
|
||||
</tr>
|
||||
|
||||
<?php if($row['isvaltd'] != 0) { ?>
|
||||
<?php
|
||||
$user = session()->get('userid');
|
||||
$val1 = $row['val1user'];
|
||||
$val2 = $row['val2user'];
|
||||
|
||||
$valButton = (($val1 === '-') || ($val2 === '-')) && ($user !== $val1 && $user !== $val2);
|
||||
$unvalButton = ($val1 !== '-') || ($val2 !== '-');
|
||||
?>
|
||||
<tr>
|
||||
|
||||
<td id="btnContainer-<?=$accessnumber;?>">
|
||||
<?php if ($unvalButton): ?>
|
||||
<button id='unvalBtn-<?=$accessnumber;?>' class='btn btn-xs btn-outline-secondary px-1 py-0 mb-1' onclick="unvalidateShow(<?=$row['sp_accessnumber'];?>)" data-bs-toggle="modal" data-bs-target="#unvalidateModal"><i class="bi bi-arrow-counterclockwise"></i></button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($valButton): ?>
|
||||
<button id='valBtn-<?= $accessnumber; ?>' class='btn btn-xs btn-outline-success px-1 py-0 mb-1' onclick="validateShow(<?= $row['sp_accessnumber']; ?>)" data-bs-toggle="modal" data-bs-target="#validateModal"> <i class="bi bi-check-lg"></i></button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
|
||||
|
||||
<td class="pointercol <?= esc($class) ?>" data-access="<?= $row['sp_accessnumber'] ?>" onclick="detailShow('<?=session('userrole');?>')" data-bs-toggle="modal" data-bs-target="#detailModal"> <?= esc($row['stats']) ?> </td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Specimen Collection-->
|
||||
<div class="modal fade" id="detailModal" aria-hidden="true" aria-labelledby="detailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fw-bold fs-4" id="detailModal">Specimen Collection</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="specimenModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="validateModal" aria-hidden="true" aria-labelledby="validateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="validateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="unvalidateModal" aria-hidden="true" aria-labelledby="unvalidateModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body" id="unvalidateModalContent"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('script') ?>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
table = $('#datatables').DataTable({
|
||||
layout: {
|
||||
topEnd: 'search',
|
||||
bottomStart: null, bottomEnd: null
|
||||
},
|
||||
order: [[0, 'asc'], [3, 'asc']],
|
||||
paging: false,
|
||||
scrollCollapse: true,
|
||||
scrollY: '70vh',
|
||||
columnDefs: [{
|
||||
className: 'text-center',
|
||||
targets: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
}, // semua kolom di tengah
|
||||
{
|
||||
className: 'text-start',
|
||||
targets: [10]
|
||||
}
|
||||
],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
$('#filterGroup .btn').on('click', function () {
|
||||
$('#filterGroup .btn').each(function () {
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-${type}`).addClass(`btn-outline-${type}`);
|
||||
});
|
||||
|
||||
const type = $(this).data('type');
|
||||
$(this).removeClass(`btn-outline-${type}`).addClass(`btn-${type}`);
|
||||
let vals;
|
||||
if(type=='secondary') { vals = [0]; }
|
||||
if(type=='info') { vals = ['1','2']; }
|
||||
if(type=='primary') { vals = ['3','4','5']; }
|
||||
if(type=='warning') { vals = [6,7]; }
|
||||
if(type=='success') { vals = [8]; }
|
||||
if(type=='dark') {vals=[];}
|
||||
regex = vals.join('|');
|
||||
table.column(0).search(regex, true, false).draw();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Select the column by index (1) and apply a search that excludes "Test"
|
||||
// The regex: ^((?!Test).)*$
|
||||
// - ^...$ anchors the start and end
|
||||
// - (?!Test) is a negative lookahead, failing the match if "Test" is found
|
||||
function filterVal(userid) {
|
||||
var currentSearch = table.column(9).search();
|
||||
if (currentSearch.includes(userid)) {
|
||||
table.column(9).search( '' ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-primary`).addClass(`btn-outline-primary`);
|
||||
} else {
|
||||
table.column(9).search( `^((?!${userid}).)*$`, true, false ).draw();
|
||||
$("#filterValBtn").removeClass(`btn-outline-primary`).addClass(`btn-primary`);
|
||||
}
|
||||
}
|
||||
|
||||
function unvalidate(accessnumber,userid) {
|
||||
if(confirm("Are you sure?")) {
|
||||
fetch(`${BASE_URL}/api/request/validate/${accessnumber}`, {
|
||||
method: "DELETE",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}` })
|
||||
}).then(response => {
|
||||
console.log("Response:", response);
|
||||
$(`#val1user-${accessnumber}`).text("1 : ");
|
||||
$(`#val2user-${accessnumber}`).text("2 : ");
|
||||
$(`#valBtn-${accessnumber}`).prop('disabled', false);
|
||||
$(`#unvalBtn-${accessnumber}`).prop('disabled', false);
|
||||
}).catch(error => {
|
||||
console.log("Error:", error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const BASE_URL = "<?= base_url(); ?>";
|
||||
</script>
|
||||
<script src="<?=base_url('js/admin/specimen.js');?>"></script>
|
||||
<script src="<?=base_url('js/admin/request.js');?>"></script>
|
||||
<?= $this->endSection() ?>
|
||||
@ -1,40 +0,0 @@
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class="col">val : <?=$val1user;?> - <?=$val2user;?></div>
|
||||
<div class='col text-end'>
|
||||
<button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button>
|
||||
<!-- <button id='result-button' class='btn btn-xs btn-success' onclick='validate(<?=$accessnumber;?>, "<?=session('userid');?>" )' disabled>Verify</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mx-auto">
|
||||
<iframe id="result-iframe" src="http://glenlis/spooler_db/main_dev.php?acc=<?=$accessnumber;?>" width="750px" height="600px"> -->
|
||||
<!-- <iframe id="result-iframe" src="<?=base_url('dummypage');?>" width="750px" height="600px"> -->
|
||||
|
||||
<p>Your browser does not support iframes. Please contact the technical support.</p>
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{
|
||||
// verifymodal
|
||||
let iframe = document.getElementById('result-iframe');
|
||||
let button = document.getElementById('result-button');
|
||||
|
||||
iframe.addEventListener('load', () => {
|
||||
//console.log('loaded');
|
||||
let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
let scrollElement = iframeDoc.scrollingElement || iframeDoc.documentElement;
|
||||
|
||||
iframeDoc.addEventListener('scroll', () => {
|
||||
let scrollTop = scrollElement.scrollTop;
|
||||
let scrollHeight = scrollElement.scrollHeight;
|
||||
let clientHeight = scrollElement.clientHeight;
|
||||
|
||||
// Check if scrolled to bottom
|
||||
if (scrollTop + clientHeight >= scrollHeight - 2) {
|
||||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -1,106 +0,0 @@
|
||||
<div class="row mb-4 p-3">
|
||||
|
||||
<div class="col-8">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Patient</div>
|
||||
<div class="info-value">: {{patientIdentity.name}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Age</div>
|
||||
<div class="info-value">: {{patientIdentity.age}} years</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-25 fw-bold">Gender</div>
|
||||
<div class="info-value">: {{patientIdentity.gender}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">Lab#</div>
|
||||
<div class="info-value">: {{accessnumber}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">MR#</div>
|
||||
<div class="info-value">: {{patientIdentity.rm}}</div>
|
||||
</div>
|
||||
<div class="info-pair d-flex">
|
||||
<div class="info-label w-50 fw-bold">KTP</div>
|
||||
<div class="info-value">: {{patientIdentity.ktp}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fs-4 mb-2">
|
||||
<i class="bi bi-flask-florence-fill"></i> Tube
|
||||
</h5>
|
||||
|
||||
<table class="table fs-6 text-start table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Sample Code</th>
|
||||
<th>Sample Name</th>
|
||||
<th class='text-center'>Collected</th>
|
||||
<th class='text-center'>Received</th>
|
||||
<th>Action</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td> <td>Collection</td> <td></td> <td></td>
|
||||
<td><button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td> <td>All</td> <td></td> <td></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick=""><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick=""><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
</tr>
|
||||
{{#samples}}
|
||||
<tr id='row-{{accessnumber}}-{{sampcode}}'>
|
||||
<td>{{sampcode}}</td>
|
||||
<td>{{name}}</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input coll-checkbox"
|
||||
{{#is_coll}}checked{{/is_coll}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td class='text-center'>
|
||||
<input type="checkbox" class="form-check-input recv-checkbox"
|
||||
{{#is_recv}}checked{{/is_recv}}
|
||||
disabled
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-secondary px-2 py-1"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-success px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 1)"><h6 class="p-0 m-0">Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="collect({{accessnumber}}, {{sampcode}}, 0)"><h6 class="p-0 m-0">Un-Coll.</h6></button>
|
||||
<button class="btn btn-warning px-2 py-1" onclick="unreceive({{accessnumber}}, {{sampcode}})"><h6 class="p-0 m-0">Un-Recv.</h6></button>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{{/samples}}
|
||||
|
||||
<tr class="text-center">
|
||||
<td colspan="6">
|
||||
<h6 class="p-0 m-0">
|
||||
<i class="bi bi-pencil-square" role="button" onclick="commentValue()"></i>
|
||||
</h6>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,16 +0,0 @@
|
||||
<form id='unValForm'>
|
||||
<div class="row mb-2">
|
||||
<h4>Unvalidate <?=$accessnumber;?></h4>
|
||||
</div>
|
||||
<div class="row m-2">
|
||||
<input type='hidden' name='accessnumber' value='<?=$accessnumber;?>'>
|
||||
<input type='hidden' name='userid' value='<?=session('userid');?>'>
|
||||
<textarea class='form-control' name='comment' placeholder="Note ..." rows='5'></textarea>
|
||||
</div>
|
||||
|
||||
<div class='row mb-2 mx-2'>
|
||||
<div class='col text-end'>
|
||||
<button class='btn btn-xs btn-warning' onclick="event.preventDefault();unvalidateRequest('unValForm', <?=$accessnumber;?>);">Un-Validate</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -1,96 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<title> Test display of HTML elements </title>
|
||||
<!-- based off of https://www.cs.tut.fi/~jkorpela/www/testel.html, with additions of newer features -->
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Testing display of HTML elements</h1>
|
||||
<p>This is the document <code>header</code></p>
|
||||
</header>
|
||||
<main>
|
||||
<article aria-label="headings">
|
||||
<h1>This is 1st level heading</h1>
|
||||
<p>This is a test paragraph.</p>
|
||||
<h2>This is 2nd level heading</h2>
|
||||
<p>This is a test paragraph.</p>
|
||||
<h3>This is 3rd level heading</h3>
|
||||
<p>This is a test paragraph.</p>
|
||||
<h4>This is 4th level heading</h4>
|
||||
<p>This is a test paragraph.</p>
|
||||
<h5>This is 5th level heading</h5>
|
||||
<p>This is a test paragraph.</p>
|
||||
<h6>This is 6th level heading</h6>
|
||||
<p>This is a test paragraph.</p>
|
||||
</article>
|
||||
<article>
|
||||
<h2>Basic block level elements</h2>
|
||||
<p>
|
||||
This is a normal paragraph (<code>p</code> element). To add some length to it, let us mention that this page was primarily written for testing the effect of <strong>user style sheets</strong>. You can use it for various other purposes as well, like just checking how your browser displays various HTML elements by default. It can also be useful when testing conversions from HTML format to other formats, since some elements can go wrong then.
|
||||
</p>
|
||||
<p>
|
||||
This is another paragraph. I think it needs to be added that the set of elements tested is not exhaustive in any sense. I have selected those elements for which it can make sense to write user style sheet rules, in my opionion.
|
||||
</p>
|
||||
<div>This is a <code>div</code> element. Authors may use such elements instead of paragraph markup for various reasons. (End of <code>div</code>.)</div>
|
||||
<blockquote>
|
||||
<p>
|
||||
This is a block quotation containing a single paragraph. Well, not quite, since this is not <em>really</em> quoted text, but I hope you understand the point. After all, this page does not use HTML markup very normally anyway.
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>The following contains address information about the author, in an <code>address</code> element.</p>
|
||||
<address>
|
||||
<a href="../somelink.html" lang="fr" hreflang="en">Mon nom en francais</a>,
|
||||
<a href="mailto:example@example.com">example@example.com</a><br>
|
||||
3 Rue Jules Ferry, Neuilly Sur Seine, France 94000
|
||||
</address>
|
||||
</article>
|
||||
<article>
|
||||
<h2>Lists</h2>
|
||||
<p>
|
||||
This is a paragraph before an <strong>unnumbered</strong> list (<code>ul</code>). Note that the spacing between a paragraph and a list before or after that is hard to tune in a user style sheet. You can't guess which paragraphs are logically related to a list, e.g. as a "list header".
|
||||
</p>
|
||||
<ul>
|
||||
<li> One.</li>
|
||||
<li> Two.</li>
|
||||
<li> Three. Well, probably this list item should be longer. Note that for short items lists look better if they are compactly presented, whereas for long items, it would be better to have more vertical spacing between items.</li>
|
||||
<li> Four. This is the last item in this list. Let us terminate the list now without making any more fuss about it.</li>
|
||||
</ul>
|
||||
<p>The following is a <code>menu</code> list:</p>
|
||||
<menu>
|
||||
<li> One.</li>
|
||||
<li> Two.</li>
|
||||
<li> Three. Well, probably this list item should be longer so that it will probably wrap to the next line in rendering.</li>
|
||||
</menu>
|
||||
<p>The following is a <code>dir</code> list:</p>
|
||||
<dir>
|
||||
<li> One.</li>
|
||||
<li> Two.</li>
|
||||
<li> Three. Well, probably this list item should be longer so that it will probably wrap to the next line in rendering.</li>
|
||||
</dir>
|
||||
<p>
|
||||
This is a paragraph before a <strong>numbered</strong> list (<code>ol</code>). Note that the spacing between a paragraph and a list before or after that is hard to tune in a user style sheet. You can't guess which paragraphs are logically related to a list, e.g. as a "list header".
|
||||
</p>
|
||||
<ol>
|
||||
<li> One.</li>
|
||||
<li> Two.</li>
|
||||
<li> Three. Well, probably this list item should be longer. Note that if items are short, lists look better if they are compactly presented, whereas for long items, it would be better to have more vertical spacing between items.</li>
|
||||
<li> Four. This is the last item in this list. Let us terminate the list now without making any more fuss about it.</li>
|
||||
</ol>
|
||||
<p>
|
||||
This is a paragraph before a <strong>definition</strong> list (<code>dl</code>). In principle, such a list should consist of <em>terms</em> and associated definitions. But many authors use <code>dl</code> elements for fancy "layout" things. Usually the effect is not <em>too</em> bad, if you design user style sheet rules for <code>dl</code> which are suitable for real definition lists.
|
||||
</p>
|
||||
<dl>
|
||||
<dt> recursion</dt>
|
||||
<dd> see recursion</dd>
|
||||
<dt> recursion, indirect</dt>
|
||||
<dd> see indirect recursion</dd>
|
||||
<dt> indirect recursion</dt>
|
||||
<dd> see recursion, indirect</dd>
|
||||
<dt> term</dt>
|
||||
<dd> a word or other expression taken into specific use in a well-defined meaning, which is often defined rather rigorously, even formally, and may differ quite a lot from an everyday meaning</dd>
|
||||
</dl>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,4 +1,4 @@
|
||||
<?= $this->extend('v2/lab/main'); ?>
|
||||
<?= $this->extend('lab/main'); ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<main class="p-4 flex-1 flex flex-col gap-2" x-data="dashboard">
|
||||
@ -67,66 +67,94 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto px-4 pb-4">
|
||||
<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 class='flex gap-1 items-center'>
|
||||
<div class='w-15'>
|
||||
<p>1: <span x-text="req.VAL1USER"></span></p>
|
||||
<p>2: <span x-text="req.VAL2USER"></span></p>
|
||||
</div>
|
||||
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
||||
<div class='text-center'>
|
||||
<template 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 x-if="req.VAL1USER != '<?=session('userid');?>' && req.VAL2USER != '<?=session('userid');?>'">
|
||||
<button class="btn btn-xs btn-outline btn-success" @click="openValDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-check"></i></button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
<td><button x-text="req.STATS === 'Fin' ? 'Final' : req.STATS" class="btn btn-xs"
|
||||
:class="statusColor[req.STATS]" @click="openSampleDialog(req.SP_ACCESSNUMBER)"></button></td>
|
||||
<div class="flex-1 overflow-y-auto 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:7%;'><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:7%;'><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: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>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
</div>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="i in 5" :key="i">
|
||||
<tr>
|
||||
<td colspan="9"><div class="skeleton h-4 w-full"></div></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<template x-if="!isLoading && !list.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 && 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: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.REFF"></td>
|
||||
<td x-text="req.DOC"></td>
|
||||
<td x-text="req.TESTS"></td>
|
||||
<td x-text="req.ODR_CRESULT_TO"></td>
|
||||
<td>
|
||||
<div class='flex gap-1 items-center'>
|
||||
<div class='w-15'>
|
||||
<p>1: <span x-text="req.VAL1USER"></span></p>
|
||||
<p>2: <span x-text="req.VAL2USER"></span></p>
|
||||
</div>
|
||||
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
||||
<div class='text-center'>
|
||||
<template 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 x-if="req.VAL1USER != '<?=session('userid');?>' && req.VAL2USER != '<?=session('userid');?>'">
|
||||
<button class="btn btn-xs btn-outline btn-success" @click="openValDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-check"></i></button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
<td><button x-text="req.STATS === 'Fin' ? 'Final' : 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/admin/dialog_sample'); ?>
|
||||
<?php echo $this->include('v2/admin/dialog_val'); ?>
|
||||
<?php echo $this->include('v2/admin/dialog_unval'); ?>
|
||||
<?php echo $this->include('admin/dialog_sample'); ?>
|
||||
<?php echo $this->include('admin/dialog_val'); ?>
|
||||
<?php echo $this->include('admin/dialog_unval'); ?>
|
||||
|
||||
</main>
|
||||
<?= $this->endSection(); ?>
|
||||
@ -139,9 +167,10 @@
|
||||
Alpine.data("dashboard", ()=> ({
|
||||
// dashboard
|
||||
today: "",
|
||||
filter: { date1: "", date2: "" },
|
||||
list: [],
|
||||
counters: { Pend: 0, Coll: 0, Recv: 0, Inc: 0, Fin: 0, Total: 0 },
|
||||
filter: { date1: "", date2: "" },
|
||||
list: [],
|
||||
isLoading: false,
|
||||
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',
|
||||
@ -164,20 +193,19 @@
|
||||
|
||||
init() {
|
||||
this.today = new Date().toISOString().slice(0, 10);
|
||||
this.filter.date1 = "2025-03-03";
|
||||
this.filter.date2 = "2025-03-03";
|
||||
//this.filter.date1 = this.today;
|
||||
//this.filter.date2 = this.today;
|
||||
//this.fetchList();
|
||||
this.filter.date1 = this.today;
|
||||
this.filter.date2 = this.today;
|
||||
this.fetchList();
|
||||
},
|
||||
|
||||
fetchList(){
|
||||
this.isLoading = true;
|
||||
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}/admin/api/requests?${param}`, {
|
||||
fetch(`${BASEURL}/api/requests?${param}`, {
|
||||
method: 'GET',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
}).then(res => res.json()).then(data => {
|
||||
@ -197,6 +225,8 @@
|
||||
let codeB = statusOrder[b.STATS] ?? 0;
|
||||
return codeA - codeB;
|
||||
});
|
||||
}).finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
@ -238,6 +268,7 @@
|
||||
*/
|
||||
item : '',
|
||||
isDialogSampleOpen : false,
|
||||
isSampleLoading: false,
|
||||
|
||||
openSampleDialog (accessnumber) {
|
||||
this.isDialogSampleOpen = true;
|
||||
@ -249,16 +280,19 @@
|
||||
},
|
||||
|
||||
fetchItem(accessnumber){
|
||||
this.isSampleLoading = true;
|
||||
this.item = [];
|
||||
fetch(`${BASEURL}/admin/api/samples/${accessnumber}`, { method: 'GET', headers: {'Content-Type': 'application/json'}})
|
||||
fetch(`${BASEURL}/api/samples/${accessnumber}`, { method: 'GET', headers: {'Content-Type': 'application/json'}})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.item = data.data ?? {};
|
||||
if (!Array.isArray(this.item.samples)) this.item.samples = [];
|
||||
}).finally(() => {
|
||||
this.isSampleLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
collect(sampcode, accessnumber) {
|
||||
fetch(`${BASEURL}/admin/api/samples/collect/${accessnumber}`, {
|
||||
fetch(`${BASEURL}/api/samples/collect/${accessnumber}`, {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid: '<?= session('userid'); ?>'})
|
||||
})
|
||||
@ -269,7 +303,7 @@
|
||||
|
||||
uncollect(sampcode, accessnumber) {
|
||||
if(!confirm(`Uncollect sample ${sampcode} from request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/admin/api/samples/collect/${accessnumber}`, {
|
||||
fetch(`${BASEURL}/api/samples/collect/${accessnumber}`, {
|
||||
method: 'DELETE', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid: '<?= session('userid'); ?>'})
|
||||
})
|
||||
@ -280,7 +314,7 @@
|
||||
|
||||
unreceive(sampcode, accessnumber) {
|
||||
if(!confirm(`Unreceive sample ${sampcode} from request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/admin/api/samples/unreceive/${accessnumber}`, {
|
||||
fetch(`${BASEURL}/api/samples/unreceive/${accessnumber}`, {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid : '<?= session('userid'); ?>'})
|
||||
})
|
||||
@ -333,7 +367,7 @@
|
||||
this.isDialogValOpen = false;
|
||||
},
|
||||
validate(accessnumber, userid) {
|
||||
fetch(`${BASEURL}/admin/api/requests/validate/${accessnumber}`, {
|
||||
fetch(`${BASEURL}/api/requests/validate/${accessnumber}`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}` })
|
||||
@ -357,7 +391,7 @@
|
||||
},
|
||||
unvalidate(accessnumber, userid) {
|
||||
if(!confirm(`Unvalidate request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/admin/api/requests/validate/${accessnumber}`, {
|
||||
fetch(`${BASEURL}/api/requests/validate/${accessnumber}`, {
|
||||
method: "DELETE",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}`, comment: this.unvalReason.trim() })
|
||||
@ -41,10 +41,10 @@
|
||||
<span class="text-xl"><i class="fa fa-user"></i></span>
|
||||
</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('v2/lab') ?>"><i class="fa fa-chart-bar mr-2"></i> Dashboard</a></li>
|
||||
<li><a class="active:bg-primary" href="<?=base_url('v2/setPassword') ?>"><i class="fa fa-key mr-2"></i> Set Password</a></li>
|
||||
<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('v2/logout')?>" class="text-error hover:bg-error/10"><i class="fa fa-sign-out-alt mr-2"></i> Logout</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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,7 +55,7 @@
|
||||
<footer class='bg-base-100 p-1'>© <?=date('Y');?> - 5Panda</footer>
|
||||
|
||||
<script>
|
||||
window.BASEURL = "<?=base_url("v2/lab");?>";
|
||||
window.BASEURL = "<?=base_url("lab");?>";
|
||||
</script>
|
||||
<?=$this->renderSection('script');?>
|
||||
</body>
|
||||
@ -1,100 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-theme="corporate">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login | GDC System</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #e0e7ff, #f8fafc);
|
||||
font-family: "Inter", "Segoe UI", sans-serif;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
||||
border: none;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, #2563eb, #1e40af);
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
color: #1e3a8a;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - 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>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-5">
|
||||
<div class="card p-5 shadow-lg">
|
||||
<div class="text-center ">
|
||||
<div class="app-title">🔒 GDC Login</div>
|
||||
<p class="text-muted">Masukkan kredensial Anda untuk melanjutkan</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<?php if(session()->getFlashdata('error')): ?>
|
||||
<div class="error-message">
|
||||
<?= esc(session()->getFlashdata('error')) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST" action="<?= base_url('login') ?>">
|
||||
<?= csrf_field() ?>
|
||||
<div class="mb-3 mt-3">
|
||||
<label class="form-label text-muted">User ID</label>
|
||||
<input type="text" style="text-transform: uppercase;" class="form-control" name="userid" placeholder="" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-muted">Password</label>
|
||||
<input type="password" class="form-control" name="password" placeholder="" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<small class="text-muted">© <?= date('Y') ?> GDC Panda Laboratory System</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<body class="min-h-screen flex items-center justify-center bg-base-200">
|
||||
<div class="w-full max-w-sm mx-auto">
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body items-center text-center">
|
||||
<div class="mb-4">
|
||||
<div class="w-16 h-16 rounded-full bg-primary/10 flex items-center justify-center mx-auto text-primary">
|
||||
<i class="fa fa-user-shield text-3xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="card-title text-2xl font-semibold text-base-content mb-1">CMOD</h2>
|
||||
<p class="mb-6 text-sm text-base-content/70">Sign in to continue</p>
|
||||
<form method="post" action="<?=base_url('login')?>" class="w-full flex flex-col gap-4">
|
||||
<div class="form-control">
|
||||
<label class="input input-bordered flex items-center gap-2 w-full">
|
||||
<i class="fa fa-user text-base-content/50"></i>
|
||||
<input type="text" name="userid" placeholder="User ID" class="grow" required />
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="input input-bordered flex items-center gap-2 w-full">
|
||||
<i class="fa fa-lock text-base-content/50"></i>
|
||||
<input type="password" name="password" placeholder="Password" class="grow" required />
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block mt-2 w-full font-medium">
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
<div class="mt-6 text-center">
|
||||
<a href="#" class="link link-hover text-sm text-base-content/60">Forgot password?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-6 text-xs text-base-content/40">© 2025 - 5Panda. All rights reserved.</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,207 +0,0 @@
|
||||
<?= $this->extend('_layouts/main.php') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<title>User Management Glenlis</title>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<div class="container-fluid px-5 py-4">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 mt-3">
|
||||
<h3 class="fw-bold text-dark mb-0">👤 User Management Glenlis</h3>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addUserModal">
|
||||
✚ Add User
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php if (session('success')): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?= esc(session('success')) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-info">
|
||||
<tr>
|
||||
<th width="5%">#</th>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
<th>Level</th>
|
||||
<th width="20%" class="text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php $i=1; foreach ($data as $row) : ?>
|
||||
<tr>
|
||||
<td><?= $i++ ?></td>
|
||||
<td><?= $row['USERID'] ?></td>
|
||||
|
||||
<!-- Untuk User Level -->
|
||||
<?php if ($row['USERLEVEL'] == 1) : ?>
|
||||
<td> <button type="button" class="btn btn-light btn-sm position-relative"> 👨🏻💻 Admin </button> </td>
|
||||
<td><span class="badge text-bg-success"><?= $row['USERLEVEL'] ?></span></td>
|
||||
<?php elseif ($row['USERLEVEL'] == 2) : ?>
|
||||
<td> <button type="button" class="btn btn-light btn-sm position-relative"> 🩺 Doctor </button> </td>
|
||||
<td><span class="badge text-bg-primary"><?= $row['USERLEVEL'] ?></span></td>
|
||||
<?php elseif ($row['USERLEVEL'] == 3) : ?>
|
||||
<td> <button type="button" class="btn btn-light btn-sm position-relative"> 👩🏻🏭 Analyst </button> </td>
|
||||
<td><span class="badge text-bg-info"><?= $row['USERLEVEL'] ?></span></td>
|
||||
<?php elseif ($row['USERLEVEL'] == 4) : ?>
|
||||
<td> <button type="button" class="btn btn-light btn-sm position-relative"> 👩💼 Customer Service </button> </td>
|
||||
<td><span class="badge text-bg-warning"><?= $row['USERLEVEL'] ?></span></td>
|
||||
<?php endif; ?>
|
||||
|
||||
<td class="text-center">
|
||||
<div class="row g-1 justify-content-center">
|
||||
<div class="col-8">
|
||||
<button class="btn btn-sm btn-outline-primary w-100"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editUserModal<?= $row['USERID'] ?>">
|
||||
✎ Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Add User -->
|
||||
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="<?= base_url('admin/user/create') ?>">
|
||||
<?= csrf_field() ?>
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title" id="addUserModalLabel">✚ Add New User</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<?php if (session('errors.server_error')): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= esc(session('errors.server_error')) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Userid</label>
|
||||
<input type="text" name="userid" class="form-control" placeholder="enter userid" style="text-transform: uppercase;" value="<?= old('userid') ?>" required>
|
||||
<?php if (session('errors.userid')): ?>
|
||||
<div class="text-danger small"> <?= esc(session('errors.userid')) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Role</label>
|
||||
<select class="form-select" name="userlevel" required>
|
||||
<option value="1" <?= old('userlevel') == '1' ? 'selected' : '' ?>>Admin</option>
|
||||
<option value="2" <?= old('userlevel') == '2' ? 'selected' : '' ?>>Doctor</option>
|
||||
<option value="3" <?= old('userlevel') == '3' ? 'selected' : '' ?>>Analyst</option>
|
||||
<option value="4" <?= old('userlevel') == '4' ? 'selected' : '' ?>>Customer Service</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="password" name="password" class="form-control" placeholder="Enter Password" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Confirm Password</label>
|
||||
<input type="password" name="password_2" class="form-control" placeholder="Confirm Password" required>
|
||||
<?php if (session('errors.password')): ?>
|
||||
<div class="text-danger small"> <?= esc(session('errors.password')) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Save User</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Edit User -->
|
||||
<?php foreach ($data as $row) : ?>
|
||||
<div class="modal fade" id="editUserModal<?=$row['USERID']?>" tabindex="-1" aria-labelledby="editUserModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="<?= base_url('admin/user/update') ?>">
|
||||
<?= csrf_field() ?>
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title" id="editUserModalLabel">Edit User</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="userid" value="<?= esc($row['USERID']) ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
<?php if (session('errors.server_error')): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<?= esc(session('errors.server_error')) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Userid</label>
|
||||
<input type="text" disabled class="form-control" style="text-transform: uppercase;" value="<?= esc($row['USERID']) ?>">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Role</label>
|
||||
<select class="form-select" name="userlevel" required>
|
||||
<option value="1" <?= esc($row['USERLEVEL']) == '1' ? 'selected' : '' ?>>Admin</option>
|
||||
<option value="2" <?= esc($row['USERLEVEL']) == '2' ? 'selected' : '' ?>>Doctor</option>
|
||||
<option value="3" <?= esc($row['USERLEVEL']) == '3' ? 'selected' : '' ?>>Analyst</option>
|
||||
<option value="4" <?= esc($row['USERLEVEL']) == '4' ? 'selected' : '' ?>>Customer Service</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="password" name="password" class="form-control" placeholder="Enter Password">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Confirm Password</label>
|
||||
<input type="password" name="password_2" class="form-control" placeholder="Confirm Password">
|
||||
<?php if (session('errors.password')): ?>
|
||||
<div class="text-danger small"> <?= esc(session('errors.password')) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('script') ?>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
<?php if (session('showModal')): ?>
|
||||
var targetModal = new bootstrap.Modal(document.getElementById('<?= session('showModal') ?>'));
|
||||
targetModal.show();
|
||||
<?php endif; ?>
|
||||
});
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
@ -1,23 +0,0 @@
|
||||
<dialog class="modal" :open="isDialogPrintOpen">
|
||||
<template x-if="printAccessnumber">
|
||||
<div class="modal-box w-11/12 max-w-7xl h-[90vh] flex flex-col p-0 overflow-hidden bg-base-100">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center p-3 bg-base-200 border-b border-base-300">
|
||||
<h3 class="font-bold text-lg flex items-center gap-2">
|
||||
<i class="fa fa-print text-primary"></i>
|
||||
Print Preview
|
||||
<span class="badge badge-neutral text-xs" x-text="printType ? printType.toUpperCase() : ''"></span>
|
||||
<span class="badge badge-ghost text-xs" x-text="printAccessnumber"></span>
|
||||
</h3>
|
||||
<button class="btn btn-sm btn-circle btn-ghost" @click="closePrintDialog()">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="flex-1 bg-base-300 relative p-1">
|
||||
<iframe :src="getPrintUrl()" class="w-full h-full rounded shadow-sm bg-white"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</dialog>
|
||||
@ -1,425 +0,0 @@
|
||||
<?= $this->extend('v2/admin/main'); ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
<main class="p-4 flex-1 flex flex-col gap-2" x-data="dashboard">
|
||||
<div class="card bg-base-100 shadow-xl h-full border border-base-200 overflow-hidden">
|
||||
<div class="card-body p-0 h-full flex flex-col">
|
||||
|
||||
<!-- 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="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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
<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>
|
||||
</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='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" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto px-4 pb-4">
|
||||
<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:3%;'>ResTo</th>
|
||||
<th style='width:5%;'>Val</th>
|
||||
<th style='width:5%;'>Result</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 class='flex gap-1 items-center'>
|
||||
<div class='w-15'>
|
||||
<p>1: <span x-text="req.VAL1USER"></span></p>
|
||||
<p>2: <span x-text="req.VAL2USER"></span></p>
|
||||
</div>
|
||||
<template x-if="req.ISVAL == 1 && req.ISPENDING != 1">
|
||||
<div class='text-center'>
|
||||
<template 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 x-if="req.VAL1USER != '<?=session('userid');?>' && req.VAL2USER != '<?=session('userid');?>'">
|
||||
<button class="btn btn-xs btn-outline btn-success" @click="openValDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-check"></i></button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<template x-if="!req.VAL2USER">
|
||||
<button class="btn btn-xs btn-outline btn-primary" @click="openPrintDialog(req.SP_ACCESSNUMBER, 'preview')">Preview</button>
|
||||
</template>
|
||||
<template x-if="req.VAL2USER">
|
||||
<div class="dropdown dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn btn-xs btn-outline btn-primary">
|
||||
Print <i class="fa fa-chevron-down text-[10px]"></i>
|
||||
</div>
|
||||
<ul tabindex="0" class="dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box w-28 border border-base-200">
|
||||
<li><a @click="openPrintDialog(req.SP_ACCESSNUMBER, 'preview')">Preview</a></li>
|
||||
<li><a @click="openPrintDialog(req.SP_ACCESSNUMBER, 'ind')">IND</a></li>
|
||||
<li><a @click="openPrintDialog(req.SP_ACCESSNUMBER, 'eng')">ENG</a></li>
|
||||
<li><a @click="openPrintDialog(req.SP_ACCESSNUMBER, 'pdf')">PDF</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</td>
|
||||
<td><button x-text="req.STATS === 'Fin' ? 'Final' : 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/admin/dialog_sample'); ?>
|
||||
<?php echo $this->include('v2/admin/dialog_val'); ?>
|
||||
<?php echo $this->include('v2/admin/dialog_unval'); ?>
|
||||
<?php echo $this->include('v2/admin/dialog_print'); ?>
|
||||
|
||||
</main>
|
||||
<?= $this->endSection(); ?>
|
||||
|
||||
<?= $this->section('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-03-03";
|
||||
this.filter.date2 = "2025-03-03";
|
||||
//this.filter.date1 = this.today;
|
||||
//this.filter.date2 = this.today;
|
||||
//this.fetchList();
|
||||
},
|
||||
|
||||
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}/admin/api/requests?${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();
|
||||
},
|
||||
|
||||
isValidated (item) {
|
||||
return item.ISVAL == 1 && item.ISPENDING != 1;
|
||||
},
|
||||
get filtered() {
|
||||
let filteredList = this.list;
|
||||
if (this.filterKey === 'Validated') {
|
||||
filteredList = filteredList.filter(item => this.isValidated(item));
|
||||
} else {
|
||||
const validStatuses = this.statusMap[this.filterKey];
|
||||
if (validStatuses.length > 0) {
|
||||
filteredList = filteredList.filter(item => validStatuses.includes(item.STATS));
|
||||
}
|
||||
}
|
||||
if (this.filterTable) {
|
||||
const searchTerm = this.filterTable.toLowerCase();
|
||||
filteredList = filteredList.filter(item =>
|
||||
Object.values(item).some(value =>
|
||||
String(value).toLowerCase().includes(searchTerm)
|
||||
)
|
||||
);
|
||||
}
|
||||
return filteredList;
|
||||
},
|
||||
get validatedCount() {
|
||||
return this.list.filter(r => this.isValidated(r)).length;
|
||||
},
|
||||
|
||||
/*
|
||||
sample dialog
|
||||
*/
|
||||
item : '',
|
||||
isDialogSampleOpen : false,
|
||||
|
||||
openSampleDialog (accessnumber) {
|
||||
this.isDialogSampleOpen = true;
|
||||
this.fetchItem(accessnumber)
|
||||
},
|
||||
|
||||
closeSampleDialog () {
|
||||
this.isDialogSampleOpen = false;
|
||||
},
|
||||
|
||||
fetchItem(accessnumber){
|
||||
this.item = [];
|
||||
fetch(`${BASEURL}/admin/api/samples/${accessnumber}`, { method: 'GET', headers: {'Content-Type': 'application/json'}})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.item = data.data ?? {};
|
||||
if (!Array.isArray(this.item.samples)) this.item.samples = [];
|
||||
});
|
||||
},
|
||||
|
||||
collect(sampcode, accessnumber) {
|
||||
fetch(`${BASEURL}/admin/api/samples/collect/${accessnumber}`, {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid: '<?= session('userid'); ?>'})
|
||||
})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.fetchItem(accessnumber);
|
||||
});
|
||||
},
|
||||
|
||||
uncollect(sampcode, accessnumber) {
|
||||
if(!confirm(`Uncollect sample ${sampcode} from request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/admin/api/samples/collect/${accessnumber}`, {
|
||||
method: 'DELETE', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid: '<?= session('userid'); ?>'})
|
||||
})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.fetchItem(accessnumber);
|
||||
});
|
||||
},
|
||||
|
||||
unreceive(sampcode, accessnumber) {
|
||||
if(!confirm(`Unreceive sample ${sampcode} from request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/admin/api/samples/unreceive/${accessnumber}`, {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({samplenumber: sampcode, userid : '<?= session('userid'); ?>'})
|
||||
})
|
||||
.then(res => res.json()).then(data => {
|
||||
this.fetchItem(accessnumber);
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
validate dialog
|
||||
*/
|
||||
isDialogValOpen : false,
|
||||
isValidateEnabled: false,
|
||||
valAccessnumber : null,
|
||||
openValDialog (accessnumber) {
|
||||
this.isDialogValOpen = true;
|
||||
this.valAccessnumber = accessnumber;
|
||||
this.$nextTick(() => {
|
||||
// refs will be available after render
|
||||
const iframe = this.$root.querySelector('#result-iframe') || (this.$refs && this.$refs.resultIframe);
|
||||
const validateBtn = this.$root.querySelector('#validate-btn') || (this.$refs && this.$refs.validateBtn);
|
||||
if (!iframe || !validateBtn) return;
|
||||
|
||||
const setup = () => {
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const scrollable = doc.documentElement || doc.body;
|
||||
const checkScroll = () => {
|
||||
try {
|
||||
const atBottom = (scrollable.scrollHeight - scrollable.scrollTop - scrollable.clientHeight) < 2;
|
||||
this.isValidateEnabled = atBottom;
|
||||
validateBtn.disabled = !atBottom;
|
||||
} catch (e) { /* cross-origin or not ready */ }
|
||||
};
|
||||
iframe.contentWindow.removeEventListener('scroll', checkScroll);
|
||||
iframe.contentWindow.addEventListener('scroll', checkScroll);
|
||||
checkScroll();
|
||||
} catch (e) { /* ignore cross-origin */ }
|
||||
};
|
||||
|
||||
// If iframe already loaded, setup immediately; otherwise wait for load
|
||||
if (iframe.contentWindow && (iframe.contentDocument && iframe.contentDocument.readyState === 'complete')) {
|
||||
setup();
|
||||
} else {
|
||||
iframe.addEventListener('load', setup);
|
||||
}
|
||||
});
|
||||
},
|
||||
closeValDialog () {
|
||||
this.isDialogValOpen = false;
|
||||
},
|
||||
validate(accessnumber, userid) {
|
||||
fetch(`${BASEURL}/admin/api/requests/validate/${accessnumber}`, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}` })
|
||||
}).then(response => {
|
||||
this.closeValDialog();
|
||||
this.fetchList();
|
||||
console.log('Validate clicked for', this.valAccessnumber, 'by user', userid);
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
unvalidate dialog
|
||||
*/
|
||||
isDialogUnvalOpen : false,
|
||||
unvalReason : '',
|
||||
unvalAccessnumber : null,
|
||||
openUnvalDialog (accessnumber) {
|
||||
this.unvalReason = '';
|
||||
this.isDialogUnvalOpen = true;
|
||||
this.unvalAccessnumber = accessnumber;
|
||||
},
|
||||
unvalidate(accessnumber, userid) {
|
||||
if(!confirm(`Unvalidate request ${accessnumber}?`)) { return ;}
|
||||
fetch(`${BASEURL}/admin/api/requests/validate/${accessnumber}`, {
|
||||
method: "DELETE",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({ userid:`${userid}`, comment: this.unvalReason.trim() })
|
||||
}).then(response => {
|
||||
this.closeUnvalDialog();
|
||||
this.fetchList();
|
||||
console.log(`Unvalidate clicked for ${accessnumber}, by user ${userid}`);
|
||||
});
|
||||
},
|
||||
closeUnvalDialog () {
|
||||
this.isDialogUnvalOpen = false;
|
||||
},
|
||||
|
||||
/*
|
||||
print dialog
|
||||
*/
|
||||
isDialogPrintOpen : false,
|
||||
printAccessnumber : null,
|
||||
printType : null,
|
||||
openPrintDialog (accessnumber, type) {
|
||||
this.printAccessnumber = accessnumber;
|
||||
this.printType = type;
|
||||
this.isDialogPrintOpen = true;
|
||||
},
|
||||
closePrintDialog () {
|
||||
this.isDialogPrintOpen = false;
|
||||
},
|
||||
getPrintUrl() {
|
||||
// Base URL from existing modal_request.php reference
|
||||
let base = 'http://glenlis/spooler_db/main_dev.php';
|
||||
let url = `${base}?acc=${this.printAccessnumber}`;
|
||||
|
||||
// Append params based on type
|
||||
if (this.printType === 'ind') url += '&lang=ID';
|
||||
if (this.printType === 'eng') url += '&lang=EN';
|
||||
if (this.printType === 'pdf') url += '&output=pdf';
|
||||
|
||||
return url;
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
Alpine.start();
|
||||
</script>
|
||||
<?= $this->endSection(); ?>
|
||||
@ -1,35 +0,0 @@
|
||||
<dialog class="modal" :class="{ 'modal-open': isDialogSetPasswordOpen }">
|
||||
<div class="modal-box">
|
||||
<form method="dialog">
|
||||
<button type="button" class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" @click="closeDialogSetPassword()">✕</button>
|
||||
</form>
|
||||
<h3 class="font-bold text-lg mb-4 text-base-content"><i class="fa fa-key text-primary mr-2"></i>Change Password</h3>
|
||||
|
||||
<form @submit.prevent="savePassword('<?= session('userid'); ?>')">
|
||||
<div class="form-control w-full mb-3">
|
||||
<label class="input w-full mb-3">
|
||||
<span class="label-text font-medium">Password</span>
|
||||
<input type="password" placeholder="••••••••" x-model="password" />
|
||||
</label>
|
||||
<label class="input w-full mb-3">
|
||||
<span class="label-text font-medium">Confirm</span>
|
||||
<input type="password" placeholder="••••••••" x-model="confirm_password" />
|
||||
</label>
|
||||
<label class="label p-1 mt-1" x-show="error" x-transition>
|
||||
<span class="label-text-alt text-error flex items-center gap-1"><i class="fa-solid fa-circle-exclamation"></i> <span x-text="error"></span></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="modal-action border-t border-base-200 mt-2 pt-4">
|
||||
<button type="button" class="btn btn-ghost" @click="closeDialogSetPassword()">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary px-6" :disabled="isLoading">
|
||||
<span x-show="isLoading" class="loading loading-spinner loading-xs"></span>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button type="button" @click="closeDialogSetPassword()">close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
@ -1,10 +0,0 @@
|
||||
<dialog class="modal" :open="isDialogUnvalOpen">
|
||||
<div class="modal-box">
|
||||
<textarea class="textarea textarea-bordered w-full" rows="5" x-model="unvalReason" placeholder="Enter reason for unvalidation..."></textarea>
|
||||
<p class='text-right mt-2'>
|
||||
<button class="btn btn-sm btn-neutral" @click="closeUnvalDialog()">Cancel</button>
|
||||
<button id="unvalidate-btn" x-ref="unvalidateBtn" class="btn btn-sm btn-warning"
|
||||
@click="unvalidate(unvalAccessnumber, '<?=session('userid');?>')" :disabled="!unvalReason.trim()">Unvalidate</button>
|
||||
</p>
|
||||
</div>
|
||||
</dialog>
|
||||
@ -1,13 +0,0 @@
|
||||
<dialog class="modal" :open="isDialogValOpen">
|
||||
<template x-if="valAccessnumber">
|
||||
<div class="modal-box w-2/3 max-w-5xl">
|
||||
<p class='text-right mx-3 mb-2'>
|
||||
<button class="btn btn-sm btn-neutral" @click="closeValDialog()">Cancel</button>
|
||||
<button id="validate-btn" x-ref="validateBtn" class="btn btn-sm btn-success"
|
||||
@click="validate(valAccessnumber, '<?=session('userid');?>')" :disabled="!isValidateEnabled">Validate</button>
|
||||
</p>
|
||||
<!-- <iframe id="result-iframe" src="http://glenlis/spooler_db/main_dev.php?acc=" width="750px" height="600px"></iframe> -->
|
||||
<iframe id="result-iframe" x-ref="resultIframe" src="<?=base_url('dummypage');?>" width="750px" height="600px"></iframe>
|
||||
</div>
|
||||
</template>
|
||||
</dialog>
|
||||
@ -1,48 +0,0 @@
|
||||
<!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>Login - 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>
|
||||
</head>
|
||||
<body class="min-h-screen flex items-center justify-center bg-base-200">
|
||||
<div class="w-full max-w-sm mx-auto">
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body items-center text-center">
|
||||
<div class="mb-4">
|
||||
<div class="w-16 h-16 rounded-full bg-primary/10 flex items-center justify-center mx-auto text-primary">
|
||||
<i class="fa fa-user-shield text-3xl"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="card-title text-2xl font-semibold text-base-content mb-1">CMOD</h2>
|
||||
<p class="mb-6 text-sm text-base-content/70">Sign in to continue</p>
|
||||
<form method="post" action="<?=base_url('v2/login')?>" class="w-full flex flex-col gap-4">
|
||||
<div class="form-control">
|
||||
<label class="input input-bordered flex items-center gap-2 w-full">
|
||||
<i class="fa fa-user text-base-content/50"></i>
|
||||
<input type="text" name="userid" placeholder="User ID" class="grow" required />
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="input input-bordered flex items-center gap-2 w-full">
|
||||
<i class="fa fa-lock text-base-content/50"></i>
|
||||
<input type="password" name="password" placeholder="Password" class="grow" required />
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block mt-2 w-full font-medium">
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
<div class="mt-6 text-center">
|
||||
<a href="#" class="link link-hover text-sm text-base-content/60">Forgot password?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-6 text-xs text-base-content/40">© 2025 - 5Panda. All rights reserved.</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user