go go alpine ranger

This commit is contained in:
mahdahar 2025-12-05 16:54:55 +07:00
parent 5f6139b85d
commit 656aef8f7c
7 changed files with 200 additions and 35 deletions

View File

@ -62,7 +62,13 @@ $routes->post('api/specimen/collect/(:any)', 'Specimen::collect/$1');
$routes->delete('api/specimen/receive/(:any)', 'Specimen::unreceive/$1'); $routes->delete('api/specimen/receive/(:any)', 'Specimen::unreceive/$1');
/*- lets go alpine -*/ /*- lets go alpine -*/
$routes->group('v2', function($routes) { $routes->group('v2', function($routes) {
$routes->get('', 'v2::index'); $routes->get('', 'V2::index');
$routes->get('login', 'V2::loginPage');
$routes->post('login', 'V2::login');
$routes->group('Admin/', ['filter' => 'role:1'], function($routes) {
$routes->get('', 'V2\Admin::index');
});
}); });

View File

@ -51,8 +51,8 @@ class Request extends BaseController {
$userid = $input['userid']; $userid = $input['userid'];
$comment = $input['comment']; $comment = $input['comment'];
$db = db_connect(); $db = db_connect();
$sql = "update GDC_CMOD.dbo.CM_REQUESTS set ISVAL1=0, VAL1USER=null, VAL1DATE=null, ISVAL2=0, VAL2USER=null, VAL2DATE=null, $sql = "update GDC_CMOD.dbo.CM_REQUESTS set ISVAL1=null, VAL1USER=null, VAL1DATE=null, ISVAL2=null, VAL2USER=null, VAL2DATE=null,
PENDINGTEXT='$comment', PENDINGUSER='$userid', PENDINGDATE=GETDATE() where ACCESSNUMBER='$accessnumber'"; ISPENDING=1, PENDINGTEXT='$comment', PENDINGUSER='$userid', PENDINGDATE=GETDATE() where ACCESSNUMBER='$accessnumber'";
$db->query($sql); $db->query($sql);
$data = ['status' => 'success', 'message' => 'Data updated successfully', 'data' => "$accessnumber" ]; $data = ['status' => 'success', 'message' => 'Data updated successfully', 'data' => "$accessnumber" ];

View File

@ -4,11 +4,75 @@ namespace App\Controllers;
use App\Controllers\BaseController; use App\Controllers\BaseController;
class V2 extends BaseController { class V2 extends BaseController {
/*
public function index() { public function index() {
return view("v2/index"); return view("v2/index");
} }
*/
public function index() {
$session = session();
public function modal_specimen() { 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/dokter');
case 3: return redirect()->to('v2/analis');
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 = '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('v2/admin');
case 2: return redirect()->to('v2/doctor');
case 3: return redirect()->to('v2/analyst');
case 4: return redirect()->to('v2/cs');
default: return redirect()->to('v2/login');
}
} else {
$session->setFlashdata('error', 'USERID atau PASSWORD salah.');
return redirect()->back();
}
} }
} }

View File

@ -0,0 +1,10 @@
<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>

View File

@ -6,10 +6,8 @@
<button id="validate-btn" x-ref="validateBtn" class="btn btn-sm btn-success" <button id="validate-btn" x-ref="validateBtn" class="btn btn-sm btn-success"
@click="validate(valAccessnumber, '<?=session('userid');?>')" :disabled="!isValidateEnabled">Validate</button> @click="validate(valAccessnumber, '<?=session('userid');?>')" :disabled="!isValidateEnabled">Validate</button>
</p> </p>
<!-- <iframe id="result-iframe" src="http://glenlis/spooler_db/main_dev.php?acc=" width="750px" height="600px"></iframe> -->
<!-- <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>
<iframe id="result-iframe" x-ref="resultIframe" src="<?=base_url('dummypage');?>" width="750px" height="600px"></iframe>
</div> </div>
</template> </template>
</dialog> </dialog>

View File

