ABDM M3: flatten nested consent.status from bridge (root cause fix) and redesign OPD Fetch ABDM Records UI

This commit is contained in:
DevSoftTech Server 2026-07-28 02:48:02 +05:30
parent 716ad04d13
commit a1127522ef
2 changed files with 76 additions and 48 deletions

View File

@ -335,6 +335,23 @@ class M3HiuGatewayClient
$response['abdm_consent_artifact_id'] = $abdmConsentId; $response['abdm_consent_artifact_id'] = $abdmConsentId;
} }
// Per bridge contract, consent status/date_range/expiry come back nested
// under `consent{}` (e.g. { consent: { status: "GRANTED", ... } }) rather
// than at the top level. Flatten them so resolveState()/snapshot logic
// (which reads $response['consent_status']) can see the real value —
// otherwise GRANTED consents keep getting misclassified as pending.
$abdmConsentStatus = $this->findFirstValueByKeys($response, ['status']);
if ($abdmConsentStatus !== '') {
$response['consent_status'] = strtoupper($abdmConsentStatus);
}
if (isset($response['consent']) && is_array($response['consent'])) {
foreach (['date_range', 'dateRange', 'expiry', 'purpose', 'hi_types', 'abha_address', 'granted_at'] as $flattenKey) {
if (! isset($response[$flattenKey]) && isset($response['consent'][$flattenKey])) {
$response[$flattenKey] = $response['consent'][$flattenKey];
}
}
}
if (! isset($response['transaction_id'])) { if (! isset($response['transaction_id'])) {
$response['transaction_id'] = (string) ($response['transactionId'] ?? $cleanQuery['transaction_id'] ?? ''); $response['transaction_id'] = (string) ($response['transactionId'] ?? $cleanQuery['transaction_id'] ?? '');
} }

View File

