diff --git a/app/Controllers/Abha.php b/app/Controllers/Abha.php index 144df6f..6222e59 100644 --- a/app/Controllers/Abha.php +++ b/app/Controllers/Abha.php @@ -474,6 +474,7 @@ class Abha extends BaseController 'abha_qr_url' => $abhaQrUrl, 'patient_mobile' => $mobileNo, 'mobile_verified' => $mobileVerified, + 'stored_abha_card' => trim((string) ($patient['abha_card_base64'] ?? '')), ]); } @@ -803,6 +804,7 @@ class Abha extends BaseController $abhaMeta = [ 'abha_address' => (string) ($this->request->getPost('abha_address') ?? ''), 'profile_photo' => (string) ($this->request->getPost('photo') ?? ''), + 'card_base64' => (string) ($this->request->getPost('card_base64') ?? ''), 'verified_status' => (string) ($this->request->getPost('verified_status') ?? ''), 'verification_type' => (string) ($this->request->getPost('verification_type') ?? ''), 'kyc_verified' => $this->request->getPost('kyc_verified'), @@ -1025,7 +1027,7 @@ class Abha extends BaseController } $selectFields = array_values(array_intersect( - ['id', 'p_code', 'p_fname', 'p_lname', 'gender', 'dob', 'age', 'mphone1', 'udai', 'add1', 'district', 'state', 'zip'], + ['id', 'p_code', 'p_fname', 'p_lname', 'gender', 'dob', 'age', 'mphone1', 'udai', 'add1', 'district', 'state', 'zip', 'profile_file_id', 'profile_picture', 'abha_profile_photo_base64'], $fields )); $select = implode(',', $selectFields); @@ -1109,6 +1111,7 @@ class Abha extends BaseController 'dob' => $rowDob, 'age' => $rowAgeYears, 'mobile' => (string) ($row['mphone1'] ?? ''), + 'photo_url' => $this->resolvePatientPhotoUrl($row), 'address' => trim(implode(', ', array_filter([ (string) ($row['add1'] ?? ''), (string) ($row['district'] ?? ''), @@ -1391,6 +1394,11 @@ class Abha extends BaseController if ($profilePhoto !== '' && in_array('abha_profile_photo_base64', $fields, true)) { $target['abha_profile_photo_base64'] = $profilePhoto; } + + $cardBase64 = trim((string) ($abhaMeta['card_base64'] ?? '')); + if ($cardBase64 !== '' && in_array('abha_card_base64', $fields, true)) { + $target['abha_card_base64'] = $cardBase64; + } } private function toDbBool($value): int diff --git a/app/Database/Migrations/2026-08-13-000001_AddAbhaCardToPatientMaster.php b/app/Database/Migrations/2026-08-13-000001_AddAbhaCardToPatientMaster.php new file mode 100644 index 0000000..869caab --- /dev/null +++ b/app/Database/Migrations/2026-08-13-000001_AddAbhaCardToPatientMaster.php @@ -0,0 +1,43 @@ +tableExists('patient_master')) { + return; + } + + $fields = $this->getColumns('patient_master'); + if (! in_array('abha_card_base64', $fields, true)) { + $this->forge->addColumn('patient_master', [ + 'abha_card_base64' => [ + 'type' => 'LONGTEXT', + 'null' => true, + ], + ]); + } + } + + public function down(): void + { + if ($this->tableExists('patient_master') && in_array('abha_card_base64', $this->getColumns('patient_master'), true)) { + $this->forge->dropColumn('patient_master', 'abha_card_base64'); + } + } + + private function getColumns(string $table): array + { + $result = $this->db->query('SHOW COLUMNS FROM `' . $table . '`')->getResultArray(); + return array_map(static fn (array $row): string => (string) ($row['Field'] ?? ''), $result); + } + + private function tableExists(string $table): bool + { + return ! empty($this->db->query("SHOW TABLES LIKE '" . $table . "'")->getRowArray()); + } +} diff --git a/app/Views/abha/card.php b/app/Views/abha/card.php index ce7b7fc..6e60560 100644 --- a/app/Views/abha/card.php +++ b/app/Views/abha/card.php @@ -168,6 +168,8 @@ background: #fff; } .qr-value { margin-top: 5px; font-size: 10px; color: #3b5474; word-break: break-word; } + .official-card-wrap { margin-top: 14px; text-align: center; } + .official-card { display: block; width: 100%; max-height: 480px; object-fit: contain; border: 1px solid #d8e6fb; border-radius: 8px; background: #fff; } .abha-number-wrap { margin-top: 12px; border: 1px solid #d3e3f9; @@ -241,6 +243,10 @@ $abhaQrUrl = esc($abha_qr_url ?? ''); $barcodeImage = esc($hms_barcode_image ?? ''); $patientMobile = esc($patient_mobile ?? ''); $mobileVerified = (bool) ($mobile_verified ?? false); +$storedAbhaCard = trim((string) ($stored_abha_card ?? '')); +$storedAbhaCardSrc = $storedAbhaCard !== '' + ? (str_starts_with($storedAbhaCard, 'data:') ? $storedAbhaCard : 'data:image/png;base64,' . $storedAbhaCard) + : ''; ?>
@@ -263,6 +269,13 @@ $mobileVerified = (bool) ($mobile_verified ?? false);
+ +
+
Stored official ABHA card
+ Stored official ABHA card +
+ +
diff --git a/app/Views/partials/abha_patient_match_modal.php b/app/Views/partials/abha_patient_match_modal.php index 0603f79..c3da691 100644 --- a/app/Views/partials/abha_patient_match_modal.php +++ b/app/Views/partials/abha_patient_match_modal.php @@ -60,6 +60,7 @@ 0 found
+
No matching patient found in HMS by name, age or gender. You can create a new patient record.
@@ -118,6 +119,27 @@ window.AbhaPatientMatchModal = (function () { function alertHtml(type, msg) { return '
' + msg + '
'; } + function profileValue(value) { return String(value == null ? '' : value).trim().toUpperCase(); } + function normalizedGender(value) { + var gender = profileValue(value); + if (gender === '1' || gender === 'M' || gender === 'MALE') return 'M'; + if (gender === '2' || gender === 'F' || gender === 'FEMALE') return 'F'; + if (gender === '3' || gender === 'O' || gender === 'OTHER') return 'O'; + return gender; + } + function renderUpdatePreview(c) { + var fields = []; + var p = _profile || {}; + if (profileValue(p.name) !== profileValue(c.name)) fields.push('Name'); + if (normalizedGender(p.gender) !== normalizedGender(c.gender_label)) fields.push('Gender'); + if (String(p.dob || '').slice(0, 4) !== String(c.dob || '').slice(0, 4)) fields.push('Birth year'); + if (String(p.mobile || '').replace(/\D/g, '') !== String(c.mobile || '').replace(/\D/g, '')) fields.push('Mobile'); + var abhaAddress = [p.address, p.district, p.state, p.zip].filter(Boolean).join(', '); + if (profileValue(abhaAddress) !== profileValue(c.address)) fields.push('Address'); + $('#abhaMatch_update_preview').toggleClass('d-none', !fields.length).html( + fields.length ? 'Update Details will change: ' + fields.map(esc).join(', ') : 'No HMS demographic changes detected.' + ); + } function renderProfile(p) { var abhaNum = p.abha_number || ''; @@ -157,7 +179,8 @@ window.AbhaPatientMatchModal = (function () { + '
' + '
' + '
' - + '
' + esc(c.name || '—') + ' (' + esc(c.p_code || '') + ')
' + + (c.photo_url ? ' Patient photo' : '') + + '
' + esc(c.name || '—') + ' (' + esc(c.p_code || '') + ')
' + '
' + badge(m.name, 'Name') + badge(m.age, 'Age ' + (c.age != null ? c.age : '?')) @@ -185,6 +208,8 @@ window.AbhaPatientMatchModal = (function () { $('.abhaMatch-candidate[data-id="' + id + '"]').addClass('border-success bg-success-subtle'); $('.abhaMatch-radio[value="' + id + '"]').prop('checked', true); $('#abhaMatch_keep_existing_btn,#abhaMatch_update_existing_btn').prop('disabled', !id); + var selected = (_profile && window._abhaMatchCandidates || []).find(function (candidate) { return Number(candidate.id) === Number(id); }); + if (selected) renderUpdatePreview(selected); } function submitConfirm(action, patientId, updateMode, $btn) { @@ -205,6 +230,7 @@ window.AbhaPatientMatchModal = (function () { dob: _profile.dob || '', abha_address: _profile.abha_address || '', photo: _profile.photo || '', + card_base64: _profile.card_base64 || '', verified_status: _profile.verified_status || '', verification_type: _profile.verification_type || '', kyc_verified: _profile.kyc_verified, @@ -264,12 +290,14 @@ window.AbhaPatientMatchModal = (function () { return { open: function (profile, candidates, onResolved) { _profile = profile || {}; + window._abhaMatchCandidates = candidates || []; _selectedId = 0; _onResolved = onResolved; $('#abhaMatch_alert').empty(); $('#abhaMatch_keep_existing_btn,#abhaMatch_update_existing_btn').prop('disabled', true); renderProfile(_profile); renderCandidates(candidates || []); + $('#abhaMatch_update_preview').addClass('d-none').empty(); if (_bsModal) { _bsModal.show(); } else { $modalEl.modal('show'); } } };