feat: Clinical Master workspace — Complaints + Diagnosis edit with SNOMED CT search
This commit is contained in:
parent
e33b0809a0
commit
32c1dc20e1
|
|
@ -712,6 +712,17 @@ $routes->get('Opd_prescription/opd_invest_profile_master_get/(:num)', 'Opd_presc
|
|||
$routes->post('Opd_prescription/opd_invest_profile_master_save', 'Opd_prescription::opd_invest_profile_master_save');
|
||||
$routes->post('Opd_prescription/opd_invest_profile_master_remove/(:num)', 'Opd_prescription::opd_invest_profile_master_remove/$1');
|
||||
|
||||
// Clinical Master (Complaints + Diagnosis)
|
||||
$routes->get('Opd_prescription/clinical_master_workspace', 'Opd_prescription::clinical_master_workspace', ['filter' => 'permission:doctor_work.medicine.manage,doctor_work.access']);
|
||||
$routes->get('Opd_prescription/complaints_master_data', 'Opd_prescription::complaints_master_data');
|
||||
$routes->get('Opd_prescription/complaints_master_get/(:num)', 'Opd_prescription::complaints_master_get/$1');
|
||||
$routes->post('Opd_prescription/complaints_master_save', 'Opd_prescription::complaints_master_save');
|
||||
$routes->post('Opd_prescription/complaints_master_remove/(:num)', 'Opd_prescription::complaints_master_remove/$1');
|
||||
$routes->get('Opd_prescription/disease_master_data', 'Opd_prescription::disease_master_data');
|
||||
$routes->get('Opd_prescription/disease_master_get/(:num)', 'Opd_prescription::disease_master_get/$1');
|
||||
$routes->post('Opd_prescription/disease_master_save', 'Opd_prescription::disease_master_save');
|
||||
$routes->post('Opd_prescription/disease_master_remove/(:num)', 'Opd_prescription::disease_master_remove/$1');
|
||||
|
||||
$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_list/(:num)', 'Opd_prescription::medicine_list/$1');
|
||||
|
|
|
|||
|
|
@ -4052,6 +4052,282 @@ class Opd_prescription extends BaseController
|
|||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
// ─── Clinical Master Workspace (Complaints + Diagnosis/Disease) ────────
|
||||
|
||||
public function clinical_master_workspace(): string
|
||||
{
|
||||
return view('billing/opd_clinical_master_workspace');
|
||||
}
|
||||
|
||||
// --- Complaints Master -------------------------------------------------
|
||||
|
||||
public function complaints_master_data(): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->db->tableExists('complaints_master')) {
|
||||
return $this->response->setJSON(['data' => [], 'recordsTotal' => 0, 'recordsFiltered' => 0]);
|
||||
}
|
||||
|
||||
$start = (int) ($this->request->getGet('start') ?? 0);
|
||||
$length = (int) ($this->request->getGet('length') ?? 25);
|
||||
$filter = trim((string) ($this->request->getGet('filter') ?? ''));
|
||||
$snomed = (int) ($this->request->getGet('snomed_only') ?? 0);
|
||||
|
||||
$qb = $this->db->table('complaints_master');
|
||||
|
||||
if ($filter !== '') {
|
||||
$qb->groupStart()
|
||||
->like('Name', $filter)
|
||||
->orLike('name_hinglish', $filter)
|
||||
->orLike('keywords', $filter)
|
||||
->groupEnd();
|
||||
}
|
||||
if ($snomed === 1) {
|
||||
$qb->where('snomed_concept_id IS NOT NULL')->where("snomed_concept_id != ''");
|
||||
}
|
||||
|
||||
$total = (clone $qb)->countAllResults(false);
|
||||
$rows = $qb->select('Code, Name, show_in_short, name_hinglish, ai_hint, is_active, snomed_concept_id, snomed_term')
|
||||
->orderBy('show_in_short', 'DESC')
|
||||
->orderBy('Name', 'ASC')
|
||||
->limit($length, $start)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
return $this->response->setJSON([
|
||||
'recordsTotal' => $total,
|
||||
'recordsFiltered' => $total,
|
||||
'data' => $rows,
|
||||
]);
|
||||
}
|
||||
|
||||
public function complaints_master_get(int $code): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->db->tableExists('complaints_master')) {
|
||||
return $this->response->setStatusCode(404)->setJSON(['error' => 'Table not found']);
|
||||
}
|
||||
$row = $this->db->table('complaints_master')
|
||||
->where('Code', $code)
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
if (! $row) {
|
||||
return $this->response->setStatusCode(404)->setJSON(['error' => 'Not found']);
|
||||
}
|
||||
return $this->response->setJSON(['row' => $row]);
|
||||
}
|
||||
|
||||
public function complaints_master_save(): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON(['update' => 0]);
|
||||
}
|
||||
if (! $this->db->tableExists('complaints_master')) {
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Table not found']);
|
||||
}
|
||||
|
||||
$code = (int) ($this->request->getPost('Code') ?? 0);
|
||||
$name = strtoupper(trim((string) ($this->request->getPost('Name') ?? '')));
|
||||
$nameHinglish = trim((string) ($this->request->getPost('name_hinglish') ?? ''));
|
||||
$keywords = trim((string) ($this->request->getPost('keywords') ?? ''));
|
||||
$aiHint = trim((string) ($this->request->getPost('ai_hint') ?? ''));
|
||||
$showInShort = (int) ($this->request->getPost('show_in_short') ?? 0);
|
||||
$isActive = (int) ($this->request->getPost('is_active') ?? 1);
|
||||
$snomedConceptId = trim((string) ($this->request->getPost('snomed_concept_id') ?? ''));
|
||||
$snomedTerm = trim((string) ($this->request->getPost('snomed_term') ?? ''));
|
||||
|
||||
if ($name === '') {
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Name is required']);
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'Name' => $name,
|
||||
'name_hinglish' => $nameHinglish !== '' ? $nameHinglish : null,
|
||||
'keywords' => $keywords !== '' ? $keywords : null,
|
||||
'ai_hint' => $aiHint !== '' ? $aiHint : null,
|
||||
'show_in_short' => $showInShort ? 1 : 0,
|
||||
'is_active' => $isActive ? 1 : 0,
|
||||
'snomed_concept_id'=> $snomedConceptId !== '' ? $snomedConceptId : null,
|
||||
'snomed_term' => $snomedTerm !== '' ? $snomedTerm : null,
|
||||
'updated_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
if ($code > 0) {
|
||||
$this->db->table('complaints_master')->where('Code', $code)->update($payload);
|
||||
$newCode = $code;
|
||||
$msg = 'Complaint updated';
|
||||
} else {
|
||||
// auto Code
|
||||
$maxCode = (int) ($this->db->table('complaints_master')->selectMax('Code')->get()->getRowArray()['Code'] ?? 0);
|
||||
$newCode = $maxCode + 1;
|
||||
$payload['Code'] = $newCode;
|
||||
$payload['created_at'] = date('Y-m-d H:i:s');
|
||||
$this->db->table('complaints_master')->insert($payload);
|
||||
$msg = 'Complaint added';
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'error_text' => $msg,
|
||||
'code' => $newCode,
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function complaints_master_remove(int $code): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON(['update' => 0]);
|
||||
}
|
||||
if (! $this->db->tableExists('complaints_master')) {
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Table not found']);
|
||||
}
|
||||
|
||||
// Soft-delete: set is_active = 0
|
||||
$this->db->table('complaints_master')
|
||||
->where('Code', $code)
|
||||
->update(['is_active' => 0, 'updated_at' => date('Y-m-d H:i:s')]);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'error_text' => 'Complaint deactivated',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
// --- Disease/Diagnosis Master ------------------------------------------
|
||||
|
||||
public function disease_master_data(): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->db->tableExists('disease_master')) {
|
||||
return $this->response->setJSON(['data' => [], 'recordsTotal' => 0, 'recordsFiltered' => 0]);
|
||||
}
|
||||
|
||||
$start = (int) ($this->request->getGet('start') ?? 0);
|
||||
$length = (int) ($this->request->getGet('length') ?? 25);
|
||||
$filter = trim((string) ($this->request->getGet('filter') ?? ''));
|
||||
$snomed = (int) ($this->request->getGet('snomed_only') ?? 0);
|
||||
|
||||
$fields = $this->db->getFieldNames('disease_master') ?? [];
|
||||
$hasIsActive = in_array('is_active', $fields, true);
|
||||
|
||||
$qb = $this->db->table('disease_master');
|
||||
|
||||
if ($filter !== '') {
|
||||
$qb->groupStart()->like('Name', $filter)->groupEnd();
|
||||
}
|
||||
if ($snomed === 1) {
|
||||
$qb->where('snomed_concept_id IS NOT NULL')->where("snomed_concept_id != ''");
|
||||
}
|
||||
|
||||
$total = (clone $qb)->countAllResults(false);
|
||||
$select = 'Code, Name' . ($hasIsActive ? ', is_active' : '') . ', snomed_concept_id, snomed_term';
|
||||
$rows = $qb->select($select)
|
||||
->orderBy('Name', 'ASC')
|
||||
->limit($length, $start)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
return $this->response->setJSON([
|
||||
'recordsTotal' => $total,
|
||||
'recordsFiltered' => $total,
|
||||
'data' => $rows,
|
||||
]);
|
||||
}
|
||||
|
||||
public function disease_master_get(int $code): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->db->tableExists('disease_master')) {
|
||||
return $this->response->setStatusCode(404)->setJSON(['error' => 'Table not found']);
|
||||
}
|
||||
$row = $this->db->table('disease_master')
|
||||
->where('Code', $code)
|
||||
->get(1)
|
||||
->getRowArray();
|
||||
if (! $row) {
|
||||
return $this->response->setStatusCode(404)->setJSON(['error' => 'Not found']);
|
||||
}
|
||||
return $this->response->setJSON(['row' => $row]);
|
||||
}
|
||||
|
||||
public function disease_master_save(): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON(['update' => 0]);
|
||||
}
|
||||
if (! $this->db->tableExists('disease_master')) {
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Table not found']);
|
||||
}
|
||||
|
||||
$code = (int) ($this->request->getPost('Code') ?? 0);
|
||||
$name = strtoupper(trim((string) ($this->request->getPost('Name') ?? '')));
|
||||
$snomedConceptId = trim((string) ($this->request->getPost('snomed_concept_id') ?? ''));
|
||||
$snomedTerm = trim((string) ($this->request->getPost('snomed_term') ?? ''));
|
||||
$isActive = (int) ($this->request->getPost('is_active') ?? 1);
|
||||
|
||||
if ($name === '') {
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Name is required']);
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('disease_master') ?? [];
|
||||
$hasIsActive = in_array('is_active', $fields, true);
|
||||
|
||||
$payload = [
|
||||
'Name' => $name,
|
||||
'snomed_concept_id' => $snomedConceptId !== '' ? $snomedConceptId : null,
|
||||
'snomed_term' => $snomedTerm !== '' ? $snomedTerm : null,
|
||||
];
|
||||
if ($hasIsActive) {
|
||||
$payload['is_active'] = $isActive ? 1 : 0;
|
||||
}
|
||||
|
||||
if ($code > 0) {
|
||||
$this->db->table('disease_master')->where('Code', $code)->update($payload);
|
||||
$newCode = $code;
|
||||
$msg = 'Diagnosis updated';
|
||||
} else {
|
||||
$maxCode = (int) ($this->db->table('disease_master')->selectMax('Code')->get()->getRowArray()['Code'] ?? 0);
|
||||
$newCode = $maxCode + 1;
|
||||
$payload['Code'] = $newCode;
|
||||
$this->db->table('disease_master')->insert($payload);
|
||||
$msg = 'Diagnosis added';
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'error_text' => $msg,
|
||||
'code' => $newCode,
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function disease_master_remove(int $code): \CodeIgniter\HTTP\ResponseInterface
|
||||
{
|
||||
if (! $this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON(['update' => 0]);
|
||||
}
|
||||
if (! $this->db->tableExists('disease_master')) {
|
||||
return $this->response->setJSON(['update' => 0, 'error_text' => 'Table not found']);
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('disease_master') ?? [];
|
||||
if (in_array('is_active', $fields, true)) {
|
||||
$this->db->table('disease_master')->where('Code', $code)->update(['is_active' => 0]);
|
||||
} else {
|
||||
$this->db->table('disease_master')->where('Code', $code)->delete();
|
||||
}
|
||||
|
||||
return $this->response->setJSON([
|
||||
'update' => 1,
|
||||
'error_text' => 'Diagnosis deactivated',
|
||||
'csrfName' => csrf_token(),
|
||||
'csrfHash' => csrf_hash(),
|
||||
]);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
public function opd_medicince_duplicate_report()
|
||||
{
|
||||
$medicineModel = new OpdMedicineModel($this->db);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace App\Database\Migrations;
|
||||
|
||||
use CodeIgniter\Database\Migration;
|
||||
|
||||
class AddSnomedColumnsToClinicalMasters extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// complaints_master
|
||||
if ($this->db->tableExists('complaints_master')) {
|
||||
$fields = $this->db->getFieldNames('complaints_master') ?? [];
|
||||
|
||||
if (! in_array('snomed_concept_id', $fields, true)) {
|
||||
$this->forge->addColumn('complaints_master', [
|
||||
'snomed_concept_id' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 20,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'after' => 'is_active',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('snomed_term', $fields, true)) {
|
||||
$this->forge->addColumn('complaints_master', [
|
||||
'snomed_term' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'after' => 'snomed_concept_id',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// disease_master
|
||||
if ($this->db->tableExists('disease_master')) {
|
||||
$fields = $this->db->getFieldNames('disease_master') ?? [];
|
||||
|
||||
if (! in_array('snomed_concept_id', $fields, true)) {
|
||||
$this->forge->addColumn('disease_master', [
|
||||
'snomed_concept_id' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 20,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('snomed_term', $fields, true)) {
|
||||
$this->forge->addColumn('disease_master', [
|
||||
'snomed_term' => [
|
||||
'type' => 'VARCHAR',
|
||||
'constraint' => 255,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('is_active', $fields, true)) {
|
||||
$this->forge->addColumn('disease_master', [
|
||||
'is_active' => [
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => 1,
|
||||
'default' => 1,
|
||||
'after' => 'snomed_term',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if ($this->db->tableExists('complaints_master')) {
|
||||
$fields = $this->db->getFieldNames('complaints_master') ?? [];
|
||||
if (in_array('snomed_concept_id', $fields, true)) {
|
||||
$this->forge->dropColumn('complaints_master', 'snomed_concept_id');
|
||||
}
|
||||
if (in_array('snomed_term', $fields, true)) {
|
||||
$this->forge->dropColumn('complaints_master', 'snomed_term');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('disease_master')) {
|
||||
$fields = $this->db->getFieldNames('disease_master') ?? [];
|
||||
if (in_array('snomed_concept_id', $fields, true)) {
|
||||
$this->forge->dropColumn('disease_master', 'snomed_concept_id');
|
||||
}
|
||||
if (in_array('snomed_term', $fields, true)) {
|
||||
$this->forge->dropColumn('disease_master', 'snomed_term');
|
||||
}
|
||||
if (in_array('is_active', $fields, true)) {
|
||||
$this->forge->dropColumn('disease_master', 'is_active');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
661
app/Views/billing/opd_clinical_master_workspace.php
Normal file
661
app/Views/billing/opd_clinical_master_workspace.php
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
<section class="container-fluid py-3">
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<h5 class="mb-0">Clinical Master</h5>
|
||||
<span class="text-muted small">Manage Chief Complaints & Diagnosis options with SNOMED CT coding</span>
|
||||
</div>
|
||||
|
||||
<!-- Tab nav -->
|
||||
<ul class="nav nav-tabs mb-3" id="clinicalMasterTabs">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link active" data-tab="complaints" type="button">
|
||||
<i class="bi bi-chat-left-text me-1"></i> Chief Complaints
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link" data-tab="diagnosis" type="button">
|
||||
<i class="bi bi-clipboard2-pulse me-1"></i> Diagnosis / Disease
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- ══════════════ COMPLAINTS TAB ══════════════ -->
|
||||
<div id="tab_complaints">
|
||||
<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="cm_filter" class="form-control form-control-sm" style="max-width:220px;" placeholder="Search name / keyword…">
|
||||
<div class="form-check ms-2 mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="cm_snomed_only">
|
||||
<label class="form-check-label small" for="cm_snomed_only">SNOMED coded only</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm ms-auto" id="btn_cm_new">
|
||||
<i class="bi bi-plus-lg"></i> New Complaint
|
||||
</button>
|
||||
<span class="badge bg-secondary" id="cm_total_badge">—</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table id="tbl_cm" class="table table-bordered table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th width="160">SNOMED CT</th>
|
||||
<th width="80" class="text-center">Short</th>
|
||||
<th width="70" class="text-center">Active</th>
|
||||
<th width="90" class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="cm_tbody">
|
||||
<tr><td colspan="5" class="text-muted text-center py-3">Loading…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3 px-3 py-2 border-top small">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="cm_prev">‹ Prev</button>
|
||||
<span id="cm_page_info">—</span>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="cm_next">Next ›</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form -->
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><strong id="cm_form_title">New Complaint</strong></div>
|
||||
<div class="card-body">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" id="cm_code" value="0">
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Name <span class="text-danger">*</span></label>
|
||||
<input type="text" id="cm_name" class="form-control form-control-sm"
|
||||
placeholder="e.g. FEVER, HEADACHE" style="text-transform:uppercase;">
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">SNOMED CT <small class="text-muted">(finding / symptom)</small></label>
|
||||
<div class="position-relative">
|
||||
<input type="text" id="cm_snomed_search" class="form-control form-control-sm"
|
||||
placeholder="Type to search SNOMED…" autocomplete="off">
|
||||
<div id="cm_snomed_dd" class="list-group shadow position-absolute w-100"
|
||||
style="z-index:1060;display:none;max-height:210px;overflow-y:auto;"></div>
|
||||
</div>
|
||||
<div id="cm_snomed_selected" class="mt-1 small text-success" style="display:none;"></div>
|
||||
<input type="hidden" id="cm_snomed_concept_id" value="">
|
||||
<input type="hidden" id="cm_snomed_term" value="">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 mt-1" id="btn_cm_snomed_clear"
|
||||
style="display:none;font-size:.75rem;">✕ Clear SNOMED</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Hinglish Name</label>
|
||||
<input type="text" id="cm_name_hinglish" class="form-control form-control-sm" placeholder="e.g. bukhar">
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Keywords <small class="text-muted">(comma separated)</small></label>
|
||||
<input type="text" id="cm_keywords" class="form-control form-control-sm"
|
||||
placeholder="e.g. fever,bukhar,taap">
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">AI Hint</label>
|
||||
<input type="text" id="cm_ai_hint" class="form-control form-control-sm"
|
||||
placeholder="e.g. fever with duration and pattern">
|
||||
</div>
|
||||
|
||||
<div class="mb-2 d-flex gap-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="cm_show_in_short" value="1">
|
||||
<label class="form-check-label small" for="cm_show_in_short">Show in Short List</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="cm_is_active" value="1" checked>
|
||||
<label class="form-check-label small" for="cm_is_active">Active</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="btn_cm_save">Save</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_cm_reset">Reset</button>
|
||||
</div>
|
||||
<div class="small mt-2" id="cm_msg">Ready.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /tab_complaints -->
|
||||
|
||||
<!-- ══════════════ DIAGNOSIS TAB ══════════════ -->
|
||||
<div id="tab_diagnosis" style="display:none;">
|
||||
<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="dm_filter" class="form-control form-control-sm" style="max-width:220px;" placeholder="Search diagnosis name…">
|
||||
<div class="form-check ms-2 mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="dm_snomed_only">
|
||||
<label class="form-check-label small" for="dm_snomed_only">SNOMED coded only</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-sm ms-auto" id="btn_dm_new">
|
||||
<i class="bi bi-plus-lg"></i> New Diagnosis
|
||||
</button>
|
||||
<span class="badge bg-secondary" id="dm_total_badge">—</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table id="tbl_dm" class="table table-bordered table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th width="180">SNOMED CT</th>
|
||||
<th width="70" class="text-center">Active</th>
|
||||
<th width="90" class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dm_tbody">
|
||||
<tr><td colspan="4" class="text-muted text-center py-3">Loading…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3 px-3 py-2 border-top small">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="dm_prev">‹ Prev</button>
|
||||
<span id="dm_page_info">—</span>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="dm_next">Next ›</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form -->
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><strong id="dm_form_title">New Diagnosis</strong></div>
|
||||
<div class="card-body">
|
||||
<input type="hidden" id="dm_code" value="0">
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Name <span class="text-danger">*</span></label>
|
||||
<input type="text" id="dm_name" class="form-control form-control-sm"
|
||||
placeholder="e.g. DIABETES MELLITUS TYPE 2" style="text-transform:uppercase;">
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">SNOMED CT <small class="text-muted">(disorder / clinical finding)</small></label>
|
||||
<div class="position-relative">
|
||||
<input type="text" id="dm_snomed_search" class="form-control form-control-sm"
|
||||
placeholder="Type to search SNOMED…" autocomplete="off">
|
||||
<div id="dm_snomed_dd" class="list-group shadow position-absolute w-100"
|
||||
style="z-index:1060;display:none;max-height:210px;overflow-y:auto;"></div>
|
||||
</div>
|
||||
<div id="dm_snomed_selected" class="mt-1 small text-success" style="display:none;"></div>
|
||||
<input type="hidden" id="dm_snomed_concept_id" value="">
|
||||
<input type="hidden" id="dm_snomed_term" value="">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 mt-1" id="btn_dm_snomed_clear"
|
||||
style="display:none;font-size:.75rem;">✕ Clear SNOMED</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="dm_is_active" value="1" checked>
|
||||
<label class="form-check-label small" for="dm_is_active">Active</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="btn_dm_save">Save</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_dm_reset">Reset</button>
|
||||
</div>
|
||||
<div class="small mt-2" id="dm_msg">Ready.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /tab_diagnosis -->
|
||||
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
/* ── helpers ────────────────────────────────────────────────────── */
|
||||
function esc(t) { return $('<div>').text((t||'').toString()).html(); }
|
||||
|
||||
function getCsrf() {
|
||||
var inp = document.querySelector('input[name="<?= csrf_token() ?>"]');
|
||||
return inp ? { name: inp.getAttribute('name'), value: inp.value }
|
||||
: { name: '<?= csrf_token() ?>', value: '<?= csrf_hash() ?>' };
|
||||
}
|
||||
function updateCsrf(data) {
|
||||
if (!data || !data.csrfName) return;
|
||||
var inp = document.querySelector('input[name="' + data.csrfName + '"]');
|
||||
if (inp) inp.value = data.csrfHash || '';
|
||||
}
|
||||
function apiPost(url, payload, cb) {
|
||||
var csrf = getCsrf();
|
||||
payload = payload || {};
|
||||
payload[csrf.name] = csrf.value;
|
||||
$.post(url, payload, function (d) { updateCsrf(d); cb(d || {}); }, 'json')
|
||||
.fail(function (xhr) { cb({ update: 0, error_text: 'Request failed (' + xhr.status + ')' }); });
|
||||
}
|
||||
function apiGet(url, cb) {
|
||||
$.get(url, function (d) { cb(d || {}); }, 'json')
|
||||
.fail(function (xhr) { cb({ error: 'Request failed (' + xhr.status + ')' }); });
|
||||
}
|
||||
function snomedBadge(conceptId, term) {
|
||||
if (!conceptId) return '<span class="text-muted small">—</span>';
|
||||
var label = esc(term || conceptId);
|
||||
return '<span class="badge bg-info text-dark" title="' + esc(conceptId) + '">' + label + '</span>';
|
||||
}
|
||||
function toast(msg, type) {
|
||||
var cls = type === 'ok' ? 'alert-success' : type === 'err' ? 'alert-danger' : 'alert-info';
|
||||
var id = 'ct_' + Date.now();
|
||||
$('body').append('<div id="' + id + '" class="alert ' + cls + ' shadow-sm alert-dismissible"'
|
||||
+ ' style="position:fixed;top:20px;right:20px;z-index:9999;min-width:260px;max-width:400px;">'
|
||||
+ esc(msg) + '<button type="button" class="btn-close" data-bs-dismiss="alert"></button></div>');
|
||||
setTimeout(function () { $('#' + id).fadeOut(300, function () { $(this).remove(); }); }, 4000);
|
||||
}
|
||||
|
||||
/* ── Tab switching ──────────────────────────────────────────────── */
|
||||
$(document).on('click', '#clinicalMasterTabs .nav-link', function () {
|
||||
$('#clinicalMasterTabs .nav-link').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
var tab = $(this).data('tab');
|
||||
$('#tab_complaints, #tab_diagnosis').hide();
|
||||
$('#tab_' + tab).show();
|
||||
if (tab === 'complaints') { cmLoad(); }
|
||||
if (tab === 'diagnosis') { dmLoad(); }
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════
|
||||
COMPLAINTS MASTER
|
||||
══════════════════════════════════════════════════════════════════ */
|
||||
var cmState = { start: 0, length: 25, total: 0 };
|
||||
var cmFilterTimer = null;
|
||||
|
||||
function cmLoad() {
|
||||
var filter = ($('#cm_filter').val() || '').trim();
|
||||
var snomedOnly = $('#cm_snomed_only').is(':checked') ? 1 : 0;
|
||||
var url = '<?= base_url('Opd_prescription/complaints_master_data') ?>?start=' + cmState.start
|
||||
+ '&length=' + cmState.length + '&filter=' + encodeURIComponent(filter)
|
||||
+ '&snomed_only=' + snomedOnly;
|
||||
|
||||
$('#cm_tbody').html('<tr><td colspan="5" class="text-muted text-center">Loading…</td></tr>');
|
||||
apiGet(url, function (data) {
|
||||
cmState.total = parseInt(data.recordsTotal || 0);
|
||||
$('#cm_total_badge').text(cmState.total);
|
||||
var rows = data.data || [];
|
||||
if (!rows.length) {
|
||||
$('#cm_tbody').html('<tr><td colspan="5" class="text-muted text-center">No records found.</td></tr>');
|
||||
$('#cm_page_info').text('0 records');
|
||||
return;
|
||||
}
|
||||
var html = '';
|
||||
rows.forEach(function (r) {
|
||||
var active = parseInt(r.is_active || 0) === 1;
|
||||
html += '<tr>'
|
||||
+ '<td>' + esc(r.Name || '') + '</td>'
|
||||
+ '<td>' + snomedBadge(r.snomed_concept_id, r.snomed_term) + '</td>'
|
||||
+ '<td class="text-center">' + (parseInt(r.show_in_short || 0) === 1
|
||||
? '<span class="badge bg-primary">Yes</span>'
|
||||
: '<span class="text-muted">—</span>') + '</td>'
|
||||
+ '<td class="text-center">' + (active
|
||||
? '<span class="badge bg-success">Yes</span>'
|
||||
: '<span class="badge bg-secondary">No</span>') + '</td>'
|
||||
+ '<td class="text-center">'
|
||||
+ '<button class="btn btn-outline-primary btn-xs btn-cm-edit px-1 py-0 me-1" data-code="' + esc(r.Code) + '" title="Edit">Edit</button>'
|
||||
+ '<button class="btn btn-outline-danger btn-xs btn-cm-del px-1 py-0" data-code="' + esc(r.Code) + '" title="Deactivate">Del</button>'
|
||||
+ '</td></tr>';
|
||||
});
|
||||
$('#cm_tbody').html(html);
|
||||
|
||||
var from = cmState.start + 1;
|
||||
var to = Math.min(cmState.start + rows.length, cmState.total);
|
||||
$('#cm_page_info').text(from + '–' + to + ' of ' + cmState.total);
|
||||
$('#cm_prev').prop('disabled', cmState.start === 0);
|
||||
$('#cm_next').prop('disabled', cmState.start + cmState.length >= cmState.total);
|
||||
});
|
||||
}
|
||||
|
||||
$('#cm_filter').on('input', function () {
|
||||
clearTimeout(cmFilterTimer);
|
||||
cmFilterTimer = setTimeout(function () { cmState.start = 0; cmLoad(); }, 300);
|
||||
});
|
||||
$('#cm_snomed_only').on('change', function () { cmState.start = 0; cmLoad(); });
|
||||
$('#cm_prev').on('click', function () { if (cmState.start >= cmState.length) { cmState.start -= cmState.length; cmLoad(); } });
|
||||
$('#cm_next').on('click', function () { if (cmState.start + cmState.length < cmState.total) { cmState.start += cmState.length; cmLoad(); } });
|
||||
|
||||
// Edit row
|
||||
$(document).on('click', '.btn-cm-edit', function () {
|
||||
var code = $(this).data('code');
|
||||
apiGet('<?= base_url('Opd_prescription/complaints_master_get') ?>/' + code, function (data) {
|
||||
if (!data.row) { toast('Could not load record', 'err'); return; }
|
||||
cmFillForm(data.row);
|
||||
});
|
||||
});
|
||||
|
||||
// Delete row
|
||||
$(document).on('click', '.btn-cm-del', function () {
|
||||
var code = $(this).data('code');
|
||||
if (!confirm('Deactivate this complaint?')) return;
|
||||
apiPost('<?= base_url('Opd_prescription/complaints_master_remove') ?>/' + code, {}, function (data) {
|
||||
if (parseInt(data.update || 0)) { toast('Complaint deactivated', 'ok'); cmLoad(); }
|
||||
else { toast(data.error_text || 'Failed', 'err'); }
|
||||
});
|
||||
});
|
||||
|
||||
// New button
|
||||
$('#btn_cm_new').on('click', cmClearForm);
|
||||
|
||||
function cmClearForm() {
|
||||
$('#cm_code').val('0');
|
||||
$('#cm_name,#cm_name_hinglish,#cm_keywords,#cm_ai_hint').val('');
|
||||
$('#cm_snomed_search,#cm_snomed_concept_id,#cm_snomed_term').val('');
|
||||
$('#cm_snomed_selected').hide().text('');
|
||||
$('#btn_cm_snomed_clear').hide();
|
||||
$('#cm_show_in_short').prop('checked', false);
|
||||
$('#cm_is_active').prop('checked', true);
|
||||
$('#cm_form_title').text('New Complaint');
|
||||
$('#cm_msg').removeClass('text-success text-danger').addClass('text-muted').text('Ready.');
|
||||
}
|
||||
|
||||
function cmFillForm(r) {
|
||||
$('#cm_code').val(r.Code || 0);
|
||||
$('#cm_name').val((r.Name || '').toUpperCase());
|
||||
$('#cm_name_hinglish').val(r.name_hinglish || '');
|
||||
$('#cm_keywords').val(r.keywords || '');
|
||||
$('#cm_ai_hint').val(r.ai_hint || '');
|
||||
$('#cm_show_in_short').prop('checked', parseInt(r.show_in_short || 0) === 1);
|
||||
$('#cm_is_active').prop('checked', parseInt(r.is_active || 1) === 1);
|
||||
// SNOMED
|
||||
var scid = r.snomed_concept_id || '';
|
||||
var sterm = r.snomed_term || '';
|
||||
$('#cm_snomed_concept_id').val(scid);
|
||||
$('#cm_snomed_term').val(sterm);
|
||||
if (scid) {
|
||||
$('#cm_snomed_search').val(sterm || scid);
|
||||
$('#cm_snomed_selected').show().html('<i class="bi bi-check-circle text-success"></i> ' + esc(sterm || scid) + ' <small class="text-muted">(' + esc(scid) + ')</small>');
|
||||
$('#btn_cm_snomed_clear').show();
|
||||
} else {
|
||||
$('#cm_snomed_search').val('');
|
||||
$('#cm_snomed_selected').hide().text('');
|
||||
$('#btn_cm_snomed_clear').hide();
|
||||
}
|
||||
$('#cm_form_title').text('Edit: ' + esc(r.Name || ''));
|
||||
$('#cm_msg').removeClass('text-success text-danger').addClass('text-muted').text('Loaded.');
|
||||
$('html,body').animate({ scrollTop: $('#cm_form_title').offset().top - 80 }, 200);
|
||||
}
|
||||
|
||||
// Save
|
||||
$('#btn_cm_save').on('click', function () {
|
||||
var name = $('#cm_name').val().trim().toUpperCase();
|
||||
if (!name) { $('#cm_msg').removeClass('text-muted text-success').addClass('text-danger').text('Name is required.'); return; }
|
||||
var payload = {
|
||||
Code: $('#cm_code').val(),
|
||||
Name: name,
|
||||
name_hinglish: $('#cm_name_hinglish').val().trim(),
|
||||
keywords: $('#cm_keywords').val().trim(),
|
||||
ai_hint: $('#cm_ai_hint').val().trim(),
|
||||
show_in_short: $('#cm_show_in_short').is(':checked') ? 1 : 0,
|
||||
is_active: $('#cm_is_active').is(':checked') ? 1 : 0,
|
||||
snomed_concept_id: $('#cm_snomed_concept_id').val().trim(),
|
||||
snomed_term: $('#cm_snomed_term').val().trim(),
|
||||
};
|
||||
$('#btn_cm_save').prop('disabled', true).text('Saving…');
|
||||
apiPost('<?= base_url('Opd_prescription/complaints_master_save') ?>', payload, function (data) {
|
||||
$('#btn_cm_save').prop('disabled', false).text('Save');
|
||||
if (parseInt(data.update || 0)) {
|
||||
$('#cm_msg').removeClass('text-muted text-danger').addClass('text-success').text(data.error_text || 'Saved');
|
||||
toast(data.error_text || 'Saved', 'ok');
|
||||
cmLoad();
|
||||
} else {
|
||||
$('#cm_msg').removeClass('text-muted text-success').addClass('text-danger').text(data.error_text || 'Failed');
|
||||
toast(data.error_text || 'Failed', 'err');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn_cm_reset').on('click', cmClearForm);
|
||||
|
||||
// SNOMED search for Complaints (uses complaints_search → searchFinding)
|
||||
var cmSnomedTimer = null;
|
||||
$('#cm_snomed_search').on('input', function () {
|
||||
var q = $(this).val().trim();
|
||||
if (q.length < 2) { $('#cm_snomed_dd').hide().empty(); return; }
|
||||
clearTimeout(cmSnomedTimer);
|
||||
cmSnomedTimer = setTimeout(function () {
|
||||
$.getJSON('<?= base_url('Opd_prescription/complaints_search') ?>?q=' + encodeURIComponent(q), function (data) {
|
||||
var rows = data.rows || [];
|
||||
var $dd = $('#cm_snomed_dd').empty();
|
||||
if (!rows.length) { $dd.hide(); return; }
|
||||
rows.forEach(function (r) {
|
||||
var cid = r.concept_id || '';
|
||||
var name = r.name || '';
|
||||
var src = r.source || '';
|
||||
var label = esc(name) + (cid ? ' <small class="text-muted">(' + esc(cid) + ')</small>' : '')
|
||||
+ ' <small class="badge bg-' + (src === 'snomed' ? 'info text-dark' : 'secondary') + ' ms-1">' + esc(src) + '</small>';
|
||||
$dd.append('<button type="button" class="list-group-item list-group-item-action py-1 px-2 small"'
|
||||
+ ' data-name="' + esc(name) + '" data-cid="' + esc(cid) + '">' + label + '</button>');
|
||||
});
|
||||
$dd.show();
|
||||
// Position relative to the input
|
||||
var $input = $('#cm_snomed_search');
|
||||
$dd.css({ top: $input.outerHeight(true), left: 0 });
|
||||
});
|
||||
}, 280);
|
||||
});
|
||||
$(document).on('click', '#cm_snomed_dd .list-group-item', function () {
|
||||
var name = $(this).data('name');
|
||||
var cid = $(this).data('cid');
|
||||
$('#cm_snomed_concept_id').val(cid || '');
|
||||
$('#cm_snomed_term').val(name || '');
|
||||
$('#cm_snomed_search').val(cid ? cid + ' — ' + name : name);
|
||||
if (name || cid) {
|
||||
$('#cm_snomed_selected').show().html('<i class="bi bi-check-circle text-success"></i> ' + esc(name) + (cid ? ' <small class="text-muted">(' + esc(cid) + ')</small>' : ''));
|
||||
$('#btn_cm_snomed_clear').show();
|
||||
}
|
||||
$('#cm_snomed_dd').hide().empty();
|
||||
});
|
||||
$(document).on('click', function (e) {
|
||||
if (!$(e.target).closest('#cm_snomed_search, #cm_snomed_dd').length) { $('#cm_snomed_dd').hide(); }
|
||||
});
|
||||
$('#btn_cm_snomed_clear').on('click', function () {
|
||||
$('#cm_snomed_concept_id,#cm_snomed_term,#cm_snomed_search').val('');
|
||||
$('#cm_snomed_selected').hide().text('');
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════
|
||||
DIAGNOSIS / DISEASE MASTER
|
||||
══════════════════════════════════════════════════════════════════ */
|
||||
var dmState = { start: 0, length: 25, total: 0 };
|
||||
var dmFilterTimer = null;
|
||||
|
||||
function dmLoad() {
|
||||
var filter = ($('#dm_filter').val() || '').trim();
|
||||
var snomedOnly = $('#dm_snomed_only').is(':checked') ? 1 : 0;
|
||||
var url = '<?= base_url('Opd_prescription/disease_master_data') ?>?start=' + dmState.start
|
||||
+ '&length=' + dmState.length + '&filter=' + encodeURIComponent(filter)
|
||||
+ '&snomed_only=' + snomedOnly;
|
||||
|
||||
$('#dm_tbody').html('<tr><td colspan="4" class="text-muted text-center">Loading…</td></tr>');
|
||||
apiGet(url, function (data) {
|
||||
dmState.total = parseInt(data.recordsTotal || 0);
|
||||
$('#dm_total_badge').text(dmState.total);
|
||||
var rows = data.data || [];
|
||||
if (!rows.length) {
|
||||
$('#dm_tbody').html('<tr><td colspan="4" class="text-muted text-center">No records found.</td></tr>');
|
||||
$('#dm_page_info').text('0 records');
|
||||
return;
|
||||
}
|
||||
var html = '';
|
||||
rows.forEach(function (r) {
|
||||
var active = (r.is_active === undefined) ? true : parseInt(r.is_active || 0) === 1;
|
||||
html += '<tr>'
|
||||
+ '<td>' + esc(r.Name || '') + '</td>'
|
||||
+ '<td>' + snomedBadge(r.snomed_concept_id, r.snomed_term) + '</td>'
|
||||
+ '<td class="text-center">' + (active
|
||||
? '<span class="badge bg-success">Yes</span>'
|
||||
: '<span class="badge bg-secondary">No</span>') + '</td>'
|
||||
+ '<td class="text-center">'
|
||||
+ '<button class="btn btn-outline-primary btn-xs btn-dm-edit px-1 py-0 me-1" data-code="' + esc(r.Code) + '" title="Edit">Edit</button>'
|
||||
+ '<button class="btn btn-outline-danger btn-xs btn-dm-del px-1 py-0" data-code="' + esc(r.Code) + '" title="Deactivate">Del</button>'
|
||||
+ '</td></tr>';
|
||||
});
|
||||
$('#dm_tbody').html(html);
|
||||
|
||||
var from = dmState.start + 1;
|
||||
var to = Math.min(dmState.start + rows.length, dmState.total);
|
||||
$('#dm_page_info').text(from + '–' + to + ' of ' + dmState.total);
|
||||
$('#dm_prev').prop('disabled', dmState.start === 0);
|
||||
$('#dm_next').prop('disabled', dmState.start + dmState.length >= dmState.total);
|
||||
});
|
||||
}
|
||||
|
||||
$('#dm_filter').on('input', function () {
|
||||
clearTimeout(dmFilterTimer);
|
||||
dmFilterTimer = setTimeout(function () { dmState.start = 0; dmLoad(); }, 300);
|
||||
});
|
||||
$('#dm_snomed_only').on('change', function () { dmState.start = 0; dmLoad(); });
|
||||
$('#dm_prev').on('click', function () { if (dmState.start >= dmState.length) { dmState.start -= dmState.length; dmLoad(); } });
|
||||
$('#dm_next').on('click', function () { if (dmState.start + dmState.length < dmState.total) { dmState.start += dmState.length; dmLoad(); } });
|
||||
|
||||
// Edit row
|
||||
$(document).on('click', '.btn-dm-edit', function () {
|
||||
var code = $(this).data('code');
|
||||
apiGet('<?= base_url('Opd_prescription/disease_master_get') ?>/' + code, function (data) {
|
||||
if (!data.row) { toast('Could not load record', 'err'); return; }
|
||||
dmFillForm(data.row);
|
||||
});
|
||||
});
|
||||
|
||||
// Delete row
|
||||
$(document).on('click', '.btn-dm-del', function () {
|
||||
var code = $(this).data('code');
|
||||
if (!confirm('Deactivate this diagnosis?')) return;
|
||||
apiPost('<?= base_url('Opd_prescription/disease_master_remove') ?>/' + code, {}, function (data) {
|
||||
if (parseInt(data.update || 0)) { toast('Diagnosis deactivated', 'ok'); dmLoad(); }
|
||||
else { toast(data.error_text || 'Failed', 'err'); }
|
||||
});
|
||||
});
|
||||
|
||||
// New button
|
||||
$('#btn_dm_new').on('click', dmClearForm);
|
||||
|
||||
function dmClearForm() {
|
||||
$('#dm_code').val('0');
|
||||
$('#dm_name').val('');
|
||||
$('#dm_snomed_search,#dm_snomed_concept_id,#dm_snomed_term').val('');
|
||||
$('#dm_snomed_selected').hide().text('');
|
||||
$('#btn_dm_snomed_clear').hide();
|
||||
$('#dm_is_active').prop('checked', true);
|
||||
$('#dm_form_title').text('New Diagnosis');
|
||||
$('#dm_msg').removeClass('text-success text-danger').addClass('text-muted').text('Ready.');
|
||||
}
|
||||
|
||||
function dmFillForm(r) {
|
||||
$('#dm_code').val(r.Code || 0);
|
||||
$('#dm_name').val((r.Name || '').toUpperCase());
|
||||
$('#dm_is_active').prop('checked', (r.is_active === undefined) ? true : parseInt(r.is_active || 1) === 1);
|
||||
// SNOMED
|
||||
var scid = r.snomed_concept_id || '';
|
||||
var sterm = r.snomed_term || '';
|
||||
$('#dm_snomed_concept_id').val(scid);
|
||||
$('#dm_snomed_term').val(sterm);
|
||||
if (scid) {
|
||||
$('#dm_snomed_search').val(sterm || scid);
|
||||
$('#dm_snomed_selected').show().html('<i class="bi bi-check-circle text-success"></i> ' + esc(sterm || scid) + ' <small class="text-muted">(' + esc(scid) + ')</small>');
|
||||
$('#btn_dm_snomed_clear').show();
|
||||
} else {
|
||||
$('#dm_snomed_search').val('');
|
||||
$('#dm_snomed_selected').hide().text('');
|
||||
$('#btn_dm_snomed_clear').hide();
|
||||
}
|
||||
$('#dm_form_title').text('Edit: ' + esc(r.Name || ''));
|
||||
$('#dm_msg').removeClass('text-success text-danger').addClass('text-muted').text('Loaded.');
|
||||
$('html,body').animate({ scrollTop: $('#dm_form_title').offset().top - 80 }, 200);
|
||||
}
|
||||
|
||||
// Save
|
||||
$('#btn_dm_save').on('click', function () {
|
||||
var name = $('#dm_name').val().trim().toUpperCase();
|
||||
if (!name) { $('#dm_msg').removeClass('text-muted text-success').addClass('text-danger').text('Name is required.'); return; }
|
||||
var payload = {
|
||||
Code: $('#dm_code').val(),
|
||||
Name: name,
|
||||
snomed_concept_id: $('#dm_snomed_concept_id').val().trim(),
|
||||
snomed_term: $('#dm_snomed_term').val().trim(),
|
||||
is_active: $('#dm_is_active').is(':checked') ? 1 : 0,
|
||||
};
|
||||
$('#btn_dm_save').prop('disabled', true).text('Saving…');
|
||||
apiPost('<?= base_url('Opd_prescription/disease_master_save') ?>', payload, function (data) {
|
||||
$('#btn_dm_save').prop('disabled', false).text('Save');
|
||||
if (parseInt(data.update || 0)) {
|
||||
$('#dm_msg').removeClass('text-muted text-danger').addClass('text-success').text(data.error_text || 'Saved');
|
||||
toast(data.error_text || 'Saved', 'ok');
|
||||
dmLoad();
|
||||
} else {
|
||||
$('#dm_msg').removeClass('text-muted text-success').addClass('text-danger').text(data.error_text || 'Failed');
|
||||
toast(data.error_text || 'Failed', 'err');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn_dm_reset').on('click', dmClearForm);
|
||||
|
||||
// SNOMED search for Diagnosis (uses provisional_diagnosis_search → searchDiagnosis)
|
||||
var dmSnomedTimer = null;
|
||||
$('#dm_snomed_search').on('input', function () {
|
||||
var q = $(this).val().trim();
|
||||
if (q.length < 2) { $('#dm_snomed_dd').hide().empty(); return; }
|
||||
clearTimeout(dmSnomedTimer);
|
||||
dmSnomedTimer = setTimeout(function () {
|
||||
$.getJSON('<?= base_url('Opd_prescription/provisional_diagnosis_search') ?>?q=' + encodeURIComponent(q), function (data) {
|
||||
var rows = data.rows || [];
|
||||
var $dd = $('#dm_snomed_dd').empty();
|
||||
if (!rows.length) { $dd.hide(); return; }
|
||||
rows.forEach(function (r) {
|
||||
var cid = r.snomed_concept_id || '';
|
||||
var name = r.name || '';
|
||||
var src = r.source || '';
|
||||
var label = esc(name) + (cid ? ' <small class="text-muted">(' + esc(cid) + ')</small>' : '')
|
||||
+ ' <small class="badge bg-' + (src === 'csnotk' || src === 'snomed' ? 'info text-dark' : 'secondary') + ' ms-1">' + esc(src) + '</small>';
|
||||
$dd.append('<button type="button" class="list-group-item list-group-item-action py-1 px-2 small"'
|
||||
+ ' data-name="' + esc(name) + '" data-cid="' + esc(cid) + '">' + label + '</button>');
|
||||
});
|
||||
$dd.show();
|
||||
});
|
||||
}, 280);
|
||||
});
|
||||
$(document).on('click', '#dm_snomed_dd .list-group-item', function () {
|
||||
var name = $(this).data('name');
|
||||
var cid = $(this).data('cid');
|
||||
$('#dm_snomed_concept_id').val(cid || '');
|
||||
$('#dm_snomed_term').val(name || '');
|
||||
$('#dm_snomed_search').val(cid ? cid + ' — ' + name : name);
|
||||
if (name || cid) {
|
||||
$('#dm_snomed_selected').show().html('<i class="bi bi-check-circle text-success"></i> ' + esc(name) + (cid ? ' <small class="text-muted">(' + esc(cid) + ')</small>' : ''));
|
||||
$('#btn_dm_snomed_clear').show();
|
||||
}
|
||||
$('#dm_snomed_dd').hide().empty();
|
||||
});
|
||||
$(document).on('click', function (e) {
|
||||
if (!$(e.target).closest('#dm_snomed_search, #dm_snomed_dd').length) { $('#dm_snomed_dd').hide(); }
|
||||
});
|
||||
$('#btn_dm_snomed_clear').on('click', function () {
|
||||
$('#dm_snomed_concept_id,#dm_snomed_term,#dm_snomed_search').val('');
|
||||
$('#dm_snomed_selected').hide().text('');
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
/* ── Init ───────────────────────────────────────────────────────── */
|
||||
cmLoad();
|
||||
|
||||
}());
|
||||
</script>
|
||||
|
|
@ -342,6 +342,12 @@ if (! $canChargesSettings && $user && method_exists($user, 'inGroup')) {
|
|||
<span>Investigation Master</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Opd_prescription/clinical_master_workspace') ?>','Clinical Master')">
|
||||
<i class="bi bi-heart-pulse"></i>
|
||||
<span>Clinical Master</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link collapsed" href="javascript:load_form('<?= base_url('Opd_prescription/opd_invest_profile_master') ?>','Investigation Profile Master')">
|
||||
<i class="bi bi-collection"></i>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user