feat(abdm): add ABHA address filter with care context & FHIR status inspector on task board
This commit is contained in:
parent
1fcdb82eea
commit
e553215ea1
|
|
@ -44,6 +44,8 @@ class AbdmTaskBoard extends BaseController
|
|||
[$dateFrom, $dateTo] = [$dateTo, $dateFrom];
|
||||
}
|
||||
|
||||
$filterAbha = trim((string) ($this->request->getGet('abha_address') ?? $this->request->getGet('abha') ?? ''));
|
||||
|
||||
return view('abdm/task_board', [
|
||||
'tasks' => $tasks,
|
||||
'task_status' => $taskStatus,
|
||||
|
|
@ -54,6 +56,8 @@ class AbdmTaskBoard extends BaseController
|
|||
'opd_book_rows' => $this->getOpdBookRows(),
|
||||
'opd_consult_rows' => $this->getOpdConsultPublishRows(),
|
||||
'invoice_rows' => $this->getInvoiceRows(),
|
||||
'abha_patients' => $this->getAbhaPatientsList(),
|
||||
'filter_abha_address' => $filterAbha,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +116,64 @@ class AbdmTaskBoard extends BaseController
|
|||
return [];
|
||||
}
|
||||
|
||||
$db = $this->db ?? db_connect();
|
||||
|
||||
// 0. Batch-resolve patient_master for ABHA addresses, ABHA numbers, p_code, and phone
|
||||
$patientIds = [];
|
||||
foreach ($tasks as $task) {
|
||||
$pId = (int) ($task['patient_id'] ?? 0);
|
||||
if ($pId > 0) {
|
||||
$patientIds[] = $pId;
|
||||
}
|
||||
}
|
||||
$patientIds = array_values(array_filter(array_unique($patientIds)));
|
||||
$patientMap = [];
|
||||
if (! empty($patientIds) && $db->tableExists('patient_master')) {
|
||||
$pFields = $db->getFieldNames('patient_master') ?? [];
|
||||
$pSel = ['id', 'p_code', 'p_fname', 'gender', 'dob'];
|
||||
if (in_array('p_lname', $pFields, true)) {
|
||||
$pSel[] = 'p_lname';
|
||||
}
|
||||
if (in_array('mphone1', $pFields, true)) {
|
||||
$pSel[] = 'mphone1';
|
||||
}
|
||||
if (in_array('abha_address', $pFields, true)) {
|
||||
$pSel[] = 'abha_address';
|
||||
}
|
||||
if (in_array('abha_id', $pFields, true)) {
|
||||
$pSel[] = 'abha_id';
|
||||
}
|
||||
if (in_array('abha_no', $pFields, true)) {
|
||||
$pSel[] = 'abha_no';
|
||||
}
|
||||
|
||||
$pRows = $db->table('patient_master')->select(implode(', ', $pSel))->whereIn('id', $patientIds)->get()->getResultArray();
|
||||
foreach ($pRows as $pr) {
|
||||
$rawAddr = trim((string) ($pr['abha_address'] ?? ''));
|
||||
$rawId = trim((string) ($pr['abha_id'] ?? $pr['abha_no'] ?? ''));
|
||||
$addr = '';
|
||||
$num = '';
|
||||
if (str_contains($rawAddr, '@')) {
|
||||
$addr = $rawAddr;
|
||||
} elseif (str_contains($rawId, '@')) {
|
||||
$addr = $rawId;
|
||||
}
|
||||
$d1 = preg_replace('/\D/', '', $rawId);
|
||||
$d2 = preg_replace('/\D/', '', $rawAddr);
|
||||
if (is_string($d1) && strlen($d1) === 14) {
|
||||
$num = $d1;
|
||||
} elseif (is_string($d2) && strlen($d2) === 14) {
|
||||
$num = $d2;
|
||||
}
|
||||
$patientMap[(int) $pr['id']] = [
|
||||
'p_code' => (string) ($pr['p_code'] ?? ''),
|
||||
'abha_address' => $addr,
|
||||
'abha_number' => $num,
|
||||
'phone' => (string) ($pr['mphone1'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Extract invoice metadata and resolve invoice_code
|
||||
$invoiceIds = [];
|
||||
foreach ($tasks as &$task) {
|
||||
|
|
@ -127,8 +189,6 @@ class AbdmTaskBoard extends BaseController
|
|||
}
|
||||
unset($task);
|
||||
|
||||
$db = $this->db ?? db_connect();
|
||||
|
||||
if (! empty($invoiceIds) && $db->tableExists('invoice_master')) {
|
||||
$invRows = $db->table('invoice_master')
|
||||
->select('id, invoice_code')
|
||||
|
|
@ -260,6 +320,24 @@ class AbdmTaskBoard extends BaseController
|
|||
}
|
||||
}
|
||||
|
||||
$pData = $patientMap[$pId] ?? null;
|
||||
$taskAbha = trim((string) ($task['abha_id'] ?? ''));
|
||||
$taskAbhaAddr = (string) ($pData['abha_address'] ?? '');
|
||||
$taskAbhaNum = (string) ($pData['abha_number'] ?? '');
|
||||
|
||||
if (str_contains($taskAbha, '@') && $taskAbhaAddr === '') {
|
||||
$taskAbhaAddr = $taskAbha;
|
||||
}
|
||||
$cleanAbha = preg_replace('/\D/', '', $taskAbha);
|
||||
if (strlen($cleanAbha) === 14 && $taskAbhaNum === '') {
|
||||
$taskAbhaNum = $cleanAbha;
|
||||
}
|
||||
|
||||
$task['patient_p_code'] = (string) ($pData['p_code'] ?? '');
|
||||
$task['patient_abha_address'] = $taskAbhaAddr;
|
||||
$task['patient_abha_number'] = $taskAbhaNum;
|
||||
$task['patient_phone'] = (string) ($pData['phone'] ?? '');
|
||||
|
||||
$task['bridge_care_context_reference'] = $careContext;
|
||||
$task['bridge_submitted'] = in_array($pushStatus, ['queued', 'pushed', 'linked'], true) ? 1 : 0;
|
||||
}
|
||||
|
|
@ -881,7 +959,7 @@ class AbdmTaskBoard extends BaseController
|
|||
|
||||
if ($this->db->tableExists('opd_master')) {
|
||||
$builder = $this->db->table('opd_master o')
|
||||
->select("o.opd_id AS bill_id, o.opd_code AS bill_code, o.p_id AS patient_id, COALESCE(NULLIF(o.P_name, ''), p.p_fname, '') AS patient_name, o.apointment_date AS bill_date, o.opd_fee_amount AS amount, COALESCE(NULLIF(p.abha_address, ''), NULLIF(p.abha_id, ''), '') AS abha_id", false);
|
||||
->select("o.opd_id AS bill_id, o.opd_code AS bill_code, o.p_id AS patient_id, COALESCE(NULLIF(o.P_name, ''), p.p_fname, '') AS patient_name, o.apointment_date AS bill_date, o.opd_fee_amount AS amount, COALESCE(p.p_code, '') AS p_code, COALESCE(p.abha_address, '') AS patient_abha_address, COALESCE(p.abha_id, '') AS patient_abha_number, COALESCE(NULLIF(p.abha_address, ''), NULLIF(p.abha_id, ''), '') AS abha_id", false);
|
||||
if ($this->db->tableExists('patient_master')) {
|
||||
$builder->join('patient_master p', 'p.id = o.p_id', 'left');
|
||||
}
|
||||
|
|
@ -896,7 +974,7 @@ class AbdmTaskBoard extends BaseController
|
|||
|
||||
if ($this->db->tableExists('invoice_master')) {
|
||||
$builder = $this->db->table('invoice_master i')
|
||||
->select("i.id AS bill_id, i.invoice_code AS bill_code, i.attach_id AS patient_id, COALESCE(NULLIF(i.inv_name, ''), p.p_fname, '') AS patient_name, i.inv_date AS bill_date, i.net_amount AS amount, COALESCE(NULLIF(p.abha_address, ''), NULLIF(p.abha_id, ''), '') AS abha_id", false);
|
||||
->select("i.id AS bill_id, i.invoice_code AS bill_code, i.attach_id AS patient_id, COALESCE(NULLIF(i.inv_name, ''), p.p_fname, '') AS patient_name, i.inv_date AS bill_date, i.net_amount AS amount, COALESCE(p.p_code, '') AS p_code, COALESCE(p.abha_address, '') AS patient_abha_address, COALESCE(p.abha_id, '') AS patient_abha_number, COALESCE(NULLIF(p.abha_address, ''), NULLIF(p.abha_id, ''), '') AS abha_id", false);
|
||||
if ($this->db->tableExists('patient_master')) {
|
||||
$builder->join('patient_master p', 'p.id = i.attach_id AND i.attach_type = 0', 'left');
|
||||
}
|
||||
|
|
@ -911,7 +989,7 @@ class AbdmTaskBoard extends BaseController
|
|||
|
||||
if ($this->db->tableExists('ipd_master')) {
|
||||
$builder = $this->db->table('ipd_master i')
|
||||
->select("i.id AS bill_id, i.ipd_code AS bill_code, i.p_id AS patient_id, COALESCE(NULLIF(NULLIF(TRIM(i.P_name), ''), '0'), NULLIF(TRIM(p.p_fname), ''), '') AS patient_name, COALESCE(i.discharge_date, i.register_date) AS bill_date, i.net_amount AS amount, COALESCE(NULLIF(p.abha_address, ''), NULLIF(p.abha_id, ''), '') AS abha_id", false);
|
||||
->select("i.id AS bill_id, i.ipd_code AS bill_code, i.p_id AS patient_id, COALESCE(NULLIF(NULLIF(TRIM(i.P_name), ''), '0'), NULLIF(TRIM(p.p_fname), ''), '') AS patient_name, COALESCE(i.discharge_date, i.register_date) AS bill_date, i.net_amount AS amount, COALESCE(p.p_code, '') AS p_code, COALESCE(p.abha_address, '') AS patient_abha_address, COALESCE(p.abha_id, '') AS patient_abha_number, COALESCE(NULLIF(p.abha_address, ''), NULLIF(p.abha_id, ''), '') AS abha_id", false);
|
||||
if ($this->db->tableExists('patient_master')) {
|
||||
$builder->join('patient_master p', 'p.id = i.p_id', 'left');
|
||||
}
|
||||
|
|
@ -969,6 +1047,27 @@ class AbdmTaskBoard extends BaseController
|
|||
}
|
||||
|
||||
foreach ($rows as &$row) {
|
||||
$rawAddress = trim((string) ($row['patient_abha_address'] ?? ''));
|
||||
$rawNumber = trim((string) ($row['patient_abha_number'] ?? ''));
|
||||
$addr = '';
|
||||
$num = '';
|
||||
if (str_contains($rawAddress, '@')) {
|
||||
$addr = $rawAddress;
|
||||
} elseif (str_contains($rawNumber, '@')) {
|
||||
$addr = $rawNumber;
|
||||
}
|
||||
$d1 = preg_replace('/\D/', '', $rawNumber);
|
||||
$d2 = preg_replace('/\D/', '', $rawAddress);
|
||||
if (is_string($d1) && strlen($d1) === 14) {
|
||||
$num = $d1;
|
||||
} elseif (is_string($d2) && strlen($d2) === 14) {
|
||||
$num = $d2;
|
||||
}
|
||||
$row['abha_address'] = $addr;
|
||||
$row['abha_number'] = $num;
|
||||
$row['abha_id'] = $addr !== '' ? $addr : $num;
|
||||
$row['p_code'] = (string) ($row['p_code'] ?? '');
|
||||
|
||||
$billId = (string) ($row['bill_id'] ?? '');
|
||||
$keys = [(string) $row['source_key'] . ':' . $billId];
|
||||
if ($row['source_key'] === 'charges_invoice') {
|
||||
|
|
@ -1120,19 +1219,157 @@ class AbdmTaskBoard extends BaseController
|
|||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
/**
|
||||
* Distinct patients with an ABHA address or ABHA number for the board selector & datalist.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function getAbhaPatientsList(): array
|
||||
{
|
||||
if (! $this->db->tableExists('patient_master')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fields = $this->db->getFieldNames('patient_master') ?? [];
|
||||
$hasAbhaAddress = in_array('abha_address', $fields, true);
|
||||
$hasAbhaId = in_array('abha_id', $fields, true);
|
||||
$hasAbhaNo = in_array('abha_no', $fields, true);
|
||||
|
||||
if (! $hasAbhaAddress && ! $hasAbhaId && ! $hasAbhaNo) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$select = ['id', 'p_code', 'p_fname', 'gender', 'dob'];
|
||||
if (in_array('p_lname', $fields, true)) {
|
||||
$select[] = 'p_lname';
|
||||
}
|
||||
if (in_array('mphone1', $fields, true)) {
|
||||
$select[] = 'mphone1';
|
||||
}
|
||||
if ($hasAbhaAddress) {
|
||||
$select[] = 'abha_address';
|
||||
}
|
||||
if ($hasAbhaId) {
|
||||
$select[] = 'abha_id';
|
||||
}
|
||||
if ($hasAbhaNo) {
|
||||
$select[] = 'abha_no';
|
||||
}
|
||||
|
||||
$builder = $this->db->table('patient_master')->select(implode(', ', $select));
|
||||
|
||||
$whereOr = [];
|
||||
if ($hasAbhaAddress) {
|
||||
$whereOr[] = "NULLIF(TRIM(abha_address), '') IS NOT NULL";
|
||||
}
|
||||
if ($hasAbhaId) {
|
||||
$whereOr[] = "NULLIF(TRIM(abha_id), '') IS NOT NULL";
|
||||
}
|
||||
if ($hasAbhaNo) {
|
||||
$whereOr[] = "NULLIF(TRIM(abha_no), '') IS NOT NULL";
|
||||
}
|
||||
|
||||
if ($whereOr !== []) {
|
||||
$builder->where('(' . implode(' OR ', $whereOr) . ')', null, false);
|
||||
}
|
||||
|
||||
$rows = $builder->orderBy('id', 'DESC')->limit(500)->get()->getResultArray();
|
||||
$patients = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$rawAddress = trim((string) ($row['abha_address'] ?? ''));
|
||||
$rawId = trim((string) ($row['abha_id'] ?? $row['abha_no'] ?? ''));
|
||||
|
||||
$addr = '';
|
||||
$num = '';
|
||||
|
||||
if (str_contains($rawAddress, '@')) {
|
||||
$addr = $rawAddress;
|
||||
} elseif (str_contains($rawId, '@')) {
|
||||
$addr = $rawId;
|
||||
}
|
||||
|
||||
$d1 = preg_replace('/\D/', '', $rawId);
|
||||
$d2 = preg_replace('/\D/', '', $rawAddress);
|
||||
if (is_string($d1) && strlen($d1) === 14) {
|
||||
$num = $d1;
|
||||
} elseif (is_string($d2) && strlen($d2) === 14) {
|
||||
$num = $d2;
|
||||
}
|
||||
|
||||
if ($addr === '' && $num === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fname = trim((string) ($row['p_fname'] ?? ''));
|
||||
$lname = trim((string) ($row['p_lname'] ?? ''));
|
||||
$fullName = trim($fname . ' ' . $lname);
|
||||
if ($fullName === '') {
|
||||
$fullName = 'Patient #' . ($row['id'] ?? '');
|
||||
}
|
||||
|
||||
$genderInt = (int) ($row['gender'] ?? 0);
|
||||
$genderStr = ($genderInt === 1 ? 'M' : ($genderInt === 2 ? 'F' : 'O'));
|
||||
|
||||
$dob = trim((string) ($row['dob'] ?? ''));
|
||||
$yob = 0;
|
||||
if ($dob !== '' && ! str_starts_with($dob, '0000')) {
|
||||
$ts = strtotime($dob);
|
||||
if ($ts !== false) {
|
||||
$yob = (int) date('Y', $ts);
|
||||
}
|
||||
}
|
||||
|
||||
$patients[] = [
|
||||
'id' => (int) ($row['id'] ?? 0),
|
||||
'p_code' => (string) ($row['p_code'] ?? ''),
|
||||
'name' => $fullName,
|
||||
'abha_address' => $addr,
|
||||
'abha_number' => $num,
|
||||
'phone' => (string) ($row['mphone1'] ?? ''),
|
||||
'gender' => $genderStr,
|
||||
'dob' => $dob,
|
||||
'yob' => $yob,
|
||||
];
|
||||
}
|
||||
|
||||
return $patients;
|
||||
}
|
||||
|
||||
private function getOpdBookRows(): array
|
||||
{
|
||||
if (! $this->db->tableExists('abdm_opd_tokens')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->db->table('abdm_opd_tokens')
|
||||
->where('queue_date >=', date('Y-m-d', strtotime('-7 days')))
|
||||
->orderBy('queue_date', 'DESC')
|
||||
->orderBy('gateway_token_id', 'DESC')
|
||||
$builder = $this->db->table('abdm_opd_tokens t');
|
||||
if ($this->db->tableExists('patient_master')) {
|
||||
$builder->select('t.*, p.p_code, p.mphone1 as patient_phone')
|
||||
->join('patient_master p', 'p.id = t.patient_id', 'left');
|
||||
} else {
|
||||
$builder->select('t.*');
|
||||
}
|
||||
|
||||
$rows = $builder->where('t.queue_date >=', date('Y-m-d', strtotime('-7 days')))
|
||||
->orderBy('t.queue_date', 'DESC')
|
||||
->orderBy('t.gateway_token_id', 'DESC')
|
||||
->limit(200)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
foreach ($rows as &$r) {
|
||||
$rawAddress = trim((string) ($r['abha_address'] ?? ''));
|
||||
$rawNumber = trim((string) ($r['abha_number'] ?? ''));
|
||||
if (str_contains($rawNumber, '@') && $rawAddress === '') {
|
||||
$rawAddress = $rawNumber;
|
||||
}
|
||||
$r['abha_address'] = $rawAddress;
|
||||
$r['abha_number'] = preg_replace('/\D/', '', $rawNumber);
|
||||
$r['p_code'] = (string) ($r['p_code'] ?? '');
|
||||
}
|
||||
unset($r);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1147,23 +1384,77 @@ class AbdmTaskBoard extends BaseController
|
|||
}
|
||||
|
||||
$patientFields = $this->db->getFieldNames('patient_master') ?? [];
|
||||
$abhaCol = $this->resolveExistingColumn($patientFields, ['abha_id', 'abha_no', 'abha_address', 'abha']);
|
||||
if ($abhaCol === null) {
|
||||
return [];
|
||||
$selectCols = [
|
||||
'o.opd_id', 'o.p_id', 'o.P_name', 'o.apointment_date', 'o.opd_status', 'o.doc_name',
|
||||
];
|
||||
if (in_array('p_code', $patientFields, true)) {
|
||||
$selectCols[] = 'p.p_code';
|
||||
}
|
||||
if (in_array('abha_address', $patientFields, true)) {
|
||||
$selectCols[] = 'p.abha_address';
|
||||
}
|
||||
if (in_array('abha_id', $patientFields, true)) {
|
||||
$selectCols[] = 'p.abha_id';
|
||||
}
|
||||
if (in_array('abha_no', $patientFields, true)) {
|
||||
$selectCols[] = 'p.abha_no';
|
||||
}
|
||||
if (in_array('mphone1', $patientFields, true)) {
|
||||
$selectCols[] = 'p.mphone1 as patient_phone';
|
||||
}
|
||||
|
||||
$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)
|
||||
$builder = $this->db->table('opd_master o')
|
||||
->select(implode(', ', $selectCols), 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();
|
||||
->where('DATE(o.apointment_date) >=', date('Y-m-d', strtotime('-30 days')), false);
|
||||
|
||||
$whereAbhaOr = [];
|
||||
if (in_array('abha_address', $patientFields, true)) {
|
||||
$whereAbhaOr[] = "NULLIF(TRIM(p.abha_address), '') IS NOT NULL";
|
||||
}
|
||||
if (in_array('abha_id', $patientFields, true)) {
|
||||
$whereAbhaOr[] = "NULLIF(TRIM(p.abha_id), '') IS NOT NULL";
|
||||
}
|
||||
if (in_array('abha_no', $patientFields, true)) {
|
||||
$whereAbhaOr[] = "NULLIF(TRIM(p.abha_no), '') IS NOT NULL";
|
||||
}
|
||||
if ($whereAbhaOr !== []) {
|
||||
$builder->where('(' . implode(' OR ', $whereAbhaOr) . ')', null, false);
|
||||
}
|
||||
|
||||
$rawRows = $builder->orderBy('o.opd_id', 'DESC')->limit(300)->get()->getResultArray();
|
||||
|
||||
$rows = [];
|
||||
foreach ($rawRows as $r) {
|
||||
$rawAddress = trim((string) ($r['abha_address'] ?? ''));
|
||||
$rawId = trim((string) ($r['abha_id'] ?? $r['abha_no'] ?? ''));
|
||||
$addr = '';
|
||||
$num = '';
|
||||
if (str_contains($rawAddress, '@')) {
|
||||
$addr = $rawAddress;
|
||||
} elseif (str_contains($rawId, '@')) {
|
||||
$addr = $rawId;
|
||||
}
|
||||
$d1 = preg_replace('/\D/', '', $rawId);
|
||||
$d2 = preg_replace('/\D/', '', $rawAddress);
|
||||
if (is_string($d1) && strlen($d1) === 14) {
|
||||
$num = $d1;
|
||||
} elseif (is_string($d2) && strlen($d2) === 14) {
|
||||
$num = $d2;
|
||||
}
|
||||
|
||||
if ($addr === '' && $num === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$r['abha_address'] = $addr;
|
||||
$r['abha_number'] = $num;
|
||||
$r['abha_id'] = $addr !== '' ? $addr : $num;
|
||||
$r['p_code'] = (string) ($r['p_code'] ?? '');
|
||||
$rows[] = $r;
|
||||
}
|
||||
|
||||
$rows = array_values(array_filter($rows, static fn ($r) => preg_match('/^\d{14}$/', trim((string) ($r['abha_id'] ?? ''))) === 1));
|
||||
if (empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -1398,7 +1689,7 @@ class AbdmTaskBoard extends BaseController
|
|||
$patientId = (int) ($row['p_id'] ?? 0);
|
||||
$docId = (int) ($row['id'] ?? 0);
|
||||
$abhaId = trim((string) ($row['abha_id'] ?? ''));
|
||||
if ($patientId <= 0 || $docId <= 0 || preg_match('/^\d{14}$/', $abhaId) !== 1) {
|
||||
if ($patientId <= 0 || $docId <= 0 || ! $this->isValidAbhaNumber($abhaId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1447,7 +1738,7 @@ class AbdmTaskBoard extends BaseController
|
|||
$patientId = (int) ($fRow['pid'] ?? 0);
|
||||
$fileId = (int) ($fRow['id'] ?? 0);
|
||||
$abhaId = trim((string) ($fRow['abha_id'] ?? ''));
|
||||
if ($patientId <= 0 || $fileId <= 0 || preg_match('/^\d{14}$/', $abhaId) !== 1) {
|
||||
if ($patientId <= 0 || $fileId <= 0 || ! $this->isValidAbhaNumber($abhaId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1524,7 +1815,7 @@ class AbdmTaskBoard extends BaseController
|
|||
$rxId = (int) ($row['id'] ?? 0);
|
||||
$opdId = (int) ($row['opd_id'] ?? 0);
|
||||
$abhaId = trim((string) ($row['abha_id'] ?? ''));
|
||||
if ($patientId <= 0 || $rxId <= 0 || preg_match('/^\d{14}$/', $abhaId) !== 1) {
|
||||
if ($patientId <= 0 || $rxId <= 0 || ! $this->isValidAbhaNumber($abhaId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
.container-wrap { width: 100%; margin: 0; padding: 20px 24px; box-sizing: border-box; }
|
||||
.table td, .table th { vertical-align: middle; }
|
||||
.status-pill { text-transform: uppercase; font-size: 11px; letter-spacing: .4px; }
|
||||
.clickable-abha-filter { cursor: pointer; transition: all 0.15s ease-in-out; }
|
||||
.clickable-abha-filter:hover { opacity: 0.85; transform: scale(1.02); filter: drop-shadow(0 1px 2px rgba(0,0,0,0.15)); }
|
||||
.quick-abha-pill { transition: all 0.15s ease-in-out; }
|
||||
.quick-abha-pill:hover { background-color: #0d6efd !important; color: #fff !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -44,6 +48,176 @@
|
|||
<button type="button" class="btn btn-sm btn-outline-primary filter-btn" data-filter="ipd_discharge_publish">IPD Discharge</button>
|
||||
</div>
|
||||
|
||||
<!-- ABHA Address & Patient Global Filter Bar -->
|
||||
<div class="card shadow-sm mb-3 border-0 bg-white" id="globalAbhaFilterCard" style="border-radius: 8px;">
|
||||
<div class="card-body py-2 px-3">
|
||||
<div class="row g-2 align-items-center">
|
||||
<div class="col-12 col-md-auto d-flex align-items-center gap-2">
|
||||
<span class="badge bg-primary-subtle text-primary border border-primary-subtle p-2">
|
||||
<i class="bi bi-person-badge fs-6"></i>
|
||||
</span>
|
||||
<div>
|
||||
<strong class="small d-block text-dark">ABHA Filter & FHIR Inspector</strong>
|
||||
<span class="text-muted" style="font-size: 11px;">Filter board & inspect patient care contexts</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-light"><i class="bi bi-search text-muted"></i></span>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="globalAbhaFilterInput"
|
||||
list="abhaPatientDataList"
|
||||
placeholder="Enter or select ABHA Address (e.g. user@sbx), 14-digit ABHA Number, or Patient Name..."
|
||||
value="<?= esc((string) ($filter_abha_address ?? '')) ?>"
|
||||
autocomplete="off"
|
||||
>
|
||||
<button type="button" class="btn btn-primary" id="btnApplyAbhaFilter">
|
||||
<i class="bi bi-funnel me-1"></i>Filter Records
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="btnClearAbhaFilter" title="Clear ABHA filter">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
<datalist id="abhaPatientDataList">
|
||||
<?php foreach (($abha_patients ?? []) as $ap): ?>
|
||||
<?php
|
||||
$val = ! empty($ap['abha_address']) ? $ap['abha_address'] : $ap['abha_number'];
|
||||
$displayLabel = ($ap['name'] ?? '') . ' (' . ($ap['p_code'] ?? '') . ') - ' . (! empty($ap['abha_address']) ? $ap['abha_address'] : $ap['abha_number']);
|
||||
?>
|
||||
<option value="<?= esc($val) ?>"><?= esc($displayLabel) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
</div>
|
||||
<?php if (! empty($abha_patients ?? [])): ?>
|
||||
<div class="col-12 col-lg-auto d-flex align-items-center gap-1 flex-wrap">
|
||||
<span class="text-muted small me-1" style="font-size:11px;">Quick:</span>
|
||||
<?php
|
||||
$quickPills = array_slice($abha_patients ?? [], 0, 4);
|
||||
foreach ($quickPills as $qp):
|
||||
$qpVal = ! empty($qp['abha_address']) ? $qp['abha_address'] : $qp['abha_number'];
|
||||
if (empty($qpVal)) continue;
|
||||
?>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-light border py-0 px-2 text-primary quick-abha-pill"
|
||||
data-abha="<?= esc($qpVal) ?>"
|
||||
style="font-size: 11px;"
|
||||
>
|
||||
<i class="bi bi-person me-1"></i><?= esc($qp['name'] ?? $qpVal) ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div id="activeAbhaFilterNotice" class="d-none mt-2 pt-2 border-top d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-2 small">
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle"><i class="bi bi-funnel-fill me-1"></i>Active ABHA Filter</span>
|
||||
<span>Filtering all queues for: <strong class="text-primary font-monospace" id="activeAbhaFilterText">-</strong></span>
|
||||
<span class="badge bg-light text-dark border" id="activeAbhaMatchStats">Scanning records...</span>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-link text-decoration-none py-0 px-1 text-danger" id="btnNoticeClearFilter">
|
||||
<i class="bi bi-x-circle me-1"></i>Reset Filter
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Patient ABHA Context & FHIR Status Inspector Card -->
|
||||
<div class="card shadow-sm mb-3 border-primary d-none" id="patientAbhaInspectorCard">
|
||||
<div class="card-header bg-primary text-white py-2 d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<i class="bi bi-shield-check fs-5"></i>
|
||||
<strong class="fs-6" id="inspPatientName">Patient Demographics & Context Status</strong>
|
||||
<span class="badge bg-light text-dark" id="inspPatientGender">M</span>
|
||||
<span class="badge bg-light text-dark" id="inspPatientYob">YOB: -</span>
|
||||
<span class="badge bg-white text-primary fw-semibold" id="inspPatientUhid">UHID: -</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button type="button" class="btn btn-sm btn-light py-1 px-2" id="btnVerifyAbhaGateway" title="Query confirmed patient care context links on ABDM National Gateway">
|
||||
<i class="bi bi-cloud-check text-primary me-1"></i>Live Gateway Check
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-light py-1 px-2" id="btnRefreshInspector" title="Refresh local contexts">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-light py-1 px-2" id="btnCloseInspector">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<!-- Patient Profile & Status Bar -->
|
||||
<div class="row g-2 mb-3 align-items-center pb-2 border-bottom">
|
||||
<div class="col-md-3">
|
||||
<div class="text-muted small">ABHA Address</div>
|
||||
<div class="fw-semibold text-primary font-monospace" id="inspAbhaAddress" style="word-break: break-all;">-</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-muted small">ABHA Number</div>
|
||||
<div class="fw-semibold font-monospace" id="inspAbhaNumber">-</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-muted small">Phone Number</div>
|
||||
<div class="fw-semibold" id="inspPhone">-</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="text-muted small">Gateway Link Verification</div>
|
||||
<div id="inspGatewayStatus"><span class="badge bg-secondary">Unchecked</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary KPI Pill Tiles -->
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="border rounded p-2 bg-light text-center">
|
||||
<div class="small text-muted">Total Care Contexts</div>
|
||||
<div class="fs-5 fw-bold text-dark" id="inspTotalContexts">0</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="border rounded p-2 bg-light text-center">
|
||||
<div class="small text-muted">Linked Contexts</div>
|
||||
<div class="fs-5 fw-bold text-success" id="inspLinkedContexts">0</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="border rounded p-2 bg-light text-center">
|
||||
<div class="small text-muted">Unlinked Contexts</div>
|
||||
<div class="fs-5 fw-bold text-warning" id="inspUnlinkedContexts">0</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="border rounded p-2 bg-light text-center">
|
||||
<div class="small text-muted">FHIR Ready Bundles</div>
|
||||
<div class="fs-5 fw-bold text-info" id="inspFhirCount">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Care Contexts & FHIR Bundles Details Table -->
|
||||
<div class="table-responsive border rounded">
|
||||
<table class="table table-sm table-hover mb-0" id="inspCareContextTable">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Care Context Ref</th>
|
||||
<th>Visit / Record Description</th>
|
||||
<th>HI Type</th>
|
||||
<th>FHIR Bundle Status</th>
|
||||
<th>Context Link Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="inspCareContextTbody">
|
||||
<tr><td colspan="6" class="text-center text-muted py-3">Loading care contexts and FHIR status...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php $dashboard = $dashboard_metrics ?? []; ?>
|
||||
<div id="dashboardCard">
|
||||
<form method="get" action="<?= current_url() ?>" class="d-flex flex-wrap align-items-end gap-2 mb-3" id="dashboardRangeForm">
|
||||
|
|
@ -171,6 +345,11 @@
|
|||
$bridgeCareContext,
|
||||
(string) ($t['last_action_result'] ?? ''),
|
||||
])));
|
||||
$taskAbhaAddr = trim((string) ($t['patient_abha_address'] ?? ''));
|
||||
$taskAbhaNum = trim((string) ($t['patient_abha_number'] ?? ''));
|
||||
$taskPCode = trim((string) ($t['patient_p_code'] ?? ''));
|
||||
$taskPhone = trim((string) ($t['patient_phone'] ?? ''));
|
||||
$effectiveAbha = $taskAbhaAddr !== '' ? $taskAbhaAddr : ($taskAbhaNum !== '' ? $taskAbhaNum : $abhaId);
|
||||
?>
|
||||
<tr
|
||||
data-task-id="<?= (int) ($t['id'] ?? 0) ?>"
|
||||
|
|
@ -179,12 +358,16 @@
|
|||
data-is-linked="<?= $isTaskLinked ? '1' : '0' ?>"
|
||||
data-entity-id="<?= esc((string) ($t['entity_id'] ?? '')) ?>"
|
||||
data-patient-id="<?= (int) ($t['patient_id'] ?? 0) ?>"
|
||||
data-abha-id="<?= esc($abhaId) ?>"
|
||||
data-patient-name="<?= esc(strtolower((string) ($t['patient_name'] ?? ''))) ?>"
|
||||
data-patient-p-code="<?= esc(strtolower($taskPCode)) ?>"
|
||||
data-patient-abha-address="<?= esc($taskAbhaAddr) ?>"
|
||||
data-patient-abha-number="<?= esc($taskAbhaNum) ?>"
|
||||
data-abha-id="<?= esc($effectiveAbha) ?>"
|
||||
data-opd-id="<?= $opdId ?>"
|
||||
data-opd-session-id="<?= $opdSessionId ?>"
|
||||
data-sandbox-eligible="<?= $showSandbox ? '1' : '0' ?>"
|
||||
data-bridge-submitted="<?= $bridgeSubmitted ? '1' : '0' ?>"
|
||||
data-search="<?= esc($searchText) ?>"
|
||||
data-search="<?= esc($searchText . ' ' . $taskAbhaAddr . ' ' . $taskAbhaNum . ' ' . $taskPCode . ' ' . $taskPhone) ?>"
|
||||
>
|
||||
<td><?= (int) ($t['id'] ?? 0) ?></td>
|
||||
<td>
|
||||
|
|
@ -196,13 +379,27 @@
|
|||
</td>
|
||||
<td>
|
||||
<div><?= esc((string) ($t['patient_name'] ?? '')) ?></div>
|
||||
<div class="text-muted small">#<?= (int) ($t['patient_id'] ?? 0) ?></div>
|
||||
<div class="text-muted small">
|
||||
#<?= (int) ($t['patient_id'] ?? 0) ?>
|
||||
<?php if ($taskPCode !== ''): ?>
|
||||
<span class="ms-1 fw-semibold text-secondary">(<?= esc($taskPCode) ?>)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($isTaskLinked): ?>
|
||||
<div class="small"><span class="badge bg-light text-dark border"><i class="bi bi-person-check text-success me-1"></i><?= esc((string) ($t['abha_id'] ?? '')) ?></span></div>
|
||||
<div class="small">
|
||||
<span class="badge bg-light text-dark border clickable-abha-filter" data-filter-abha="<?= esc($effectiveAbha) ?>" title="Click to filter board by this ABHA">
|
||||
<i class="bi bi-person-check text-success me-1"></i><?= esc($effectiveAbha) ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<input type="text" class="form-control form-control-sm abha-input" value="<?= esc((string) ($t['abha_id'] ?? '')) ?>" placeholder="14-digit ABHA">
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control form-control-sm abha-input" value="<?= esc($effectiveAbha) ?>" placeholder="14-digit ABHA / address">
|
||||
<?php if ($effectiveAbha !== ''): ?>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm clickable-abha-filter" data-filter-abha="<?= esc($effectiveAbha) ?>" title="Filter board by this ABHA"><i class="bi bi-funnel"></i></button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -393,10 +590,23 @@
|
|||
<tbody>
|
||||
<?php if (! empty($opd_book_rows ?? [])): ?>
|
||||
<?php foreach (($opd_book_rows ?? []) as $r): ?>
|
||||
<tr>
|
||||
<?php
|
||||
$rowAbha = (string) (($r['abha_address'] ?? '') ?: ($r['abha_number'] ?? ''));
|
||||
?>
|
||||
<tr
|
||||
data-patient-id="<?= (int) ($r['patient_id'] ?? 0) ?>"
|
||||
data-abha-address="<?= esc((string) ($r['abha_address'] ?? '')) ?>"
|
||||
data-abha-number="<?= esc((string) ($r['abha_number'] ?? '')) ?>"
|
||||
data-patient-name="<?= esc(strtolower((string) ($r['patient_name'] ?? ''))) ?>"
|
||||
data-patient-p-code="<?= esc(strtolower((string) ($r['p_code'] ?? ''))) ?>"
|
||||
>
|
||||
<td><?= esc((string) (($r['token_number'] ?? '') ?: ('#' . ($r['gateway_token_id'] ?? '')))) ?></td>
|
||||
<td><?= esc((string) ($r['patient_name'] ?? '')) ?></td>
|
||||
<td><span class="text-primary small"><?= esc((string) (($r['abha_number'] ?? '') ?: ($r['abha_address'] ?? ''))) ?></span></td>
|
||||
<td>
|
||||
<span class="text-primary small clickable-abha-filter" data-filter-abha="<?= esc($rowAbha) ?>" title="Click to filter board by this ABHA">
|
||||
<i class="bi bi-person-check me-1"></i><?= esc($rowAbha) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= esc((string) ($r['queue_date'] ?? '')) ?></td>
|
||||
<td><?= esc(ucwords(str_replace('_', ' ', (string) ($r['source'] ?? '')))) ?></td>
|
||||
<?php $tokenStatus = strtoupper((string) ($r['status'] ?? 'PENDING')); ?>
|
||||
|
|
@ -447,11 +657,31 @@
|
|||
if ($statusTone === 'warning') {
|
||||
$statusBadgeClass .= ' text-dark';
|
||||
}
|
||||
$consultAbha = trim((string) (($r['abha_address'] ?? '') ?: ($r['abha_number'] ?? '') ?: ($r['abha_id'] ?? '')));
|
||||
?>
|
||||
<tr data-push-status="<?= esc($pushStatusRaw) ?>">
|
||||
<tr
|
||||
data-push-status="<?= esc($pushStatusRaw) ?>"
|
||||
data-opd-id="<?= (int) ($r['opd_id'] ?? 0) ?>"
|
||||
data-patient-id="<?= (int) ($r['p_id'] ?? 0) ?>"
|
||||
data-patient-name="<?= esc(strtolower((string) ($r['P_name'] ?? ''))) ?>"
|
||||
data-patient-p-code="<?= esc(strtolower((string) ($r['p_code'] ?? ''))) ?>"
|
||||
data-abha-address="<?= esc((string) ($r['abha_address'] ?? '')) ?>"
|
||||
data-abha-number="<?= esc((string) ($r['abha_number'] ?? '')) ?>"
|
||||
data-abha-id="<?= esc($consultAbha) ?>"
|
||||
data-care-context="<?= esc(strtolower((string) ($careContext ?? ''))) ?>"
|
||||
>
|
||||
<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>
|
||||
<div><?= esc((string) ($r['P_name'] ?? '')) ?></div>
|
||||
<?php if (! empty($r['p_code'])): ?>
|
||||
<div class="text-muted small"><?= esc((string) $r['p_code']) ?></div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-primary small clickable-abha-filter" data-filter-abha="<?= esc($consultAbha) ?>" title="Click to filter board by this ABHA">
|
||||
<i class="bi bi-person-check me-1"></i><?= esc($consultAbha) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= esc(substr((string) ($r['apointment_date'] ?? ''), 0, 16)) ?></td>
|
||||
<td><?= esc((string) ($r['doc_name'] ?? '')) ?></td>
|
||||
<td><span class="<?= esc($statusBadgeClass) ?> opd-consult-status-badge"><?= esc($statusLabel) ?></span></td>
|
||||
|
|
@ -530,22 +760,37 @@
|
|||
$pushStatus = strtolower(trim((string) ($invoice['push_status'] ?? '')));
|
||||
$linkStatus = strtolower(trim((string) ($invoice['link_status'] ?? '')));
|
||||
$alreadySubmitted = in_array($pushStatus, ['queued', 'pushed', 'linked'], true) || $linkStatus === 'linked';
|
||||
$invAbha = trim((string) (($invoice['abha_address'] ?? '') ?: ($invoice['abha_number'] ?? '') ?: ($invoice['abha_id'] ?? '')));
|
||||
$invPCode = trim((string) ($invoice['p_code'] ?? ''));
|
||||
?>
|
||||
<tr
|
||||
data-invoice-source="<?= esc((string) ($invoice['source_key'] ?? '')) ?>"
|
||||
data-bill-id="<?= (int) ($invoice['bill_id'] ?? 0) ?>"
|
||||
data-patient-id="<?= (int) ($invoice['patient_id'] ?? 0) ?>"
|
||||
data-abha-id="<?= esc((string) ($invoice['abha_id'] ?? '')) ?>"
|
||||
data-care-context="<?= esc((string) ($invoice['care_context_reference'] ?? '')) ?>"
|
||||
data-patient-name="<?= esc(strtolower((string) ($invoice['patient_name'] ?? ''))) ?>"
|
||||
data-patient-p-code="<?= esc(strtolower($invPCode)) ?>"
|
||||
data-abha-address="<?= esc((string) ($invoice['abha_address'] ?? '')) ?>"
|
||||
data-abha-number="<?= esc((string) ($invoice['abha_number'] ?? '')) ?>"
|
||||
data-abha-id="<?= esc($invAbha) ?>"
|
||||
data-care-context="<?= esc(strtolower((string) ($invoice['care_context_reference'] ?? ''))) ?>"
|
||||
data-push-status="<?= esc($pushStatus) ?>"
|
||||
>
|
||||
<td><?= esc((string) ($invoice['source'] ?? '')) ?></td>
|
||||
<td><strong><?= esc((string) (($invoice['bill_code'] ?? '') ?: ('#' . ($invoice['bill_id'] ?? '')))) ?></strong></td>
|
||||
<td>
|
||||
<div><?= esc((string) ($invoice['patient_name'] ?? '')) ?></div>
|
||||
<small class="text-muted">#<?= (int) ($invoice['patient_id'] ?? 0) ?></small>
|
||||
<?php if (! empty($invoice['abha_id'])): ?>
|
||||
<div><span class="text-primary small" title="ABHA"><i class="bi bi-person-check me-1"></i><?= esc((string) $invoice['abha_id']) ?></span></div>
|
||||
<small class="text-muted">
|
||||
#<?= (int) ($invoice['patient_id'] ?? 0) ?>
|
||||
<?php if ($invPCode !== ''): ?>
|
||||
<span class="ms-1 fw-semibold text-secondary">(<?= esc($invPCode) ?>)</span>
|
||||
<?php endif; ?>
|
||||
</small>
|
||||
<?php if ($invAbha !== ''): ?>
|
||||
<div>
|
||||
<span class="text-primary small clickable-abha-filter" data-filter-abha="<?= esc($invAbha) ?>" title="Click to filter board by this ABHA">
|
||||
<i class="bi bi-person-check me-1"></i><?= esc($invAbha) ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= esc(substr((string) ($invoice['bill_date'] ?? ''), 0, 16)) ?></td>
|
||||
|
|
@ -729,6 +974,7 @@
|
|||
var currentCategoryFilter = 'dashboard';
|
||||
var currentStatusFilter = 'all';
|
||||
var currentSearchQuery = '';
|
||||
var currentAbhaFilter = '<?= esc((string) ($filter_abha_address ?? '')) ?>'.trim();
|
||||
|
||||
function updateTaskTableDisplay() {
|
||||
var rows = document.querySelectorAll('#taskTable tbody tr:not(#taskTableEmptyRow)');
|
||||
|
|
@ -776,7 +1022,29 @@
|
|||
// Check search match
|
||||
var searchMatch = !q || searchData.indexOf(q) !== -1;
|
||||
|
||||
if (statusMatch && searchMatch) {
|
||||
// Check ABHA filter match
|
||||
var abhaMatch = true;
|
||||
if (currentAbhaFilter !== '') {
|
||||
var patAbhaAddr = (row.getAttribute('data-patient-abha-address') || '').toLowerCase();
|
||||
var patAbhaNum = (row.getAttribute('data-patient-abha-number') || '').toLowerCase();
|
||||
var rowAbhaId = (row.getAttribute('data-abha-id') || '').toLowerCase();
|
||||
var patPCode = (row.getAttribute('data-patient-p-code') || '').toLowerCase();
|
||||
var patId = (row.getAttribute('data-patient-id') || '').toLowerCase();
|
||||
var patName = (row.getAttribute('data-patient-name') || '').toLowerCase();
|
||||
var sTerm = currentAbhaFilter.toLowerCase();
|
||||
var sDigits = sTerm.replace(/\D/g, '');
|
||||
|
||||
abhaMatch = (patAbhaAddr !== '' && patAbhaAddr.indexOf(sTerm) !== -1)
|
||||
|| (rowAbhaId !== '' && rowAbhaId.indexOf(sTerm) !== -1)
|
||||
|| (sDigits.length >= 6 && patAbhaNum !== '' && patAbhaNum.indexOf(sDigits) !== -1)
|
||||
|| (sDigits.length >= 6 && rowAbhaId.replace(/\D/g, '').indexOf(sDigits) !== -1)
|
||||
|| (patPCode !== '' && patPCode.indexOf(sTerm) !== -1)
|
||||
|| (patId !== '' && patId === sTerm)
|
||||
|| (patName !== '' && patName.indexOf(sTerm) !== -1)
|
||||
|| (searchData.indexOf(sTerm) !== -1);
|
||||
}
|
||||
|
||||
if (statusMatch && searchMatch && abhaMatch) {
|
||||
row.style.display = '';
|
||||
visibleCount++;
|
||||
} else {
|
||||
|
|
@ -799,6 +1067,294 @@
|
|||
}
|
||||
}
|
||||
|
||||
function filterOpdConsultTable() {
|
||||
var rows = document.querySelectorAll('#opdConsultCard tbody tr');
|
||||
var sTerm = currentAbhaFilter.toLowerCase();
|
||||
var sDigits = sTerm.replace(/\D/g, '');
|
||||
var matchCount = 0;
|
||||
|
||||
rows.forEach(function (row) {
|
||||
if (sTerm === '') {
|
||||
row.style.display = '';
|
||||
matchCount++;
|
||||
return;
|
||||
}
|
||||
var abhaAddr = (row.getAttribute('data-abha-address') || '').toLowerCase();
|
||||
var abhaNum = (row.getAttribute('data-abha-number') || '').toLowerCase();
|
||||
var abhaId = (row.getAttribute('data-abha-id') || '').toLowerCase();
|
||||
var pCode = (row.getAttribute('data-patient-p-code') || '').toLowerCase();
|
||||
var pName = (row.getAttribute('data-patient-name') || '').toLowerCase();
|
||||
var pId = (row.getAttribute('data-patient-id') || '').toLowerCase();
|
||||
var cContext = (row.getAttribute('data-care-context') || '').toLowerCase();
|
||||
|
||||
var matches = (abhaAddr !== '' && abhaAddr.indexOf(sTerm) !== -1)
|
||||
|| (abhaId !== '' && abhaId.indexOf(sTerm) !== -1)
|
||||
|| (sDigits.length >= 6 && abhaNum !== '' && abhaNum.indexOf(sDigits) !== -1)
|
||||
|| (sDigits.length >= 6 && abhaId.replace(/\D/g, '').indexOf(sDigits) !== -1)
|
||||
|| (pCode !== '' && pCode.indexOf(sTerm) !== -1)
|
||||
|| (pName !== '' && pName.indexOf(sTerm) !== -1)
|
||||
|| (pId !== '' && pId === sTerm)
|
||||
|| (cContext !== '' && cContext.indexOf(sTerm) !== -1)
|
||||
|| (row.textContent || '').toLowerCase().indexOf(sTerm) !== -1;
|
||||
|
||||
if (matches) {
|
||||
row.style.display = '';
|
||||
matchCount++;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
function filterInvoiceTable() {
|
||||
var rows = document.querySelectorAll('#invoiceCard tbody tr');
|
||||
var sTerm = currentAbhaFilter.toLowerCase();
|
||||
var sDigits = sTerm.replace(/\D/g, '');
|
||||
var matchCount = 0;
|
||||
|
||||
rows.forEach(function (row) {
|
||||
if (sTerm === '') {
|
||||
row.style.display = '';
|
||||
matchCount++;
|
||||
return;
|
||||
}
|
||||
var abhaAddr = (row.getAttribute('data-abha-address') || '').toLowerCase();
|
||||
var abhaNum = (row.getAttribute('data-abha-number') || '').toLowerCase();
|
||||
var abhaId = (row.getAttribute('data-abha-id') || '').toLowerCase();
|
||||
var pCode = (row.getAttribute('data-patient-p-code') || '').toLowerCase();
|
||||
var pName = (row.getAttribute('data-patient-name') || '').toLowerCase();
|
||||
var pId = (row.getAttribute('data-patient-id') || '').toLowerCase();
|
||||
var cContext = (row.getAttribute('data-care-context') || '').toLowerCase();
|
||||
|
||||
var matches = (abhaAddr !== '' && abhaAddr.indexOf(sTerm) !== -1)
|
||||
|| (abhaId !== '' && abhaId.indexOf(sTerm) !== -1)
|
||||
|| (sDigits.length >= 6 && abhaNum !== '' && abhaNum.indexOf(sDigits) !== -1)
|
||||
|| (sDigits.length >= 6 && abhaId.replace(/\D/g, '').indexOf(sDigits) !== -1)
|
||||
|| (pCode !== '' && pCode.indexOf(sTerm) !== -1)
|
||||
|| (pName !== '' && pName.indexOf(sTerm) !== -1)
|
||||
|| (pId !== '' && pId === sTerm)
|
||||
|| (cContext !== '' && cContext.indexOf(sTerm) !== -1)
|
||||
|| (row.textContent || '').toLowerCase().indexOf(sTerm) !== -1;
|
||||
|
||||
if (matches) {
|
||||
row.style.display = '';
|
||||
matchCount++;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
function filterOpdBookTable() {
|
||||
var rows = document.querySelectorAll('#opdBookCard tbody tr');
|
||||
var sTerm = currentAbhaFilter.toLowerCase();
|
||||
var sDigits = sTerm.replace(/\D/g, '');
|
||||
var matchCount = 0;
|
||||
|
||||
rows.forEach(function (row) {
|
||||
if (sTerm === '') {
|
||||
row.style.display = '';
|
||||
matchCount++;
|
||||
return;
|
||||
}
|
||||
var abhaAddr = (row.getAttribute('data-abha-address') || '').toLowerCase();
|
||||
var abhaNum = (row.getAttribute('data-abha-number') || '').toLowerCase();
|
||||
var pCode = (row.getAttribute('data-patient-p-code') || '').toLowerCase();
|
||||
var pName = (row.getAttribute('data-patient-name') || '').toLowerCase();
|
||||
var pId = (row.getAttribute('data-patient-id') || '').toLowerCase();
|
||||
|
||||
var matches = (abhaAddr !== '' && abhaAddr.indexOf(sTerm) !== -1)
|
||||
|| (sDigits.length >= 6 && abhaNum !== '' && abhaNum.indexOf(sDigits) !== -1)
|
||||
|| (pCode !== '' && pCode.indexOf(sTerm) !== -1)
|
||||
|| (pName !== '' && pName.indexOf(sTerm) !== -1)
|
||||
|| (pId !== '' && pId === sTerm)
|
||||
|| (row.textContent || '').toLowerCase().indexOf(sTerm) !== -1;
|
||||
|
||||
if (matches) {
|
||||
row.style.display = '';
|
||||
matchCount++;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
return matchCount;
|
||||
}
|
||||
|
||||
function updateAllTablesForAbhaFilter() {
|
||||
updateTaskTableDisplay();
|
||||
var consultMatches = filterOpdConsultTable();
|
||||
var invoiceMatches = filterInvoiceTable();
|
||||
var opdBookMatches = filterOpdBookTable();
|
||||
|
||||
var qRows = document.querySelectorAll('#taskTable tbody tr:not(#taskTableEmptyRow)');
|
||||
var qVisible = 0;
|
||||
qRows.forEach(function (r) { if (r.style.display !== 'none') qVisible++; });
|
||||
|
||||
var total = qVisible + consultMatches + invoiceMatches + opdBookMatches;
|
||||
var statsEl = document.getElementById('activeAbhaMatchStats');
|
||||
if (statsEl) {
|
||||
statsEl.textContent = total + ' matches found (' + qVisible + ' queue, ' + consultMatches + ' consult, ' + invoiceMatches + ' invoice, ' + opdBookMatches + ' tokens)';
|
||||
}
|
||||
}
|
||||
|
||||
function applyGlobalAbhaFilter(val) {
|
||||
val = (val || '').trim();
|
||||
currentAbhaFilter = val;
|
||||
var inputEl = document.getElementById('globalAbhaFilterInput');
|
||||
if (inputEl) {
|
||||
inputEl.value = val;
|
||||
}
|
||||
|
||||
var noticeEl = document.getElementById('activeAbhaFilterNotice');
|
||||
var textEl = document.getElementById('activeAbhaFilterText');
|
||||
var inspectorCard = document.getElementById('patientAbhaInspectorCard');
|
||||
|
||||
if (val !== '') {
|
||||
if (noticeEl) noticeEl.classList.remove('d-none');
|
||||
if (textEl) textEl.textContent = val;
|
||||
if (currentCategoryFilter === 'dashboard') {
|
||||
applyFilter('all');
|
||||
}
|
||||
loadPatientAbhaInspector(val);
|
||||
} else {
|
||||
if (noticeEl) noticeEl.classList.add('d-none');
|
||||
if (inspectorCard) inspectorCard.classList.add('d-none');
|
||||
}
|
||||
|
||||
updateAllTablesForAbhaFilter();
|
||||
}
|
||||
|
||||
function clearGlobalAbhaFilter() {
|
||||
applyGlobalAbhaFilter('');
|
||||
}
|
||||
|
||||
function loadPatientAbhaInspector(abhaAddress) {
|
||||
var card = document.getElementById('patientAbhaInspectorCard');
|
||||
var tbody = document.getElementById('inspCareContextTbody');
|
||||
if (!card || !tbody) {
|
||||
return;
|
||||
}
|
||||
|
||||
card.classList.remove('d-none');
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-3"><div class="spinner-border spinner-border-sm text-primary me-2"></div>Loading patient care contexts & FHIR readiness...</td></tr>';
|
||||
|
||||
var url = '<?= base_url('AbdmGateway/hip_patient_care_contexts') ?>?abha_address=' + encodeURIComponent(abhaAddress);
|
||||
|
||||
fetch(url, {
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
}).then(function (r) {
|
||||
return r.json();
|
||||
}).then(function (res) {
|
||||
if (!res || res.ok !== 1 || !res.patient) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-warning py-3"><i class="bi bi-info-circle me-1"></i>No patient demographics or active care contexts found for <strong>' + hesc(abhaAddress) + '</strong>. You can initiate linking via the HIP Link button.</td></tr>';
|
||||
document.getElementById('inspPatientName').textContent = 'Patient Not Linked Yet';
|
||||
document.getElementById('inspAbhaAddress').textContent = abhaAddress;
|
||||
document.getElementById('inspAbhaNumber').textContent = '-';
|
||||
document.getElementById('inspPhone').textContent = '-';
|
||||
document.getElementById('inspGatewayStatus').innerHTML = '<span class="badge bg-secondary">Unregistered</span>';
|
||||
document.getElementById('inspTotalContexts').textContent = '0';
|
||||
document.getElementById('inspLinkedContexts').textContent = '0';
|
||||
document.getElementById('inspUnlinkedContexts').textContent = '0';
|
||||
document.getElementById('inspFhirCount').textContent = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
var pat = res.patient;
|
||||
var contexts = res.care_contexts || [];
|
||||
var linkedRefs = res.linked_refs || [];
|
||||
|
||||
document.getElementById('inspPatientName').textContent = pat.name || 'Patient';
|
||||
document.getElementById('inspPatientGender').textContent = pat.gender || 'O';
|
||||
document.getElementById('inspPatientYob').textContent = 'YOB: ' + (pat.year_of_birth || '-');
|
||||
document.getElementById('inspPatientUhid').textContent = 'UHID: ' + (pat.patient_ref || ('P-' + pat.id));
|
||||
document.getElementById('inspAbhaAddress').textContent = pat.abha_address || abhaAddress;
|
||||
document.getElementById('inspAbhaNumber').textContent = pat.abha_number || '-';
|
||||
document.getElementById('inspPhone').textContent = pat.phone || '-';
|
||||
|
||||
var linkedCount = 0;
|
||||
var fhirCount = 0;
|
||||
contexts.forEach(function (c) {
|
||||
if (c.is_linked) linkedCount++;
|
||||
if (c.is_fhir_ready) fhirCount++;
|
||||
});
|
||||
|
||||
document.getElementById('inspTotalContexts').textContent = contexts.length;
|
||||
document.getElementById('inspLinkedContexts').textContent = linkedCount;
|
||||
document.getElementById('inspUnlinkedContexts').textContent = contexts.length - linkedCount;
|
||||
document.getElementById('inspFhirCount').textContent = fhirCount;
|
||||
|
||||
if (linkedRefs.length > 0) {
|
||||
document.getElementById('inspGatewayStatus').innerHTML = '<span class="badge bg-success"><i class="bi bi-shield-check me-1"></i>Gateway Verified (' + linkedRefs.length + ' links)</span>';
|
||||
} else if (linkedCount > 0) {
|
||||
document.getElementById('inspGatewayStatus').innerHTML = '<span class="badge bg-primary"><i class="bi bi-check2-circle me-1"></i>Local Linked</span>';
|
||||
} else {
|
||||
document.getElementById('inspGatewayStatus').innerHTML = '<span class="badge bg-warning text-dark"><i class="bi bi-exclamation-triangle me-1"></i>Unlinked</span>';
|
||||
}
|
||||
|
||||
if (contexts.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-3">No care contexts registered for this patient yet.</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
contexts.forEach(function (c) {
|
||||
var ref = c.ref || '';
|
||||
var isLinked = !!c.is_linked;
|
||||
var isFhir = !!c.is_fhir_ready;
|
||||
var hiType = c.hi_type || 'OPConsultRecord';
|
||||
|
||||
var linkBadge = isLinked
|
||||
? '<span class="badge bg-success"><i class="bi bi-check-circle me-1"></i>LINKED</span>'
|
||||
: '<span class="badge bg-warning text-dark"><i class="bi bi-clock me-1"></i>UNLINKED</span>';
|
||||
|
||||
var fhirBadge = isFhir
|
||||
? '<span class="badge bg-info"><i class="bi bi-file-earmark-code me-1"></i>Ready</span>'
|
||||
: '<span class="badge bg-light text-muted border">Pending</span>';
|
||||
|
||||
// Resolve preview button
|
||||
var fhirPreviewBtn = '';
|
||||
var opdMatch = ref.match(/^OPD-(\d+)/i);
|
||||
if (opdMatch) {
|
||||
var opdId = opdMatch[1];
|
||||
var sMatch = ref.match(/-S(\d+)-/i);
|
||||
var sId = sMatch ? sMatch[1] : 0;
|
||||
var previewUrl = sId > 0
|
||||
? '<?= base_url('Opd_prescription/fhir_bundle_preview') ?>/' + opdId + '/' + sId
|
||||
: '<?= base_url('Opd_prescription/fhir_bundle_preview') ?>/' + opdId;
|
||||
fhirPreviewBtn = '<button type="button" class="btn btn-sm btn-outline-primary py-0 px-2 ms-1 insp-preview-fhir" data-url="' + previewUrl + '" data-title="Care Context ' + ref + ' FHIR"><i class="bi bi-eye me-1"></i>Preview FHIR</button>';
|
||||
}
|
||||
|
||||
var actionBtn = '';
|
||||
if (isLinked) {
|
||||
actionBtn = '<span class="badge bg-success-subtle text-success border border-success-subtle py-1 px-2"><i class="bi bi-check-all me-1"></i>Active</span>';
|
||||
} else {
|
||||
actionBtn = '<button type="button" class="btn btn-sm btn-outline-success py-0 px-2 btn-insp-link" data-ref="' + hesc(ref) + '" data-type="' + hesc(hiType) + '" data-patient-id="' + pat.id + '" data-patient-name="' + hesc(pat.name) + '" data-abha="' + hesc(pat.abha_address || abhaAddress) + '"><i class="bi bi-link-45deg me-1"></i>Link Context</button>';
|
||||
}
|
||||
|
||||
html += '<tr>';
|
||||
html += '<td><code class="fw-semibold text-primary">' + hesc(ref) + '</code></td>';
|
||||
html += '<td>' + hesc(c.display || ref) + '</td>';
|
||||
html += '<td><span class="badge bg-secondary-subtle text-secondary border">' + hesc(hiType) + '</span></td>';
|
||||
html += '<td>' + fhirBadge + ' ' + fhirPreviewBtn + '</td>';
|
||||
html += '<td>' + linkBadge + '</td>';
|
||||
html += '<td class="text-end">' + actionBtn + '</td>';
|
||||
html += '</tr>';
|
||||
});
|
||||
|
||||
tbody.innerHTML = html;
|
||||
}).catch(function (err) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-danger py-3"><i class="bi bi-exclamation-octagon me-1"></i>Failed to load contexts: ' + hesc(err.message || 'Network error') + '</td></tr>';
|
||||
});
|
||||
}
|
||||
|
||||
function applyFilter(filter) {
|
||||
currentCategoryFilter = filter;
|
||||
|
||||
|
|
@ -814,12 +1370,15 @@
|
|||
} else if (filter === 'opd_book') {
|
||||
if (taskTableCard) taskTableCard.classList.add('d-none');
|
||||
if (opdBookCard) opdBookCard.classList.remove('d-none');
|
||||
filterOpdBookTable();
|
||||
} else if (filter === 'opd_consult_publish') {
|
||||
if (taskTableCard) taskTableCard.classList.add('d-none');
|
||||
if (opdConsultCard) opdConsultCard.classList.remove('d-none');
|
||||
filterOpdConsultTable();
|
||||
} else if (filter === 'invoice') {
|
||||
if (taskTableCard) taskTableCard.classList.add('d-none');
|
||||
if (invoiceCard) invoiceCard.classList.remove('d-none');
|
||||
filterInvoiceTable();
|
||||
} else {
|
||||
if (taskTableCard) taskTableCard.classList.remove('d-none');
|
||||
var titleEl = document.getElementById('taskTableTitle');
|
||||
|
|
@ -1468,7 +2027,165 @@
|
|||
});
|
||||
});
|
||||
|
||||
applyFilter('dashboard');
|
||||
// Wire ABHA Filter Bar Buttons
|
||||
var btnApplyAbha = document.getElementById('btnApplyAbhaFilter');
|
||||
if (btnApplyAbha) {
|
||||
btnApplyAbha.addEventListener('click', function () {
|
||||
var input = document.getElementById('globalAbhaFilterInput');
|
||||
applyGlobalAbhaFilter(input ? input.value : '');
|
||||
});
|
||||
}
|
||||
|
||||
var inputAbhaFilter = document.getElementById('globalAbhaFilterInput');
|
||||
if (inputAbhaFilter) {
|
||||
inputAbhaFilter.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
applyGlobalAbhaFilter(inputAbhaFilter.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var btnClearAbha = document.getElementById('btnClearAbhaFilter');
|
||||
if (btnClearAbha) {
|
||||
btnClearAbha.addEventListener('click', function () {
|
||||
clearGlobalAbhaFilter();
|
||||
});
|
||||
}
|
||||
|
||||
var btnNoticeClear = document.getElementById('btnNoticeClearFilter');
|
||||
if (btnNoticeClear) {
|
||||
btnNoticeClear.addEventListener('click', function () {
|
||||
clearGlobalAbhaFilter();
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('.quick-abha-pill').forEach(function (pill) {
|
||||
pill.addEventListener('click', function () {
|
||||
var abhaVal = pill.getAttribute('data-abha') || '';
|
||||
applyGlobalAbhaFilter(abhaVal);
|
||||
});
|
||||
});
|
||||
|
||||
// Wire Inspector Actions
|
||||
var btnCloseInsp = document.getElementById('btnCloseInspector');
|
||||
if (btnCloseInsp) {
|
||||
btnCloseInsp.addEventListener('click', function () {
|
||||
var card = document.getElementById('patientAbhaInspectorCard');
|
||||
if (card) card.classList.add('d-none');
|
||||
});
|
||||
}
|
||||
|
||||
var btnRefreshInsp = document.getElementById('btnRefreshInspector');
|
||||
if (btnRefreshInsp) {
|
||||
btnRefreshInsp.addEventListener('click', function () {
|
||||
if (currentAbhaFilter !== '') {
|
||||
loadPatientAbhaInspector(currentAbhaFilter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var btnVerifyGateway = document.getElementById('btnVerifyAbhaGateway');
|
||||
if (btnVerifyGateway) {
|
||||
btnVerifyGateway.addEventListener('click', function () {
|
||||
if (!currentAbhaFilter) {
|
||||
setStatus('Please specify an ABHA address to query the ABDM Gateway.', true);
|
||||
return;
|
||||
}
|
||||
var statusEl = document.getElementById('inspGatewayStatus');
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<span class="badge bg-info"><span class="spinner-border spinner-border-sm me-1"></span>Verifying on Gateway...</span>';
|
||||
}
|
||||
var gwUrl = '<?= base_url('AbdmGateway/hip_patient_links') ?>?abha_address=' + encodeURIComponent(currentAbhaFilter);
|
||||
fetch(gwUrl, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }
|
||||
}).then(function (r) { return r.json(); })
|
||||
.then(function (res) {
|
||||
if (res && res.ok === 1 && res.links && res.links.length > 0) {
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<span class="badge bg-success"><i class="bi bi-shield-check me-1"></i>Gateway Verified (' + res.links.length + ' links)</span>';
|
||||
}
|
||||
setStatus('ABDM Gateway verified ' + res.links.length + ' linked care context(s) for patient.');
|
||||
} else if (res && res.ok === 1) {
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<span class="badge bg-warning text-dark"><i class="bi bi-info-circle me-1"></i>No Gateway Links Found</span>';
|
||||
}
|
||||
setStatus('No active links recorded on Gateway for this ABHA address yet.');
|
||||
} else {
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<span class="badge bg-danger"><i class="bi bi-exclamation-octagon me-1"></i>Check Failed</span>';
|
||||
}
|
||||
setStatus('Gateway check returned error: ' + (res ? (res.error_text || res.message || 'Unknown') : 'Network error'), true);
|
||||
}
|
||||
}).catch(function (err) {
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<span class="badge bg-danger"><i class="bi bi-exclamation-octagon me-1"></i>Error</span>';
|
||||
}
|
||||
setStatus('Gateway verification failed: ' + err.message, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Delegated click handler for inspector actions and table filter triggers
|
||||
document.addEventListener('click', function (e) {
|
||||
// Clickable ABHA badge on any table
|
||||
var abhaFilterTarget = e.target.closest('.clickable-abha-filter');
|
||||
if (abhaFilterTarget) {
|
||||
e.preventDefault();
|
||||
var abhaVal = (abhaFilterTarget.getAttribute('data-patient-abha-address')
|
||||
|| abhaFilterTarget.getAttribute('data-patient-abha-number')
|
||||
|| abhaFilterTarget.getAttribute('data-abha-id')
|
||||
|| abhaFilterTarget.textContent || '').trim();
|
||||
if (abhaVal) {
|
||||
applyGlobalAbhaFilter(abhaVal);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Preview FHIR from within inspector table
|
||||
var previewFhirTarget = e.target.closest('.insp-preview-fhir');
|
||||
if (previewFhirTarget) {
|
||||
e.preventDefault();
|
||||
var previewUrl = previewFhirTarget.getAttribute('data-url') || '';
|
||||
var previewTitle = previewFhirTarget.getAttribute('data-title') || 'FHIR Preview';
|
||||
if (previewUrl) {
|
||||
showInlineFhirPreview(previewUrl, previewTitle);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Link context from within inspector table
|
||||
var linkTarget = e.target.closest('.btn-insp-link');
|
||||
if (linkTarget) {
|
||||
e.preventDefault();
|
||||
var patId = parseInt(linkTarget.getAttribute('data-patient-id') || '0', 10);
|
||||
var patName = linkTarget.getAttribute('data-patient-name') || '';
|
||||
var abhaAddr = linkTarget.getAttribute('data-abha') || currentAbhaFilter;
|
||||
var cRef = linkTarget.getAttribute('data-ref') || '';
|
||||
var cType = linkTarget.getAttribute('data-type') || 'OPConsultRecord';
|
||||
|
||||
if (typeof window.openAbdmHipLinkModal === 'function') {
|
||||
window.openAbdmHipLinkModal(patId, abhaAddr, {
|
||||
patient_name: patName,
|
||||
care_context_reference: cRef,
|
||||
hi_type: cType,
|
||||
onLinked: function () {
|
||||
if (currentAbhaFilter !== '') {
|
||||
loadPatientAbhaInspector(currentAbhaFilter);
|
||||
}
|
||||
updateAllTablesForAbhaFilter();
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (currentAbhaFilter !== '') {
|
||||
applyGlobalAbhaFilter(currentAbhaFilter);
|
||||
} else {
|
||||
applyFilter('dashboard');
|
||||
}
|
||||
|
||||
modalConfirmBtn.addEventListener('click', function () {
|
||||
if (!selectedRow || !selectedAction) {
|
||||
|
|
|
|||
|
|
@ -130,4 +130,128 @@ final class AbdmTaskBoardBackfillTest extends CIUnitTestCase
|
|||
$this->assertSame('status', $params[0]->getName());
|
||||
$this->assertSame('all', $params[0]->getDefaultValue());
|
||||
}
|
||||
|
||||
public function testGetAbhaPatientsListReturnsNormalizedArray(): void
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
$db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('patient_master') . " (
|
||||
id INTEGER PRIMARY KEY,
|
||||
p_code TEXT,
|
||||
p_fname TEXT,
|
||||
p_lname TEXT,
|
||||
dob TEXT,
|
||||
age TEXT,
|
||||
gender INTEGER,
|
||||
abha_id TEXT,
|
||||
abha_address TEXT,
|
||||
abha_no TEXT,
|
||||
mphone1 TEXT
|
||||
)");
|
||||
|
||||
$testPatientId = 998876;
|
||||
$db->table('patient_master')->where('id', $testPatientId)->delete();
|
||||
$db->table('patient_master')->insert([
|
||||
'id' => $testPatientId,
|
||||
'p_code' => 'P-TEST-998876',
|
||||
'p_fname' => 'Janvi',
|
||||
'p_lname' => 'Bisht',
|
||||
'mphone1' => '9876543212',
|
||||
'gender' => 2,
|
||||
'abha_address' => 'janvibisht2506@sbx',
|
||||
'abha_id' => '91747451787144',
|
||||
]);
|
||||
|
||||
try {
|
||||
$controller = (new ReflectionClass(AbdmTaskBoard::class))->newInstanceWithoutConstructor();
|
||||
$dbProp = new ReflectionProperty(AbdmTaskBoard::class, 'db');
|
||||
$dbProp->setAccessible(true);
|
||||
$dbProp->setValue($controller, $db);
|
||||
|
||||
$method = new ReflectionMethod(AbdmTaskBoard::class, 'getAbhaPatientsList');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$patients = $method->invoke($controller);
|
||||
$this->assertIsArray($patients);
|
||||
$this->assertNotEmpty($patients);
|
||||
|
||||
$found = false;
|
||||
foreach ($patients as $p) {
|
||||
if ($p['id'] === $testPatientId) {
|
||||
$this->assertSame('janvibisht2506@sbx', $p['abha_address']);
|
||||
$this->assertSame('91747451787144', $p['abha_number']);
|
||||
$this->assertSame('P-TEST-998876', $p['p_code']);
|
||||
$this->assertSame('Janvi Bisht', $p['name']);
|
||||
$this->assertSame('F', $p['gender']);
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($found, 'Inserted test patient was not found in getAbhaPatientsList');
|
||||
} finally {
|
||||
$db->table('patient_master')->where('id', $testPatientId)->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function testEnrichTasksWithHealthRecordStateAttachesPatientAbha(): void
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
$db->query("CREATE TABLE IF NOT EXISTS " . $db->prefixTable('patient_master') . " (
|
||||
id INTEGER PRIMARY KEY,
|
||||
p_code TEXT,
|
||||
p_fname TEXT,
|
||||
p_lname TEXT,
|
||||
dob TEXT,
|
||||
age TEXT,
|
||||
gender INTEGER,
|
||||
abha_id TEXT,
|
||||
abha_address TEXT,
|
||||
abha_no TEXT,
|
||||
mphone1 TEXT
|
||||
)");
|
||||
|
||||
$testPatientId = 998877;
|
||||
$db->table('patient_master')->where('id', $testPatientId)->delete();
|
||||
$db->table('patient_master')->insert([
|
||||
'id' => $testPatientId,
|
||||
'p_code' => 'P-TEST-998877',
|
||||
'p_fname' => 'Test',
|
||||
'p_lname' => 'AbhaPatient',
|
||||
'mphone1' => '9876543210',
|
||||
'gender' => 1,
|
||||
'abha_address' => 'testpatient998877@sbx',
|
||||
'abha_id' => '91747451787143',
|
||||
]);
|
||||
|
||||
try {
|
||||
$controller = (new ReflectionClass(AbdmTaskBoard::class))->newInstanceWithoutConstructor();
|
||||
$dbProp = new ReflectionProperty(AbdmTaskBoard::class, 'db');
|
||||
$dbProp->setAccessible(true);
|
||||
$dbProp->setValue($controller, $db);
|
||||
|
||||
$method = new ReflectionMethod(AbdmTaskBoard::class, 'enrichTasksWithHealthRecordState');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$mockTasks = [
|
||||
[
|
||||
'id' => 1,
|
||||
'task_code' => 'ABDM-TEST-ENRICH-1',
|
||||
'task_type' => 'opd_prescription_publish',
|
||||
'patient_id' => $testPatientId,
|
||||
'entity_id' => '100',
|
||||
'status' => 'pending',
|
||||
'abha_id' => '',
|
||||
]
|
||||
];
|
||||
|
||||
$enriched = $method->invoke($controller, $mockTasks);
|
||||
$this->assertCount(1, $enriched);
|
||||
$this->assertSame('testpatient998877@sbx', $enriched[0]['patient_abha_address']);
|
||||
$this->assertSame('91747451787143', $enriched[0]['patient_abha_number']);
|
||||
$this->assertSame('P-TEST-998877', $enriched[0]['patient_p_code']);
|
||||
$this->assertSame('9876543210', $enriched[0]['patient_phone']);
|
||||
} finally {
|
||||
$db->table('patient_master')->where('id', $testPatientId)->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,9 @@ final class TestHipDemographicsCareContext extends CIUnitTestCase
|
|||
]);
|
||||
|
||||
$this->gateway = new AbdmGateway();
|
||||
$req = \Config\Services::request();
|
||||
\Config\Services::resetSingle('request');
|
||||
\Config\Services::resetSingle('response');
|
||||
$req = \Config\Services::request(null, false);
|
||||
$resp = \Config\Services::response();
|
||||
$logger = \Config\Services::logger();
|
||||
$this->gateway->initController($req, $resp, $logger);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user