feat: ABDM task board — remove All, rename ABHA Update, add OPD Book + OPD Consult Publish sections

This commit is contained in:
DevSoftTech Server 2026-05-20 00:29:00 +05:30
parent 368c0b831d
commit ed4e7c1fe4
2 changed files with 233 additions and 17 deletions

View File

@ -21,8 +21,10 @@ class AbdmTaskBoard extends BaseController
$tasks = $this->taskService->getOpenTasks(300);
return view('abdm/task_board', [
'tasks' => $tasks,
'today_credit_opd_rows' => $this->getTodayCreditOpdConsultRows(),
'tasks' => $tasks,
'today_credit_opd_rows' => $this->getTodayCreditOpdConsultRows(),
'opd_book_rows' => $this->getOpdBookRows(),
'opd_consult_rows' => $this->getOpdConsultPublishRows(),
]);
}
@ -435,4 +437,65 @@ class AbdmTaskBoard extends BaseController
return null;
}
/**
* OPD Book rows: HMS OPD appointments (last 7 days) where patient has ABHA.
*
* @return array<int, array<string, mixed>>
*/
private function getOpdBookRows(): array
{
if (! $this->db->tableExists('opd_master') || ! $this->db->tableExists('patient_master')) {
return [];
}
$patientFields = $this->db->getFieldNames('patient_master') ?? [];
$abhaCol = $this->resolveExistingColumn($patientFields, ['abha_id', 'abha_no', 'abha_address', 'abha']);
if ($abhaCol === null) {
return [];
}
$rows = $this->db->table('opd_master o')
->select('o.opd_id, o.p_id, o.P_name, o.apointment_date, o.opd_status, o.doc_name, p.' . $abhaCol . ' as abha_id', false)
->join('patient_master p', 'p.id = o.p_id', 'left')
->where('DATE(o.apointment_date) >=', date('Y-m-d', strtotime('-7 days')), false)
->where('p.' . $abhaCol . ' !=', '')
->orderBy('o.opd_id', 'DESC')
->limit(200)
->get()
->getResultArray();
return array_values(array_filter($rows, static fn ($r) => preg_match('/^\d{14}$/', trim((string) ($r['abha_id'] ?? ''))) === 1));
}
/**
* OPD Consult Publish rows: done OPD (opd_status=2) with ABHA, last 30 days.
*
* @return array<int, array<string, mixed>>
*/
private function getOpdConsultPublishRows(): array
{
if (! $this->db->tableExists('opd_master') || ! $this->db->tableExists('patient_master')) {
return [];
}
$patientFields = $this->db->getFieldNames('patient_master') ?? [];
$abhaCol = $this->resolveExistingColumn($patientFields, ['abha_id', 'abha_no', 'abha_address', 'abha']);
if ($abhaCol === null) {
return [];
}
$rows = $this->db->table('opd_master o')
->select('o.opd_id, o.p_id, o.P_name, o.apointment_date, o.opd_status, o.doc_name, p.' . $abhaCol . ' as abha_id', false)
->join('patient_master p', 'p.id = o.p_id', 'left')
->where('o.opd_status', 2)
->where('DATE(o.apointment_date) >=', date('Y-m-d', strtotime('-30 days')), false)
->where('p.' . $abhaCol . ' !=', '')
->orderBy('o.opd_id', 'DESC')
->limit(300)
->get()
->getResultArray();
return array_values(array_filter($rows, static fn ($r) => preg_match('/^\d{14}$/', trim((string) ($r['abha_id'] ?? ''))) === 1));
}
}

View File