@ -295,49 +295,57 @@ if ($patientPhotoPath === '') {
</div> </div>
<div class="tab-pane fade" id="opd-abdm-tab" role="tabpanel"> <div class="tab-pane fade" id="opd-abdm-tab" role="tabpanel">
<div class="alert alert-light border d-flex justify-content-between align-items-center flex-wrap gap-2 mt-2"> <div class="card border mt-2 mb-3">
<div> <div class="card-body">
<div class="fw-semibold">ABHA Context</div> <div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
<div class="small text-muted"> <div>
ABHA ID: <strong><?= $patientAbhaId !== '' ? esc($patientAbhaId) : 'Not available' ?></strong> <div class="fw-semibold mb-1">ABDM M3 — Fetch Patient Health Records</div>
&nbsp;|&nbsp; <div class="small text-muted">
ABHA Address: <strong><?= $patientAbhaAddress !== '' ? esc($patientAbhaAddress) : 'Not available' ?></strong> ABHA Address: <strong><?= $patientAbhaAddress !== '' ? esc($patientAbhaAddress) : 'Not available' ?></strong>
&nbsp;|&nbsp; &nbsp;|&nbsp;
Status: ABHA Status:
<?php if ($abhaIsVerified) { ?> <?php if ($abhaIsVerified) { ?>
<span class="badge bg-success-subtle text-success border border-success-subtle">VERIFIED</span> <span class="badge bg-success-subtle text-success border border-success-subtle">VERIFIED</span>
<?php } else { ?> <?php } else { ?>
<span class="badge bg-warning-subtle text-warning border border-warning-subtle"><?= $abhaVerifiedStatus !== '' ? esc($abhaVerifiedStatus) : 'UNVERIFIED' ?></span> <span class="badge bg-warning-subtle text-warning border border-warning-subtle"><?= $abhaVerifiedStatus !== '' ? esc($abhaVerifiedStatus) : 'UNVERIFIED' ?></span>
<?php } ?> <?php } ?>
&nbsp;|&nbsp; </div>
Last Consent: <span class="badge bg-secondary" id="abdmLastConsentBadge">Unknown</span> </div>
<span <div class="text-end">
class="ms-1 text-muted" <button type="button" class="btn btn-primary" id="btnAutoAbdmFlow" <?= ($abhaIsVerified && $patientAbhaAddress !== '') ? '' : 'disabled' ?>>
role="button" <span class="abdm-btn-spinner spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>
tabindex="0" <span id="btnAutoAbdmFlowLabel">Fetch ABDM Records</span>
data-bs-toggle="tooltip" </button>
data-bs-placement="top" <div>
title="Completed: data fetched. Granted: approved. Pending: waiting patient action. Failed: previous request failed. Failed - New Required: click New Consent Request." <button type="button" class="btn btn-link btn-sm p-0 mt-1" id="btnLoadAbdmDocs">Refresh list</button>
aria-label="Consent status help" </div>
>?</span> </div>
</div> </div>
</div>
<div class="d-flex gap-2 flex-wrap">
<button type="button" class="btn btn-outline-primary btn-sm" id="btnLoadAbdmDocs">Refresh Fetched Data</button>
<button type="button" class="btn btn-primary btn-sm" id="btnAutoAbdmFlow" <?= ($abhaIsVerified && $patientAbhaAddress !== '') ? '' : 'disabled' ?>>Fetch Request</button>
</div>
</div>
<div id="abdmStatusBox" class="small text-muted mb-2">Click "Refresh Fetched Data" to load ABDM records mapped to this patient.</div> <?php if (! $abhaIsVerified || $patientAbhaAddress === '') { ?>
<div class="alert alert-warning mt-3 mb-0 small">
ABHA must be linked and verified for this patient before ABDM records can be requested. Verify ABHA from the patient's profile first.
</div>
<?php } else { ?>
<div class="small text-muted mt-2 mb-0">
One click sends a consent request to the patient's ABHA (PHR) app. Once the patient approves it there, HMS automatically fetches their health records — no need to click again.
</div>
<?php } ?>
<div class="border rounded p-2 mb-3 bg-light"> <div class="border rounded p-3 mt-3 bg-light">
<div class="d-flex flex-wrap align-items-center gap-2 mb-2" id="abdmFlowSteps"> <div class="d-flex flex-wrap align-items-center gap-2 mb-2" id="abdmFlowSteps">
<span class="abdm-flow-step" data-step="1">1. Requested</span> <span class="abdm-flow-step" data-step="1">1. Consent Requested</span>
<span class="abdm-flow-step" data-step="2">2. Granted</span> <span class="abdm-flow-step" data-step="2">2. Consent Granted</span>
<span class="abdm-flow-step" data-step="3">3. Fetched</span> <span class="abdm-flow-step" data-step="3">3. Records Fetched</span>
</div> </div>
<div class="progress" style="height: 8px;"> <div class="progress mb-2" style="height: 8px;">
<div id="abdmFlowProgressBar" class="progress-bar" role="progressbar" style="width:0%"></div> <div id="abdmFlowProgressBar" class="progress-bar" role="progressbar" style="width:0%"></div>
</div>
<div class="d-flex align-items-center gap-2">
<span class="badge bg-secondary" id="abdmLastConsentBadge">Unknown</span>
<span id="abdmStatusBox" class="small text-muted flex-grow-1">Click "Fetch ABDM Records" to request and load the patient's ABDM health records.</span>
</div>
</div>
</div> </div>
</div> </div>
@ -420,6 +428,7 @@ $(function() {
var autoFlowAttempts = 0; var autoFlowAttempts = 0;
var autoFlowMaxAttempts = 15; var autoFlowMaxAttempts = 15;
var autoFlowNeedsFreshRequest = false; var autoFlowNeedsFreshRequest = false;
var autoFlowRunning = false;
function updateConsentBadge(phase, needsFresh) { function updateConsentBadge(phase, needsFresh) {
var text = (phase || '').toString().toUpperCase(); var text = (phase || '').toString().toUpperCase();
@ -448,7 +457,9 @@ $(function() {
} }
function applyConsentButtonState() { function applyConsentButtonState() {
$('#btnAutoAbdmFlow').text(autoFlowTimer ? 'Fetching...' : 'Fetch Request'); $('#btnAutoAbdmFlow').prop('disabled', autoFlowRunning);
$('#btnAutoAbdmFlow .abdm-btn-spinner').toggleClass('d-none', !autoFlowRunning);
$('#btnAutoAbdmFlowLabel').text(autoFlowRunning ? 'Fetching...' : 'Fetch ABDM Records');
} }
var zoom = 1; var zoom = 1;
@ -760,7 +771,7 @@ $(function() {
autoFlowRequestId = ''; autoFlowRequestId = '';
stopAutoFlowLoop(); stopAutoFlowLoop();
updateConsentBadge('FAILED', false); updateConsentBadge('FAILED', false);
$('#btnAutoAbdmFlow').prop('disabled', false); autoFlowRunning = false;
applyConsentButtonState(); applyConsentButtonState();
$('#abdmStatusBox').removeClass('text-danger').addClass('text-muted').text('Previous request is still being processed. Please wait for the first request to finish before starting a new one.'); $('#abdmStatusBox').removeClass('text-danger').addClass('text-muted').text('Previous request is still being processed. Please wait for the first request to finish before starting a new one.');
return; return;
@ -773,7 +784,7 @@ $(function() {
return; return;
} }
$('#btnAutoAbdmFlow').prop('disabled', false); autoFlowRunning = false;
applyConsentButtonState(); applyConsentButtonState();
if (phase === 'COMPLETED') { if (phase === 'COMPLETED') {
loadAbdmDocs(); loadAbdmDocs();
@ -781,7 +792,7 @@ $(function() {
}) })
.catch(function(err) { .catch(function(err) {
stopAutoFlowLoop(); stopAutoFlowLoop();
$('#btnAutoAbdmFlow').prop('disabled', false); autoFlowRunning = false;
applyConsentButtonState(); applyConsentButtonState();
$('#abdmStatusBox').removeClass('text-muted').addClass('text-danger').text('Auto flow failed: ' + (err.message || err)); $('#abdmStatusBox').removeClass('text-muted').addClass('text-danger').text('Auto flow failed: ' + (err.message || err));
}); });
@ -827,8 +838,7 @@ $(function() {
}); });
$(document).off('click.abdmOpd', '#btnAutoAbdmFlow').on('click.abdmOpd', '#btnAutoAbdmFlow', function() { $(document).off('click.abdmOpd', '#btnAutoAbdmFlow').on('click.abdmOpd', '#btnAutoAbdmFlow', function() {
var $btn = $(this); if (autoFlowRunning) {
if ($btn.prop('disabled')) {
return; return;
} }
stopAutoFlowLoop(); stopAutoFlowLoop();
@ -838,8 +848,9 @@ $(function() {
autoFlowNeedsFreshRequest = false; autoFlowNeedsFreshRequest = false;
} }
setFlowProgress(1); setFlowProgress(1);
$btn.prop('disabled', true).text('Sync Running...'); autoFlowRunning = true;
$('#abdmStatusBox').removeClass('text-danger').addClass('text-muted').text('Starting one-click sync flow...'); applyConsentButtonState();
$('#abdmStatusBox').removeClass('text-danger').addClass('text-muted').text('Sending consent request to patient\'s ABHA app...');
runAutoFlowStep(); runAutoFlowStep();
}); });