Investigation Master: CRUD, favourites, specialization filter
This commit is contained in:
parent
e27a69df5d
commit
771fa64778
|
|
@ -594,6 +594,13 @@ $routes->get('Opd_prescription/investigation_list/(:num)', 'Opd_prescription::in
|
||||||
$routes->get('Opd_prescription/investigation_list/(:num)/(:num)', 'Opd_prescription::investigation_list/$1/$2');
|
$routes->get('Opd_prescription/investigation_list/(:num)/(:num)', 'Opd_prescription::investigation_list/$1/$2');
|
||||||
$routes->post('Opd_prescription/investigation_add', 'Opd_prescription::investigation_add');
|
$routes->post('Opd_prescription/investigation_add', 'Opd_prescription::investigation_add');
|
||||||
$routes->post('Opd_prescription/investigation_remove/(:num)', 'Opd_prescription::investigation_remove/$1');
|
$routes->post('Opd_prescription/investigation_remove/(:num)', 'Opd_prescription::investigation_remove/$1');
|
||||||
|
$routes->get('Opd_prescription/opd_invest_master', 'Opd_prescription::opd_invest_master', ['filter' => 'permission:doctor_work.medicine.manage,doctor_work.access']);
|
||||||
|
$routes->get('Opd_prescription/opd_invest_master_specs', 'Opd_prescription::opd_invest_master_specs');
|
||||||
|
$routes->get('Opd_prescription/opd_invest_master_data', 'Opd_prescription::opd_invest_master_data');
|
||||||
|
$routes->get('Opd_prescription/opd_invest_master_get/(:num)', 'Opd_prescription::opd_invest_master_get/$1');
|
||||||
|
$routes->post('Opd_prescription/opd_invest_master_save', 'Opd_prescription::opd_invest_master_save');
|
||||||
|
$routes->post('Opd_prescription/opd_invest_master_remove/(:num)', 'Opd_prescription::opd_invest_master_remove/$1');
|
||||||
|
$routes->post('Opd_prescription/opd_invest_master_toggle_fav/(:num)', 'Opd_prescription::opd_invest_master_toggle_fav/$1');
|
||||||
|
|
||||||
$routes->get('Opd_prescription/medicine_search', 'Opd_prescription::medicine_search');
|
$routes->get('Opd_prescription/medicine_search', 'Opd_prescription::medicine_search');
|
||||||
$routes->get('Opd_prescription/medicine_substitutes', 'Opd_prescription::medicine_substitutes');
|
$routes->get('Opd_prescription/medicine_substitutes', 'Opd_prescription::medicine_substitutes');
|
||||||
|
|
|
||||||
|
|
@ -3043,6 +3043,235 @@ class Opd_prescription extends BaseController
|
||||||
return $this->response->setJSON(['update' => 1, 'error_text' => 'Advice removed']);
|
return $this->response->setJSON(['update' => 1, 'error_text' => 'Advice removed']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Investigation Master CRUD ─────────────────────────────────────────
|
||||||
|
|
||||||
|
public function opd_invest_master()
|
||||||
|
{
|
||||||
|
$doctorModel = new \App\Models\DoctorModel($this->db);
|
||||||
|
return view('billing/opd_invest_master_workspace', [
|
||||||
|
'initial_inv_id' => 0,
|
||||||
|
'med_specs' => $doctorModel->getMedSpecs(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_master_specs()
|
||||||
|
{
|
||||||
|
$specs = [];
|
||||||
|
if ($this->db->tableExists('med_spec')) {
|
||||||
|
$specs = $this->db->table('med_spec')
|
||||||
|
->select('id, SpecName')
|
||||||
|
->orderBy('SpecName', 'ASC')
|
||||||
|
->get()->getResultArray();
|
||||||
|
}
|
||||||
|
return $this->response->setJSON(['specs' => $specs]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_master_data()
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('investigation')) {
|
||||||
|
return $this->response->setJSON(['draw' => 1, 'recordsTotal' => 0, 'recordsFiltered' => 0, 'data' => []]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
$codeField = $this->resolveFirstField($fields, ['Code', 'code']);
|
||||||
|
$nameField = $this->resolveFirstField($fields, ['Name', 'name']);
|
||||||
|
if ($codeField === null || $nameField === null) {
|
||||||
|
return $this->response->setJSON(['draw' => 1, 'recordsTotal' => 0, 'recordsFiltered' => 0, 'data' => []]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter = trim((string) $this->request->getGet('filter'));
|
||||||
|
$specId = (int) $this->request->getGet('spec_id');
|
||||||
|
$favOnly = (int) $this->request->getGet('fav_only');
|
||||||
|
$draw = (int) $this->request->getGet('draw');
|
||||||
|
$start = max(0, (int) $this->request->getGet('start'));
|
||||||
|
$length = (int) $this->request->getGet('length');
|
||||||
|
if ($length <= 0) {
|
||||||
|
$length = 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
$selectCols = 'id, ' . $codeField . ' as code, ' . $nameField . ' as name';
|
||||||
|
foreach (['short_name', 'is_favourite', 'spec_ids', 'category_name'] as $f) {
|
||||||
|
if (in_array($f, $fields, true)) {
|
||||||
|
$selectCols .= ', ' . $f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$applyFilters = function ($b) use ($filter, $favOnly, $specId, $fields, $nameField, $codeField) {
|
||||||
|
if ($filter !== '') {
|
||||||
|
$b->groupStart()
|
||||||
|
->like($nameField, $filter)
|
||||||
|
->orLike($codeField, $filter);
|
||||||
|
if (in_array('short_name', $fields, true)) {
|
||||||
|
$b->orLike('short_name', $filter);
|
||||||
|
}
|
||||||
|
$b->groupEnd();
|
||||||
|
}
|
||||||
|
if ($favOnly && in_array('is_favourite', $fields, true)) {
|
||||||
|
$b->where('is_favourite', 1);
|
||||||
|
}
|
||||||
|
if ($specId > 0 && in_array('spec_ids', $fields, true)) {
|
||||||
|
$b->where("FIND_IN_SET({$specId}, spec_ids) > 0", null, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$total = $this->db->table('investigation')->countAllResults();
|
||||||
|
|
||||||
|
$countBuilder = $this->db->table('investigation');
|
||||||
|
$applyFilters($countBuilder);
|
||||||
|
$filtered = $countBuilder->countAllResults();
|
||||||
|
|
||||||
|
$builder = $this->db->table('investigation')->select($selectCols);
|
||||||
|
$applyFilters($builder);
|
||||||
|
if (in_array('is_favourite', $fields, true)) {
|
||||||
|
$builder->orderBy('is_favourite', 'DESC');
|
||||||
|
}
|
||||||
|
$builder->orderBy($nameField, 'ASC');
|
||||||
|
$builder->limit($length, $start);
|
||||||
|
$rows = $builder->get()->getResultArray();
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'draw' => $draw,
|
||||||
|
'recordsTotal' => $total,
|
||||||
|
'recordsFiltered' => $filtered,
|
||||||
|
'data' => $rows,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_master_get(int $invId)
|
||||||
|
{
|
||||||
|
if ($invId <= 0 || ! $this->db->tableExists('investigation')) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Not found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
$codeField = $this->resolveFirstField($fields, ['Code', 'code']);
|
||||||
|
$nameField = $this->resolveFirstField($fields, ['Name', 'name']);
|
||||||
|
|
||||||
|
$row = $this->db->table('investigation')->where('id', $invId)->limit(1)->get()->getRowArray();
|
||||||
|
if (! $row) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Not found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'update' => 1,
|
||||||
|
'row' => [
|
||||||
|
'id' => (int) ($row['id'] ?? 0),
|
||||||
|
'code' => (string) ($row[$codeField ?? 'code'] ?? ''),
|
||||||
|
'name' => (string) ($row[$nameField ?? 'name'] ?? ''),
|
||||||
|
'short_name' => (string) ($row['short_name'] ?? ''),
|
||||||
|
'category_name' => (string) ($row['category_name'] ?? ''),
|
||||||
|
'is_favourite' => (int) ($row['is_favourite'] ?? 0),
|
||||||
|
'spec_ids' => (string) ($row['spec_ids'] ?? ''),
|
||||||
|
],
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_master_save()
|
||||||
|
{
|
||||||
|
if (! $this->request->isAJAX()) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON(['update' => 0]);
|
||||||
|
}
|
||||||
|
if (! $this->db->tableExists('investigation')) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Investigation table not found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (int) $this->request->getPost('id');
|
||||||
|
$name = trim((string) $this->request->getPost('name'));
|
||||||
|
if ($name === '') {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Investigation name is required']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
$codeField = $this->resolveFirstField($fields, ['Code', 'code']);
|
||||||
|
$nameField = $this->resolveFirstField($fields, ['Name', 'name']);
|
||||||
|
if ($codeField === null || $nameField === null) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid table structure']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = trim((string) $this->request->getPost('code'));
|
||||||
|
if ($code === '') {
|
||||||
|
$code = strtoupper(preg_replace('/[^A-Z0-9]/i', '', substr($name, 0, 8))) . rand(100, 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
$nameField => $name,
|
||||||
|
$codeField => $code,
|
||||||
|
];
|
||||||
|
foreach (['short_name', 'category_name', 'spec_ids'] as $f) {
|
||||||
|
if (in_array($f, $fields, true)) {
|
||||||
|
$data[$f] = trim((string) $this->request->getPost($f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (in_array('is_favourite', $fields, true)) {
|
||||||
|
$data['is_favourite'] = ((int) $this->request->getPost('is_favourite')) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($id > 0) {
|
||||||
|
$this->db->table('investigation')->where('id', $id)->update($data);
|
||||||
|
$savedId = $id;
|
||||||
|
$message = 'Investigation updated';
|
||||||
|
} else {
|
||||||
|
$this->db->table('investigation')->insert($data);
|
||||||
|
$savedId = (int) $this->db->insertID();
|
||||||
|
$message = 'Investigation added';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'update' => 1,
|
||||||
|
'insertid' => $savedId,
|
||||||
|
'error_text' => $message,
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_master_remove(int $invId)
|
||||||
|
{
|
||||||
|
if (! $this->request->isAJAX()) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON(['update' => 0]);
|
||||||
|
}
|
||||||
|
if ($invId <= 0 || ! $this->db->tableExists('investigation')) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Not found']);
|
||||||
|
}
|
||||||
|
$this->db->table('investigation')->where('id', $invId)->delete();
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'update' => 1,
|
||||||
|
'error_text' => 'Investigation deleted',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_master_toggle_fav(int $invId)
|
||||||
|
{
|
||||||
|
if (! $this->request->isAJAX()) {
|
||||||
|
return $this->response->setStatusCode(400)->setJSON(['update' => 0]);
|
||||||
|
}
|
||||||
|
if ($invId <= 0 || ! $this->db->tableExists('investigation')) {
|
||||||
|
return $this->response->setJSON(['update' => 0]);
|
||||||
|
}
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
if (! in_array('is_favourite', $fields, true)) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Run database migration first']);
|
||||||
|
}
|
||||||
|
$row = $this->db->table('investigation')->where('id', $invId)->limit(1)->get()->getRowArray();
|
||||||
|
if (! $row) {
|
||||||
|
return $this->response->setJSON(['update' => 0]);
|
||||||
|
}
|
||||||
|
$newVal = ((int) ($row['is_favourite'] ?? 0)) === 1 ? 0 : 1;
|
||||||
|
$this->db->table('investigation')->where('id', $invId)->update(['is_favourite' => $newVal]);
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'update' => 1,
|
||||||
|
'is_favourite' => $newVal,
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
public function opd_medicince_duplicate_report()
|
public function opd_medicince_duplicate_report()
|
||||||
{
|
{
|
||||||
$medicineModel = new OpdMedicineModel($this->db);
|
$medicineModel = new OpdMedicineModel($this->db);
|
||||||
|
|
@ -3534,29 +3763,59 @@ class Opd_prescription extends BaseController
|
||||||
|
|
||||||
public function investigation_search()
|
public function investigation_search()
|
||||||
{
|
{
|
||||||
$q = trim((string) $this->request->getGet('q'));
|
$q = trim((string) $this->request->getGet('q'));
|
||||||
if ($q === '' || !$this->db->tableExists('investigation')) {
|
$favOnly = (int) $this->request->getGet('fav_only');
|
||||||
|
$specId = (int) $this->request->getGet('spec_id');
|
||||||
|
|
||||||
|
if (! $this->db->tableExists('investigation')) {
|
||||||
return $this->response->setJSON(['rows' => []]);
|
return $this->response->setJSON(['rows' => []]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fields = $this->db->getFieldNames('investigation');
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
$codeField = in_array('Code', $fields, true) ? 'Code' : (in_array('code', $fields, true) ? 'code' : null);
|
$codeField = in_array('Code', $fields, true) ? 'Code' : (in_array('code', $fields, true) ? 'code' : null);
|
||||||
$nameField = in_array('Name', $fields, true) ? 'Name' : (in_array('name', $fields, true) ? 'name' : null);
|
$nameField = in_array('Name', $fields, true) ? 'Name' : (in_array('name', $fields, true) ? 'name' : null);
|
||||||
$shortField = in_array('short_name', $fields, true) ? 'short_name' : null;
|
$shortField = in_array('short_name', $fields, true) ? 'short_name' : null;
|
||||||
|
$hasFav = in_array('is_favourite', $fields, true);
|
||||||
|
$hasSpec = in_array('spec_ids', $fields, true);
|
||||||
|
|
||||||
if ($codeField === null || $nameField === null) {
|
if ($codeField === null || $nameField === null) {
|
||||||
return $this->response->setJSON(['rows' => []]);
|
return $this->response->setJSON(['rows' => []]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$builder = $this->db->table('investigation')->select($codeField . ' as code,' . $nameField . ' as name');
|
// When no search term but fav/spec filter applied, still return results
|
||||||
$builder->groupStart()->like($nameField, $q);
|
if ($q === '' && $favOnly === 0 && $specId === 0) {
|
||||||
if ($shortField !== null) {
|
return $this->response->setJSON(['rows' => []]);
|
||||||
$builder->orLike($shortField, $q);
|
}
|
||||||
|
|
||||||
|
$selectCols = $codeField . ' as code, ' . $nameField . ' as name';
|
||||||
|
if ($hasFav) {
|
||||||
|
$selectCols .= ', is_favourite';
|
||||||
|
}
|
||||||
|
|
||||||
|
$builder = $this->db->table('investigation')->select($selectCols);
|
||||||
|
|
||||||
|
if ($q !== '') {
|
||||||
|
$builder->groupStart()->like($nameField, $q);
|
||||||
|
if ($shortField !== null) {
|
||||||
|
$builder->orLike($shortField, $q);
|
||||||
|
}
|
||||||
|
$builder->groupEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($favOnly && $hasFav) {
|
||||||
|
$builder->where('is_favourite', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($specId > 0 && $hasSpec) {
|
||||||
|
$builder->where("FIND_IN_SET({$specId}, spec_ids) > 0", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($hasFav) {
|
||||||
|
$builder->orderBy('is_favourite', 'DESC');
|
||||||
}
|
}
|
||||||
$builder->groupEnd();
|
|
||||||
$builder->orderBy('LOWER(' . $this->db->protectIdentifiers($nameField) . ')', 'ASC', false);
|
$builder->orderBy('LOWER(' . $this->db->protectIdentifiers($nameField) . ')', 'ASC', false);
|
||||||
|
|
||||||
$rows = $builder->limit(20)->get()->getResultArray();
|
$rows = $builder->limit(40)->get()->getResultArray();
|
||||||
return $this->response->setJSON(['rows' => $rows]);
|
return $this->response->setJSON(['rows' => $rows]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Database\Migrations;
|
||||||
|
|
||||||
|
use CodeIgniter\Database\Migration;
|
||||||
|
|
||||||
|
class AddInvestigationMasterFields extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('investigation')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
$add = [];
|
||||||
|
|
||||||
|
if (! in_array('is_favourite', $fields, true)) {
|
||||||
|
$add['is_favourite'] = ['type' => 'TINYINT', 'constraint' => 1, 'null' => false, 'default' => 0];
|
||||||
|
}
|
||||||
|
if (! in_array('spec_ids', $fields, true)) {
|
||||||
|
$add['spec_ids'] = ['type' => 'VARCHAR', 'constraint' => 500, 'null' => true, 'default' => null];
|
||||||
|
}
|
||||||
|
if (! in_array('category_name', $fields, true)) {
|
||||||
|
$add['category_name'] = ['type' => 'VARCHAR', 'constraint' => 100, 'null' => true, 'default' => null];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($add)) {
|
||||||
|
$this->forge->addColumn('investigation', $add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('investigation')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
foreach (['is_favourite', 'spec_ids', 'category_name'] as $col) {
|
||||||
|
if (in_array($col, $fields, true)) {
|
||||||
|
$this->forge->dropColumn('investigation', $col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
401
app/Views/billing/opd_invest_master_workspace.php
Normal file
401
app/Views/billing/opd_invest_master_workspace.php
Normal file
|
|
@ -0,0 +1,401 @@
|
||||||
|
<?php
|
||||||
|
/** @var array $med_specs */
|
||||||
|
$med_specs = $med_specs ?? [];
|
||||||
|
?>
|
||||||
|
<section class="container-fluid py-3">
|
||||||
|
<div class="d-flex align-items-center gap-2 mb-3">
|
||||||
|
<h5 class="mb-0"><i class="bi bi-clipboard2-pulse me-1"></i> Investigation Master</h5>
|
||||||
|
<span class="badge bg-secondary" id="inv_total_badge">—</span>
|
||||||
|
<div class="ms-auto d-flex gap-2">
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_inv_download_csv">
|
||||||
|
<i class="bi bi-download"></i> Export CSV
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<!-- ── LIST ──────────────────────────────────────────────────── -->
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex flex-wrap gap-2 align-items-center">
|
||||||
|
<input type="text" id="inv_filter_text" class="form-control form-control-sm" style="max-width:220px;" placeholder="Search name / code…">
|
||||||
|
<select id="inv_filter_spec" class="form-select form-select-sm" style="max-width:200px;">
|
||||||
|
<option value="0">All Specializations</option>
|
||||||
|
<?php foreach ($med_specs as $sp): ?>
|
||||||
|
<option value="<?= (int) ($sp->id ?? 0) ?>"><?= esc($sp->SpecName ?? '') ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" id="btn_inv_filter_fav" title="Show favourites only">
|
||||||
|
<i class="bi bi-star-fill"></i> Favourites
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm d-none" id="btn_inv_filter_all">
|
||||||
|
Show All
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table id="tbl_invest_master" class="table table-bordered table-sm table-hover mb-0" style="width:100%;">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th width="32"></th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th width="90">Code</th>
|
||||||
|
<th width="80">Short</th>
|
||||||
|
<th width="110">Category</th>
|
||||||
|
<th width="140">Specializations</th>
|
||||||
|
<th width="100">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td colspan="7" class="text-muted text-center py-3">Loading…</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── FORM ──────────────────────────────────────────────────── -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header"><strong id="inv_form_title">New Investigation</strong></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<input type="hidden" id="inv_id" value="0">
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">Name <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" id="inv_name" class="form-control form-control-sm" placeholder="e.g. CBC, LFT, ECG">
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">Code <small class="text-muted">(auto if blank)</small></label>
|
||||||
|
<input type="text" id="inv_code" class="form-control form-control-sm" placeholder="e.g. CBC01">
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">Short Name / Abbreviation</label>
|
||||||
|
<input type="text" id="inv_short_name" class="form-control form-control-sm" placeholder="e.g. CBC">
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">Category</label>
|
||||||
|
<input type="text" id="inv_category_name" class="form-control form-control-sm" placeholder="e.g. Blood Test, Radiology">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label">Specialization(s)</label>
|
||||||
|
<div id="inv_spec_checkboxes" class="border rounded p-2" style="max-height:140px;overflow-y:auto;">
|
||||||
|
<?php foreach ($med_specs as $sp): ?>
|
||||||
|
<div class="form-check form-check-sm">
|
||||||
|
<input class="form-check-input inv-spec-cb" type="checkbox"
|
||||||
|
id="inv_spec_<?= (int) ($sp->id ?? 0) ?>"
|
||||||
|
value="<?= (int) ($sp->id ?? 0) ?>">
|
||||||
|
<label class="form-check-label small" for="inv_spec_<?= (int) ($sp->id ?? 0) ?>">
|
||||||
|
<?= esc($sp->SpecName ?? '') ?>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($med_specs)): ?>
|
||||||
|
<span class="small text-muted">No specializations found. Add them in Doctor > Specialization.</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="inv_is_favourite" value="1">
|
||||||
|
<label class="form-check-label" for="inv_is_favourite">
|
||||||
|
<i class="bi bi-star-fill text-warning"></i> Mark as Favourite
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="btn_inv_save">Save</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_inv_reset">Reset</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small mt-2" id="inv_msg">Ready.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var invDataTable = null;
|
||||||
|
var favOnlyMode = false;
|
||||||
|
var filterTimer = null;
|
||||||
|
|
||||||
|
/* ── Toast ─────────────────────────────────────────────────────── */
|
||||||
|
function showToast(message, type) {
|
||||||
|
var cls = type === 'ok' ? 'alert-success' : (type === 'err' ? 'alert-danger' : 'alert-info');
|
||||||
|
var id = 'inv_toast_' + Date.now();
|
||||||
|
var html = '<div id="' + id + '" class="alert ' + cls + ' shadow-sm d-flex align-items-start justify-content-between"'
|
||||||
|
+ ' role="alert" style="position:fixed;top:20px;right:20px;z-index:9999;min-width:260px;max-width:420px;gap:12px;">'
|
||||||
|
+ '<div>' + $('<div>').text(message || '').html() + '</div>'
|
||||||
|
+ '<button type="button" class="btn-close btn-sm" data-tid="' + id + '"></button></div>';
|
||||||
|
$('body').append(html);
|
||||||
|
setTimeout(function() { $('#' + id).fadeOut(200, function() { $(this).remove(); }); }, 4000);
|
||||||
|
}
|
||||||
|
$(document).on('click', '.btn-close[data-tid]', function() {
|
||||||
|
$('#' + $(this).data('tid')).fadeOut(150, function() { $(this).remove(); });
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── CSRF ───────────────────────────────────────────────────────── */
|
||||||
|
function getCsrfPair() {
|
||||||
|
var input = document.querySelector('input[name="<?= csrf_token() ?>"]');
|
||||||
|
return input
|
||||||
|
? { name: input.getAttribute('name'), value: input.value }
|
||||||
|
: { name: '<?= csrf_token() ?>', value: '<?= csrf_hash() ?>' };
|
||||||
|
}
|
||||||
|
function updateCsrf(data) {
|
||||||
|
if (!data || !data.csrfName) return;
|
||||||
|
var input = document.querySelector('input[name="' + data.csrfName + '"]');
|
||||||
|
if (input) input.value = data.csrfHash || '';
|
||||||
|
}
|
||||||
|
function apiPost(url, payload, cb) {
|
||||||
|
var csrf = getCsrfPair();
|
||||||
|
payload = payload || {};
|
||||||
|
payload[csrf.name] = csrf.value;
|
||||||
|
$.post(url, payload, function(data) { updateCsrf(data); cb(data || {}); }, 'json');
|
||||||
|
}
|
||||||
|
function apiGet(url, cb) {
|
||||||
|
$.get(url, function(data) { cb(data || {}); }, 'json');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Form helpers ───────────────────────────────────────────────── */
|
||||||
|
function setMsg(type, text) {
|
||||||
|
var $m = $('#inv_msg');
|
||||||
|
$m.removeClass('text-success text-danger text-muted');
|
||||||
|
$m.addClass(type === 'ok' ? 'text-success' : type === 'err' ? 'text-danger' : 'text-muted');
|
||||||
|
$m.text(text || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCheckedSpecIds() {
|
||||||
|
var ids = [];
|
||||||
|
$('.inv-spec-cb:checked').each(function() { ids.push($(this).val()); });
|
||||||
|
return ids.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearForm() {
|
||||||
|
$('#inv_id').val('0');
|
||||||
|
$('#inv_name,#inv_code,#inv_short_name,#inv_category_name').val('');
|
||||||
|
$('.inv-spec-cb').prop('checked', false);
|
||||||
|
$('#inv_is_favourite').prop('checked', false);
|
||||||
|
$('#inv_form_title').text('New Investigation');
|
||||||
|
setMsg('normal', 'Ready.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillForm(row) {
|
||||||
|
row = row || {};
|
||||||
|
$('#inv_id').val(row.id || 0);
|
||||||
|
$('#inv_name').val(row.name || '');
|
||||||
|
$('#inv_code').val(row.code || '');
|
||||||
|
$('#inv_short_name').val(row.short_name || '');
|
||||||
|
$('#inv_category_name').val(row.category_name || '');
|
||||||
|
$('#inv_is_favourite').prop('checked', parseInt(row.is_favourite || 0) === 1);
|
||||||
|
$('.inv-spec-cb').prop('checked', false);
|
||||||
|
var specIds = (row.spec_ids || '').split(',').map(function(s) { return s.trim(); }).filter(Boolean);
|
||||||
|
specIds.forEach(function(sid) {
|
||||||
|
$('#inv_spec_' + sid).prop('checked', true);
|
||||||
|
});
|
||||||
|
$('#inv_form_title').text('Edit: ' + (row.name || ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPayload() {
|
||||||
|
return {
|
||||||
|
id: parseInt($('#inv_id').val() || '0', 10),
|
||||||
|
name: ($('#inv_name').val() || '').trim(),
|
||||||
|
code: ($('#inv_code').val() || '').trim(),
|
||||||
|
short_name: ($('#inv_short_name').val() || '').trim(),
|
||||||
|
category_name: ($('#inv_category_name').val() || '').trim(),
|
||||||
|
spec_ids: getCheckedSpecIds(),
|
||||||
|
is_favourite: $('#inv_is_favourite').is(':checked') ? 1 : 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── DataTable ──────────────────────────────────────────────────── */
|
||||||
|
function getAjaxParams() {
|
||||||
|
return {
|
||||||
|
filter: ($('#inv_filter_text').val() || '').trim(),
|
||||||
|
spec_id: parseInt($('#inv_filter_spec').val() || '0', 10),
|
||||||
|
fav_only: favOnlyMode ? 1 : 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTable() {
|
||||||
|
if (invDataTable) return;
|
||||||
|
if (!window.jQuery || typeof $.fn.DataTable !== 'function') return;
|
||||||
|
|
||||||
|
invDataTable = $('#tbl_invest_master').DataTable({
|
||||||
|
serverSide: true,
|
||||||
|
processing: true,
|
||||||
|
searching: false,
|
||||||
|
paging: true,
|
||||||
|
pageLength: 25,
|
||||||
|
lengthChange: false,
|
||||||
|
ajax: {
|
||||||
|
url: '<?= base_url('Opd_prescription/opd_invest_master_data') ?>',
|
||||||
|
type: 'GET',
|
||||||
|
data: function(d) {
|
||||||
|
var p = getAjaxParams();
|
||||||
|
d.filter = p.filter;
|
||||||
|
d.spec_id = p.spec_id;
|
||||||
|
d.fav_only = p.fav_only;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
data: 'is_favourite', orderable: false, className: 'text-center',
|
||||||
|
render: function(val, type, row) {
|
||||||
|
var isFav = parseInt(val || 0) === 1;
|
||||||
|
return '<button class="btn btn-link p-0 btn-toggle-fav" data-id="' + (row.id || 0) + '" '
|
||||||
|
+ 'title="' + (isFav ? 'Remove from favourites' : 'Mark as favourite') + '">'
|
||||||
|
+ '<i class="bi bi-star' + (isFav ? '-fill text-warning' : ' text-muted') + '"></i></button>';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ data: 'name' },
|
||||||
|
{ data: 'code', orderable: false },
|
||||||
|
{ data: 'short_name', defaultContent: '', orderable: false },
|
||||||
|
{ data: 'category_name', defaultContent: '', orderable: false },
|
||||||
|
{
|
||||||
|
data: 'spec_ids', orderable: false,
|
||||||
|
render: function(val) {
|
||||||
|
if (!val) return '<span class="text-muted small">—</span>';
|
||||||
|
return '<span class="small">' + $('<div>').text(val).html() + '</span>';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: 'id', orderable: false,
|
||||||
|
render: function(id, type, row) {
|
||||||
|
return '<button class="btn btn-outline-primary btn-sm btn-inv-edit" data-id="' + id + '">Edit</button> '
|
||||||
|
+ '<button class="btn btn-outline-danger btn-sm btn-inv-del" data-id="' + id + '">Del</button>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
drawCallback: function(settings) {
|
||||||
|
var info = settings.json || {};
|
||||||
|
$('#inv_total_badge').text((info.recordsTotal || 0) + ' total');
|
||||||
|
},
|
||||||
|
language: { processing: 'Loading…', zeroRecords: 'No investigations found.' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
if (!invDataTable) { initTable(); return; }
|
||||||
|
invDataTable.ajax.reload(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Save ───────────────────────────────────────────────────────── */
|
||||||
|
function saveInvestigation(payload) {
|
||||||
|
if (!payload.name) { setMsg('err', 'Name is required.'); return; }
|
||||||
|
apiPost('<?= base_url('Opd_prescription/opd_invest_master_save') ?>', payload, function(data) {
|
||||||
|
if (parseInt(data.update || 0) !== 1) {
|
||||||
|
setMsg('err', data.error_text || 'Unable to save');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#inv_id').val(data.insertid || 0);
|
||||||
|
setMsg('ok', data.error_text || 'Saved');
|
||||||
|
showToast(data.error_text || 'Saved', 'ok');
|
||||||
|
reloadTable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Event bindings ─────────────────────────────────────────────── */
|
||||||
|
$('#btn_inv_save').on('click', function() {
|
||||||
|
saveInvestigation(buildPayload());
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_inv_reset').on('click', function() {
|
||||||
|
clearForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter text / spec — debounced
|
||||||
|
$('#inv_filter_text, #inv_filter_spec').on('input change', function() {
|
||||||
|
clearTimeout(filterTimer);
|
||||||
|
filterTimer = setTimeout(reloadTable, 350);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fav toggle filter
|
||||||
|
$('#btn_inv_filter_fav').on('click', function() {
|
||||||
|
favOnlyMode = true;
|
||||||
|
$(this).addClass('d-none');
|
||||||
|
$('#btn_inv_filter_all').removeClass('d-none');
|
||||||
|
reloadTable();
|
||||||
|
});
|
||||||
|
$('#btn_inv_filter_all').on('click', function() {
|
||||||
|
favOnlyMode = false;
|
||||||
|
$(this).addClass('d-none');
|
||||||
|
$('#btn_inv_filter_fav').removeClass('d-none');
|
||||||
|
reloadTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Row edit
|
||||||
|
$(document).on('click', '.btn-inv-edit', function() {
|
||||||
|
var id = parseInt($(this).data('id') || '0', 10);
|
||||||
|
if (id <= 0) return;
|
||||||
|
apiGet('<?= base_url('Opd_prescription/opd_invest_master_get') ?>/' + id, function(data) {
|
||||||
|
if (parseInt(data.update || 0) !== 1) { setMsg('err', data.error_text || 'Cannot load'); return; }
|
||||||
|
fillForm(data.row || {});
|
||||||
|
setMsg('normal', 'Loaded for edit.');
|
||||||
|
$('html, body').animate({ scrollTop: $('#inv_name').offset().top - 80 }, 200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Row delete
|
||||||
|
$(document).on('click', '.btn-inv-del', function() {
|
||||||
|
var id = parseInt($(this).data('id') || '0', 10);
|
||||||
|
if (id <= 0) return;
|
||||||
|
if (!confirm('Delete this investigation from master?')) return;
|
||||||
|
apiPost('<?= base_url('Opd_prescription/opd_invest_master_remove') ?>/' + id, {}, function(data) {
|
||||||
|
if (parseInt(data.update || 0) !== 1) { showToast(data.error_text || 'Error', 'err'); return; }
|
||||||
|
showToast('Deleted', 'ok');
|
||||||
|
reloadTable();
|
||||||
|
if (parseInt($('#inv_id').val() || '0', 10) === id) clearForm();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Row fav toggle
|
||||||
|
$(document).on('click', '.btn-toggle-fav', function() {
|
||||||
|
var id = parseInt($(this).data('id') || '0', 10);
|
||||||
|
if (id <= 0) return;
|
||||||
|
apiPost('<?= base_url('Opd_prescription/opd_invest_master_toggle_fav') ?>/' + id, {}, function(data) {
|
||||||
|
if (parseInt(data.update || 0) !== 1) { showToast(data.error_text || 'Error', 'err'); return; }
|
||||||
|
reloadTable();
|
||||||
|
// sync form star if editing this row
|
||||||
|
if (parseInt($('#inv_id').val() || '0', 10) === id) {
|
||||||
|
$('#inv_is_favourite').prop('checked', data.is_favourite === 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// CSV export — simple GET with current filter params
|
||||||
|
$('#btn_inv_download_csv').on('click', function() {
|
||||||
|
var p = getAjaxParams();
|
||||||
|
var url = '<?= base_url('Opd_prescription/opd_invest_master_data') ?>?length=9999&start=0'
|
||||||
|
+ '&filter=' + encodeURIComponent(p.filter)
|
||||||
|
+ '&spec_id=' + p.spec_id
|
||||||
|
+ '&fav_only=' + p.fav_only;
|
||||||
|
$.get(url, function(data) {
|
||||||
|
var rows = (data && data.data) ? data.data : [];
|
||||||
|
if (!rows.length) { showToast('No data to export', 'err'); return; }
|
||||||
|
var csv = 'Name,Code,Short Name,Category,Specialization IDs,Favourite\n';
|
||||||
|
rows.forEach(function(r) {
|
||||||
|
csv += [r.name, r.code, r.short_name || '', r.category_name || '', r.spec_ids || '', r.is_favourite || 0]
|
||||||
|
.map(function(v) { return '"' + String(v).replace(/"/g, '""') + '"'; })
|
||||||
|
.join(',') + '\n';
|
||||||
|
});
|
||||||
|
var blob = new Blob([csv], { type: 'text/csv' });
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.download = 'investigation_master.csv';
|
||||||
|
a.click();
|
||||||
|
}, 'json');
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Init ───────────────────────────────────────────────────────── */
|
||||||
|
$(function() {
|
||||||
|
initTable();
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
@ -724,8 +724,17 @@
|
||||||
<select class="form-select" id="investigation_name_select2" style="width:100%;">
|
<select class="form-select" id="investigation_name_select2" style="width:100%;">
|
||||||
<option value="">Search investigation...</option>
|
<option value="">Search investigation...</option>
|
||||||
</select>
|
</select>
|
||||||
<input type="text" class="form-control d-none mt-2" id="investigation_name" list="investigation_suggest" placeholder="Type investigation name">
|
<div class="d-flex flex-wrap gap-1 mt-1 align-items-center" id="inv_quick_filter_bar">
|
||||||
<datalist id="investigation_suggest"></datalist>
|
<button type="button" class="btn btn-outline-warning btn-sm px-2 py-0" id="btn_inv_fav_toggle" title="Search favourites only">
|
||||||
|
<i class="bi bi-star"></i> Fav
|
||||||
|
</button>
|
||||||
|
<select id="inv_spec_filter" class="form-select form-select-sm py-0" style="max-width:160px;height:26px;">
|
||||||
|
<option value="0">All Specs</option>
|
||||||
|
</select>
|
||||||
|
<span class="small text-muted" id="inv_filter_hint" style="display:none;">Filter active — type anything to search</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="form-control d-none mt-2" id="investigation_name" list="investigation_suggest" placeholder="Type investigation name">
|
||||||
|
<datalist id="investigation_suggest"></datalist>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2 d-grid gap-1">
|
<div class="col-md-2 d-grid gap-1">
|
||||||
<button type="button" class="btn btn-primary" id="btn_add_investigation">Add</button>
|
<button type="button" class="btn btn-primary" id="btn_add_investigation">Add</button>
|
||||||
|
|
@ -2003,6 +2012,98 @@
|
||||||
|
|
||||||
$fallback.removeClass('d-none');
|
$fallback.removeClass('d-none');
|
||||||
}
|
}
|
||||||
|
var invFavOnly = false;
|
||||||
|
var invSpecId = 0;
|
||||||
|
|
||||||
|
function loadInvSpecDropdown() {
|
||||||
|
$.get('<?= base_url('Opd_prescription/opd_invest_master_specs') ?>', function(data) {
|
||||||
|
var specs = (data && data.specs) ? data.specs : [];
|
||||||
|
var $sel = $('#inv_spec_filter');
|
||||||
|
$sel.find('option:not(:first)').remove();
|
||||||
|
specs.forEach(function(sp) {
|
||||||
|
$sel.append($('<option>').val(sp.id).text(sp.SpecName));
|
||||||
|
});
|
||||||
|
}, 'json');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateInvFilterHint() {
|
||||||
|
var active = invFavOnly || invSpecId > 0;
|
||||||
|
$('#inv_filter_hint').toggle(active);
|
||||||
|
$('#btn_inv_fav_toggle').toggleClass('btn-warning', invFavOnly).toggleClass('btn-outline-warning', !invFavOnly);
|
||||||
|
}
|
||||||
|
var invFavOnly = false;
|
||||||
|
var invSpecId = 0;
|
||||||
|
|
||||||
|
function loadInvSpecDropdown() {
|
||||||
|
$.get('<?= base_url('Opd_prescription/opd_invest_master_specs') ?>', function(data) {
|
||||||
|
var specs = (data && data.specs) ? data.specs : [];
|
||||||
|
var $sel = $('#inv_spec_filter');
|
||||||
|
$sel.find('option:not(:first)').remove();
|
||||||
|
specs.forEach(function(sp) {
|
||||||
|
$sel.append($('<option>').val(sp.id).text(sp.SpecName));
|
||||||
|
});
|
||||||
|
}, 'json');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateInvFilterHint() {
|
||||||
|
var active = invFavOnly || invSpecId > 0;
|
||||||
|
$('#inv_filter_hint').toggle(active);
|
||||||
|
$('#btn_inv_fav_toggle')
|
||||||
|
.toggleClass('btn-warning', invFavOnly)
|
||||||
|
.toggleClass('btn-outline-warning', !invFavOnly)
|
||||||
|
.find('i').toggleClass('bi-star-fill', invFavOnly).toggleClass('bi-star', !invFavOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initInvestigationPicker() {
|
||||||
|
var $select = $('#investigation_name_select2');
|
||||||
|
var $fallback = $('#investigation_name');
|
||||||
|
|
||||||
|
if (!$select.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($.fn && $.fn.select2) {
|
||||||
|
$select.select2({
|
||||||
|
width: '100%',
|
||||||
|
placeholder: 'Search investigation...',
|
||||||
|
allowClear: true,
|
||||||
|
ajax: {
|
||||||
|
delay: 250,
|
||||||
|
transport: function(params, success, failure) {
|
||||||
|
var term = (params.data && params.data.term) ? params.data.term : '';
|
||||||
|
$.ajax({
|
||||||
|
url: '<?= base_url('Opd_prescription/investigation_search') ?>',
|
||||||
|
dataType: 'json',
|
||||||
|
data: { q: term, fav_only: invFavOnly ? 1 : 0, spec_id: invSpecId },
|
||||||
|
success: success,
|
||||||
|
error: failure
|
||||||
|
});
|
||||||
|
},
|
||||||
|
processResults: function(data) {
|
||||||
|
var rows = (data && data.rows) ? data.rows : [];
|
||||||
|
return {
|
||||||
|
results: rows.map(function(row) {
|
||||||
|
var name = (row.name || '').toString();
|
||||||
|
var code = (row.code || '').toString();
|
||||||
|
var isFav = parseInt(row.is_favourite || 0) === 1;
|
||||||
|
var label = (isFav ? '★ ' : '') + name + (code ? ' [' + code + ']' : '');
|
||||||
|
return {
|
||||||
|
id: name + (code ? ' [' + code + ']' : ''),
|
||||||
|
text: label,
|
||||||
|
name: name,
|
||||||
|
code: code
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$fallback.addClass('d-none');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fallback.removeClass('d-none');
|
||||||
|
}
|
||||||
|
|
||||||
function setStatus(type, message) {
|
function setStatus(type, message) {
|
||||||
var $badge = $('#rx_status_badge');
|
var $badge = $('#rx_status_badge');
|
||||||
|
|
@ -4024,7 +4125,26 @@
|
||||||
refreshCustomProfileSelect();
|
refreshCustomProfileSelect();
|
||||||
loadInvestigationShortcutsFromLegacy();
|
loadInvestigationShortcutsFromLegacy();
|
||||||
initInvestigationPicker();
|
initInvestigationPicker();
|
||||||
loadMedicineDoseMasters();
|
loadInvSpecDropdown();
|
||||||
|
|
||||||
|
$('#btn_inv_fav_toggle').on('click', function() {
|
||||||
|
invFavOnly = !invFavOnly;
|
||||||
|
updateInvFilterHint();
|
||||||
|
// Trigger Select2 to re-search with current term
|
||||||
|
if ($.fn && $.fn.select2 && $('#investigation_name_select2').length) {
|
||||||
|
$('#investigation_name_select2').trigger('input.select2');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#inv_spec_filter').on('change', function() {
|
||||||
|
invSpecId = parseInt($(this).val() || '0', 10);
|
||||||
|
updateInvFilterHint();
|
||||||
|
if ($.fn && $.fn.select2 && $('#investigation_name_select2').length) {
|
||||||
|
$('#investigation_name_select2').trigger('input.select2');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loadMedicineDoseMasters();
|
||||||
$('#advise_investigation_notes').val($('#investigation').val() || '');
|
$('#advise_investigation_notes').val($('#investigation').val() || '');
|
||||||
|
|
||||||
$(document).on('click', '.btn-del-invest', function() {
|
$(document).on('click', '.btn-del-invest', function() {
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,13 @@ if (! $canDiagnosis && $user && method_exists($user, 'inGroup')) {
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Opd_prescription/opd_advice') ?>','OPD Advice Master')">
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Opd_prescription/opd_advice') ?>','OPD Advice Master')">
|
||||||
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Opd_prescription/opd_invest_master') ?>','Investigation Master')">
|
||||||
|
<i class="bi bi-clipboard2-pulse"></i>
|
||||||
|
<span>Investigation Master</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Opd_prescription/opd_advice') ?>','OPD Advice Master')">
|
||||||
<i class="bi bi-chat-left-text"></i>
|
<i class="bi bi-chat-left-text"></i>
|
||||||
<span>OPD Advice Master</span>
|
<span>OPD Advice Master</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user