From 78060d03c804aa14645ac27eef0f4a3feb64bd27 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 20 May 2026 03:02:03 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20FHIR=20form=20view=20=E2=80=94=20inline?= =?UTF-8?q?=20diagnosis=20SNOMED=20edit=20+=20show=20doctor's=20original?= =?UTF-8?q?=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Config/Routes.php | 1 + app/Controllers/Opd_prescription.php | 69 +++++++++++ app/Views/abdm/task_board.php | 165 ++++++++++++++++++++++++--- 3 files changed, 218 insertions(+), 17 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index e5fbb36..a449ef3 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -591,6 +591,7 @@ $routes->get('Opd_prescription/fhir_bundle_history/(:num)/(:num)', 'Opd_prescrip $routes->get('Opd_prescription/fhir_bundle_preview/(:num)', 'Opd_prescription::fhir_bundle_preview/$1'); $routes->get('Opd_prescription/fhir_bundle_preview/(:num)/(:num)', 'Opd_prescription::fhir_bundle_preview/$1/$2'); $routes->post('Opd_prescription/fhir_complaint_recode', 'Opd_prescription::fhir_complaint_recode'); +$routes->post('Opd_prescription/fhir_diagnosis_recode', 'Opd_prescription::fhir_diagnosis_recode'); $routes->get('Opd_prescription/complaints_search', 'Opd_prescription::complaints_search'); $routes->get('Opd_prescription/complaints_extract', 'Opd_prescription::complaints_extract'); $routes->get('Opd_prescription/section_recent_entries', 'Opd_prescription::section_recent_entries'); diff --git a/app/Controllers/Opd_prescription.php b/app/Controllers/Opd_prescription.php index 529f956..37ce65a 100644 --- a/app/Controllers/Opd_prescription.php +++ b/app/Controllers/Opd_prescription.php @@ -4728,6 +4728,75 @@ class Opd_prescription extends BaseController ]); } + public function fhir_diagnosis_recode() + { + if (! $this->request->isAJAX()) { + return $this->response->setStatusCode(403)->setJSON(['error_text' => 'Forbidden']); + } + $opdId = (int) $this->request->getPost('opd_id'); + $sessionId = (int) $this->request->getPost('opd_session_id'); + $diagType = trim((string) $this->request->getPost('diagnosis_type')); // confirmed|provisional + $diagText = trim((string) $this->request->getPost('diagnosis_text')); + $snomedId = trim((string) $this->request->getPost('snomed_concept_id')); + $snomedTerm = trim((string) $this->request->getPost('snomed_term')); + + if ($opdId <= 0 || $diagText === '') { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Invalid parameters']); + } + if (! $this->db->tableExists('opd_prescription')) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Table missing']); + } + + $row = []; + if ($sessionId > 0) { + $row = $this->db->table('opd_prescription') + ->where('opd_session_id', $sessionId)->where('opd_id', $opdId) + ->get(1)->getRowArray() ?? []; + } + if (empty($row)) { + $row = $this->db->table('opd_prescription') + ->where('opd_id', $opdId)->orderBy('id', 'DESC') + ->get(1)->getRowArray() ?? []; + } + if (empty($row)) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'Prescription not found']); + } + + $prescId = (int) $row['id']; + $fields = $this->db->getFieldNames('opd_prescription') ?? []; + + if ($diagType === 'provisional') { + $idCol = 'provisional_diagnosis_snomed_id'; + $termCol = 'provisional_diagnosis_snomed_term'; + $srcCol = 'provisional_diagnosis_snomed_source'; + } else { + $idCol = 'diagnosis_snomed_id'; + $termCol = 'diagnosis_snomed_term'; + $srcCol = 'diagnosis_snomed_source'; + } + + $update = []; + if (in_array($idCol, $fields, true)) $update[$idCol] = $snomedId; + if (in_array($termCol, $fields, true)) $update[$termCol] = $snomedTerm !== '' ? $snomedTerm : $diagText; + if (in_array($srcCol, $fields, true)) $update[$srcCol] = $snomedId !== '' ? 'snomed' : 'local'; + + if (empty($update)) { + return $this->response->setJSON(['update' => 0, 'error_text' => 'No columns to update']); + } + + $this->db->table('opd_prescription')->where('id', $prescId)->update($update); + + return $this->response->setJSON([ + 'update' => 1, + 'error_text' => 'Saved', + 'presc_id' => $prescId, + 'snomed_concept_id'=> $snomedId, + 'snomed_term' => $snomedTerm, + 'csrfName' => csrf_token(), + 'csrfHash' => csrf_hash(), + ]); + } + public function advice_search() { $q = trim((string) $this->request->getGet('q')); diff --git a/app/Views/abdm/task_board.php b/app/Views/abdm/task_board.php index 49af2db..bb9f32e 100644 --- a/app/Views/abdm/task_board.php +++ b/app/Views/abdm/task_board.php @@ -999,16 +999,40 @@ var saveBtn = e.target.closest('.fhir-complaint-save'); var ddItem = e.target.closest('.fhir-snomed-dd-item'); + var diagEditBtn = e.target.closest('.fhir-diag-edit'); + var diagCancelBtn = e.target.closest('.fhir-diag-cancel'); + var diagSaveBtn = e.target.closest('.fhir-diag-save'); + var diagDdItem = e.target.closest('.fhir-diag-dd-item'); + if (editBtn) { var panel = fhirFormView.querySelector('.fhir-edit-panel[data-idx="' + editBtn.dataset.idx + '"]'); if (!panel) return; // Close others - fhirFormView.querySelectorAll('.fhir-edit-panel').forEach(function(p) { if (p !== panel) p.style.display = 'none'; }); + fhirFormView.querySelectorAll('.fhir-edit-panel,.fhir-diag-panel').forEach(function(p) { if (p !== panel) p.style.display = 'none'; }); panel.style.display = panel.style.display === 'none' ? '' : 'none'; } + if (diagEditBtn) { + var dPanel = fhirFormView.querySelector('.fhir-diag-panel[data-didx="' + diagEditBtn.dataset.didx + '"]'); + if (!dPanel) return; + fhirFormView.querySelectorAll('.fhir-edit-panel,.fhir-diag-panel').forEach(function(p) { if (p !== dPanel) p.style.display = 'none'; }); + dPanel.style.display = dPanel.style.display === 'none' ? '' : 'none'; + } if (cancelBtn) { cancelBtn.closest('.fhir-edit-panel').style.display = 'none'; } + if (diagCancelBtn) { + diagCancelBtn.closest('.fhir-diag-panel').style.display = 'none'; + } + if (diagDdItem) { + var dPanel2 = diagDdItem.closest('.fhir-diag-panel'); + dPanel2.querySelector('.fhir-diag-id').value = diagDdItem.dataset.id || ''; + dPanel2.querySelector('.fhir-diag-term').value = diagDdItem.dataset.term || ''; + dPanel2.querySelector('.fhir-diag-search').value = diagDdItem.dataset.term || ''; + dPanel2.querySelector('.fhir-diag-dd').style.display = 'none'; + var dSel = dPanel2.querySelector('.fhir-diag-selected'); + dSel.textContent = '\u2713 ' + (diagDdItem.dataset.id || '') + ' \u2014 ' + (diagDdItem.dataset.term || ''); + dSel.style.display = ''; + } if (ddItem) { var panel2 = ddItem.closest('.fhir-edit-panel'); panel2.querySelector('.fhir-snomed-id').value = ddItem.dataset.id || ''; @@ -1065,7 +1089,6 @@ } } panel3.style.display = 'none'; - // Refresh quality badges } else { alert(res.error_text || 'Save failed'); } @@ -1073,9 +1096,91 @@ .catch(function(e){ alert('Error: ' + e.message); }) .finally(function(){ saveBtn.disabled = false; saveBtn.textContent = 'Save'; }); } + if (diagSaveBtn) { + var dPanel3 = diagSaveBtn.closest('.fhir-diag-panel'); + var dId = dPanel3.querySelector('.fhir-diag-id').value.trim(); + var dTerm = dPanel3.querySelector('.fhir-diag-term').value.trim(); + var dText = dPanel3.dataset.diagText || ''; + var dType = dPanel3.dataset.diagType || 'provisional'; + if (!dId && !dTerm) { dTerm = dPanel3.querySelector('.fhir-diag-search').value.trim(); } + var dBody = new URLSearchParams({ + opd_id: _fhirOpdId, + opd_session_id: _fhirSessionId, + diagnosis_type: dType, + diagnosis_text: dText, + snomed_concept_id: dId, + snomed_term: dTerm + }); + dBody.append(csrfName, csrfHash); + diagSaveBtn.disabled = true; + diagSaveBtn.textContent = 'Saving\u2026'; + fetch('', { + method: 'POST', + headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded' }, + body: dBody.toString() + }) + .then(function(r){ return r.json(); }) + .then(function(res){ + if (res.update == 1) { + if (res.csrfName && res.csrfHash) { csrfName = res.csrfName; csrfHash = res.csrfHash; } + var dLi = dPanel3.closest('li'); + if (dLi && dId) { + var dExisting = dLi.querySelector('.fhir-diag-snomed-badge'); + if (dExisting) { dExisting.textContent = 'SNOMED\u00a0' + dId; } + else { + var dBadge = document.createElement('span'); + dBadge.className = 'badge bg-success ms-1 fhir-diag-snomed-badge'; + dBadge.style.fontSize = '.7rem'; + dBadge.textContent = 'SNOMED\u00a0' + dId; + var dStrong = dLi.querySelector('strong'); + if (dStrong) dStrong.insertAdjacentElement('afterend', dBadge); + } + } + dPanel3.style.display = 'none'; + } else { + alert(res.error_text || 'Save failed'); + } + }) + .catch(function(e){ alert('Error: ' + e.message); }) + .finally(function(){ diagSaveBtn.disabled = false; diagSaveBtn.textContent = 'Save'; }); + } }); fhirFormView.addEventListener('input', function(e) { var searchInput = e.target.closest('.fhir-snomed-search'); + var diagSearch = e.target.closest('.fhir-diag-search'); + if (!searchInput && !diagSearch) return; + if (diagSearch) { + var dPanel4 = diagSearch.closest('.fhir-diag-panel'); + var dDd = dPanel4.querySelector('.fhir-diag-dd'); + var dQ = diagSearch.value.trim(); + if (dQ.length < 2) { dDd.style.display = 'none'; return; } + clearTimeout(_editTimer); + _editTimer = setTimeout(function() { + fetch('?q=' + encodeURIComponent(dQ), { + headers: { 'X-Requested-With': 'XMLHttpRequest' } + }) + .then(function(r){ return r.json(); }) + .then(function(res){ + dDd.innerHTML = ''; + var rows = (res.rows || res.results || []); + if (!rows.length) { dDd.style.display = 'none'; return; } + rows.slice(0, 10).forEach(function(row) { + var id = row.concept_id || row.snomed_concept_id || ''; + var name = row.name || row.term || row.snomed_term || ''; + var btn = document.createElement('button'); + btn.type = 'button'; + btn.className = 'list-group-item list-group-item-action py-1 px-2 fhir-diag-dd-item'; + btn.dataset.id = id; + btn.dataset.term = name; + btn.innerHTML = '' + hesc(name) + (id ? ' ' + hesc(id) + '' : '') + ''; + dDd.appendChild(btn); + }); + dDd.style.display = ''; + }) + .catch(function(){ dDd.style.display = 'none'; }); + }, 280); + return; + } if (!searchInput) return; var panel = searchInput.closest('.fhir-edit-panel'); var dd = panel.querySelector('.fhir-snomed-dd'); @@ -1108,11 +1213,12 @@ }, 280); }); fhirFormView.addEventListener('focusout', function(e) { - if (!e.target.closest('.fhir-edit-panel')) return; - // Delay hiding so click on dd item registers first + var inPanel = e.target.closest('.fhir-edit-panel'); + var inDPanel = e.target.closest('.fhir-diag-panel'); + if (!inPanel && !inDPanel) return; setTimeout(function() { - var dd = e.target.closest('.fhir-edit-panel'); - if (dd) { var d = dd.querySelector('.fhir-snomed-dd'); if (d) d.style.display = 'none'; } + if (inPanel) { var d = inPanel.querySelector('.fhir-snomed-dd'); if (d) d.style.display = 'none'; } + if (inDPanel) { var dd2 = inDPanel.querySelector('.fhir-diag-dd'); if (dd2) dd2.style.display = 'none'; } }, 200); }); @@ -1185,23 +1291,24 @@ html += '
  • '; html += '
    '; html += '
    '; - html += '' + hesc(display || codeText) + ''; + html += '' + hesc(codeText) + ''; if (snomed) html += ' SNOMED ' + hesc(snomed) + ''; if (sevText) html += ' ' + hesc(sevText) + ''; if (note) html += ' (' + hesc(note) + ')'; + if (display && display !== codeText) html += ' SNOMED term: ' + hesc(display) + ''; html += '
    '; html += ''; html += '
    '; // inline edit panel - html += '