Add discharge diagnosis and narrative template masters

Integrate Final Diagnosis with canonical disease master data and add reusable My/Master template CRUD for discharge narrative sections, including summary investigations, course, other advice, and discharge summary.
This commit is contained in:
DevSoftTech Server 2026-08-09 19:50:05 +05:30
parent b39688dade
commit b2926d5a0b
3 changed files with 502 additions and 35 deletions

View File

@ -396,6 +396,7 @@ $routes->match(['GET', 'POST'], 'Ipd_discharge/ipd_select/(:num)/(:num)', 'Ipd_d
$routes->get('Ipd_discharge/section_past_data', 'Ipd_discharge::section_past_data');
$routes->get('Ipd_discharge/section_template_list', 'Ipd_discharge::section_template_list');
$routes->post('Ipd_discharge/section_template_save', 'Ipd_discharge::section_template_save');
$routes->post('Ipd_discharge/section_template_remove', 'Ipd_discharge::section_template_remove');
$routes->get('Ipd_discharge/surgery_master_lookup', 'Ipd_discharge::surgery_master_lookup');
$routes->get('Ipd_discharge/surgery_master_list', 'Ipd_discharge::surgery_master_list');
$routes->post('Ipd_discharge/surgery_master_save', 'Ipd_discharge::surgery_master_save');

View File

@ -1618,7 +1618,7 @@ class Ipd_discharge extends BaseController
private function isNarrativeTemplateSectionAllowed(string $section): bool
{
return in_array($section, ['diagnosis_remark', 'course_remark', 'instruction_other', 'instruction_remark'], true);
return in_array($section, ['diagnosis_remark', 'inhos_remark', 'course_remark', 'instruction_other', 'instruction_remark'], true);
}
private function narrativeSectionTable(string $section): ?string
@ -1724,6 +1724,7 @@ class Ipd_discharge extends BaseController
}
$section = trim((string) $this->request->getPost('section'));
$templateId = max(0, (int) ($this->request->getPost('template_id') ?? 0));
$templateName = trim((string) $this->request->getPost('template_name'));
$templateText = trim((string) $this->request->getPost('template_text'));
$templateScope = strtolower(trim((string) $this->request->getPost('template_scope')));
@ -1742,20 +1743,34 @@ class Ipd_discharge extends BaseController
$docId = $templateScope === 'master' ? 0 : $this->getCurrentUserId();
$table = $this->db->table('ipd_discharge_narrative_templates');
$existing = $table
->where('doc_id', $docId)
->where('section_key', $section)
->where('template_name', $templateName)
->where('is_active', 1)
->get(1)
->getRowArray();
if ($templateId > 0) {
$existing = $table
->where('id', $templateId)
->where('doc_id', $docId)
->where('section_key', $section)
->get(1)
->getRowArray();
if (empty($existing)) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Template not found']);
}
} else {
$existing = $table
->where('doc_id', $docId)
->where('section_key', $section)
->where('template_name', $templateName)
->get(1)
->getRowArray();
}
$now = date('Y-m-d H:i:s');
if (! empty($existing)) {
$table->where('id', (int) ($existing['id'] ?? 0))->update([
'template_name' => $templateName,
'template_text' => $templateText,
'is_active' => 1,
'updated_at' => $now,
]);
$templateId = (int) ($existing['id'] ?? 0);
} else {
$table->insert([
'doc_id' => $docId,
@ -1766,10 +1781,12 @@ class Ipd_discharge extends BaseController
'created_at' => $now,
'updated_at' => $now,
]);
$templateId = (int) $this->db->insertID();
}
return $this->response->setJSON([
'update' => 1,
'id' => $templateId,
'error_text' => $docId === 0 ? 'Master template saved' : 'My template saved',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
@ -1805,6 +1822,46 @@ class Ipd_discharge extends BaseController
return $this->response->setJSON(['rows' => $rows]);
}
public function section_template_remove()
{
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(400)->setJSON(['update' => 0, 'error_text' => 'Invalid request']);
}
$templateId = max(0, (int) ($this->request->getPost('template_id') ?? 0));
$section = trim((string) $this->request->getPost('section'));
if ($templateId <= 0 || ! $this->isNarrativeTemplateSectionAllowed($section) || ! $this->ensureDischargeNarrativeTemplateTable()) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Template not found']);
}
$docId = $this->getCurrentUserId();
$row = $this->db->table('ipd_discharge_narrative_templates')
->select('id')
->where('id', $templateId)
->where('section_key', $section)
->groupStart()
->where('doc_id', $docId)
->orWhere('doc_id', 0)
->groupEnd()
->get(1)
->getRowArray();
if (empty($row)) {
return $this->response->setJSON(['update' => 0, 'error_text' => 'Template not found']);
}
$this->db->table('ipd_discharge_narrative_templates')
->where('id', $templateId)
->update(['is_active' => 0, 'updated_at' => date('Y-m-d H:i:s')]);
return $this->response->setJSON([
'update' => 1,
'error_text' => 'Template deactivated',
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
]);
}
private function byIpdRows(string $table, array $columns = ['*'], string $orderBy = 'id ASC', int $ipdId = 0): array
{
if ($ipdId <= 0 || ! $this->tableHasColumns($table, ['ipd_id'])) {
@ -3812,6 +3869,43 @@ class Ipd_discharge extends BaseController
$rows = [];
if ($q !== '') {
if ($this->db->tableExists('disease_master')) {
$fields = $this->db->getFieldNames('disease_master') ?? [];
$select = ['Code', 'Name'];
foreach (['snomed_concept_id', 'snomed_term'] as $field) {
if (in_array($field, $fields, true)) {
$select[] = $field;
}
}
$builder = $this->db->table('disease_master')
->select(implode(',', $select))
->groupStart()
->like('Name', $q);
if (in_array('snomed_term', $fields, true)) {
$builder->orLike('snomed_term', $q);
}
if (in_array('snomed_concept_id', $fields, true)) {
$builder->orLike('snomed_concept_id', $q);
}
$builder->groupEnd();
if (in_array('is_active', $fields, true)) {
$builder->where('is_active', 1);
}
foreach ($builder->orderBy('Name', 'ASC')->limit(20)->get()->getResultArray() as $row) {
$rows[] = [
'id' => (int) ($row['Code'] ?? 0),
'master_code' => (int) ($row['Code'] ?? 0),
'name' => (string) ($row['Name'] ?? ''),
'icd_code' => '',
'snomed_concept_id' => (string) ($row['snomed_concept_id'] ?? ''),
'snomed_term' => (string) ($row['snomed_term'] ?? ''),
'source' => 'disease_master',
];
}
}
if ($this->ensureDischargeIcdMasterTable()) {
$icdRows = $this->db->table('ipd_discharge_icd_master')
->select('id,icd_code,diagnosis_text')
@ -3828,35 +3922,15 @@ class Ipd_discharge extends BaseController
foreach ($icdRows as $row) {
$rows[] = [
'id' => (int) ($row['id'] ?? 0),
'master_code' => 0,
'name' => (string) ($row['diagnosis_text'] ?? ''),
'icd_code' => (string) ($row['icd_code'] ?? ''),
'snomed_concept_id' => '',
'snomed_term' => '',
'source' => 'icd_master',
];
}
}
if (count($rows) < 25 && $this->db->tableExists('complaints_master')) {
$fallbackRows = $this->db->table('complaints_master')
->select('Code as id, Name as name, name_hinglish')
->groupStart()
->like('Name', $q)
->orLike('name_hinglish', $q)
->groupEnd()
->orderBy('Name', 'ASC')
->limit(25 - count($rows))
->get()
->getResultArray();
foreach ($fallbackRows as $row) {
$rows[] = [
'id' => (int) ($row['id'] ?? 0),
'name' => (string) ($row['name'] ?? ''),
'icd_code' => '',
'source' => 'complaints_master',
'name_hinglish' => (string) ($row['name_hinglish'] ?? ''),
];
}
}
}
return $this->response->setJSON(['rows' => $rows]);
@ -5801,15 +5875,27 @@ class Ipd_discharge extends BaseController
} elseif ($action === 'add_diagnosis') {
$name = trim((string) ($this->request->getPost('new_diagnosis_name') ?? ''));
$remark = trim((string) ($this->request->getPost('new_diagnosis_remark') ?? ''));
$masterCode = max(0, (int) ($this->request->getPost('new_diagnosis_master_code') ?? 0));
$snomedConceptId = trim((string) ($this->request->getPost('new_diagnosis_snomed_concept_id') ?? ''));
$snomedTerm = trim((string) ($this->request->getPost('new_diagnosis_snomed_term') ?? ''));
$diagnosisRemarkText = trim((string) ($this->request->getPost('diagnosis_remark') ?? ''));
if ($name !== '' && $this->tableHasColumns('ipd_discharge_diagnosis', ['ipd_id', 'comp_report'])) {
$insert = [
'ipd_id' => $ipdId,
'comp_code' => 0,
'comp_code' => $masterCode,
'comp_report' => $name,
'comp_remark' => $remark,
'update_by' => $userLabel,
];
if ($this->db->fieldExists('snomed_concept_id', 'ipd_discharge_diagnosis')) {
$insert['snomed_concept_id'] = $snomedConceptId;
}
if ($this->db->fieldExists('snomed_term', 'ipd_discharge_diagnosis')) {
$insert['snomed_term'] = $snomedTerm;
}
if ($this->db->fieldExists('snomed_source', 'ipd_discharge_diagnosis')) {
$insert['snomed_source'] = $snomedConceptId !== '' ? 'disease_master' : '';
}
if ($this->db->fieldExists('order_id', 'ipd_discharge_diagnosis')) {
$insert['order_id'] = 0;
}

View File

@ -1075,7 +1075,10 @@ $historyFields = [
<div class="card border-secondary mt-3" id="section-diagnosis">
<div class="card-header py-2 d-flex justify-content-between align-items-center">
<strong>Final Diagnosis</strong>
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_discharge_seed_icd">Load ICD Starter</button>
<div class="d-flex gap-2">
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_discharge_manage_diagnosis_master">Master CRUD</button>
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_discharge_seed_icd">Load ICD Starter</button>
</div>
</div>
<div class="card-body">
<table class="table table-sm table-bordered">
@ -1102,13 +1105,16 @@ $historyFields = [
</tbody>
</table>
<input type="hidden" name="diagnosis_remove_id" id="diagnosis_remove_id" value="0">
<input type="hidden" name="new_diagnosis_master_code" id="new_diagnosis_master_code" value="0">
<input type="hidden" name="new_diagnosis_snomed_concept_id" id="new_diagnosis_snomed_concept_id" value="">
<input type="hidden" name="new_diagnosis_snomed_term" id="new_diagnosis_snomed_term" value="">
<div class="row g-2">
<div class="col-md-6"><input type="text" class="form-control" name="new_diagnosis_name" id="new_diagnosis_name" list="discharge_diagnosis_suggest" autocomplete="off" placeholder="Diagnosis"></div>
<div class="col-md-5"><input type="text" class="form-control" name="new_diagnosis_remark" placeholder="Remark"></div>
<div class="col-md-1"><button type="submit" class="btn btn-primary btn-sm" name="action" value="add_diagnosis">+ADD</button></div>
</div>
<datalist id="discharge_diagnosis_suggest"></datalist>
<small class="text-muted">Tip: if ICD match exists, selected value appends code in diagnosis text.</small>
<small class="text-muted">Type a diagnosis name, SNOMED term, or ICD code to search master data.</small>
<div id="discharge_diagnosis_status" class="complaint-status text-muted"></div>
<div class="mt-3">
@ -1116,6 +1122,7 @@ $historyFields = [
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-field-past" data-section="diagnosis_remark" data-target="diagnosis_remark">Past Data</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-load" data-section="diagnosis_remark" data-target="diagnosis_remark">Load Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-save" data-section="diagnosis_remark" data-target="diagnosis_remark">Save as Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-master" data-section="diagnosis_remark" data-target="diagnosis_remark" data-title="Final Diagnosis Narrative">Template Master</button>
</label>
<textarea class="form-control" name="diagnosis_remark" id="diagnosis_remark" rows="3"><?= esc((string) ($diagnosis_remark ?? '')) ?></textarea>
</div>
@ -1129,7 +1136,12 @@ $historyFields = [
<div class="card border-secondary mt-3" id="section-summary-invest">
<div class="card-header py-2"><strong>Summary of key investigation during Hospitalization</strong></div>
<div class="card-body">
<textarea class="form-control" name="inhos_remark" rows="4"><?= esc($inhosRemark) ?></textarea>
<div class="mb-2">
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-load" data-section="inhos_remark" data-target="inhos_remark">Load Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-save" data-section="inhos_remark" data-target="inhos_remark">Save as Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-master" data-section="inhos_remark" data-target="inhos_remark" data-title="Summary of Key Investigation during Hospitalization">Template Master</button>
</div>
<textarea class="form-control" name="inhos_remark" id="inhos_remark" rows="4"><?= esc($inhosRemark) ?></textarea>
<div class="mt-3 d-flex justify-content-end">
<button type="submit" class="btn btn-outline-success btn-sm" name="action" value="save_main" data-reload-section="section-summary-invest">Save Summary Investigation</button>
</div>
@ -1185,6 +1197,7 @@ $historyFields = [
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-field-past" data-section="course_remark" data-target="course_remark">Past Data</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-load" data-section="course_remark" data-target="course_remark">Load Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-save" data-section="course_remark" data-target="course_remark">Save as Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-master" data-section="course_remark" data-target="course_remark" data-title="Course / Treatment in Hospital Narrative">Template Master</button>
</label>
<textarea class="form-control" name="course_remark" id="course_remark" rows="3"><?= esc((string) ($course_remark ?? '')) ?></textarea>
</div>
@ -1453,6 +1466,7 @@ $historyFields = [
<label class="form-label">Other Advice
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-load" data-section="instruction_other" data-target="instruction_other">Load Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-save" data-section="instruction_other" data-target="instruction_other">Save as Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-master" data-section="instruction_other" data-target="instruction_other" data-title="Other Advice">Template Master</button>
</label>
<textarea class="form-control" name="instruction_other" id="instruction_other" rows="2" placeholder="Additional custom advice..."><?= esc($instructionOther) ?></textarea>
<div id="discharge_instruction_other_status" class="complaint-status text-muted"></div>
@ -1462,6 +1476,7 @@ $historyFields = [
<label class="form-label">Discharge Summary
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-load" data-section="instruction_remark" data-target="instruction_remark">Load Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-save" data-section="instruction_remark" data-target="instruction_remark">Save as Template</button>
<button type="button" class="btn btn-outline-secondary btn-sm btn-discharge-template-master" data-section="instruction_remark" data-target="instruction_remark" data-title="Discharge Summary">Template Master</button>
</label>
<textarea class="form-control" name="instruction_remark" id="instruction_remark" rows="3"><?= esc((string) ($instruction_remark ?? '')) ?></textarea>
<div id="discharge_instruction_remark_status" class="complaint-status text-muted"></div>
@ -1623,6 +1638,58 @@ $historyFields = [
</div>
</div>
<div class="modal fade" id="ipdNarrativeTemplateMasterModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ipd_narrative_template_master_title">Narrative Template Master</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="table-responsive" style="max-height:240px;overflow:auto;">
<table class="table table-sm table-bordered align-middle mb-2">
<thead>
<tr>
<th style="width:110px;">Scope</th>
<th>Name</th>
<th>Template Text</th>
<th style="width:150px;">Action</th>
</tr>
</thead>
<tbody id="ipd_narrative_template_master_rows">
<tr><td colspan="4" class="text-center text-muted">No templates.</td></tr>
</tbody>
</table>
</div>
<hr>
<input type="hidden" id="ipd_narrative_template_master_id" value="0">
<div class="row g-2">
<div class="col-md-3">
<label class="form-label">Scope</label>
<select class="form-select form-select-sm" id="ipd_narrative_template_master_scope">
<option value="doctor">My Template</option>
<option value="master">Master Template</option>
</select>
</div>
<div class="col-md-9">
<label class="form-label">Template Name</label>
<input type="text" class="form-control form-control-sm" id="ipd_narrative_template_master_name" maxlength="120">
</div>
<div class="col-12">
<label class="form-label">Template Text</label>
<textarea class="form-control form-control-sm" id="ipd_narrative_template_master_text" rows="5"></textarea>
</div>
</div>
<div class="mt-2 d-flex gap-2">
<button type="button" class="btn btn-primary btn-sm" id="btn_ipd_narrative_template_master_save">Save</button>
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_ipd_narrative_template_master_new">New</button>
</div>
<div id="ipd_narrative_template_master_status" class="complaint-status text-muted mt-2"></div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="ipdSurgeryMasterModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@ -1700,6 +1767,58 @@ $historyFields = [
</div>
</div>
<div class="modal fade" id="ipdDiagnosisMasterModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Final Diagnosis Master</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-2 mb-2">
<div class="col-md-9">
<input type="text" class="form-control form-control-sm" id="diagnosis_master_search" placeholder="Search diagnosis or SNOMED term">
</div>
<div class="col-md-3 d-grid">
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_diagnosis_master_refresh">Refresh List</button>
</div>
</div>
<div class="table-responsive" style="max-height:260px;overflow:auto;">
<table class="table table-sm table-bordered align-middle">
<thead><tr><th>Name</th><th>SNOMED ID</th><th>Status</th><th>Action</th></tr></thead>
<tbody id="diagnosis_master_rows"><tr><td colspan="4" class="text-center text-muted">No records.</td></tr></tbody>
</table>
</div>
<hr>
<input type="hidden" id="diagnosis_master_code" value="0">
<div class="row g-2">
<div class="col-md-6">
<label class="form-label">Name</label>
<input type="text" class="form-control form-control-sm" id="diagnosis_master_name">
</div>
<div class="col-md-3">
<label class="form-label">SNOMED ID</label>
<input type="text" class="form-control form-control-sm" id="diagnosis_master_snomed_id">
</div>
<div class="col-md-3">
<label class="form-label">Status</label>
<select class="form-select form-select-sm" id="diagnosis_master_active"><option value="1">Active</option><option value="0">Inactive</option></select>
</div>
<div class="col-12">
<label class="form-label">SNOMED Term</label>
<input type="text" class="form-control form-control-sm" id="diagnosis_master_snomed_term">
</div>
</div>
<div class="mt-2 d-flex gap-2">
<button type="button" class="btn btn-primary btn-sm" id="btn_diagnosis_master_save">Save</button>
<button type="button" class="btn btn-outline-secondary btn-sm" id="btn_diagnosis_master_clear">New</button>
</div>
<div id="diagnosis_master_status" class="complaint-status text-muted mt-2"></div>
</div>
</div>
</div>
</div>
<!-- Quick Add Surgery/Procedure Modal -->
<div class="modal fade" id="quickAddTermModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
@ -1772,6 +1891,12 @@ $historyFields = [
text: '',
target: ''
};
var narrativeTemplateMasterState = {
section: '',
target: '',
title: '',
rows: []
};
var narrativeTemplateToolsBound = false;
var surgeryMasterState = {
surgery: [],
@ -3034,6 +3159,130 @@ $historyFields = [
setNarrativeStatus(section, mode === 'append' ? 'Template appended.' : 'Template loaded.', 'success');
hideModalById('ipdNarrativeTemplateModal');
});
function resetNarrativeTemplateMasterForm() {
$('#ipd_narrative_template_master_id').val('0');
$('#ipd_narrative_template_master_scope').val('doctor').prop('disabled', false);
$('#ipd_narrative_template_master_name').val('');
$('#ipd_narrative_template_master_text').val('');
$('#ipd_narrative_template_master_status').text('').attr('class', 'complaint-status text-muted mt-2');
}
function renderNarrativeTemplateMasterRows(rows) {
narrativeTemplateMasterState.rows = rows || [];
var $body = $('#ipd_narrative_template_master_rows');
$body.empty();
if (!narrativeTemplateMasterState.rows.length) {
$body.html('<tr><td colspan="4" class="text-center text-muted">No templates.</td></tr>');
return;
}
narrativeTemplateMasterState.rows.forEach(function(row, index) {
var scope = parseInt(row.doc_id || '0', 10) === 0 ? 'Master' : 'My';
var scopeClass = scope === 'Master' ? 'bg-primary' : 'bg-secondary';
var $tr = $('<tr>');
$tr.append($('<td>').append($('<span>').addClass('badge ' + scopeClass).text(scope)));
$tr.append($('<td>').text(row.template_name || ''));
$tr.append($('<td>').addClass('text-break').text(row.template_text || ''));
var $actions = $('<td>');
$actions.append($('<button type="button" class="btn btn-outline-primary btn-sm me-1 btn-edit-ipd-narrative-template">Edit</button>').attr('data-index', index));
$actions.append($('<button type="button" class="btn btn-outline-danger btn-sm btn-remove-ipd-narrative-template">Deactivate</button>').attr('data-index', index));
$tr.append($actions);
$body.append($tr);
});
}
function loadNarrativeTemplateMaster() {
var section = (narrativeTemplateMasterState.section || '').toString();
if (!section) {
return;
}
$.get('<?= base_url('Ipd_discharge/section_template_list') ?>?section=' + encodeURIComponent(section), function(data) {
renderNarrativeTemplateMasterRows((data && data.rows) ? data.rows : []);
}, 'json').fail(function() {
$('#ipd_narrative_template_master_status').text('Unable to load templates.').attr('class', 'complaint-status text-danger mt-2');
});
}
$(document)
.off('click.ipdNarrativeMaster', '.btn-discharge-template-master')
.on('click.ipdNarrativeMaster', '.btn-discharge-template-master', function() {
narrativeTemplateMasterState.section = ($(this).data('section') || '').toString();
narrativeTemplateMasterState.target = ($(this).data('target') || '').toString();
narrativeTemplateMasterState.title = ($(this).data('title') || 'Narrative').toString();
$('#ipd_narrative_template_master_title').text(narrativeTemplateMasterState.title + ' Template Master');
resetNarrativeTemplateMasterForm();
loadNarrativeTemplateMaster();
showModalById('ipdNarrativeTemplateMasterModal');
})
.off('click.ipdNarrativeMaster', '#btn_ipd_narrative_template_master_new')
.on('click.ipdNarrativeMaster', '#btn_ipd_narrative_template_master_new', resetNarrativeTemplateMasterForm)
.off('click.ipdNarrativeMaster', '.btn-edit-ipd-narrative-template')
.on('click.ipdNarrativeMaster', '.btn-edit-ipd-narrative-template', function() {
var row = narrativeTemplateMasterState.rows[parseInt($(this).data('index') || '0', 10)] || null;
if (!row) {
return;
}
$('#ipd_narrative_template_master_id').val(row.id || '0');
$('#ipd_narrative_template_master_scope').val(parseInt(row.doc_id || '0', 10) === 0 ? 'master' : 'doctor').prop('disabled', true);
$('#ipd_narrative_template_master_name').val(row.template_name || '');
$('#ipd_narrative_template_master_text').val(row.template_text || '');
})
.off('click.ipdNarrativeMaster', '#btn_ipd_narrative_template_master_save')
.on('click.ipdNarrativeMaster', '#btn_ipd_narrative_template_master_save', function() {
var form = getDischargeForm();
var section = (narrativeTemplateMasterState.section || '').toString();
var name = ($('#ipd_narrative_template_master_name').val() || '').toString().trim();
var text = ($('#ipd_narrative_template_master_text').val() || '').toString().trim();
if (!form || !name || !text) {
$('#ipd_narrative_template_master_status').text('Template name and text are required.').attr('class', 'complaint-status text-danger mt-2');
return;
}
var csrf = getCsrfPair(form);
var payload = {
section: section,
template_id: $('#ipd_narrative_template_master_id').val() || '0',
template_scope: $('#ipd_narrative_template_master_scope').val() || 'doctor',
template_name: name,
template_text: text
};
payload[csrf.name] = csrf.value;
$.post('<?= base_url('Ipd_discharge/section_template_save') ?>', payload, function(data) {
if (data && data.csrfName && data.csrfHash) {
form.querySelector('input[name="' + data.csrfName + '"]').value = data.csrfHash;
}
if (!data || parseInt(data.update || '0', 10) !== 1) {
$('#ipd_narrative_template_master_status').text((data && data.error_text) || 'Unable to save template.').attr('class', 'complaint-status text-danger mt-2');
return;
}
resetNarrativeTemplateMasterForm();
$('#ipd_narrative_template_master_status').text(data.error_text || 'Template saved.').attr('class', 'complaint-status text-success mt-2');
loadNarrativeTemplateMaster();
}, 'json');
})
.off('click.ipdNarrativeMaster', '.btn-remove-ipd-narrative-template')
.on('click.ipdNarrativeMaster', '.btn-remove-ipd-narrative-template', function() {
var row = narrativeTemplateMasterState.rows[parseInt($(this).data('index') || '0', 10)] || null;
var form = getDischargeForm();
if (!row || !form || !window.confirm('Deactivate this template?')) {
return;
}
var csrf = getCsrfPair(form);
var payload = { section: narrativeTemplateMasterState.section || '', template_id: row.id || '0' };
payload[csrf.name] = csrf.value;
$.post('<?= base_url('Ipd_discharge/section_template_remove') ?>', payload, function(data) {
if (data && data.csrfName && data.csrfHash) {
form.querySelector('input[name="' + data.csrfName + '"]').value = data.csrfHash;
}
if (!data || parseInt(data.update || '0', 10) !== 1) {
$('#ipd_narrative_template_master_status').text((data && data.error_text) || 'Unable to deactivate template.').attr('class', 'complaint-status text-danger mt-2');
return;
}
resetNarrativeTemplateMasterForm();
$('#ipd_narrative_template_master_status').text(data.error_text || 'Template deactivated.').attr('class', 'complaint-status text-success mt-2');
loadNarrativeTemplateMaster();
}, 'json');
});
}
function bindSmartTermLookup(form, lookupId, suggestId, addBtnId, targetInputId, statusId, emptyText) {
@ -3048,6 +3297,9 @@ $historyFields = [
var suggestions = [];
lookup.addEventListener('input', function() {
$('#new_diagnosis_master_code').val('0');
$('#new_diagnosis_snomed_concept_id').val('');
$('#new_diagnosis_snomed_term').val('');
var q = (lookup.value || '').trim();
if (q.length < 2 || !window.jQuery) {
return;
@ -3527,6 +3779,9 @@ $historyFields = [
diagnosisText += ' [ICD: ' + code + ']';
}
lookup.value = diagnosisText;
$('#new_diagnosis_master_code').val(String(chosen.master_code || '0'));
$('#new_diagnosis_snomed_concept_id').val(String(chosen.snomed_concept_id || ''));
$('#new_diagnosis_snomed_term').val(String(chosen.snomed_term || ''));
setSectionStatus('discharge_diagnosis_status', code !== '' ? 'Diagnosis with ICD selected. Click +ADD to save row.' : 'Diagnosis selected. Click +ADD to save row.', 'success');
return;
}
@ -3563,6 +3818,130 @@ $historyFields = [
}
}
function initDiagnosisMasterCrud(form) {
if (!window.jQuery || !form || !$('#ipdDiagnosisMasterModal').length) {
return;
}
var diagnosisMasterRows = [];
var $search = $('#diagnosis_master_search');
var $rows = $('#diagnosis_master_rows');
function setStatus(text, level) {
setSectionStatus('diagnosis_master_status', text, level || 'muted');
}
function clearForm() {
$('#diagnosis_master_code').val('0');
$('#diagnosis_master_name').val('');
$('#diagnosis_master_snomed_id').val('');
$('#diagnosis_master_snomed_term').val('');
$('#diagnosis_master_active').val('1');
}
function renderRows() {
if (!diagnosisMasterRows.length) {
$rows.html('<tr><td colspan="4" class="text-center text-muted">No records.</td></tr>');
return;
}
var html = '';
diagnosisMasterRows.forEach(function(row) {
var code = parseInt(row.Code || '0', 10);
var active = typeof row.is_active === 'undefined' || parseInt(row.is_active || '0', 10) === 1;
html += '<tr>'
+ '<td>' + $('<div>').text(row.Name || '').html() + '</td>'
+ '<td>' + $('<div>').text(row.snomed_concept_id || '').html() + '</td>'
+ '<td>' + (active ? 'Active' : 'Inactive') + '</td>'
+ '<td><button type="button" class="btn btn-outline-primary btn-sm btn-diagnosis-master-edit" data-code="' + code + '">Edit</button> '
+ (active ? '<button type="button" class="btn btn-outline-danger btn-sm btn-diagnosis-master-remove" data-code="' + code + '">Deactivate</button>' : '')
+ '</td></tr>';
});
$rows.html(html);
}
function fetchRows() {
$.get('<?= base_url('Opd_prescription/disease_master_data') ?>', {
start: 0,
length: 100,
filter: ($search.val() || '').toString().trim()
}, function(data) {
diagnosisMasterRows = (data && data.data) ? data.data : [];
renderRows();
}, 'json').fail(function() {
setStatus('Unable to load diagnosis master.', 'error');
});
}
$(document)
.off('click.ipdDiagnosisCrud', '#btn_discharge_manage_diagnosis_master')
.on('click.ipdDiagnosisCrud', '#btn_discharge_manage_diagnosis_master', function() {
clearForm();
$search.val('');
setStatus('', 'muted');
fetchRows();
showModalById('ipdDiagnosisMasterModal');
})
.off('click.ipdDiagnosisCrud', '.btn-diagnosis-master-edit')
.on('click.ipdDiagnosisCrud', '.btn-diagnosis-master-edit', function() {
var code = parseInt($(this).data('code') || '0', 10);
var row = diagnosisMasterRows.find(function(item) {
return parseInt(item.Code || '0', 10) === code;
});
if (!row) return;
$('#diagnosis_master_code').val(String(code));
$('#diagnosis_master_name').val(row.Name || '');
$('#diagnosis_master_snomed_id').val(row.snomed_concept_id || '');
$('#diagnosis_master_snomed_term').val(row.snomed_term || '');
$('#diagnosis_master_active').val(typeof row.is_active === 'undefined' || parseInt(row.is_active || '0', 10) === 1 ? '1' : '0');
})
.off('click.ipdDiagnosisCrud', '.btn-diagnosis-master-remove')
.on('click.ipdDiagnosisCrud', '.btn-diagnosis-master-remove', function() {
var code = parseInt($(this).data('code') || '0', 10);
if (code <= 0 || !window.confirm('Deactivate this diagnosis?')) return;
var csrf = getCsrfPair(form);
var payload = {};
payload[csrf.name] = csrf.value;
$.post('<?= base_url('Opd_prescription/disease_master_remove') ?>/' + code, payload, function(data) {
updateFormCsrf(form, data);
setStatus((data && data.error_text) ? data.error_text : 'Diagnosis deactivated.', data && parseInt(data.update || '0', 10) === 1 ? 'success' : 'error');
fetchRows();
}, 'json');
});
$('#btn_diagnosis_master_refresh').off('.ipdDiagnosisCrud').on('click.ipdDiagnosisCrud', fetchRows);
$search.off('.ipdDiagnosisCrud').on('input.ipdDiagnosisCrud', fetchRows);
$('#btn_diagnosis_master_clear').off('.ipdDiagnosisCrud').on('click.ipdDiagnosisCrud', clearForm);
$('#btn_diagnosis_master_save').off('.ipdDiagnosisCrud').on('click.ipdDiagnosisCrud', function() {
var name = ($('#diagnosis_master_name').val() || '').toString().trim();
if (name === '') {
setStatus('Name is required.', 'error');
return;
}
var csrf = getCsrfPair(form);
var payload = {
Code: parseInt($('#diagnosis_master_code').val() || '0', 10),
Name: name,
snomed_concept_id: ($('#diagnosis_master_snomed_id').val() || '').toString().trim(),
snomed_term: ($('#diagnosis_master_snomed_term').val() || '').toString().trim(),
is_active: parseInt($('#diagnosis_master_active').val() || '1', 10)
};
payload[csrf.name] = csrf.value;
$.post('<?= base_url('Opd_prescription/disease_master_save') ?>', payload, function(data) {
updateFormCsrf(form, data);
if (!data || parseInt(data.update || '0', 10) !== 1) {
setStatus((data && data.error_text) ? data.error_text : 'Unable to save diagnosis.', 'error');
return;
}
setStatus(data.error_text || 'Diagnosis saved.', 'success');
clearForm();
fetchRows();
}, 'json').fail(function() {
setStatus('Unable to save diagnosis.', 'error');
});
});
}
function initDiagnosisTools() {
var section = document.getElementById('section-diagnosis');
if (!section || section.dataset.toolsBound === '1') {
@ -3576,6 +3955,7 @@ $historyFields = [
}
bindDiagnosisIcdLookup(form);
initDiagnosisMasterCrud(form);
}
function initCourseTools() {