diff --git a/.gitignore b/.gitignore
index 364a1bc..8d85d65 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,3 +125,4 @@ _modules/*
/results/
/phpunit*.xml
+.roo/
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..88b9211
--- /dev/null
+++ b/AGENTS.md
@@ -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 `` syntax
+- Pass data as associative array
+
+```php
+// Controller
+$data['dataList'] = $results;
+return view('admin/index', $data);
+
+// View
+
+
| = esc($item['name']) ?> |
+
+```
+
+### 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`
diff --git a/README.md b/README.md
index d14b4c9..5da909d 100644
--- a/README.md
+++ b/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,
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..2d05bc5
--- /dev/null
+++ b/TODO.md
@@ -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"
+}
+```
diff --git a/app/Config/Routes.php b/app/Config/Routes.php
index b1f81c4..68626c7 100644
--- a/app/Config/Routes.php
+++ b/app/Config/Routes.php
@@ -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');
- });
-});
\ No newline at end of file
diff --git a/app/Controllers/Admin.php b/app/Controllers/Admin.php
index db43355..55ea447 100644
--- a/app/Controllers/Admin.php
+++ b/app/Controllers/Admin.php
@@ -1,116 +1,21 @@
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');
}
-
+
}
diff --git a/app/Controllers/Analyst.php b/app/Controllers/Analyst.php
deleted file mode 100644
index 1966ab2..0000000
--- a/app/Controllers/Analyst.php
+++ /dev/null
@@ -1,115 +0,0 @@
-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');
- }
-
-}
diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php
index 5aac2bf..988f550 100644
--- a/app/Controllers/Auth.php
+++ b/app/Controllers/Auth.php
@@ -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);
}
}
diff --git a/app/Controllers/CustomerService.php b/app/Controllers/CustomerService.php
deleted file mode 100644
index 21fba42..0000000
--- a/app/Controllers/CustomerService.php
+++ /dev/null
@@ -1,111 +0,0 @@
-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);
- }
-
-}
diff --git a/app/Controllers/Doctor.php b/app/Controllers/Doctor.php
deleted file mode 100644
index 9896980..0000000
--- a/app/Controllers/Doctor.php
+++ /dev/null
@@ -1,115 +0,0 @@
-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');
- }
-
-}
diff --git a/app/Controllers/Home.php b/app/Controllers/Home.php
index c99740a..6e249ef 100644
--- a/app/Controllers/Home.php
+++ b/app/Controllers/Home.php
@@ -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');
}
}
diff --git a/app/Controllers/V2/Lab.php b/app/Controllers/Lab.php
similarity index 75%
rename from app/Controllers/V2/Lab.php
rename to app/Controllers/Lab.php
index f18d7c5..7f73f80 100644
--- a/app/Controllers/V2/Lab.php
+++ b/app/Controllers/Lab.php
@@ -1,5 +1,6 @@
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;
diff --git a/app/Controllers/V2/Samples.php b/app/Controllers/Samples.php
similarity index 99%
rename from app/Controllers/V2/Samples.php
rename to app/Controllers/Samples.php
index 87967e8..1128cd3 100644
--- a/app/Controllers/V2/Samples.php
+++ b/app/Controllers/Samples.php
@@ -1,5 +1,5 @@
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'];
diff --git a/app/Controllers/V2/Users.php b/app/Controllers/Users.php
similarity index 80%
rename from app/Controllers/V2/Users.php
rename to app/Controllers/Users.php
index 17d7eec..62aa69f 100644
--- a/app/Controllers/V2/Users.php
+++ b/app/Controllers/Users.php
@@ -1,5 +1,5 @@
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.']);
}
diff --git a/app/Controllers/V2.php b/app/Controllers/V2.php
deleted file mode 100644
index ef3fd02..0000000
--- a/app/Controllers/V2.php
+++ /dev/null
@@ -1,112 +0,0 @@
-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);
- }
-}
diff --git a/app/Controllers/V2/Admin.php b/app/Controllers/V2/Admin.php
deleted file mode 100644
index ab0138b..0000000
--- a/app/Controllers/V2/Admin.php
+++ /dev/null
@@ -1,24 +0,0 @@
-db = \Config\Database::connect();
- helper(['url', 'form', 'text']);
- }
-
- public function index() {
- return view('v2/admin/index');
- }
-
- public function users() {
- return view('v2/admin/users');
- }
-}
diff --git a/app/Views/_layouts/main.php b/app/Views/_layouts/main.php
deleted file mode 100644
index 812473f..0000000
--- a/app/Views/_layouts/main.php
+++ /dev/null
@@ -1,157 +0,0 @@
-getPath();
- $activeUrls = ['/admin', '/cs', '/analyst', '/doctor'];
-?>
-
-
-
-
- = $this->renderSection('title'); ?>
-
-
-
-
-
- = $this->renderSection('css'); ?>
-
-
-
- = $this->renderSection('content'); ?>
-
-
-
-
-
-
-
-
-
-
- = $this->renderSection('script'); ?>
-
-
-
\ No newline at end of file
diff --git a/app/Views/admin/dialog_preview.php b/app/Views/admin/dialog_preview.php
new file mode 100644
index 0000000..d6f3e1f
--- /dev/null
+++ b/app/Views/admin/dialog_preview.php
@@ -0,0 +1,45 @@
+
diff --git a/app/Views/v2/lab/dialog_sample.php b/app/Views/admin/dialog_sample.php
similarity index 84%
rename from app/Views/v2/lab/dialog_sample.php
rename to app/Views/admin/dialog_sample.php
index 2f82f5b..2201541 100644
--- a/app/Views/v2/lab/dialog_sample.php
+++ b/app/Views/admin/dialog_sample.php
@@ -2,6 +2,15 @@
+
+
+
+
+
+
| MR# | |
@@ -13,10 +22,10 @@
| Note |
-
-
-
- |
+
+
+
+ |
@@ -44,8 +53,8 @@
-
-
+
+
|
@@ -75,7 +84,7 @@
-
+
|
@@ -84,5 +93,7 @@
+
+
diff --git a/app/Views/admin/dialog_setPassword.php b/app/Views/admin/dialog_setPassword.php
new file mode 100644
index 0000000..623e8b2
--- /dev/null
+++ b/app/Views/admin/dialog_setPassword.php
@@ -0,0 +1,28 @@
+
diff --git a/app/Views/admin/dialog_unval.php b/app/Views/admin/dialog_unval.php
new file mode 100644
index 0000000..d869e30
--- /dev/null
+++ b/app/Views/admin/dialog_unval.php
@@ -0,0 +1,20 @@
+
diff --git a/app/Views/admin/index.php b/app/Views/admin/index.php
index 159b651..e8efed8 100644
--- a/app/Views/admin/index.php
+++ b/app/Views/admin/index.php
@@ -1,287 +1,401 @@
-= $this->extend('_layouts/main.php') ?>
-
-= $this->section('title') ?>
-Admin Glenlis
-= $this->endSection() ?>
+= $this->extend('admin/main'); ?>
= $this->section('content') ?>
-
-
-
Dashboard, =session('userrole');?>
-
- Hi, =session('userid');?>
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ Requests Overview
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-= $this->endSection() ?>
+
+
+ include('admin/dialog_sample'); ?>
+ include('admin/dialog_unval'); ?>
+ include('admin/dialog_preview'); ?>
+
+
+= $this->endSection(); ?>
= $this->section('script') ?>
-
-
-
-= $this->endSection() ?>
\ No newline at end of file
+ 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();
+
+= $this->endSection(); ?>
diff --git a/app/Views/v2/admin/main.php b/app/Views/admin/main.php
similarity index 82%
rename from app/Views/v2/admin/main.php
rename to app/Views/admin/main.php
index bf788c4..e3605f0 100644
--- a/app/Views/v2/admin/main.php
+++ b/app/Views/admin/main.php
@@ -20,7 +20,7 @@
}
.card-body {
font-size: 0.71rem !important;
- }
+ }
@@ -41,22 +41,22 @@
=$this->renderSection('content');?>
- =$this->include('v2/dialog_setPassword');?>
+ =$this->include('admin/dialog_setPassword');?>
=$this->renderSection('script');?>
-