@ -30,7 +30,7 @@
<a class=''>CMOD</a> <a class=''>CMOD</a>
</div> </div>
<div class="mr-2"> <div class="mr-2">
<a>Hi, lisfse</a> <a>Hi, <?=session('userid'); ?></a>
</div> </div>
<div class="dropdown dropdown-end p-0"> <div class="dropdown dropdown-end p-0">
<div tabindex="0" role="button" class="btn btn-sm btn-secondary">Menu</div> <div tabindex="0" role="button" class="btn btn-sm btn-secondary">Menu</div>
@ -97,14 +97,22 @@
<td x-text="req.TESTS"></td> <td x-text="req.TESTS"></td>
<td x-text="req.ODR_CRESULT_TO"></td> <td x-text="req.ODR_CRESULT_TO"></td>
<td> <td>
<div>1: <span x-text="req.val1user"></span></div> <div class='flex gap-1 items-center'>
<div>2: <span x-text="req.val2user"></span></div> <div class='w-15'>
<template x-if="req.ISVAL == 1"> <p>1: <span x-text="req.VAL1USER"></span></p>
<div> <p>2: <span x-text="req.VAL2USER"></span></p>
<button class="btn btn-xs btn-outline btn-secondary" @click="unval(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-rotate-right"></i></button>
<button class="btn btn-xs btn-outline btn-success" @click="openValDialog(req.SP_ACCESSNUMBER)"><i class="fa-solid fa-check"></i></button>
</div> </div>
</template> <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>
<td><button x-text="req.STATS === 'Fin' ? 'Final' : req.STATS" class="btn btn-xs" <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> :class="statusColor[req.STATS]" @click="openSampleDialog(req.SP_ACCESSNUMBER)"></button></td>
@ -118,6 +126,7 @@
<?php echo $this->include('v2/dialog_sample'); ?> <?php echo $this->include('v2/dialog_sample'); ?>
<?php echo $this->include('v2/dialog_val'); ?> <?php echo $this->include('v2/dialog_val'); ?>
<?php echo $this->include('v2/dialog_unval'); ?>
</main> </main>
@ -200,28 +209,31 @@
this.fetchList(); this.fetchList();
}, },
isValidated (item) {
return item.ISVAL == 1 && item.ISPENDING != 1;
},
get filtered() { get filtered() {
let data = this.list; let filteredList = this.list;
if (this.filterKey === 'Validated') { if (this.filterKey === 'Validated') {
data = data.filter(i => i.ISVAL == 1); filteredList = filteredList.filter(item => this.isValidated(item));
} else { } else {
const valid = this.statusMap[this.filterKey] const validStatuses = this.statusMap[this.filterKey];
if (valid.length > 0) { if (validStatuses.length > 0) {
data = data.filter(i => valid.includes(i.STATS)); filteredList = filteredList.filter(item => validStatuses.includes(item.STATS));
} }
} }
if (this.filterTable) { if (this.filterTable) {
const s = this.filterTable.toLowerCase(); const searchTerm = this.filterTable.toLowerCase();
data = data.filter(i => filteredList = filteredList.filter(item =>
Object.values(i).some(v => Object.values(item).some(value =>
String(v).toLowerCase().includes(s) String(value).toLowerCase().includes(searchTerm)
) )
); );
} }
return data; return filteredList;
}, },
get validatedCount() { get validatedCount() {
return this.list.filter(r => r.ISVAL == 1).length; return this.list.filter(r => this.isValidated(r)).length;
}, },
/* /*
@ -291,21 +303,47 @@
closeValDialog () { closeValDialog () {
this.isDialogValOpen = false; this.isDialogValOpen = false;
}, },
unval(accessnumber) {
console.log("Unvalidate access number:", accessnumber);
},
validate(accessnumber, userid) { validate(accessnumber, userid) {
fetch(`${BASE_URL}/api/request/validate/${accessnumber}`, { fetch(`${BASEURL}/api/request/validate/${accessnumber}`, {
method: "POST", method: "POST",
headers: {"Content-Type": "application/json"}, headers: {"Content-Type": "application/json"},
body: JSON.stringify({ userid:`${userid}` }) body: JSON.stringify({ userid:`${userid}` })
}).then(response => { }).then(response => {
this.closeValDialog() this.closeValDialog();
console.log('Validate clicked for', this.valAccessnumber); 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}/api/request/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(); Alpine.start();
</script> </script>

49
app/Views/v2/login.php Normal file
View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en" data-theme="cupcake">
<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-gradient-to-br from-green-200 via-blue-100 to-yellow-100">
<div class="w-full max-w-sm mx-auto">
<div class="card bg-base-100 shadow-xl border-2 border-primary">
<div class="card-body items-center text-center">
<div class="avatar mb-2">
<div class="w-20 rounded-full ring ring-primary ring-offset-base-100 ring-offset-2">
<img src="https://api.dicebear.com/7.x/bottts/svg?seed=fun" alt="Fun Avatar" />
</div>
</div>
<h2 class="card-title text-2xl font-bold text-primary mb-2">Welcome to CMOD!</h2>
<p class="mb-4 text-base text-gray-500">Sign in to continue your adventure 🚀</p>
<form method="post" action="<?=base_url('v2/login')?>" class="w-full flex flex-col gap-3">
<div class="form-control">
<label class="input input-bordered flex items-center gap-2 w-full">
<i class="fa fa-user text-primary"></i>
<input type="text" name="userid" placeholder="UserID" 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-primary"></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">
<i class="fa fa-sign-in-alt mr-2"></i> Login
</button>
</form>
<div class="mt-4">
<span class="text-xs text-gray-400">Forgot password?</span>
<a href="#" class="link link-primary ml-1">Contact admin</a>
</div>
</div>
</div>
<div class="text-center mt-4 text-sm text-gray-400">&copy; 2025 - 5Panda</div>
</div>
</body>
</html>