Improve radiology template workflow and editor stability

This commit is contained in:
DevSoftTech Server 2026-05-30 18:01:20 +05:30
parent 78c23af7e5
commit 7253fa0cca
8 changed files with 766 additions and 143 deletions

View File

@ -828,6 +828,7 @@ $routes->post('Lab_Admin/report_insert', 'Setting\Template::report_insert');
$routes->post('Lab_Admin/report_delete/(:num)', 'Setting\Template::report_delete/$1');
$routes->post('Lab_Admin/report_ultrasound_insert/(:num)', 'Setting\Template::report_ultrasound_insert/$1');
$routes->post('Lab_Admin/report_ultrasound_update/(:num)', 'Setting\Template::report_ultrasound_update/$1');
$routes->post('Lab_Admin/report_ultrasound_delete/(:num)/(:num)', 'Setting\Template::report_ultrasound_delete/$1/$2');
$routes->post('Lab_Admin/add_test_repo/(:num)/(:num)', 'Setting\Template::add_test_repo/$1/$2');
$routes->post('Lab_Admin/remove_test_item/(:num)/(:num)', 'Setting\Template::remove_test_item/$1/$2');
$routes->post('Lab_Admin/test_item_search', 'Setting\Template::test_item_search');

View File

@ -1052,7 +1052,7 @@ class Diagnosis extends BaseController
->getRow();
$templateBuilder = $this->db->table('radiology_ultrasound_template')
->select('id, template_name')
->select('id, template_name, title, keywords, impression_cat')
->where('Modality', (int) ($reportRow->lab_type ?? 0));
if (! empty($invoiceItem->item_id)) {
@ -1132,7 +1132,7 @@ class Diagnosis extends BaseController
->getRow();
$templateBuilder = $this->db->table('radiology_ultrasound_template')
->select('id, template_name')
->select('id, template_name, title, keywords, impression_cat')
->where('Modality', (int) ($reportRow->lab_type ?? 0));
if (! empty($invoiceItem->item_id)) {
@ -1165,12 +1165,16 @@ class Diagnosis extends BaseController
}
$row = $this->db->table('radiology_ultrasound_template')
->select('Findings, Impression')
->select('template_name, title, keywords, impression_cat, Findings, Impression')
->where('id', $templateId)
->get()
->getRow();
return $this->response->setJSON([
'template_name' => (string) ($row->template_name ?? ''),
'title' => (string) ($row->title ?? ''),
'keywords' => (string) ($row->keywords ?? ''),
'impression_cat' => (string) ($row->impression_cat ?? ''),
'Findings' => (string) ($row->Findings ?? ''),
'Impression' => (string) ($row->Impression ?? ''),
]);

View File

@ -144,6 +144,28 @@ HTML;
return $this->requirePermission($permission);
}
private function buildRadiologyTemplatePayload(int $modality): array
{
$payload = [
'template_name' => trim((string) $this->request->getPost('input_Reportname')),
'title' => trim((string) $this->request->getPost('group_id')),
'charge_id' => (int) $this->request->getPost('charge_id'),
'Findings' => (string) $this->request->getPost('HTMLData'),
'Impression' => (string) $this->request->getPost('Impression'),
'modality' => $modality,
];
if ($this->db->fieldExists('keywords', 'radiology_ultrasound_template')) {
$payload['keywords'] = trim((string) $this->request->getPost('keywords'));
}
if ($this->db->fieldExists('impression_cat', 'radiology_ultrasound_template')) {
$payload['impression_cat'] = trim((string) $this->request->getPost('impression_cat'));
}
return $payload;
}
private function resolveBridgeGatewayConfig(): array
{
$gwUrl = '';
@ -737,10 +759,6 @@ HTML;
}
$inputReportName = trim((string) $this->request->getPost('input_Reportname'));
$chargeId = (int) $this->request->getPost('charge_id');
$groupId = (string) $this->request->getPost('group_id');
$htmlData = (string) $this->request->getPost('HTMLData');
$impression = (string) $this->request->getPost('Impression');
if ($inputReportName === '') {
return $this->response->setJSON([
@ -750,14 +768,7 @@ HTML;
}
$pathLab = new PathLabModel();
$insertId = $pathLab->insertUltrasoundReport([
'template_name' => $inputReportName,
'title' => $groupId,
'charge_id' => $chargeId,
'Findings' => $htmlData,
'Impression' => $impression,
'modality' => $modality,
]);
$insertId = $pathLab->insertUltrasoundReport($this->buildRadiologyTemplatePayload($modality));
return $this->response->setJSON([
'insertid' => $insertId,
@ -780,11 +791,6 @@ HTML;
$repoId = (int) $this->request->getPost('repo_id');
$inputReportName = trim((string) $this->request->getPost('input_Reportname'));
$chargeId = (int) $this->request->getPost('charge_id');
$groupId = (string) $this->request->getPost('group_id');
$htmlData = (string) $this->request->getPost('HTMLData');
$impression = (string) $this->request->getPost('Impression');
if ($repoId <= 0 || $inputReportName === '') {
return $this->response->setJSON([
'update_record' => 0,
@ -793,14 +799,7 @@ HTML;
}
$pathLab = new PathLabModel();
$pathLab->updateUltrasoundReport([
'template_name' => $inputReportName,
'title' => $groupId,
'charge_id' => $chargeId,
'Findings' => $htmlData,
'Impression' => $impression,
'modality' => $modality,
], $repoId);
$pathLab->updateUltrasoundReport($this->buildRadiologyTemplatePayload($modality), $repoId);
return $this->response->setJSON([
'update_record' => 1,
@ -808,6 +807,52 @@ HTML;
]);
}
public function report_ultrasound_delete(int $modality = 2, int $repoId = 0)
{
if ($resp = $this->requireModalityPermission($modality)) {
return $resp;
}
if (! $this->request->isAJAX()) {
return $this->response->setStatusCode(400)->setJSON([
'ok' => 0,
'error' => 'Invalid request',
]);
}
if ($repoId <= 0) {
return $this->response->setStatusCode(400)->setJSON([
'ok' => 0,
'error' => 'Invalid template ID',
]);
}
$builder = $this->db->table('radiology_ultrasound_template')
->where('id', $repoId)
->where('Modality', $modality);
$exists = $builder->countAllResults(false);
if ($exists <= 0) {
return $this->response->setStatusCode(404)->setJSON([
'ok' => 0,
'error' => 'Template not found for selected modality',
]);
}
$deleted = $builder->delete();
if (! $deleted) {
return $this->response->setStatusCode(500)->setJSON([
'ok' => 0,
'error' => 'Failed to delete template',
]);
}
return $this->response->setJSON([
'ok' => 1,
'message' => 'Template deleted successfully',
]);
}
public function test_parameter_edit()
{
if ($resp = $this->requirePermission('template.pathology')) {

View File

@ -1,27 +1,66 @@
<?= form_open() ?>
<style>
.radiology-edit-shell {
background: #ffffff;
border: 1px solid #dbe4ef;
border-radius: 10px;
box-shadow: 0 8px 20px rgba(0, 39, 91, 0.05);
}
.radiology-edit-shell .card-header h3 {
color: #2d3f56;
}
.radiology-meta-badge {
display: inline-block;
padding: 6px 10px;
border-radius: 4px;
background: #2f72b1;
color: #fff;
font-size: 12px;
font-weight: 600;
margin-bottom: 10px;
}
.radiology-edit-shell .form-group label {
font-weight: 600;
color: #2d3f56;
font-size: 12px;
letter-spacing: 0.2px;
text-transform: uppercase;
}
.radiology-editor-card .card-header h4 {
font-size: 15px;
font-weight: 700;
margin: 0;
color: #2d3f56;
}
</style>
<div class="card admin-card">
<div class="card-header bg-white">
<h3 class="mb-0">Radiology Report Edit</h3>
<h3 class="mb-0">Radiology Template Edit</h3>
</div>
<div class="card-body">
<div class="card-body radiology-edit-shell">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Report Name</label>
<label>Template Name</label>
<?php
$repo_name = '';
$repo_id = '0';
$HTMLData = '';
$repo_title = '';
$Impression = '';
$keywords = '';
$impressionCat = '';
if (count($labReport_master ?? []) > 0) {
$repo_id = $labReport_master[0]->id ?? '0';
$repo_name = $labReport_master[0]->template_name ?? '';
$repo_title = $labReport_master[0]->title ?? '';
$HTMLData = $labReport_master[0]->Findings ?? '';
$Impression = $labReport_master[0]->Impression ?? '';
$keywords = $labReport_master[0]->keywords ?? '';
$impressionCat = $labReport_master[0]->impression_cat ?? '';
}
?>
<div class="radiology-meta-badge">Template ID: <?= esc($repo_id) ?></div>
<input class="form-control" id="input_Reportname" name="input_Reportname" placeholder="Report Name" type="text" value="<?= esc($repo_name) ?>" />
<input type="hidden" id="repo_id" value="<?= esc($repo_id) ?>">
</div>
@ -49,41 +88,56 @@
<input class="form-control" id="input_Reporttitle" name="input_Reporttitle" placeholder="Report Title" type="text" value="<?= esc($repo_title) ?>" />
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-12">
<label>Findings</label>
<textarea id="HTMLData" name="HTMLData" placeholder="Place some text here"><?= esc($HTMLData) ?></textarea>
<script>
if (typeof CKEDITOR !== 'undefined') {
CKEDITOR.replace('HTMLData');
}
</script>
<div class="col-md-6">
<div class="form-group">
<label>Search Keywords (comma separated)</label>
<input class="form-control" id="keywords" name="keywords" placeholder="e.g. chest, pleural effusion, pa view" type="text" value="<?= esc($keywords) ?>" />
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Impression Category</label>
<input class="form-control" id="impression_cat" name="impression_cat" list="impression_category_list" placeholder="e.g. Normal / Abnormal / Urgent" type="text" value="<?= esc($impressionCat) ?>" />
<datalist id="impression_category_list">
<option value="Normal"></option>
<option value="Abnormal"></option>
<option value="Borderline"></option>
<option value="Urgent"></option>
<option value="Follow-up"></option>
</datalist>
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-12">
<label>Impression</label>
<textarea id="Impression" name="Impression" class="form-control" placeholder="Place some text here"><?= esc($Impression) ?></textarea>
<script>
if (typeof CKEDITOR !== 'undefined') {
CKEDITOR.replace('Impression', {
toolbar: [
['Bold', 'Italic', 'Underline', '-', 'FontSize', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight'],
['NumberedList', 'BulletedList', '-', 'Link', 'Unlink', '-', 'Source']
]
});
}
</script>
<div class="card mt-3 radiology-editor-card">
<div class="card-header bg-white">
<h4>Findings Template</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<textarea id="HTMLData" name="HTMLData" placeholder="Place some text here"><?= esc($HTMLData) ?></textarea>
</div>
</div>
</div>
</div>
<div class="card mt-3 radiology-editor-card">
<div class="card-header bg-white">
<h4>Impression Template</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<textarea id="Impression" name="Impression" class="form-control" placeholder="Place some text here"><?= esc($Impression) ?></textarea>
</div>
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Press Here to Save Data</label>
<label>Template Action</label>
<button id="updatereport" type="button" class="btn btn-primary">Update</button>
</div>
</div>
@ -92,11 +146,43 @@
</div>
<?= form_close() ?>
<script>
(function() {
if (typeof CKEDITOR === 'undefined') {
return;
}
function safeReplace(editorId, config) {
if (!document.getElementById(editorId)) {
return;
}
if (CKEDITOR.instances && CKEDITOR.instances[editorId]) {
try {
CKEDITOR.instances[editorId].destroy(true);
} catch (e) {
console.warn('CKEditor destroy failed for', editorId, e);
}
}
CKEDITOR.replace(editorId, config || {});
}
safeReplace('HTMLData');
safeReplace('Impression', {
toolbar: [
['Bold', 'Italic', 'Underline', '-', 'FontSize', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight'],
['NumberedList', 'BulletedList', '-', 'Link', 'Unlink', '-', 'Source']
]
});
})();
$('#updatereport').click(function() {
var repo_id = $('#repo_id').val();
var input_Reportname = $('#input_Reportname').val();
var charge_id = $('#charge_id').val();
var group_id = $('#input_Reporttitle').val();
var keywords = $('#keywords').val();
var impression_cat = $('#impression_cat').val();
var HTMLData = (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances.HTMLData) ? CKEDITOR.instances.HTMLData.getData() : $('#HTMLData').val();
var Impression = (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances.Impression) ? CKEDITOR.instances.Impression.getData() : $('#Impression').val();
var csrf_value = $('input[name="<?= csrf_token() ?>"]').first().val() || '<?= csrf_hash() ?>';
@ -108,10 +194,17 @@
"input_Reportname": input_Reportname,
"charge_id": charge_id,
"group_id": group_id,
"keywords": keywords,
"impression_cat": impression_cat,
"HTMLData": HTMLData,
"Impression": Impression,
"<?= csrf_token() ?>": csrf_value
}, function(data) {
if (data && Number(data.update_record || 0) === 1 && typeof window.refreshRadiologyTemplateList === 'function') {
window.refreshRadiologyTemplateList();
return;
}
if (typeof notify === 'function') {
notify('success', 'Saved', data.showcontent || 'Saved');
}
@ -122,12 +215,18 @@
"input_Reportname": input_Reportname,
"charge_id": charge_id,
"group_id": group_id,
"keywords": keywords,
"impression_cat": impression_cat,
"HTMLData": HTMLData,
"Impression": Impression,
"<?= csrf_token() ?>": csrf_value
}, function(data) {
if (data.insertid > 0) {
load_form_div('<?= base_url('Lab_Admin/report_ultrasound_list') ?>/' + modality, 'maindiv', 'Diagnosis Template');
if (typeof load_form === 'function') {
load_form('<?= base_url('Lab_Admin/report_ultrasound_list') ?>/' + modality, 'Diagnosis Template');
} else {
window.location.href = '<?= base_url('Lab_Admin/report_ultrasound_list') ?>/' + modality;
}
}
}, 'json');
}

View File

@ -1,66 +1,153 @@
<?= form_open() ?>
<?php
$rows = (isset($labReport_master) && is_array($labReport_master)) ? $labReport_master : [];
$selectedModality = (int) ($modality ?? 2);
?>
<section class="content">
<style>
.report-split {
.radiology-template-wrap {
background: #ffffff;
border: 1px solid #dbe4ef;
border-radius: 10px;
box-shadow: 0 8px 20px rgba(0, 39, 91, 0.05);
padding: 10px;
height: 100vh;
display: flex;
gap: 12px;
align-items: stretch;
flex-direction: column;
}
.report-split-left {
flex: 0 0 32%;
min-width: 260px;
.radiology-template-wrap .card.admin-card {
height: 100%;
display: flex;
flex-direction: column;
}
.report-split-right {
.radiology-template-scroll {
flex: 1 1 auto;
min-width: 0;
min-height: 0;
overflow-y: auto;
}
.report-split-handle {
flex: 0 0 6px;
cursor: col-resize;
border-radius: 6px;
background: linear-gradient(180deg, #e5e7eb 0%, #cbd5f5 100%);
opacity: 0.7;
.radiology-template-table th,
.radiology-template-table td {
vertical-align: middle;
font-size: 13px;
}
.report-split-handle:hover {
opacity: 1;
.radiology-template-table .template-name {
font-weight: 600;
color: #2d3f56;
}
.radiology-template-table .category-pill {
display: inline-block;
padding: 3px 8px;
border-radius: 4px;
background: #eef5ff;
border: 1px solid #d5e5ff;
color: #29568f;
font-size: 11px;
font-weight: 700;
}
.radiology-template-table .tag-cell {
color: #4d627a;
}
#radiologyActionModal .modal-dialog {
max-width: min(1320px, 96vw);
}
#radiologyActionModal .modal-content {
height: 92vh;
border: 0;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 16px 45px rgba(0, 29, 74, 0.28);
}
#radiologyActionModal .modal-header,
#radiologyActionModal .modal-footer {
position: sticky;
background: #fff;
z-index: 2;
}
#radiologyActionModal .modal-header {
top: 0;
border-bottom: 1px solid #e7edf5;
}
#radiologyActionModal .modal-footer {
bottom: 0;
border-top: 1px solid #e7edf5;
}
#radiologyActionModalBody {
overflow: auto;
background: #f8fafc;
padding: 12px;
}
@media (max-width: 991.98px) {
.report-split {
flex-direction: column;
.radiology-template-wrap {
height: auto;
min-height: calc(100vh - 120px);
}
.report-split-left,
.report-split-right {
flex: 1 1 auto;
width: 100%;
#radiologyActionModal .modal-content {
height: 96vh;
}
.report-split-handle {
display: none;
#radiologyActionModalBody {
padding: 8px;
}
}
</style>
<div class="report-split" id="reportSplit">
<div class="report-split-left">
<div class="radiology-template-wrap">
<div class="card admin-card">
<div class="card-header bg-white d-flex align-items-center justify-content-between">
<h3 class="mb-0">Report List</h3>
<button onclick="load_form_div('<?= base_url('Lab_Admin/reportedit_ultrasound_load') ?>/<?= esc($modality ?? 2) ?>/0','test_div');" type="button" class="btn btn-primary btn-sm">Add New Report</button>
<h3 class="mb-0">Radiology Template Mappings</h3>
<button
type="button"
class="btn btn-primary btn-sm js-radiology-action"
data-title="Radiology Template - Add"
data-url="<?= base_url('Lab_Admin/reportedit_ultrasound_load') ?>/<?= $selectedModality ?>/0"
>Add New Report</button>
</div>
<div class="card-body" style="height:500px;overflow-y:auto;">
<table id="report_list" class="table table-striped table-hover align-middle TableData">
<div class="card-body radiology-template-scroll">
<table id="report_list" class="table table-bordered table-hover align-middle radiology-template-table mb-0">
<thead class="table-light">
<tr>
<th>Report Title</th>
<th>Group</th>
<th>Category</th>
<th>Keywords</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php for ($i = 0; $i < count($labReport_master); ++$i) { ?>
<?php for ($i = 0; $i < count($rows); ++$i) { ?>
<tr>
<td><?= esc($labReport_master[$i]->template_name ?? '') ?></td>
<td><?= esc($labReport_master[$i]->title ?? '') ?></td>
<td><span class="template-name"><?= esc($rows[$i]->template_name ?? '') ?></span></td>
<td><?= esc($rows[$i]->title ?? '') ?></td>
<td>
<button onclick="load_form_div('<?= base_url('Lab_Admin/reportedit_ultrasound_load') ?>/<?= esc($modality ?? 2) ?>/<?= esc($labReport_master[$i]->id ?? 0) ?>','test_div');" type="button" class="btn btn-outline-secondary btn-sm">Edit</button>
<?php if (trim((string) ($rows[$i]->impression_cat ?? '')) !== ''): ?>
<span class="category-pill"><?= esc($rows[$i]->impression_cat ?? '') ?></span>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td class="tag-cell"><?= esc($rows[$i]->keywords ?? '') ?></td>
<td>
<button
type="button"
class="btn btn-outline-secondary btn-sm js-radiology-action"
data-title="Radiology Template - Edit"
data-url="<?= base_url('Lab_Admin/reportedit_ultrasound_load') ?>/<?= $selectedModality ?>/<?= (int) ($rows[$i]->id ?? 0) ?>"
>Edit</button>
<button
type="button"
class="btn btn-outline-danger btn-sm js-radiology-delete"
data-template-id="<?= (int) ($rows[$i]->id ?? 0) ?>"
data-template-name="<?= esc($rows[$i]->template_name ?? '') ?>"
>Delete</button>
</td>
</tr>
<?php } ?>
@ -69,66 +156,250 @@
<tr>
<th>Report Title</th>
<th>Group</th>
<th>Category</th>
<th>Keywords</th>
<th>Action</th>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="modal fade" id="radiologyActionModal" tabindex="-1" aria-labelledby="radiologyActionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable modal-dialog-centered">
<div class="modal-content">
<div class="modal-header py-2">
<h5 class="modal-title" id="radiologyActionModalLabel">Radiology Template</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="radiologyActionModalBody">
<div class="text-muted">Loading...</div>
</div>
<div class="modal-footer py-2">
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
<div class="report-split-handle" id="reportSplitHandle" aria-hidden="true"></div>
<div class="report-split-right" id="test_div"></div>
</div>
</div>
</section>
<?= form_close() ?>
<script>
if (window.jQuery && $.fn && $.fn.DataTable) {
if ($.fn.DataTable.isDataTable('#report_list')) {
$('#report_list').DataTable().destroy();
}
$('#report_list').DataTable();
}
</script>
<script>
(function() {
var split = document.getElementById('reportSplit');
var left = split ? split.querySelector('.report-split-left') : null;
var right = split ? split.querySelector('.report-split-right') : null;
var handle = document.getElementById('reportSplitHandle');
var deleteBaseUrl = '<?= base_url('Lab_Admin/report_ultrasound_delete') ?>/<?= $selectedModality ?>';
var csrfTokenName = '<?= csrf_token() ?>';
var fallbackCsrfHash = '<?= csrf_hash() ?>';
var actionModalEl = document.getElementById('radiologyActionModal');
var actionModalTitleEl = document.getElementById('radiologyActionModalLabel');
var actionModalBodyEl = document.getElementById('radiologyActionModalBody');
var activeUrl = '';
var globalScope = window;
if (!split || !left || !right || !handle) {
return;
if (!globalScope.__radiologyTemplateDocHandlers) {
globalScope.__radiologyTemplateDocHandlers = {};
}
var isDragging = false;
var startX = 0;
var startLeftWidth = 0;
function bindDocumentHandler(key, eventName, handler) {
var bucket = globalScope.__radiologyTemplateDocHandlers;
if (bucket[key]) {
document.removeEventListener(eventName, bucket[key]);
}
bucket[key] = handler;
document.addEventListener(eventName, handler);
}
handle.addEventListener('mousedown', function(event) {
if (window.matchMedia('(max-width: 991.98px)').matches) {
function destroyRadiologyEditors() {
if (typeof CKEDITOR === 'undefined' || !CKEDITOR.instances) {
return;
}
isDragging = true;
startX = event.clientX;
startLeftWidth = left.getBoundingClientRect().width;
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
});
window.addEventListener('mousemove', function(event) {
if (!isDragging) {
['HTMLData', 'Impression'].forEach(function(editorId) {
if (!CKEDITOR.instances[editorId]) {
return;
}
try {
CKEDITOR.instances[editorId].destroy(true);
} catch (e) {
console.warn('Unable to destroy CKEditor instance', editorId, e);
}
});
}
function getCsrfHash() {
var input = document.querySelector('input[name="' + csrfTokenName + '"]');
return input ? (input.value || fallbackCsrfHash) : fallbackCsrfHash;
}
function executeInlineScripts(containerEl) {
if (!containerEl) {
return;
}
var delta = event.clientX - startX;
var containerWidth = split.getBoundingClientRect().width;
var newLeftWidth = Math.max(240, Math.min(startLeftWidth + delta, containerWidth * 0.6));
left.style.flex = '0 0 ' + newLeftWidth + 'px';
});
window.addEventListener('mouseup', function() {
if (!isDragging) {
var scripts = containerEl.querySelectorAll('script');
scripts.forEach(function(oldScript) {
var newScript = document.createElement('script');
Array.from(oldScript.attributes || []).forEach(function(attr) {
newScript.setAttribute(attr.name, attr.value);
});
if (oldScript.src) {
var exists = document.querySelector('script[src="' + oldScript.src + '"]');
if (exists) {
return;
}
document.head.appendChild(newScript);
} else {
newScript.text = oldScript.textContent || '';
document.head.appendChild(newScript);
document.head.removeChild(newScript);
}
});
}
function openRadiologyActionModal(url, title) {
if (!actionModalEl || !actionModalTitleEl || !actionModalBodyEl || !url) {
return;
}
isDragging = false;
document.body.style.cursor = '';
document.body.style.userSelect = '';
destroyRadiologyEditors();
activeUrl = url;
actionModalTitleEl.textContent = title || 'Radiology Template';
actionModalBodyEl.innerHTML = '<div class="d-flex align-items-center gap-2 text-muted"><span class="spinner-border spinner-border-sm" role="status"></span><span>Loading...</span></div>';
if (window.bootstrap && window.bootstrap.Modal) {
var modal = window.bootstrap.Modal.getInstance(actionModalEl) || new window.bootstrap.Modal(actionModalEl);
modal.show();
} else if (window.jQuery && $('#radiologyActionModal').modal) {
$('#radiologyActionModal').modal('show');
}
fetch(url, {
credentials: 'same-origin',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(function(response) {
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
return response.text();
})
.then(function(html) {
actionModalBodyEl.innerHTML = html;
executeInlineScripts(actionModalBodyEl);
})
.catch(function(error) {
console.error('Failed to load modal content', error);
actionModalBodyEl.innerHTML = '<div class="alert alert-warning mb-0">Unable to load content. Please try again.</div>';
});
}
bindDocumentHandler('radiologyActionClick', 'click', function(event) {
var actionBtn = event.target.closest('.js-radiology-action');
if (!actionBtn) {
return;
}
event.preventDefault();
var url = actionBtn.getAttribute('data-url') || '';
var title = actionBtn.getAttribute('data-title') || 'Radiology Template';
openRadiologyActionModal(url, title);
});
bindDocumentHandler('radiologyDeleteClick', 'click', function(event) {
var deleteBtn = event.target.closest('.js-radiology-delete');
if (!deleteBtn) {
return;
}
event.preventDefault();
var templateId = Number(deleteBtn.getAttribute('data-template-id') || 0);
var templateName = deleteBtn.getAttribute('data-template-name') || 'this template';
if (!templateId) {
alert('Invalid template ID.');
return;
}
if (!window.confirm('Delete radiology template "' + templateName + '"?')) {
return;
}
var bodyData = encodeURIComponent(csrfTokenName) + '=' + encodeURIComponent(getCsrfHash());
fetch(deleteBaseUrl + '/' + templateId, {
method: 'POST',
credentials: 'same-origin',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: bodyData
})
.then(function(response) {
return response.json().then(function(data) {
return { ok: response.ok, data: data };
}).catch(function() {
return { ok: response.ok, data: {} };
});
})
.then(function(payload) {
if (!payload.ok || Number(payload.data.ok || 0) !== 1) {
var err = payload.data.error || payload.data.message || 'Failed to delete template';
alert(err);
return;
}
if (typeof window.refreshRadiologyTemplateList === 'function') {
window.refreshRadiologyTemplateList();
return;
}
window.location.reload();
})
.catch(function(error) {
console.error('Delete failed', error);
alert('Unable to delete template. Please try again.');
});
});
if (actionModalEl) {
actionModalEl.addEventListener('hidden.bs.modal', function() {
destroyRadiologyEditors();
activeUrl = '';
if (actionModalBodyEl) {
actionModalBodyEl.innerHTML = '<div class="text-muted">Loading...</div>';
}
});
} else if (window.jQuery && $('#radiologyActionModal').on) {
$('#radiologyActionModal').on('hidden.bs.modal', function() {
destroyRadiologyEditors();
activeUrl = '';
if (actionModalBodyEl) {
actionModalBodyEl.innerHTML = '<div class="text-muted">Loading...</div>';
}
});
}
window.refreshRadiologyTemplateList = function() {
if (typeof load_form === 'function') {
load_form('<?= base_url('Lab_Admin/report_ultrasound_list') ?>/<?= $selectedModality ?>', 'Diagnosis Template');
return;
}
window.location.reload();
};
})();
</script>

View File

@ -161,7 +161,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
<?php } ?>
<?php if ($canUltrasound) { ?>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('Lab_Admin/report_ultrasound_list/1') ?>','maindiv','Ultra Sound Template');">
<a class="admin-tile" href="javascript:load_form('<?= base_url('Lab_Admin/report_ultrasound_list/1') ?>','Ultra Sound Template');">
<span class="tile-icon"><i class="bi bi-broadcast"></i></span>
<span>Ultra Sound Template</span>
</a>
@ -169,7 +169,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
<?php } ?>
<?php if ($canXray) { ?>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('Lab_Admin/report_ultrasound_list/3') ?>','maindiv','X-Ray Template');">
<a class="admin-tile" href="javascript:load_form('<?= base_url('Lab_Admin/report_ultrasound_list/3') ?>','X-Ray Template');">
<span class="tile-icon"><i class="bi bi-camera"></i></span>
<span>X-Ray Template</span>
</a>
@ -177,7 +177,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
<?php } ?>
<?php if ($canCt) { ?>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('Lab_Admin/report_ultrasound_list/4') ?>','maindiv','CT-Scan Template');">
<a class="admin-tile" href="javascript:load_form('<?= base_url('Lab_Admin/report_ultrasound_list/4') ?>','CT-Scan Template');">
<span class="tile-icon"><i class="bi bi-cpu"></i></span>
<span>CT-Scan Template</span>
</a>
@ -185,7 +185,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
<?php } ?>
<?php if ($canMri) { ?>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('Lab_Admin/report_ultrasound_list/2') ?>','maindiv','MRI Template');">
<a class="admin-tile" href="javascript:load_form('<?= base_url('Lab_Admin/report_ultrasound_list/2') ?>','MRI Template');">
<span class="tile-icon"><i class="bi bi-activity"></i></span>
<span>MRI Template</span>
</a>
@ -193,7 +193,7 @@ $hasTemplateAccess = $canPathology || $canUltrasound || $canXray || $canCt || $c
<?php } ?>
<?php if ($canEcho) { ?>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:load_form_div('<?= base_url('Lab_Admin/report_ultrasound_list/6') ?>','maindiv','ECHO Template');">
<a class="admin-tile" href="javascript:load_form('<?= base_url('Lab_Admin/report_ultrasound_list/6') ?>','ECHO Template');">
<span class="tile-icon"><i class="bi bi-soundwave"></i></span>
<span>ECHO Template</span>
</a>

View File

@ -121,11 +121,56 @@ $editReason = trim((string) ($edit_reason ?? ''));
<div class="col-md-4" style="border-left:1px solid #e4ebf5; padding-left:12px;">
<label class="form-label"><strong>Templates</strong></label>
<div class="card border-light mb-2">
<div class="card-body p-2">
<div class="small fw-semibold mb-1">Apply Mode</div>
<div class="d-flex gap-2 flex-wrap mb-2">
<label class="form-check-label me-2">
<input class="form-check-input" type="radio" name="tpl_apply_mode" value="replace" checked>
Replace
</label>
<label class="form-check-label">
<input class="form-check-input" type="radio" name="tpl_apply_mode" value="append">
Append
</label>
</div>
<div class="d-flex gap-2 flex-wrap small">
<label class="form-check-label me-2">
<input class="form-check-input" type="checkbox" id="tpl_apply_findings" checked>
Findings
</label>
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="tpl_apply_impression" checked>
Impression
</label>
</div>
</div>
</div>
<input type="text" id="template_search" class="form-control form-control-sm" placeholder="Search templates..." autocomplete="off" />
<div id="templateList" style="max-height:60vh; overflow-y:auto; margin-top:8px;">
<?php foreach ($templates as $tpl): ?>
<div class="template-item mb-1">
<a href="javascript:set_template(<?= (int) ($tpl->id ?? 0) ?>)"><?= esc($tpl->template_name ?? '') ?></a>
<?php
$tplName = (string) ($tpl->template_name ?? '');
$tplTitle = trim((string) ($tpl->title ?? ''));
$tplKeywords = trim((string) ($tpl->keywords ?? ''));
$tplCategory = trim((string) ($tpl->impression_cat ?? ''));
$searchBlob = strtolower(trim($tplName . ' ' . $tplTitle . ' ' . $tplKeywords . ' ' . $tplCategory));
?>
<div class="template-item mb-2" data-search="<?= esc($searchBlob) ?>">
<a href="javascript:set_template(<?= (int) ($tpl->id ?? 0) ?>)" class="d-block p-2 border rounded text-decoration-none">
<div class="fw-semibold"><?= esc($tplName) ?></div>
<?php if ($tplTitle !== ''): ?>
<div class="small text-muted"><?= esc($tplTitle) ?></div>
<?php endif; ?>
<div class="mt-1 small">
<?php if ($tplCategory !== ''): ?>
<span class="badge text-bg-light border">Category: <?= esc($tplCategory) ?></span>
<?php endif; ?>
<?php if ($tplKeywords !== ''): ?>
<span class="badge text-bg-light border">Tags: <?= esc($tplKeywords) ?></span>
<?php endif; ?>
</div>
</a>
</div>
<?php endforeach; ?>
<div id="no_templates_msg" style="display:none; color:#888; padding:6px;">No templates found</div>
@ -213,7 +258,8 @@ $editReason = trim((string) ($edit_reason ?? ''));
let count = 0;
items.forEach(function (item) {
const show = item.textContent.toLowerCase().indexOf(q) !== -1;
const source = (item.getAttribute('data-search') || item.textContent || '').toLowerCase();
const show = source.indexOf(q) !== -1;
item.style.display = show ? '' : 'none';
if (show) {
count++;
@ -227,6 +273,42 @@ $editReason = trim((string) ($edit_reason ?? ''));
});
})();
function getEditorValue(editorId) {
if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances[editorId]) {
return CKEDITOR.instances[editorId].getData();
}
const el = document.getElementById(editorId);
return el ? el.value : '';
}
function setEditorValue(editorId, value) {
if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances[editorId]) {
CKEDITOR.instances[editorId].setData(value);
return;
}
const el = document.getElementById(editorId);
if (el) {
el.value = value;
}
}
function mergeTemplateContent(currentValue, incomingValue, mode) {
if (mode !== 'append') {
return incomingValue;
}
const current = (currentValue || '').trim();
const incoming = (incomingValue || '').trim();
if (!current) {
return incoming;
}
if (!incoming) {
return current;
}
return current + '<hr>' + incoming;
}
// Modal helper function
window._imagingSupportModalInstance = window._imagingSupportModalInstance || null;
function openImagingSupportModal(title, html) {
@ -315,16 +397,24 @@ $editReason = trim((string) ($edit_reason ?? ''));
const findings = data.Findings || '';
const impression = data.Impression || '';
if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances.HTMLShow) {
CKEDITOR.instances.HTMLShow.setData(findings);
} else {
document.getElementById('HTMLShow').value = findings;
const selectedModeEl = document.querySelector('input[name="tpl_apply_mode"]:checked');
const mode = selectedModeEl ? selectedModeEl.value : 'replace';
const applyFindings = !!document.getElementById('tpl_apply_findings')?.checked;
const applyImpression = !!document.getElementById('tpl_apply_impression')?.checked;
if (!applyFindings && !applyImpression) {
alert('Select Findings and/or Impression to apply template.');
return;
}
if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances.report_data_Impression) {
CKEDITOR.instances.report_data_Impression.setData(impression);
} else {
document.getElementById('report_data_Impression').value = impression;
if (applyFindings) {
const mergedFindings = mergeTemplateContent(getEditorValue('HTMLShow'), findings, mode);
setEditorValue('HTMLShow', mergedFindings);
}
if (applyImpression) {
const mergedImpression = mergeTemplateContent(getEditorValue('report_data_Impression'), impression, mode);
setEditorValue('report_data_Impression', mergedImpression);
}
})
.catch(e => console.error(e));

View File

@ -60,12 +60,57 @@ $templates = $radiology_ultrasound_template ?? [];
<div class="col-lg-4 xray-template-pane">
<label class="form-label">Templates</label>
<div class="card border-light mb-2">
<div class="card-body p-2">
<div class="small fw-semibold mb-1">Apply Mode</div>
<div class="d-flex gap-2 flex-wrap mb-2">
<label class="form-check-label me-2">
<input class="form-check-input" type="radio" name="tpl_apply_mode" value="replace" checked>
Replace
</label>
<label class="form-check-label">
<input class="form-check-input" type="radio" name="tpl_apply_mode" value="append">
Append
</label>
</div>
<div class="d-flex gap-2 flex-wrap small">
<label class="form-check-label me-2">
<input class="form-check-input" type="checkbox" id="tpl_apply_findings" checked>
Findings
</label>
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="tpl_apply_impression" checked>
Impression
</label>
</div>
</div>
</div>
<input type="text" id="template_search" class="form-control form-control-sm" placeholder="Search templates..." autocomplete="off" />
<div id="templateList" style="max-height: 60vh; overflow-y: auto; margin-top: 8px;">
<?php foreach ($templates as $tpl): ?>
<div class="template-item mb-1">
<a href="javascript:set_template(<?= (int) ($tpl->id ?? 0) ?>)"><?= esc($tpl->template_name ?? '') ?></a>
<?php
$tplName = (string) ($tpl->template_name ?? '');
$tplTitle = trim((string) ($tpl->title ?? ''));
$tplKeywords = trim((string) ($tpl->keywords ?? ''));
$tplCategory = trim((string) ($tpl->impression_cat ?? ''));
$searchBlob = strtolower(trim($tplName . ' ' . $tplTitle . ' ' . $tplKeywords . ' ' . $tplCategory));
?>
<div class="template-item mb-2" data-search="<?= esc($searchBlob) ?>">
<a href="javascript:set_template(<?= (int) ($tpl->id ?? 0) ?>)" class="d-block p-2 border rounded text-decoration-none">
<div class="fw-semibold"><?= esc($tplName) ?></div>
<?php if ($tplTitle !== ''): ?>
<div class="small text-muted"><?= esc($tplTitle) ?></div>
<?php endif; ?>
<div class="mt-1 small">
<?php if ($tplCategory !== ''): ?>
<span class="badge text-bg-light border">Category: <?= esc($tplCategory) ?></span>
<?php endif; ?>
<?php if ($tplKeywords !== ''): ?>
<span class="badge text-bg-light border">Tags: <?= esc($tplKeywords) ?></span>
<?php endif; ?>
</div>
</a>
</div>
<?php endforeach; ?>
<div id="no_templates_msg" style="display:none; color:#888; padding:6px;">No templates found</div>
@ -86,7 +131,8 @@ $templates = $radiology_ultrasound_template ?? [];
let count = 0;
items.forEach(function (item) {
const show = item.textContent.toLowerCase().indexOf(q) !== -1;
const source = (item.getAttribute('data-search') || item.textContent || '').toLowerCase();
const show = source.indexOf(q) !== -1;
item.style.display = show ? '' : 'none';
if (show) {
count++;
@ -111,5 +157,72 @@ $templates = $radiology_ultrasound_template ?? [];
}
});
})();
function getEditorValue(editorId) {
if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances[editorId]) {
return CKEDITOR.instances[editorId].getData();
}
const el = document.getElementById(editorId);
return el ? el.value : '';
}
function setEditorValue(editorId, value) {
if (typeof CKEDITOR !== 'undefined' && CKEDITOR.instances[editorId]) {
CKEDITOR.instances[editorId].setData(value);
return;
}
const el = document.getElementById(editorId);
if (el) {
el.value = value;
}
}
function mergeTemplateContent(currentValue, incomingValue, mode) {
if (mode !== 'append') {
return incomingValue;
}
const current = (currentValue || '').trim();
const incoming = (incomingValue || '').trim();
if (!current) {
return incoming;
}
if (!incoming) {
return current;
}
return current + '<hr>' + incoming;
}
function set_template(templateId) {
fetch('<?= base_url('diagnosis/get-template-xray') ?>/' + templateId, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(r => r.json())
.then(data => {
const findings = data.Findings || '';
const impression = data.Impression || '';
const selectedModeEl = document.querySelector('input[name="tpl_apply_mode"]:checked');
const mode = selectedModeEl ? selectedModeEl.value : 'replace';
const applyFindings = !!document.getElementById('tpl_apply_findings')?.checked;
const applyImpression = !!document.getElementById('tpl_apply_impression')?.checked;
if (!applyFindings && !applyImpression) {
alert('Select Findings and/or Impression to apply template.');
return;
}
if (applyFindings) {
const mergedFindings = mergeTemplateContent(getEditorValue('HTMLShow'), findings, mode);
setEditorValue('HTMLShow', mergedFindings);
}
if (applyImpression) {
const mergedImpression = mergeTemplateContent(getEditorValue('report_data_Impression'), impression, mode);
setEditorValue('report_data_Impression', mergedImpression);
}
})
.catch(e => console.error(e));
}
</script>
<?php endif; ?>