Add investigation shortcuts manager panel
This commit is contained in:
parent
57397d80e1
commit
aae6211f48
|
|
@ -625,6 +625,7 @@ $routes->post('Opd_prescription/advice_remove/(:num)', 'Opd_prescription::advice
|
||||||
|
|
||||||
$routes->get('Opd_prescription/investigation_search', 'Opd_prescription::investigation_search');
|
$routes->get('Opd_prescription/investigation_search', 'Opd_prescription::investigation_search');
|
||||||
$routes->get('Opd_prescription/investigation_shortcuts', 'Opd_prescription::investigation_shortcuts');
|
$routes->get('Opd_prescription/investigation_shortcuts', 'Opd_prescription::investigation_shortcuts');
|
||||||
|
$routes->get('Opd_prescription/debug_investigation_fields', 'Opd_prescription::debug_investigation_fields');
|
||||||
$routes->get('Opd_prescription/investigation_list/(:num)', 'Opd_prescription::investigation_list/$1');
|
$routes->get('Opd_prescription/investigation_list/(:num)', 'Opd_prescription::investigation_list/$1');
|
||||||
$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');
|
||||||
|
|
@ -636,6 +637,9 @@ $routes->get('Opd_prescription/opd_invest_master_get/(:num)', 'Opd_prescription:
|
||||||
$routes->post('Opd_prescription/opd_invest_master_save', 'Opd_prescription::opd_invest_master_save');
|
$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_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->post('Opd_prescription/opd_invest_master_toggle_fav/(:num)', 'Opd_prescription::opd_invest_master_toggle_fav/$1');
|
||||||
|
$routes->get('Opd_prescription/opd_invest_shortcuts_manager', 'Opd_prescription::opd_invest_shortcuts_manager', ['filter' => 'permission:doctor_work.medicine.manage,doctor_work.access']);
|
||||||
|
$routes->get('Opd_prescription/opd_invest_shortcuts_all', 'Opd_prescription::opd_invest_shortcuts_all');
|
||||||
|
$routes->post('Opd_prescription/opd_invest_shortcuts_save_item', 'Opd_prescription::opd_invest_shortcuts_save_item');
|
||||||
$routes->get('Opd_prescription/opd_invest_profile_master', 'Opd_prescription::opd_invest_profile_master', ['filter' => 'permission:doctor_work.medicine.manage,doctor_work.access']);
|
$routes->get('Opd_prescription/opd_invest_profile_master', 'Opd_prescription::opd_invest_profile_master', ['filter' => 'permission:doctor_work.medicine.manage,doctor_work.access']);
|
||||||
$routes->get('Opd_prescription/opd_invest_profile_master_list', 'Opd_prescription::opd_invest_profile_master_list');
|
$routes->get('Opd_prescription/opd_invest_profile_master_list', 'Opd_prescription::opd_invest_profile_master_list');
|
||||||
$routes->get('Opd_prescription/opd_invest_profile_master_data', 'Opd_prescription::opd_invest_profile_master_data');
|
$routes->get('Opd_prescription/opd_invest_profile_master_data', 'Opd_prescription::opd_invest_profile_master_data');
|
||||||
|
|
|
||||||
|
|
@ -3381,6 +3381,85 @@ class Opd_prescription extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function opd_invest_shortcuts_manager()
|
||||||
|
{
|
||||||
|
return view('billing/opd_invest_shortcuts_manager');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_shortcuts_all()
|
||||||
|
{
|
||||||
|
if (! $this->db->tableExists('investigation')) {
|
||||||
|
return $this->response->setJSON(['rows' => []]);
|
||||||
|
}
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
$keyField = $this->resolveFirstField($fields, ['id', 'Code', 'code']);
|
||||||
|
$codeField = $this->resolveFirstField($fields, ['Code', 'code']);
|
||||||
|
$nameField = $this->resolveFirstField($fields, ['Name', 'name']);
|
||||||
|
if ($keyField === null || $codeField === null || $nameField === null) {
|
||||||
|
return $this->response->setJSON(['rows' => []]);
|
||||||
|
}
|
||||||
|
$selectCols = $keyField . ' as id, ' . $codeField . ' as code, ' . $nameField . ' as name';
|
||||||
|
foreach (['short_name', 'sort_id'] as $f) {
|
||||||
|
if (in_array($f, $fields, true)) {
|
||||||
|
$selectCols .= ', ' . $f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rows = $this->db->table('investigation')
|
||||||
|
->select($selectCols)
|
||||||
|
->orderBy($nameField, 'ASC')
|
||||||
|
->get()->getResultArray();
|
||||||
|
$rows = array_map(static function (array $r): array {
|
||||||
|
return [
|
||||||
|
'id' => (int) ($r['id'] ?? 0),
|
||||||
|
'code' => (string) ($r['code'] ?? ''),
|
||||||
|
'name' => (string) ($r['name'] ?? ''),
|
||||||
|
'short_name' => (string) ($r['short_name'] ?? ''),
|
||||||
|
'sort_id' => (int) ($r['sort_id'] ?? 0),
|
||||||
|
];
|
||||||
|
}, $rows);
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'rows' => $rows,
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function opd_invest_shortcuts_save_item()
|
||||||
|
{
|
||||||
|
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');
|
||||||
|
if ($id <= 0) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid id']);
|
||||||
|
}
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
$keyField = $this->resolveFirstField($fields, ['id', 'Code', 'code']);
|
||||||
|
if ($keyField === null) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid table structure']);
|
||||||
|
}
|
||||||
|
$data = [];
|
||||||
|
if (in_array('short_name', $fields, true)) {
|
||||||
|
$data['short_name'] = trim((string) $this->request->getPost('short_name'));
|
||||||
|
}
|
||||||
|
if (in_array('sort_id', $fields, true)) {
|
||||||
|
$data['sort_id'] = max(0, (int) $this->request->getPost('sort_id'));
|
||||||
|
}
|
||||||
|
if (empty($data)) {
|
||||||
|
return $this->response->setJSON(['update' => 0, 'error_text' => 'Nothing to update']);
|
||||||
|
}
|
||||||
|
$this->db->table('investigation')->where($keyField, $id)->update($data);
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'update' => 1,
|
||||||
|
'error_text' => 'Saved',
|
||||||
|
'csrfName' => csrf_token(),
|
||||||
|
'csrfHash' => csrf_hash(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function opd_invest_profile_master()
|
public function opd_invest_profile_master()
|
||||||
{
|
{
|
||||||
return view('billing/opd_invest_profile_master_workspace');
|
return view('billing/opd_invest_profile_master_workspace');
|
||||||
|
|
@ -4697,25 +4776,36 @@ class Opd_prescription extends BaseController
|
||||||
|
|
||||||
if ($this->db->tableExists('investigation')) {
|
if ($this->db->tableExists('investigation')) {
|
||||||
$invFields = $this->db->getFieldNames('investigation');
|
$invFields = $this->db->getFieldNames('investigation');
|
||||||
$codeField = $this->resolveFirstField($invFields, ['Code', 'code']);
|
|
||||||
$nameField = $this->resolveFirstField($invFields, ['Name', 'name']);
|
|
||||||
$shortField = $this->resolveFirstField($invFields, ['short_name', 'shortName', 'short']);
|
|
||||||
$sortField = $this->resolveFirstField($invFields, ['sort_id', 'sort_order', 'id']);
|
|
||||||
|
|
||||||
if ($codeField !== null && $nameField !== null && $shortField !== null) {
|
$pickField = static function (array $fields, array $candidates): ?string {
|
||||||
$builder = $this->db->table('investigation')
|
$map = [];
|
||||||
->select($codeField . ' as code,' . $nameField . ' as name,' . $shortField . ' as short_name')
|
foreach ($fields as $field) {
|
||||||
|
$map[strtolower((string) $field)] = (string) $field;
|
||||||
|
}
|
||||||
|
foreach ($candidates as $candidate) {
|
||||||
|
$key = strtolower((string) $candidate);
|
||||||
|
if (isset($map[$key])) {
|
||||||
|
return $map[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
$codeField = $pickField($invFields, ['Code', 'code']);
|
||||||
|
$nameField = $pickField($invFields, ['Name', 'name']);
|
||||||
|
$shortField = $pickField($invFields, ['short_name', 'shortName', 'short']);
|
||||||
|
$sortField = $pickField($invFields, ['sort_id', 'sort_order', 'id']);
|
||||||
|
|
||||||
|
if ($codeField !== null && $nameField !== null && $shortField !== null && $sortField !== null) {
|
||||||
|
// Keep legacy CI3 behavior: show only mostly-used tests and group/order by short_name, sort_id.
|
||||||
|
$shortTests = $this->db->table('investigation')
|
||||||
|
->select($codeField . ' as code,' . $nameField . ' as name,' . $shortField . ' as short_name,' . $sortField . ' as sort_id')
|
||||||
->where($shortField . ' !=', '')
|
->where($shortField . ' !=', '')
|
||||||
->where($shortField . ' is not null', null, false)
|
->where($shortField . ' is not null', null, false)
|
||||||
->limit(200);
|
->orderBy($shortField, 'ASC')
|
||||||
|
->orderBy($sortField, 'ASC')
|
||||||
if ($sortField !== null) {
|
->get()
|
||||||
$builder->orderBy($shortField, 'ASC')->orderBy($sortField, 'ASC');
|
->getResultArray();
|
||||||
} else {
|
|
||||||
$builder->orderBy($shortField, 'ASC')->orderBy($nameField, 'ASC');
|
|
||||||
}
|
|
||||||
|
|
||||||
$shortTests = $builder->get()->getResultArray();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4796,6 +4886,36 @@ class Opd_prescription extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEBUG ENDPOINT - Remove after verifying data
|
||||||
|
public function debug_investigation_fields()
|
||||||
|
{
|
||||||
|
if ($this->db->tableExists('investigation')) {
|
||||||
|
$fields = $this->db->getFieldNames('investigation');
|
||||||
|
$total = $this->db->table('investigation')->countAll();
|
||||||
|
$withShortName = $this->db->table('investigation')
|
||||||
|
->where("short_name != ''", null, false)
|
||||||
|
->where('short_name IS NOT NULL', null, false)
|
||||||
|
->countAll();
|
||||||
|
|
||||||
|
$sample = $this->db->table('investigation')
|
||||||
|
->where("short_name != ''", null, false)
|
||||||
|
->where('short_name IS NOT NULL', null, false)
|
||||||
|
->orderBy('short_name', 'ASC')
|
||||||
|
->orderBy('sort_id', 'ASC')
|
||||||
|
->limit(10)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'table_fields' => $fields,
|
||||||
|
'total_investigations' => $total,
|
||||||
|
'with_short_name' => $withShortName,
|
||||||
|
'sample_data' => $sample,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $this->response->setJSON(['error' => 'investigation table not found']);
|
||||||
|
}
|
||||||
|
|
||||||
public function investigation_list($opdId, $sessionId = 0)
|
public function investigation_list($opdId, $sessionId = 0)
|
||||||
{
|
{
|
||||||
$table = $this->findExistingTable(['opd_prescription_investigation']);
|
$table = $this->findExistingTable(['opd_prescription_investigation']);
|
||||||
|
|
|
||||||
419
app/Views/billing/opd_invest_shortcuts_manager.php
Normal file
419
app/Views/billing/opd_invest_shortcuts_manager.php
Normal file
|
|
@ -0,0 +1,419 @@
|
||||||
|
<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-grid-3x2 me-1"></i> Investigation Shortcuts Manager</h5>
|
||||||
|
<span class="badge bg-secondary" id="ism_badge">—</span>
|
||||||
|
<div class="ms-auto d-flex gap-2">
|
||||||
|
<button type="button" class="btn btn-success btn-sm" id="btn_ism_new_group">
|
||||||
|
<i class="bi bi-plus-circle"></i> New Group
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Live Preview -->
|
||||||
|
<div class="card mb-3 border-info">
|
||||||
|
<div class="card-header py-1 bg-info bg-opacity-10">
|
||||||
|
<small class="fw-semibold text-info-emphasis"><i class="bi bi-eye me-1"></i>Panel Preview (how it will look on OPD form)</small>
|
||||||
|
</div>
|
||||||
|
<div class="card-body py-2" id="ism_preview">
|
||||||
|
<span class="text-muted small">Loading…</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<!-- Groups (left) -->
|
||||||
|
<div class="col-md-7">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-header d-flex align-items-center justify-content-between py-2">
|
||||||
|
<strong>Groups in Panel</strong>
|
||||||
|
<span class="badge bg-primary" id="ism_in_panel_count">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-2" id="ism_groups_container" style="max-height:600px;overflow-y:auto;">
|
||||||
|
<div class="text-muted text-center py-3 small">Loading…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Not in Panel (right) -->
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-header d-flex align-items-center justify-content-between py-2">
|
||||||
|
<strong>Not in Panel</strong>
|
||||||
|
<span class="badge bg-secondary" id="ism_not_in_panel_count">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-header py-1 border-top-0">
|
||||||
|
<input type="text" id="ism_search_unassigned" class="form-control form-control-sm" placeholder="Search tests…">
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0" style="max-height:560px;overflow-y:auto;">
|
||||||
|
<div id="ism_unassigned_list">
|
||||||
|
<div class="text-muted text-center py-3 small">Loading…</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- New / Rename Group Modal -->
|
||||||
|
<div class="modal fade" id="ismGroupModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-sm">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header py-2">
|
||||||
|
<h6 class="modal-title" id="ismGroupModalTitle">New Group</h6>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="ism_gmodal_old_name" value="">
|
||||||
|
<label class="form-label small">Group Name <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" id="ism_gmodal_name" class="form-control form-control-sm" maxlength="80" placeholder="e.g. Blood Tests, Urine…">
|
||||||
|
<div class="small mt-1 text-danger" id="ism_gmodal_msg"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer py-2">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="btn_ism_gmodal_ok">Create</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assign Test to Group Modal -->
|
||||||
|
<div class="modal fade" id="ismAssignModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header py-2">
|
||||||
|
<h6 class="modal-title">Assign to Group</h6>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="ism_assign_id" value="0">
|
||||||
|
<p class="mb-2">Test: <strong id="ism_assign_name">—</strong></p>
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label small">Group <span class="text-danger">*</span></label>
|
||||||
|
<select id="ism_assign_group_sel" class="form-select form-select-sm">
|
||||||
|
<option value="">— New group (type below) —</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2" id="ism_assign_newgrp_wrap">
|
||||||
|
<input type="text" id="ism_assign_new_group" class="form-control form-control-sm" maxlength="80" placeholder="New group name…">
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label small">Sort Order <small class="text-muted">(lower = first)</small></label>
|
||||||
|
<input type="number" id="ism_assign_sort" class="form-control form-control-sm" value="0" min="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer py-2">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="btn_ism_assign_ok">Assign</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var allTests = [];
|
||||||
|
var csrfName = '<?= csrf_token() ?>';
|
||||||
|
var csrfHash = '<?= csrf_hash() ?>';
|
||||||
|
var COLOR_CLS = ['btn-secondary', 'btn-primary', 'btn-success', 'btn-info', 'btn-danger', 'btn-warning'];
|
||||||
|
|
||||||
|
function groupColor(idx) { return COLOR_CLS[idx % COLOR_CLS.length]; }
|
||||||
|
|
||||||
|
/* ── XHR helpers ────────────────────────────────────────── */
|
||||||
|
function apiGet(url, cb) {
|
||||||
|
$.getJSON(url).done(function (d) { cb(null, d); }).fail(function (x) { cb('HTTP ' + x.status, null); });
|
||||||
|
}
|
||||||
|
function apiPost(url, data, cb) {
|
||||||
|
data[csrfName] = csrfHash;
|
||||||
|
$.post(url, data, function (d) {
|
||||||
|
if (d && d.csrfHash) { csrfHash = d.csrfHash; if (d.csrfName) csrfName = d.csrfName; }
|
||||||
|
cb(d);
|
||||||
|
}).fail(function (x) { cb({ update: 0, error_text: 'HTTP ' + x.status }); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Data helpers ───────────────────────────────────────── */
|
||||||
|
function computeGroups() {
|
||||||
|
var grp = {}, order = [];
|
||||||
|
allTests.forEach(function (t) {
|
||||||
|
var g = (t.short_name || '').trim();
|
||||||
|
if (!g) return;
|
||||||
|
if (!grp[g]) { grp[g] = []; order.push(g); }
|
||||||
|
grp[g].push(t);
|
||||||
|
});
|
||||||
|
order.sort();
|
||||||
|
order.forEach(function (g) {
|
||||||
|
grp[g].sort(function (a, b) { return (a.sort_id || 0) - (b.sort_id || 0); });
|
||||||
|
});
|
||||||
|
return { grp: grp, order: order };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnassigned(filter) {
|
||||||
|
var f = (filter || '').toLowerCase();
|
||||||
|
return allTests.filter(function (t) {
|
||||||
|
if ((t.short_name || '').trim()) return false;
|
||||||
|
return !f || (t.name || '').toLowerCase().indexOf(f) !== -1 || (t.code || '').toLowerCase().indexOf(f) !== -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Render: preview ────────────────────────────────────── */
|
||||||
|
function renderPreview() {
|
||||||
|
var res = computeGroups();
|
||||||
|
if (!res.order.length) {
|
||||||
|
$('#ism_preview').html('<span class="text-muted small">No groups yet.</span>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var html = '';
|
||||||
|
res.order.forEach(function (g, ci) {
|
||||||
|
html += '<br>';
|
||||||
|
res.grp[g].forEach(function (t) {
|
||||||
|
html += '<button type="button" class="btn btn-sm ' + groupColor(ci) + '" style="margin:2px;" disabled>' + esc(t.name) + '</button>';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('#ism_preview').html('<div>' + html + '</div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Render: groups ─────────────────────────────────────── */
|
||||||
|
function renderGroups() {
|
||||||
|
var res = computeGroups();
|
||||||
|
var inPanel = allTests.filter(function (t) { return (t.short_name || '').trim(); }).length;
|
||||||
|
$('#ism_in_panel_count').text(inPanel);
|
||||||
|
|
||||||
|
if (!res.order.length) {
|
||||||
|
$('#ism_groups_container').html(
|
||||||
|
'<div class="text-muted text-center py-4 small">' +
|
||||||
|
'No groups defined.<br>Click <strong>+ New Group</strong>, then assign tests from the right panel.' +
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
res.order.forEach(function (g, ci) {
|
||||||
|
var bsCls = groupColor(ci);
|
||||||
|
html += '<div class="card mb-2">';
|
||||||
|
html += '<div class="card-header py-1 d-flex align-items-center gap-2">';
|
||||||
|
html += '<span class="badge ' + bsCls + '">' + esc(g) + '</span>';
|
||||||
|
html += '<small class="text-muted">(' + res.grp[g].length + ' tests)</small>';
|
||||||
|
html += '<div class="ms-auto d-flex gap-1">';
|
||||||
|
html += '<button class="btn btn-outline-secondary btn-sm py-0 px-2 btn-ism-rename" data-group="' + esc(g) + '">Rename</button>';
|
||||||
|
html += '<button class="btn btn-outline-danger btn-sm py-0 px-2 btn-ism-del-group" data-group="' + esc(g) + '">Remove Group</button>';
|
||||||
|
html += '</div></div>';
|
||||||
|
html += '<div class="card-body p-1">';
|
||||||
|
html += '<table class="table table-sm table-hover mb-0">';
|
||||||
|
html += '<thead class="table-light"><tr><th>Test Name</th><th width="85">Sort</th><th width="90">Action</th></tr></thead><tbody>';
|
||||||
|
res.grp[g].forEach(function (t) {
|
||||||
|
html += '<tr>';
|
||||||
|
html += '<td class="align-middle small">' + esc(t.name) + '</td>';
|
||||||
|
html += '<td><input type="number" class="form-control form-control-sm ism-sort-input" value="' + (t.sort_id || 0) + '" min="0" style="width:70px;" data-id="' + t.id + '"></td>';
|
||||||
|
html += '<td><button class="btn btn-outline-danger btn-sm py-0 btn-ism-remove" data-id="' + t.id + '">Remove</button></td>';
|
||||||
|
html += '</tr>';
|
||||||
|
});
|
||||||
|
html += '</tbody></table></div></div>';
|
||||||
|
});
|
||||||
|
$('#ism_groups_container').html(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Render: unassigned ─────────────────────────────────── */
|
||||||
|
function renderUnassigned() {
|
||||||
|
var filter = $('#ism_search_unassigned').val();
|
||||||
|
var list = getUnassigned(filter);
|
||||||
|
$('#ism_not_in_panel_count').text(allTests.filter(function (t) { return !(t.short_name || '').trim(); }).length);
|
||||||
|
|
||||||
|
if (!list.length) {
|
||||||
|
$('#ism_unassigned_list').html('<div class="text-muted text-center py-3 small">All tests are assigned to groups.</div>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var html = '<table class="table table-sm table-hover mb-0"><tbody>';
|
||||||
|
list.forEach(function (t) {
|
||||||
|
html += '<tr>';
|
||||||
|
html += '<td class="align-middle small">' + esc(t.name);
|
||||||
|
if (t.code) html += ' <span class="text-muted">(' + esc(t.code) + ')</span>';
|
||||||
|
html += '</td>';
|
||||||
|
html += '<td width="90" class="align-middle"><button class="btn btn-outline-primary btn-sm py-0 btn-ism-assign" data-id="' + t.id + '" data-name="' + esc(t.name) + '">+ Group</button></td>';
|
||||||
|
html += '</tr>';
|
||||||
|
});
|
||||||
|
html += '</tbody></table>';
|
||||||
|
$('#ism_unassigned_list').html(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAll() {
|
||||||
|
renderGroups();
|
||||||
|
renderUnassigned();
|
||||||
|
renderPreview();
|
||||||
|
syncGroupSelectOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Sync group dropdown options ────────────────────────── */
|
||||||
|
function syncGroupSelectOptions() {
|
||||||
|
var res = computeGroups();
|
||||||
|
var $sel = $('#ism_assign_group_sel');
|
||||||
|
var cur = $sel.val();
|
||||||
|
$sel.empty().append('<option value="">— New group (type below) —</option>');
|
||||||
|
res.order.forEach(function (g) {
|
||||||
|
$sel.append('<option value="' + esc(g) + '"' + (g === cur ? ' selected' : '') + '>' + esc(g) + '</option>');
|
||||||
|
});
|
||||||
|
$sel.trigger('change');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Save one item ──────────────────────────────────────── */
|
||||||
|
var _saveTimers = {};
|
||||||
|
function saveItem(id, shortName, sortId, cb) {
|
||||||
|
apiPost('<?= base_url('Opd_prescription/opd_invest_shortcuts_save_item') ?>', {
|
||||||
|
id: id,
|
||||||
|
short_name: shortName,
|
||||||
|
sort_id: sortId
|
||||||
|
}, cb || function () {});
|
||||||
|
}
|
||||||
|
function debouncedSave(id, shortName, sortId) {
|
||||||
|
clearTimeout(_saveTimers[id]);
|
||||||
|
_saveTimers[id] = setTimeout(function () {
|
||||||
|
allTests.forEach(function (t) { if (t.id == id) { t.short_name = shortName; t.sort_id = sortId; } });
|
||||||
|
saveItem(id, shortName, sortId);
|
||||||
|
}, 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── HTML escape ────────────────────────────────────────── */
|
||||||
|
function esc(s) { return $('<span>').text(s || '').html(); }
|
||||||
|
|
||||||
|
/* ── Event: sort input change ───────────────────────────── */
|
||||||
|
$(document).on('change', '.ism-sort-input', function () {
|
||||||
|
var id = parseInt($(this).data('id'), 10);
|
||||||
|
var val = Math.max(0, parseInt($(this).val(), 10) || 0);
|
||||||
|
var t = allTests.find(function (x) { return x.id === id; });
|
||||||
|
if (!t) return;
|
||||||
|
t.sort_id = val;
|
||||||
|
debouncedSave(id, t.short_name, val);
|
||||||
|
renderGroups(); // re-sort displayed list
|
||||||
|
renderPreview();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Event: remove test from panel ─────────────────────── */
|
||||||
|
$(document).on('click', '.btn-ism-remove', function () {
|
||||||
|
var id = parseInt($(this).data('id'), 10);
|
||||||
|
var t = allTests.find(function (x) { return x.id === id; });
|
||||||
|
if (!t) return;
|
||||||
|
saveItem(id, '', t.sort_id, function (d) {
|
||||||
|
if ((d.update || 0) !== 1) { alert(d.error_text || 'Failed'); return; }
|
||||||
|
t.short_name = '';
|
||||||
|
renderAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Event: rename group ────────────────────────────────── */
|
||||||
|
$(document).on('click', '.btn-ism-rename', function () {
|
||||||
|
var oldName = $(this).data('group');
|
||||||
|
$('#ism_gmodal_old_name').val(oldName);
|
||||||
|
$('#ism_gmodal_name').val(oldName);
|
||||||
|
$('#ism_gmodal_msg').text('');
|
||||||
|
$('#ismGroupModalTitle').text('Rename Group');
|
||||||
|
$('#btn_ism_gmodal_ok').text('Rename');
|
||||||
|
bootstrap.Modal.getOrCreateInstance('#ismGroupModal').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Event: delete group ────────────────────────────────── */
|
||||||
|
$(document).on('click', '.btn-ism-del-group', function () {
|
||||||
|
var g = $(this).data('group');
|
||||||
|
if (!confirm('Remove all tests from group "' + g + '"?\n\nTests will move to "Not in Panel".')) return;
|
||||||
|
var tasks = allTests.filter(function (t) { return (t.short_name || '').trim() === g; });
|
||||||
|
var done = 0;
|
||||||
|
if (!tasks.length) return;
|
||||||
|
tasks.forEach(function (t) {
|
||||||
|
saveItem(t.id, '', t.sort_id, function (d) {
|
||||||
|
if ((d.update || 0) === 1) t.short_name = '';
|
||||||
|
if (++done === tasks.length) renderAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── New Group button ───────────────────────────────────── */
|
||||||
|
$('#btn_ism_new_group').on('click', function () {
|
||||||
|
$('#ism_gmodal_old_name').val('');
|
||||||
|
$('#ism_gmodal_name').val('');
|
||||||
|
$('#ism_gmodal_msg').text('');
|
||||||
|
$('#ismGroupModalTitle').text('New Group');
|
||||||
|
$('#btn_ism_gmodal_ok').text('Create');
|
||||||
|
bootstrap.Modal.getOrCreateInstance('#ismGroupModal').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Group modal OK ─────────────────────────────────────── */
|
||||||
|
$('#btn_ism_gmodal_ok').on('click', function () {
|
||||||
|
var newName = $('#ism_gmodal_name').val().trim();
|
||||||
|
var oldName = $('#ism_gmodal_old_name').val().trim();
|
||||||
|
if (!newName) { $('#ism_gmodal_msg').text('Group name is required.'); return; }
|
||||||
|
|
||||||
|
bootstrap.Modal.getOrCreateInstance('#ismGroupModal').hide();
|
||||||
|
|
||||||
|
if (oldName && oldName !== newName) {
|
||||||
|
// Rename: update all tests in oldName group
|
||||||
|
var tasks = allTests.filter(function (t) { return (t.short_name || '').trim() === oldName; });
|
||||||
|
if (!tasks.length) { renderAll(); return; }
|
||||||
|
var done = 0;
|
||||||
|
tasks.forEach(function (t) {
|
||||||
|
saveItem(t.id, newName, t.sort_id, function (d) {
|
||||||
|
if ((d.update || 0) === 1) t.short_name = newName;
|
||||||
|
if (++done === tasks.length) renderAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (!oldName) {
|
||||||
|
// New group: add to dropdown so user can immediately assign tests to it
|
||||||
|
var res = computeGroups();
|
||||||
|
if (!res.grp[newName]) {
|
||||||
|
// Pre-populate dropdown with new name so assign modal shows it
|
||||||
|
$('#ism_assign_group_sel').append('<option value="' + esc(newName) + '" selected>' + esc(newName) + '</option>');
|
||||||
|
}
|
||||||
|
renderAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Assign test to group ───────────────────────────────── */
|
||||||
|
$(document).on('click', '.btn-ism-assign', function () {
|
||||||
|
var id = parseInt($(this).data('id'), 10);
|
||||||
|
var name = $(this).data('name');
|
||||||
|
$('#ism_assign_id').val(id);
|
||||||
|
$('#ism_assign_name').text(name);
|
||||||
|
$('#ism_assign_sort').val(0);
|
||||||
|
syncGroupSelectOptions();
|
||||||
|
bootstrap.Modal.getOrCreateInstance('#ismAssignModal').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#ism_assign_group_sel').on('change', function () {
|
||||||
|
var v = $(this).val();
|
||||||
|
if (v === '') {
|
||||||
|
$('#ism_assign_newgrp_wrap').show();
|
||||||
|
} else {
|
||||||
|
$('#ism_assign_newgrp_wrap').hide();
|
||||||
|
$('#ism_assign_new_group').val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_ism_assign_ok').on('click', function () {
|
||||||
|
var id = parseInt($('#ism_assign_id').val(), 10);
|
||||||
|
var selGroup = $('#ism_assign_group_sel').val();
|
||||||
|
var newGroup = $('#ism_assign_new_group').val().trim();
|
||||||
|
var group = selGroup || newGroup;
|
||||||
|
var sortId = parseInt($('#ism_assign_sort').val(), 10) || 0;
|
||||||
|
|
||||||
|
if (!group) { alert('Please select an existing group or enter a new group name.'); return; }
|
||||||
|
|
||||||
|
bootstrap.Modal.getOrCreateInstance('#ismAssignModal').hide();
|
||||||
|
|
||||||
|
saveItem(id, group, sortId, function (d) {
|
||||||
|
if ((d.update || 0) !== 1) { alert(d.error_text || 'Failed to assign'); return; }
|
||||||
|
var t = allTests.find(function (x) { return x.id === id; });
|
||||||
|
if (t) { t.short_name = group; t.sort_id = sortId; }
|
||||||
|
renderAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Search unassigned ──────────────────────────────────── */
|
||||||
|
$('#ism_search_unassigned').on('input', function () { renderUnassigned(); });
|
||||||
|
|
||||||
|
/* ── Init ───────────────────────────────────────────────── */
|
||||||
|
apiGet('<?= base_url('Opd_prescription/opd_invest_shortcuts_all') ?>', function (err, d) {
|
||||||
|
allTests = (d && d.rows) ? d.rows : [];
|
||||||
|
$('#ism_badge').text(allTests.length + ' investigations');
|
||||||
|
renderAll();
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
@ -807,9 +807,17 @@
|
||||||
<option value="">Load profile...</option>
|
<option value="">Load profile...</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_apply_custom_inv_profile">Apply</button>
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_apply_custom_inv_profile">Apply</button>
|
||||||
|
<button type="button" class="btn btn-outline-dark btn-sm" id="btn_toggle_legacy_inv_panel">Old Type Panel</button>
|
||||||
|
<a href="javascript:load_form_div('<?= base_url('Opd_prescription/opd_invest_shortcuts_manager') ?>','maindiv','Shortcuts Manager');" class="btn btn-outline-info btn-sm" title="Configure which tests appear in the Old Type Panel and their groups"><i class="bi bi-grid-3x2"></i> Manage Shortcuts</a>
|
||||||
|
<button type="button" class="btn btn-outline-dark btn-sm" id="btn_open_custom_investigation" data-bs-toggle="modal" data-bs-target="#customInvestigationModal">Custome Investigation</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="legacy_inv_shortcuts_panel" class="border rounded p-2 mb-3" style="display:none;">
|
||||||
|
<div class="small fw-semibold mb-2">Legacy Investigation Shortcuts</div>
|
||||||
|
<div id="legacy_inv_shortcuts_grouped"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row g-2 mb-3">
|
<div class="row g-2 mb-3">
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<select class="form-select" id="investigation_name_select2" style="width:100%;">
|
<select class="form-select" id="investigation_name_select2" style="width:100%;">
|
||||||
|
|
@ -1230,6 +1238,26 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="customInvestigationModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Custome Investigation</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<label class="form-label fw-semibold" for="custom_investigation_select2">Select Investigations</label>
|
||||||
|
<select class="form-select" id="custom_investigation_select2" multiple="multiple" style="width:100%;"></select>
|
||||||
|
<div class="small text-muted mt-2">Search and select multiple tests, then click Add Selected.</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="btn_add_custom_investigations">Add Selected</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -1273,6 +1301,7 @@
|
||||||
var invCustomProfileKey = 'opd_inv_custom_profiles';
|
var invCustomProfileKey = 'opd_inv_custom_profiles';
|
||||||
var legacyInvestigationProfiles = {};
|
var legacyInvestigationProfiles = {};
|
||||||
var legacyInvestigationProfileList = [];
|
var legacyInvestigationProfileList = [];
|
||||||
|
var legacyInvestigationShortTests = [];
|
||||||
var investigationBatchActive = false;
|
var investigationBatchActive = false;
|
||||||
var activeMedicineScope = 'all';
|
var activeMedicineScope = 'all';
|
||||||
var medicineSuggestRows = [];
|
var medicineSuggestRows = [];
|
||||||
|
|
@ -2785,6 +2814,7 @@
|
||||||
function loadInvestigationShortcutsFromLegacy() {
|
function loadInvestigationShortcutsFromLegacy() {
|
||||||
apiGet('<?= base_url('Opd_prescription/investigation_shortcuts') ?>', function(data) {
|
apiGet('<?= base_url('Opd_prescription/investigation_shortcuts') ?>', function(data) {
|
||||||
var profiles = (data && data.profiles) ? data.profiles : [];
|
var profiles = (data && data.profiles) ? data.profiles : [];
|
||||||
|
var shortTests = (data && data.short_tests) ? data.short_tests : [];
|
||||||
var $profileWrap = $('#inv_profile_chip_wrap');
|
var $profileWrap = $('#inv_profile_chip_wrap');
|
||||||
|
|
||||||
if ($profileWrap.length) {
|
if ($profileWrap.length) {
|
||||||
|
|
@ -2792,6 +2822,8 @@
|
||||||
}
|
}
|
||||||
legacyInvestigationProfiles = {};
|
legacyInvestigationProfiles = {};
|
||||||
legacyInvestigationProfileList = [];
|
legacyInvestigationProfileList = [];
|
||||||
|
legacyInvestigationShortTests = Array.isArray(shortTests) ? shortTests : [];
|
||||||
|
renderLegacyInvestigationShortcuts(legacyInvestigationShortTests);
|
||||||
|
|
||||||
if (profiles.length) {
|
if (profiles.length) {
|
||||||
profiles.forEach(function(p) {
|
profiles.forEach(function(p) {
|
||||||
|
|
@ -2836,6 +2868,112 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderLegacyInvestigationShortcuts(rows) {
|
||||||
|
var $wrap = $('#legacy_inv_shortcuts_grouped');
|
||||||
|
if (!$wrap.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$wrap.empty();
|
||||||
|
rows = Array.isArray(rows) ? rows : [];
|
||||||
|
if (!rows.length) {
|
||||||
|
$wrap.append('<div class="text-muted small">No legacy investigations found.</div>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap 5 equivalents of CI3 Bootstrap 3 colors (btn-default removed in BS5 → btn-secondary)
|
||||||
|
var btnColor = ['btn-secondary', 'btn-primary', 'btn-success', 'btn-info', 'btn-danger', 'btn-warning'];
|
||||||
|
var shortName = '';
|
||||||
|
var counter = 1;
|
||||||
|
var btnStyle = 'btn-secondary';
|
||||||
|
var hasAny = false;
|
||||||
|
|
||||||
|
rows.forEach(function(row) {
|
||||||
|
var rowShortName = (row.short_name || '').toString().trim();
|
||||||
|
var testName = (row.name || '').toString().trim();
|
||||||
|
var testCode = (row.code || '').toString().trim();
|
||||||
|
|
||||||
|
// Skip if missing name or short_name
|
||||||
|
if (!testName || !rowShortName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasAny = true;
|
||||||
|
|
||||||
|
// CI3 LOGIC: if (row->short_name <> short_name) { echo '<br />'; pick new color; counter++ }
|
||||||
|
if (rowShortName !== shortName) {
|
||||||
|
$wrap.append('<br />');
|
||||||
|
btnStyle = btnColor[counter % btnColor.length];
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// btn-xs removed in Bootstrap 5 → use btn-sm
|
||||||
|
var $btn = $('<button type="button" class="btn btn-sm legacy-inv-chip inv-quick-chip" style="margin: 2px;"></button>')
|
||||||
|
.addClass(btnStyle)
|
||||||
|
.attr('data-test', testName)
|
||||||
|
.attr('data-code', testCode)
|
||||||
|
.text(testName);
|
||||||
|
$wrap.append($btn);
|
||||||
|
|
||||||
|
// Update short_name for next iteration
|
||||||
|
shortName = rowShortName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasAny) {
|
||||||
|
$wrap.append('<div class="text-muted small">No legacy investigations found.</div>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initCustomInvestigationModal() {
|
||||||
|
var $modal = $('#customInvestigationModal');
|
||||||
|
var $sel = $('#custom_investigation_select2');
|
||||||
|
if (!$modal.length || !$sel.length || !($.fn && $.fn.select2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sel.data('select2')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sel.select2({
|
||||||
|
width: '100%',
|
||||||
|
dropdownParent: $modal,
|
||||||
|
multiple: true,
|
||||||
|
placeholder: 'Search investigations...',
|
||||||
|
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 },
|
||||||
|
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();
|
||||||
|
return {
|
||||||
|
id: name + (code ? ' [' + code + ']' : ''),
|
||||||
|
text: name + (code ? ' [' + code + ']' : ''),
|
||||||
|
name: name,
|
||||||
|
code: code
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$modal.on('hidden.bs.modal', function() {
|
||||||
|
$sel.val(null).trigger('change');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initInvestigationPicker() {
|
function initInvestigationPicker() {
|
||||||
var $select = $('#investigation_name_select2');
|
var $select = $('#investigation_name_select2');
|
||||||
var $fallback = $('#investigation_name');
|
var $fallback = $('#investigation_name');
|
||||||
|
|
@ -5022,9 +5160,14 @@
|
||||||
|
|
||||||
$(document).on('click', '.inv-quick-chip', function() {
|
$(document).on('click', '.inv-quick-chip', function() {
|
||||||
var test = ($(this).data('test') || '').toString().trim();
|
var test = ($(this).data('test') || '').toString().trim();
|
||||||
|
var code = ($(this).data('code') || '').toString().trim();
|
||||||
if (!test) {
|
if (!test) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (code) {
|
||||||
|
batchAddInvestigationRows([{ name: test, code: code }]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
batchAddInvestigations([test]);
|
batchAddInvestigations([test]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -5095,6 +5238,47 @@
|
||||||
$('.jsError').removeClass('text-success text-muted').addClass('text-danger').text('Invalid profile selection.');
|
$('.jsError').removeClass('text-success text-muted').addClass('text-danger').text('Invalid profile selection.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#btn_toggle_legacy_inv_panel').on('click', function() {
|
||||||
|
var $panel = $('#legacy_inv_shortcuts_panel');
|
||||||
|
if (!$panel.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$panel.stop(true, true).slideToggle(140);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_add_custom_investigations').on('click', function() {
|
||||||
|
var selected = [];
|
||||||
|
if ($.fn && $.fn.select2 && $('#custom_investigation_select2').length) {
|
||||||
|
selected = $('#custom_investigation_select2').select2('data') || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selected.length) {
|
||||||
|
$('.jsError').removeClass('text-success text-muted').addClass('text-danger').text('Select at least one investigation.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows = selected.map(function(item) {
|
||||||
|
return {
|
||||||
|
name: (item.name || item.text || '').toString().trim(),
|
||||||
|
code: (item.code || '').toString().trim()
|
||||||
|
};
|
||||||
|
}).filter(function(item) {
|
||||||
|
return item.name !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!rows.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
batchAddInvestigationRows(rows);
|
||||||
|
|
||||||
|
if (window.bootstrap && window.bootstrap.Modal) {
|
||||||
|
window.bootstrap.Modal.getOrCreateInstance(document.getElementById('customInvestigationModal')).hide();
|
||||||
|
} else {
|
||||||
|
$('#customInvestigationModal').modal('hide');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#advise_investigation_notes').on('input', function() {
|
$('#advise_investigation_notes').on('input', function() {
|
||||||
$('#investigation').val($(this).val() || '');
|
$('#investigation').val($(this).val() || '');
|
||||||
refreshCounters();
|
refreshCounters();
|
||||||
|
|
@ -5103,6 +5287,7 @@
|
||||||
|
|
||||||
refreshCustomProfileSelect();
|
refreshCustomProfileSelect();
|
||||||
loadInvestigationShortcutsFromLegacy();
|
loadInvestigationShortcutsFromLegacy();
|
||||||
|
initCustomInvestigationModal();
|
||||||
initInvestigationPicker();
|
initInvestigationPicker();
|
||||||
loadInvSpecDropdown();
|
loadInvSpecDropdown();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user