@ -23,10 +23,9 @@
</div>
<div class="d-flex flex-wrap gap-2 mb-3" id="taskFilters">
<button class="btn btn-sm btn-primary filter-btn" data-filter="all">All</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="patient_abha_create">Unupdated ABHA</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="patient_abha_update">ABHA Update</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="opd_prescription_publish">OPD Publish</button>
<button class="btn btn-sm btn-primary filter-btn" data-filter="patient_abha_create">Unupdated ABHA</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="patient_abha_update">ABHA Verify Pending</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="opd_book">OPD Book</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="opd_consult_publish">OPD Consult Publish</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="lab_report_publish">Lab Reports</button>
<button class="btn btn-sm btn-outline-primary filter-btn" data-filter="radiology_report_publish">Radiology Reports</button>
@ -126,7 +125,7 @@
</div>
</div>
<div class="card shadow-sm">
<div class="card shadow-sm" id="taskTableCard">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm table-striped mb-0" id="taskTable">
@ -266,6 +265,99 @@
</div>
</div>
<!-- OPD Book Card -->
<div class="card shadow-sm mt-3 d-none" id="opdBookCard">
<div class="card-header py-2 d-flex justify-content-between align-items-center flex-wrap gap-2">
<div>
<strong>OPD Book — ABDM Patients</strong>
<span class="badge bg-primary ms-2" id="hmsOpdBadge"><?= count($opd_book_rows ?? []) ?></span>
</div>
<div class="d-flex gap-2 align-items-center">
<input type="date" class="form-control form-control-sm" id="opdBookDate" value="<?= date('Y-m-d') ?>" style="width:145px">
<button type="button" class="btn btn-sm btn-outline-info" id="btnFetchAbdmQueue">Fetch ABDM Queue</button>
</div>
</div>
<div class="card-body p-0">
<div class="px-3 pt-2 pb-1 bg-light border-bottom d-flex align-items-center gap-2">
<strong class="small">ABDM Gateway Queue</strong>
<span class="badge bg-secondary" id="abdmQueueBadge">-</span>
<span class="text-muted small" id="abdmQueueStatus">Click "Fetch ABDM Queue" to load</span>
</div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr><th>Token</th><th>ABHA</th><th>Name</th><th>Status</th><th>Time</th><th>HMS Patient</th></tr>
</thead>
<tbody id="abdmQueueTbody"><tr><td colspan="6" class="text-muted px-2 py-2">Not loaded yet.</td></tr></tbody>
</table>
</div>
<div class="px-3 pt-2 pb-1 bg-light border-bottom border-top d-flex align-items-center gap-2 mt-1">
<strong class="small">HMS Direct OPD with ABHA</strong>
<span class="badge bg-success"><?= count($opd_book_rows ?? []) ?></span>
<span class="text-muted small">Last 7 days · patient has 14-digit ABHA</span>
</div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr><th>OPD ID</th><th>Patient</th><th>ABHA</th><th>Date</th><th>Doctor</th><th>Status</th></tr>
</thead>
<tbody>
<?php if (! empty($opd_book_rows ?? [])): ?>
<?php foreach (($opd_book_rows ?? []) as $r): ?>
<tr>
<td>#<?= (int) ($r['opd_id'] ?? 0) ?></td>
<td><?= esc((string) ($r['P_name'] ?? '')) ?></td>
<td><span class="text-primary small"><?= esc((string) ($r['abha_id'] ?? '')) ?></span></td>
<td><?= esc(substr((string) ($r['apointment_date'] ?? ''), 0, 16)) ?></td>
<td><?= esc((string) ($r['doc_name'] ?? '')) ?></td>
<td><span class="badge bg-<?= (int) ($r['opd_status'] ?? 0) >= 2 ? 'success' : 'warning text-dark' ?>"><?= (int) ($r['opd_status'] ?? 0) >= 2 ? 'Done' : 'Pending' ?></span></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="6" class="text-muted px-2 py-2">No OPD with ABHA found in last 7 days.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- OPD Consult Publish Card -->
<div class="card shadow-sm mt-3 d-none" id="opdConsultCard">
<div class="card-header py-2 d-flex align-items-center gap-2">
<strong>OPD Consult Publish — Done Appointments with ABHA</strong>
<span class="badge bg-success"><?= count($opd_consult_rows ?? []) ?></span>
<small class="text-muted ms-1">Last 30 days · opd_status=Done · ABHA linked</small>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr><th>OPD ID</th><th>Patient</th><th>ABHA</th><th>Consult Date</th><th>Doctor</th><th>Action</th></tr>
</thead>
<tbody>
<?php if (! empty($opd_consult_rows ?? [])): ?>
<?php foreach (($opd_consult_rows ?? []) as $r): ?>
<tr>
<td>#<?= (int) ($r['opd_id'] ?? 0) ?></td>
<td><?= esc((string) ($r['P_name'] ?? '')) ?></td>
<td><span class="text-primary small"><?= esc((string) ($r['abha_id'] ?? '')) ?></span></td>
<td><?= esc(substr((string) ($r['apointment_date'] ?? ''), 0, 16)) ?></td>
<td><?= esc((string) ($r['doc_name'] ?? '')) ?></td>
<td>
<button type="button" class="btn btn-sm btn-outline-primary btn-opd-consult-fhir" data-opd-id="<?= (int) ($r['opd_id'] ?? 0) ?>">Preview FHIR</button>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="6" class="text-muted px-2 py-2">No done OPD with ABHA found in last 30 days.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<div class="small text-muted mt-2" id="statusBox">Ready</div>
</div>
@ -340,17 +432,29 @@
el.textContent = text;
}
function applyFilter(filter) {
var appliedFilter = filter;
if ((filter || '').toLowerCase() === 'opd_consult_publish') {
appliedFilter = 'opd_prescription_publish';
}
var opdBookCard = document.getElementById('opdBookCard');
var opdConsultCard = document.getElementById('opdConsultCard');
var taskTableCard = document.getElementById('taskTableCard');
var rows = document.querySelectorAll('#taskTable tbody tr');
rows.forEach(function (row) {
var type = (row.getAttribute('data-task-type') || '').toLowerCase();
row.style.display = (appliedFilter === 'all' || type === appliedFilter.toLowerCase()) ? '' : 'none';
});
function applyFilter(filter) {
// Hide dedicated section cards
if (opdBookCard) opdBookCard.classList.add('d-none');
if (opdConsultCard) opdConsultCard.classList.add('d-none');
if (filter === 'opd_book') {
if (taskTableCard) taskTableCard.classList.add('d-none');
if (opdBookCard) opdBookCard.classList.remove('d-none');
} else if (filter === 'opd_consult_publish') {
if (taskTableCard) taskTableCard.classList.add('d-none');
if (opdConsultCard) opdConsultCard.classList.remove('d-none');
} else {
if (taskTableCard) taskTableCard.classList.remove('d-none');
var rows = document.querySelectorAll('#taskTable tbody tr');
rows.forEach(function (row) {
var type = (row.getAttribute('data-task-type') || '').toLowerCase();
row.style.display = (type === filter.toLowerCase()) ? '' : 'none';
});
}
document.querySelectorAll('.filter-btn').forEach(function (btn) {
btn.classList.remove('btn-primary');
@ -852,6 +956,55 @@
});
});
// ABDM Gateway Queue fetch for OPD Book
document.getElementById('btnFetchAbdmQueue').addEventListener('click', function () {
var date = (document.getElementById('opdBookDate').value || '').trim() || '<?= date('Y-m-d') ?>';
var statusEl = document.getElementById('abdmQueueStatus');
var badge = document.getElementById('abdmQueueBadge');
var tbody = document.getElementById('abdmQueueTbody');
if (statusEl) statusEl.textContent = 'Fetching...';
fetch('<?= base_url('AbdmOpdQueue/fetch') ?>?date=' + encodeURIComponent(date) + '&status=PENDING', {
method: 'GET',
headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }
}).then(function (r) { return r.json(); }).then(function (data) {
var tokens = data.data || data.tokens || [];
if (badge) badge.textContent = tokens.length;
if (statusEl) statusEl.textContent = 'Loaded ' + tokens.length + ' token(s)';
if (! tbody) return;
if (! tokens.length) {
tbody.innerHTML = '<tr><td colspan="6" class="text-muted px-2 py-2">No PENDING tokens for ' + date + '.</td></tr>';
return;
}
tbody.innerHTML = tokens.map(function (t) {
var abha = (t.abha_id || t.abha_number || t.abhaId || '').toString().trim();
var name = (t.patient_name || t.name || t.patientName || '').toString();
var stat = (t.status || t.queue_status || '').toString();
var token = (t.token_number || t.tokenNumber || t.gateway_token_id || t.id || '').toString();
var time = (t.created_at || t.token_time || t.arrival_time || '').toString();
var hms = t.patient_id ? ('#' + t.patient_id + (t.p_fname ? ' ' + t.p_fname : '')) : '<span class="text-muted">—</span>';
return '<tr>' +
'<td>' + (token || '—') + '</td>' +
'<td><span class="text-primary small">' + (abha || '<span class="text-muted">—</span>') + '</span></td>' +
'<td>' + (name || '—') + '</td>' +
'<td><span class="badge bg-' + (stat === 'PENDING' ? 'warning text-dark' : 'secondary') + '">' + (stat || '—') + '</span></td>' +
'<td class="small">' + (time ? time.substring(0, 16) : '—') + '</td>' +
'<td>' + hms + '</td>' +
'</tr>';
}).join('');
}).catch(function (e) {
if (statusEl) statusEl.textContent = 'Fetch failed: ' + e.message;
});
});
// OPD Consult Publish — FHIR preview buttons
document.querySelectorAll('.btn-opd-consult-fhir').forEach(function (btn) {
btn.addEventListener('click', function () {
var opdId = parseInt(btn.getAttribute('data-opd-id') || '0', 10);
if (opdId <= 0) return;
showInlineFhirPreview('<?= base_url('Opd_prescription/fhir_bundle_preview') ?>/' + opdId, 'OPD Consult FHIR ' + opdId);
});
});
document.getElementById('btnRefresh').addEventListener('click', function () {
if (typeof window.load_form === 'function') {
window.load_form(refreshUrl, 'ABDM Task Board');