billing: redesign ABHA wizard, enhance patient info panels and consult page photo/layout
This commit is contained in:
parent
eac78b7014
commit
fbb03a6abb
|
|
@ -49,6 +49,32 @@ class Opd_prescription extends BaseController
|
||||||
$query = $this->db->query($sql);
|
$query = $this->db->query($sql);
|
||||||
$patient = $query->getResult();
|
$patient = $query->getResult();
|
||||||
|
|
||||||
|
$patientProfileFilePath = '/assets/images/no_image.svg';
|
||||||
|
if (!empty($patient)) {
|
||||||
|
$profilePicturePath = '';
|
||||||
|
if ($this->db->fieldExists('profile_picture', 'patient_master')) {
|
||||||
|
$profilePicturePath = trim((string) ($patient[0]->profile_picture ?? ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
$profileFileId = (int) ($patient[0]->profile_file_id ?? 0);
|
||||||
|
if ($profileFileId > 0 && $this->db->tableExists('file_upload_data')) {
|
||||||
|
$fileRow = $this->db->table('file_upload_data')
|
||||||
|
->select('full_path')
|
||||||
|
->where('id', $profileFileId)
|
||||||
|
->get(1)
|
||||||
|
->getRow();
|
||||||
|
|
||||||
|
$fullPath = trim((string) ($fileRow->full_path ?? ''));
|
||||||
|
if ($fullPath !== '') {
|
||||||
|
$pos = strpos($fullPath, '/uploads/', 1);
|
||||||
|
$patientProfileFilePath = $pos !== false ? substr($fullPath, $pos) : $fullPath;
|
||||||
|
}
|
||||||
|
} elseif ($profilePicturePath !== '') {
|
||||||
|
$pos = strpos($profilePicturePath, '/uploads/', 1);
|
||||||
|
$patientProfileFilePath = $pos !== false ? substr($profilePicturePath, $pos) : $profilePicturePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($patient)) {
|
if (!empty($patient)) {
|
||||||
$patient[0]->str_age = get_age_1($patient[0]->dob ?? null, $patient[0]->age ?? '', $patient[0]->age_in_month ?? '', $patient[0]->estimate_dob ?? '', $opdRow->apointment_date ?? null);
|
$patient[0]->str_age = get_age_1($patient[0]->dob ?? null, $patient[0]->age ?? '', $patient[0]->age_in_month ?? '', $patient[0]->estimate_dob ?? '', $opdRow->apointment_date ?? null);
|
||||||
}
|
}
|
||||||
|
|
@ -110,6 +136,7 @@ class Opd_prescription extends BaseController
|
||||||
'opd_id' => $opdId,
|
'opd_id' => $opdId,
|
||||||
'opd_master' => $opdMaster,
|
'opd_master' => $opdMaster,
|
||||||
'patient_master' => $patient,
|
'patient_master' => $patient,
|
||||||
|
'patient_profile_file_path' => $patientProfileFilePath,
|
||||||
'opd_prescription' => $prescription,
|
'opd_prescription' => $prescription,
|
||||||
'next_visit_options' => $this->getNextVisitOptions(date('Y-m-d')),
|
'next_visit_options' => $this->getNextVisitOptions(date('Y-m-d')),
|
||||||
'addiction_flags' => $this->getPatientAddictionFlags($patientId),
|
'addiction_flags' => $this->getPatientAddictionFlags($patientId),
|
||||||
|
|
|
||||||
|
|
@ -717,11 +717,23 @@ class Patient extends BaseController
|
||||||
return $this->response->setStatusCode(404)->setBody('Patient not found');
|
return $this->response->setStatusCode(404)->setBody('Patient not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$defaultBackUrl = base_url('billing/patient/person_record') . '/' . (int) ($patient->id ?? 0) . '/0';
|
||||||
|
$backUrl = trim((string) $this->request->getGet('back_url'));
|
||||||
|
$backTitle = trim((string) $this->request->getGet('back_title'));
|
||||||
|
if ($backUrl === '') {
|
||||||
|
$backUrl = $defaultBackUrl;
|
||||||
|
}
|
||||||
|
if ($backTitle === '') {
|
||||||
|
$backTitle = 'Profile';
|
||||||
|
}
|
||||||
|
|
||||||
$profileFilePath = $this->getProfileFilePath((int) ($patient->profile_file_id ?? 0));
|
$profileFilePath = $this->getProfileFilePath((int) ($patient->profile_file_id ?? 0));
|
||||||
|
|
||||||
return view('billing/Patient_Profile_Image_V', [
|
return view('billing/Patient_Profile_Image_V', [
|
||||||
'patient' => $patient,
|
'patient' => $patient,
|
||||||
'profileFilePath' => $profileFilePath,
|
'profileFilePath' => $profileFilePath,
|
||||||
|
'backUrl' => $backUrl,
|
||||||
|
'backTitle' => $backTitle,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<h1>Profile Image</h1>
|
<h1>Profile Image</h1>
|
||||||
<nav>
|
<nav>
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="javascript:load_form('<?= base_url('billing/patient/person_record') ?>/<?= esc($patient->id) ?>/0');">Profile</a></li>
|
<li class="breadcrumb-item"><a href="javascript:load_form('<?= esc($backUrl ?? (base_url('billing/patient/person_record') . '/' . (int) ($patient->id ?? 0) . '/0'), 'js') ?>','<?= esc($backTitle ?? 'Profile', 'js') ?>');"><?= esc($backTitle ?? 'Profile') ?></a></li>
|
||||||
<li class="breadcrumb-item active">Image</li>
|
<li class="breadcrumb-item active">Image</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
@ -11,7 +11,10 @@
|
||||||
<section class="section profile">
|
<section class="section profile">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3 class="card-title mb-0">Profile Image</h3>
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||||
|
<h3 class="card-title mb-0">Profile Image</h3>
|
||||||
|
<a href="javascript:load_form('<?= esc($backUrl ?? (base_url('billing/patient/person_record') . '/' . (int) ($patient->id ?? 0) . '/0'), 'js') ?>','<?= esc($backTitle ?? 'Profile', 'js') ?>');" class="btn btn-outline-secondary btn-sm">Back</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="mb-3">Patient: <strong><?= esc($patient->p_fname ?? '') ?></strong></p>
|
<p class="mb-3">Patient: <strong><?= esc($patient->p_fname ?? '') ?></strong></p>
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,94 @@
|
||||||
if ($backTitle === '') {
|
if ($backTitle === '') {
|
||||||
$backTitle = 'Profile';
|
$backTitle = 'Profile';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$patientName = trim((string) (($patient->title ?? '') . ' ' . ($patient->p_fname ?? '')));
|
||||||
|
$relationText = trim((string) ($patient->p_relative ?? ''));
|
||||||
|
$relativeName = trim((string) ($patient->p_rname ?? ''));
|
||||||
|
$relationWithName = trim($relationText . ($relativeName !== '' ? ' ' . $relativeName : ''));
|
||||||
|
|
||||||
|
$ageYears = trim((string) ($patient->age ?? ''));
|
||||||
|
$ageMonths = trim((string) ($patient->age_in_month ?? ''));
|
||||||
|
$ageDisplay = '';
|
||||||
|
if ($ageYears !== '' || $ageMonths !== '') {
|
||||||
|
$ageDisplay = trim(($ageYears !== '' ? ($ageYears . ' Year') : '') . ($ageMonths !== '' ? (' ' . $ageMonths . ' Month') : ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: derive age from DOB when explicit age fields are empty.
|
||||||
|
if ($ageDisplay === '' && !empty($patient->dob)) {
|
||||||
|
$dobTs = strtotime((string) $patient->dob);
|
||||||
|
if ($dobTs !== false) {
|
||||||
|
$today = new DateTime(date('Y-m-d'));
|
||||||
|
$dobDate = new DateTime(date('Y-m-d', $dobTs));
|
||||||
|
$diff = $dobDate->diff($today);
|
||||||
|
if ($diff->y > 0) {
|
||||||
|
$ageDisplay = $diff->y . ' Year';
|
||||||
|
} elseif ($diff->m > 0) {
|
||||||
|
$ageDisplay = $diff->m . ' Month';
|
||||||
|
} else {
|
||||||
|
$ageDisplay = $diff->d . ' Day';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$firstVisitRaw = '';
|
||||||
|
$patientData = (array) ($patient ?? []);
|
||||||
|
foreach (['date_of_registration', 'date_registration', 'insert_date', 'created_at', 'created_on', 'register_date'] as $candidate) {
|
||||||
|
if (!empty($patientData[$candidate])) {
|
||||||
|
$firstVisitRaw = (string) $patientData[$candidate];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$firstVisitDisplay = '-';
|
||||||
|
if ($firstVisitRaw !== '') {
|
||||||
|
$firstVisitTs = strtotime($firstVisitRaw);
|
||||||
|
if ($firstVisitTs !== false) {
|
||||||
|
$firstVisitDisplay = date('d-m-Y', $firstVisitTs);
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.patient-info-card {
|
||||||
|
background: #f5f9ff;
|
||||||
|
border: 1px solid #d8e7ff;
|
||||||
|
border-left: 5px solid #0d6efd;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.patient-info-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0b3b91;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
}
|
||||||
|
.patient-info-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 22px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.patient-info-item {
|
||||||
|
font-size: 1.02rem;
|
||||||
|
line-height: 1.3;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.patient-info-item .label {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #08306b;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.patient-info-item .value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="pagetitle">
|
<div class="pagetitle">
|
||||||
<h1>Consult History</h1>
|
<h1>Consult History</h1>
|
||||||
<nav>
|
<nav>
|
||||||
|
|
@ -25,13 +111,35 @@
|
||||||
<section class="section profile">
|
<section class="section profile">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
|
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||||
<div>
|
<div class="w-100">
|
||||||
<h3 class="card-title mb-0">Old Prescription With Scanned Records</h3>
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||||
<div class="small text-muted mt-1">Patient: <?= esc($patient->p_fname ?? '') ?></div>
|
<h3 class="card-title mb-0">Old Prescription With Scanned Records</h3>
|
||||||
</div>
|
<div class="d-flex align-items-center gap-2">
|
||||||
<div class="d-flex align-items-center gap-2">
|
<a href="javascript:load_form('<?= esc($backUrl, 'js') ?>','<?= esc($backTitle, 'js') ?>');" class="btn btn-outline-secondary btn-sm">Back</a>
|
||||||
<a href="javascript:load_form('<?= esc($backUrl, 'js') ?>','<?= esc($backTitle, 'js') ?>');" class="btn btn-outline-secondary btn-sm">Back</a>
|
<span class="badge bg-secondary"><?= count($opdGroups ?? []) ?> OPD Record(s)</span>
|
||||||
<span class="badge bg-secondary"><?= count($opdGroups ?? []) ?> OPD Record(s)</span>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="patient-info-card">
|
||||||
|
<div class="patient-info-title">Patient Information</div>
|
||||||
|
<div class="patient-info-row">
|
||||||
|
<div class="patient-info-item">
|
||||||
|
<span class="label">Name:</span>
|
||||||
|
<span class="value"><?= esc($patientName !== '' ? $patientName : '-') ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="patient-info-item">
|
||||||
|
<span class="label">Relation:</span>
|
||||||
|
<span class="value"><?= esc($relationWithName !== '' ? $relationWithName : '-') ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="patient-info-item">
|
||||||
|
<span class="label">Age:</span>
|
||||||
|
<span class="value"><?= esc($ageDisplay !== '' ? $ageDisplay : '-') ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="patient-info-item">
|
||||||
|
<span class="label">First Visit Date:</span>
|
||||||
|
<span class="value"><?= esc($firstVisitDisplay) ?></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,33 @@
|
||||||
input:autofill {
|
input:autofill {
|
||||||
background-color: white !important;
|
background-color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ABHA wizard progress track */
|
||||||
|
.abha-progress-wrap { display:flex; align-items:flex-start; margin-bottom:24px; }
|
||||||
|
.abha-progress-wrap .abha-node {
|
||||||
|
display:flex; flex-direction:column; align-items:center; flex:1; position:relative;
|
||||||
|
}
|
||||||
|
.abha-progress-wrap .abha-node:not(:last-child)::after {
|
||||||
|
content:''; position:absolute; top:16px; left:calc(50% + 20px);
|
||||||
|
right:calc(-50% + 20px); height:2px; background:#dee2e6; z-index:0;
|
||||||
|
}
|
||||||
|
.abha-progress-wrap .abha-node.done:not(:last-child)::after { background:#28a745; }
|
||||||
|
.abha-badge {
|
||||||
|
width:32px; height:32px; border-radius:50%;
|
||||||
|
display:flex; align-items:center; justify-content:center;
|
||||||
|
font-weight:700; font-size:13px; z-index:1;
|
||||||
|
background:#dee2e6; color:#6c757d; border:2px solid #dee2e6;
|
||||||
|
transition: all 0.25s;
|
||||||
|
}
|
||||||
|
.abha-node.active .abha-badge { background:#fff; color:#007bff; border-color:#007bff; }
|
||||||
|
.abha-node.done .abha-badge { background:#28a745; color:#fff; border-color:#28a745; }
|
||||||
|
.abha-node-label {
|
||||||
|
font-size:0.72rem; text-align:center; margin-top:5px;
|
||||||
|
color:#aaa; max-width:80px; line-height:1.2;
|
||||||
|
}
|
||||||
|
.abha-node.active .abha-node-label { color:#007bff; font-weight:600; }
|
||||||
|
.abha-node.done .abha-node-label { color:#28a745; }
|
||||||
|
.abha-panel { border-left:3px solid #007bff; padding-left:16px; }
|
||||||
</style>
|
</style>
|
||||||
<section class="content">
|
<section class="content">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
@ -294,61 +321,133 @@
|
||||||
|
|
||||||
<div class="tab-content pt-3" id="abhaSubTabsContent">
|
<div class="tab-content pt-3" id="abhaSubTabsContent">
|
||||||
<div class="tab-pane fade show active" id="abha-create" role="tabpanel" aria-labelledby="abha-create-tab">
|
<div class="tab-pane fade show active" id="abha-create" role="tabpanel" aria-labelledby="abha-create-tab">
|
||||||
<form id="abha_create_form" class="needs-validation" novalidate>
|
|
||||||
<?= csrf_field() ?>
|
<!-- Step progress indicator -->
|
||||||
<div class="row">
|
<div class="abha-progress-wrap mt-3 mb-4">
|
||||||
<div class="col-md-6">
|
<div class="abha-node active" id="abha_node_1">
|
||||||
<div class="form-group mb-3">
|
<div class="abha-badge" id="abha_badge_1">1</div>
|
||||||
<label class="form-label">Aadhaar Number</label>
|
<div class="abha-node-label">Aadhaar & Consent</div>
|
||||||
<input type="text" class="form-control" id="abha_create_aadhaar"
|
</div>
|
||||||
name="aadhaar" placeholder="12-digit Aadhaar" required
|
<div class="abha-node" id="abha_node_2">
|
||||||
data-inputmask='"mask": "9999-9999-9999"' data-mask>
|
<div class="abha-badge" id="abha_badge_2">2</div>
|
||||||
</div>
|
<div class="abha-node-label">OTP Verify</div>
|
||||||
|
</div>
|
||||||
|
<div class="abha-node" id="abha_node_3">
|
||||||
|
<div class="abha-badge" id="abha_badge_3">3</div>
|
||||||
|
<div class="abha-node-label">Mobile Confirm</div>
|
||||||
|
</div>
|
||||||
|
<div class="abha-node" id="abha_node_4">
|
||||||
|
<div class="abha-badge" id="abha_badge_4">4</div>
|
||||||
|
<div class="abha-node-label">ABHA Ready</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 1: Aadhaar entry + consent -->
|
||||||
|
<div id="abha_step_1" class="abha-panel">
|
||||||
|
<p class="text-muted small mb-3">Enter the patient's Aadhaar number. An OTP will be sent to the mobile linked with it.</p>
|
||||||
|
<div class="row g-3 align-items-end">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Aadhaar Number <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" id="abha_aadhaar_masked"
|
||||||
|
placeholder="0000-0000-0000" autocomplete="off"
|
||||||
|
data-inputmask='"mask": "9999-9999-9999"' data-mask>
|
||||||
|
<small class="text-muted">Mobile must be linked with Aadhaar for OTP.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-3">
|
||||||
<div class="form-group mb-3">
|
<label class="form-label">Authentication Via</label>
|
||||||
<label class="form-label">Mobile Number</label>
|
<select class="form-select" id="abha_auth_type">
|
||||||
<input type="text" class="form-control" id="abha_create_mobile"
|
<option value="aadhaar_otp">OTP</option>
|
||||||
name="mobile" placeholder="10-digit Mobile" required
|
<option value="biometric">Biometric</option>
|
||||||
data-inputmask='"mask": "9999999999"' data-mask>
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="abha_consent_chk">
|
||||||
|
<label class="form-check-label" for="abha_consent_chk">
|
||||||
|
I confirm the patient has given consent to share their Aadhaar details with NHA/ABDM for ABHA number creation.
|
||||||
|
<a href="#" data-bs-toggle="collapse" data-bs-target="#abha_full_consent" class="ms-1 small">(read full consent)</a>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="collapse mt-2" id="abha_full_consent">
|
||||||
|
<div class="card card-body bg-light py-2 small text-muted" style="max-height:100px;overflow-y:auto;">
|
||||||
|
I hereby declare that I am voluntarily sharing my Aadhaar number and demographic information issued by UIDAI with the National Health Authority (NHA) for the sole purpose of creating an ABHA number. I understand this number may be used for healthcare service purposes as notified by ABDM. My personal identifiable information (Name, Address, Age, DOB, Gender, Photo) may be made available to entities in the National Digital Health Ecosystem. I consent to use and disclosure of my health information to NDHE entities as permitted by law.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div id="abha_step1_alert" class="mt-2"></div>
|
||||||
<div class="col-md-6">
|
<div class="mt-3">
|
||||||
<div class="form-group mb-3">
|
<button type="button" class="btn btn-primary" id="abha_get_otp_btn">
|
||||||
<label class="form-label">Full Name</label>
|
<span id="abha_get_otp_spinner" class="spinner-border spinner-border-sm me-1 d-none"></span>
|
||||||
<input type="text" class="form-control" id="abha_create_name"
|
Send OTP
|
||||||
name="full_name" placeholder="Patient Full Name" required>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="form-group mb-3">
|
<!-- Step 2: OTP verification -->
|
||||||
<label class="form-label">Gender</label>
|
<div id="abha_step_2" class="abha-panel" style="display:none;">
|
||||||
<select class="form-select" id="abha_create_gender" name="gender" required>
|
<p class="text-muted small mb-3">Enter the OTP sent to the mobile number linked with Aadhaar.</p>
|
||||||
<option value="">Select Gender</option>
|
<div class="row g-3 align-items-end">
|
||||||
<option value="M">Male</option>
|
<div class="col-md-3">
|
||||||
<option value="F">Female</option>
|
<label class="form-label">One-Time Password <span class="text-danger">*</span></label>
|
||||||
<option value="O">Other</option>
|
<input type="text" class="form-control form-control-lg text-center letter-spacing-2"
|
||||||
</select>
|
id="abha_otp_input" placeholder="— — — — — —"
|
||||||
</div>
|
maxlength="6" autocomplete="one-time-code" inputmode="numeric">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div id="abha_step2_alert" class="mt-2"></div>
|
||||||
<div class="col-md-6">
|
<div class="mt-3 d-flex gap-2 flex-wrap">
|
||||||
<div class="form-group mb-3">
|
<button type="button" class="btn btn-success" id="abha_verify_otp_btn">
|
||||||
<label class="form-label">Date of Birth</label>
|
<span id="abha_verify_otp_spinner" class="spinner-border spinner-border-sm me-1 d-none"></span>
|
||||||
<input type="date" class="form-control" id="abha_create_dob"
|
Confirm OTP
|
||||||
name="dob" required>
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary" id="abha_resend_otp_btn">Resend OTP</button>
|
||||||
|
<button type="button" class="btn btn-link text-secondary px-0" id="abha_back_step1_btn">← Back</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 3: Mobile confirmation -->
|
||||||
|
<div id="abha_step_3" class="abha-panel" style="display:none;">
|
||||||
|
<p class="text-muted small mb-3">Verify or update the communication mobile number for this patient.</p>
|
||||||
|
<div class="row g-3 align-items-end">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Mobile Number <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" id="abha_comm_mobile"
|
||||||
|
placeholder="10-digit number" maxlength="10" inputmode="numeric" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="abha_step3_alert" class="mt-2"></div>
|
||||||
|
<div class="mt-3 d-flex gap-2">
|
||||||
|
<button type="button" class="btn btn-primary" id="abha_comm_next_btn">
|
||||||
|
<span id="abha_comm_spinner" class="spinner-border spinner-border-sm me-1 d-none"></span>
|
||||||
|
Confirm & Continue
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-link text-secondary px-0" id="abha_back_step2_btn">← Back</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 4: Done / ABHA address -->
|
||||||
|
<div id="abha_step_4" class="abha-panel" style="display:none;">
|
||||||
|
<div id="abha_created_result" class="mb-3"></div>
|
||||||
|
<div class="row g-3 align-items-end">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Health ID Address <small class="text-muted">(optional)</small></label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" id="abha_address_input" placeholder="e.g. ramesh.kumar">
|
||||||
|
<span class="input-group-text">@abdm</span>
|
||||||
</div>
|
</div>
|
||||||
|
<small class="text-muted">Choose from suggested addresses or type your own.</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div id="abha_step4_alert" class="mt-2"></div>
|
||||||
<div class="col-md-12">
|
<div class="mt-3">
|
||||||
<button type="submit" class="btn btn-primary">Generate OTP & Create ABHA</button>
|
<button type="button" class="btn btn-success" id="abha_finalise_btn">
|
||||||
</div>
|
<span id="abha_finalise_spinner" class="spinner-border spinner-border-sm me-1 d-none"></span>
|
||||||
|
Save ABHA Address
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" id="abha-verify" role="tabpanel" aria-labelledby="abha-verify-tab">
|
<div class="tab-pane fade" id="abha-verify" role="tabpanel" aria-labelledby="abha-verify-tab">
|
||||||
|
|
@ -427,6 +526,198 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ===================== ABHA CREATION WIZARD ===================== */
|
||||||
|
(function () {
|
||||||
|
var txnId = null; // transaction id from ABDM API
|
||||||
|
var csrfToken = function() { return $('input[name="<?= csrf_token() ?>"]').first().val(); };
|
||||||
|
|
||||||
|
function abhaStep(step) {
|
||||||
|
$('#abha_step_1,#abha_step_2,#abha_step_3,#abha_step_4').hide();
|
||||||
|
$('#abha_step_' + step).show();
|
||||||
|
// update progress nodes
|
||||||
|
for (var i = 1; i <= 4; i++) {
|
||||||
|
var $n = $('#abha_node_' + i);
|
||||||
|
var $b = $('#abha_badge_' + i);
|
||||||
|
$n.removeClass('active done');
|
||||||
|
if (i < step) {
|
||||||
|
$n.addClass('done');
|
||||||
|
$b.html('<i class="fas fa-check fa-xs"></i>');
|
||||||
|
} else if (i === step) {
|
||||||
|
$n.addClass('active');
|
||||||
|
$b.text(i);
|
||||||
|
} else {
|
||||||
|
$b.text(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAlert(containerId, type, msg) {
|
||||||
|
$('#' + containerId).html('<div class="alert alert-' + type + ' py-2 mt-2">' + msg + '</div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply inputmask to Aadhaar field
|
||||||
|
$('#abha_aadhaar_masked').inputmask('9999-9999-9999');
|
||||||
|
|
||||||
|
// Step 1 → Get OTP
|
||||||
|
$('#abha_get_otp_btn').on('click', function() {
|
||||||
|
var aadhaar = $('#abha_aadhaar_masked').val().replace(/\D/g, '');
|
||||||
|
if (aadhaar.length !== 12) {
|
||||||
|
showAlert('abha_step1_alert', 'danger', 'Please enter a valid 12-digit Aadhaar number.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$('#abha_consent_chk').is(':checked')) {
|
||||||
|
showAlert('abha_step1_alert', 'warning', 'You must agree to the Terms and Conditions to proceed.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#abha_step1_alert').html('');
|
||||||
|
$('#abha_get_otp_spinner').removeClass('d-none');
|
||||||
|
$('#abha_get_otp_btn').prop('disabled', true);
|
||||||
|
|
||||||
|
$.post('<?= base_url('abha/create/initiate') ?>', {
|
||||||
|
aadhaar: aadhaar,
|
||||||
|
auth_type: $('#abha_auth_type').val(),
|
||||||
|
'<?= csrf_token() ?>': csrfToken()
|
||||||
|
}, function(resp) {
|
||||||
|
$('#abha_get_otp_spinner').addClass('d-none');
|
||||||
|
$('#abha_get_otp_btn').prop('disabled', false);
|
||||||
|
if (resp && resp.ok == 1) {
|
||||||
|
txnId = resp.txn_id || null;
|
||||||
|
abhaStep(2);
|
||||||
|
} else {
|
||||||
|
showAlert('abha_step1_alert', 'danger', (resp && resp.error_text) ? resp.error_text : 'Failed to send OTP. Please try again.');
|
||||||
|
}
|
||||||
|
}, 'json').fail(function() {
|
||||||
|
$('#abha_get_otp_spinner').addClass('d-none');
|
||||||
|
$('#abha_get_otp_btn').prop('disabled', false);
|
||||||
|
showAlert('abha_step1_alert', 'danger', 'Server error. Please try again.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Back to step 1
|
||||||
|
$('#abha_back_step1_btn').on('click', function() { abhaStep(1); });
|
||||||
|
|
||||||
|
// Resend OTP
|
||||||
|
$('#abha_resend_otp_btn').on('click', function() {
|
||||||
|
$('#abha_get_otp_btn').trigger('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 2 → Verify OTP
|
||||||
|
$('#abha_verify_otp_btn').on('click', function() {
|
||||||
|
var otp = $('#abha_otp_input').val().trim();
|
||||||
|
if (!/^\d{6}$/.test(otp)) {
|
||||||
|
showAlert('abha_step2_alert', 'danger', 'Please enter the 6-digit OTP.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#abha_step2_alert').html('');
|
||||||
|
$('#abha_verify_otp_spinner').removeClass('d-none');
|
||||||
|
$('#abha_verify_otp_btn').prop('disabled', true);
|
||||||
|
|
||||||
|
$.post('<?= base_url('abha/create/verify_otp') ?>', {
|
||||||
|
otp: otp,
|
||||||
|
txn_id: txnId,
|
||||||
|
'<?= csrf_token() ?>': csrfToken()
|
||||||
|
}, function(resp) {
|
||||||
|
$('#abha_verify_otp_spinner').addClass('d-none');
|
||||||
|
$('#abha_verify_otp_btn').prop('disabled', false);
|
||||||
|
if (resp && resp.ok == 1) {
|
||||||
|
txnId = resp.txn_id || txnId;
|
||||||
|
if (resp.mobile) $('#abha_comm_mobile').val(resp.mobile);
|
||||||
|
abhaStep(3);
|
||||||
|
} else {
|
||||||
|
showAlert('abha_step2_alert', 'danger', (resp && resp.error_text) ? resp.error_text : 'Invalid OTP. Please try again.');
|
||||||
|
}
|
||||||
|
}, 'json').fail(function() {
|
||||||
|
$('#abha_verify_otp_spinner').addClass('d-none');
|
||||||
|
$('#abha_verify_otp_btn').prop('disabled', false);
|
||||||
|
showAlert('abha_step2_alert', 'danger', 'Server error. Please try again.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Back to step 2
|
||||||
|
$('#abha_back_step2_btn').on('click', function() { abhaStep(2); });
|
||||||
|
|
||||||
|
// Step 3 → Communication next
|
||||||
|
$('#abha_comm_next_btn').on('click', function() {
|
||||||
|
var mobile = $('#abha_comm_mobile').val().trim();
|
||||||
|
if (!/^\d{10}$/.test(mobile)) {
|
||||||
|
showAlert('abha_step3_alert', 'danger', 'Please enter a valid 10-digit mobile number.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#abha_step3_alert').html('');
|
||||||
|
$('#abha_comm_spinner').removeClass('d-none');
|
||||||
|
$('#abha_comm_next_btn').prop('disabled', true);
|
||||||
|
|
||||||
|
$.post('<?= base_url('abha/create/communication') ?>', {
|
||||||
|
mobile: mobile,
|
||||||
|
txn_id: txnId,
|
||||||
|
'<?= csrf_token() ?>': csrfToken()
|
||||||
|
}, function(resp) {
|
||||||
|
$('#abha_comm_spinner').addClass('d-none');
|
||||||
|
$('#abha_comm_next_btn').prop('disabled', false);
|
||||||
|
if (resp && resp.ok == 1) {
|
||||||
|
txnId = resp.txn_id || txnId;
|
||||||
|
// Show created ABHA details
|
||||||
|
if (resp.abha_number) {
|
||||||
|
$('#abha_created_result').html(
|
||||||
|
'<div class="alert alert-success">' +
|
||||||
|
'<strong>ABHA Number Created!</strong><br>' +
|
||||||
|
'ABHA Number: <strong>' + resp.abha_number + '</strong><br>' +
|
||||||
|
(resp.name ? 'Name: ' + resp.name + '<br>' : '') +
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (resp.suggested_addresses && resp.suggested_addresses.length) {
|
||||||
|
var opts = resp.suggested_addresses.map(function(a) {
|
||||||
|
return '<option value="' + a + '">' + a + '</option>';
|
||||||
|
}).join('');
|
||||||
|
$('#abha_address_input').replaceWith(
|
||||||
|
'<select class="form-select" id="abha_address_input"><option value="">Select an address...</option>' + opts + '</select>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
abhaStep(4);
|
||||||
|
} else {
|
||||||
|
showAlert('abha_step3_alert', 'danger', (resp && resp.error_text) ? resp.error_text : 'Failed to proceed. Please try again.');
|
||||||
|
}
|
||||||
|
}, 'json').fail(function() {
|
||||||
|
$('#abha_comm_spinner').addClass('d-none');
|
||||||
|
$('#abha_comm_next_btn').prop('disabled', false);
|
||||||
|
showAlert('abha_step3_alert', 'danger', 'Server error. Please try again.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 4 → Finalise ABHA address
|
||||||
|
$('#abha_finalise_btn').on('click', function() {
|
||||||
|
var addr = $('#abha_address_input').val().trim();
|
||||||
|
if (!addr) {
|
||||||
|
showAlert('abha_step4_alert', 'warning', 'Please choose an ABHA address.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('#abha_step4_alert').html('');
|
||||||
|
$('#abha_finalise_spinner').removeClass('d-none');
|
||||||
|
$('#abha_finalise_btn').prop('disabled', true);
|
||||||
|
|
||||||
|
$.post('<?= base_url('abha/create/address') ?>', {
|
||||||
|
abha_address: addr,
|
||||||
|
txn_id: txnId,
|
||||||
|
'<?= csrf_token() ?>': csrfToken()
|
||||||
|
}, function(resp) {
|
||||||
|
$('#abha_finalise_spinner').addClass('d-none');
|
||||||
|
$('#abha_finalise_btn').prop('disabled', false);
|
||||||
|
if (resp && resp.ok == 1) {
|
||||||
|
showAlert('abha_step4_alert', 'success', 'ABHA Address <strong>' + addr + '@abdm</strong> successfully created!');
|
||||||
|
$('#abha_finalise_btn').hide();
|
||||||
|
} else {
|
||||||
|
showAlert('abha_step4_alert', 'danger', (resp && resp.error_text) ? resp.error_text : 'Failed to create ABHA address.');
|
||||||
|
}
|
||||||
|
}, 'json').fail(function() {
|
||||||
|
$('#abha_finalise_spinner').addClass('d-none');
|
||||||
|
$('#abha_finalise_btn').prop('disabled', false);
|
||||||
|
showAlert('abha_step4_alert', 'danger', 'Server error. Please try again.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
/* =============================================================== */
|
||||||
|
|
||||||
$('#input_abha_id').on('change blur', function() {
|
$('#input_abha_id').on('change blur', function() {
|
||||||
var abhaId = ($(this).val() || '').toString().trim();
|
var abhaId = ($(this).val() || '').toString().trim();
|
||||||
if (abhaId !== '') {
|
if (abhaId !== '') {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,24 @@
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
padding: .75rem;
|
padding: .75rem;
|
||||||
}
|
}
|
||||||
|
.rx-patient-photo {
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 2px solid #d7e3f4;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.rx-meta-line {
|
||||||
|
margin-bottom: .2rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
.rx-meta-photo-col {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.rx-meta-photo-actions {
|
||||||
|
margin-top: .4rem;
|
||||||
|
}
|
||||||
.rx-counter {
|
.rx-counter {
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
|
|
@ -346,6 +364,19 @@
|
||||||
'back_url' => $historyBackUrl,
|
'back_url' => $historyBackUrl,
|
||||||
'back_title' => 'Consult',
|
'back_title' => 'Consult',
|
||||||
]);
|
]);
|
||||||
|
$profileImageUrl = base_url('billing/patient/show_profile_image') . '/' . (int) ($patient_master[0]->id ?? 0) . '/1?' . http_build_query([
|
||||||
|
'back_url' => $historyBackUrl,
|
||||||
|
'back_title' => 'Consult',
|
||||||
|
]);
|
||||||
|
$patientRelation = trim((string) ($patient_master[0]->p_relative ?? ''));
|
||||||
|
$patientRelativeName = trim((string) ($patient_master[0]->p_rname ?? ''));
|
||||||
|
$patientName = trim((string) ($patient_master[0]->p_fname ?? ''));
|
||||||
|
$patientNameWithRelation = $patientName;
|
||||||
|
$relationLabel = trim($patientRelation . ($patientRelativeName !== '' ? (' ' . $patientRelativeName) : ''));
|
||||||
|
if ($relationLabel !== '') {
|
||||||
|
$patientNameWithRelation .= ' (' . $relationLabel . ')';
|
||||||
|
}
|
||||||
|
$patientAbhaAddress = trim((string) ($patient_master[0]->abha_address ?? $patient_master[0]->abha ?? ''));
|
||||||
?>
|
?>
|
||||||
<a href="javascript:load_form('<?= esc($historyUrl, 'js') ?>','Consult History');"
|
<a href="javascript:load_form('<?= esc($historyUrl, 'js') ?>','Consult History');"
|
||||||
class="btn btn-info btn-sm"
|
class="btn btn-info btn-sm"
|
||||||
|
|
@ -377,19 +408,28 @@
|
||||||
<div class="row g-3 rx-two-panel">
|
<div class="row g-3 rx-two-panel">
|
||||||
<div class="col-lg-4 rx-left-panel">
|
<div class="col-lg-4 rx-left-panel">
|
||||||
<div class="rx-meta-box mb-3">
|
<div class="rx-meta-box mb-3">
|
||||||
<div><strong>Name:</strong> <?= esc($patient_master[0]->p_fname ?? '') ?></div>
|
<div class="row g-2 align-items-start">
|
||||||
<div><strong>UHID:</strong> <?= esc($patient_master[0]->p_code ?? '') ?></div>
|
<div class="col-8">
|
||||||
<div><strong>Age:</strong> <?= esc($patient_master[0]->str_age ?? '') ?></div>
|
<div class="rx-meta-line"><strong>Name:</strong> <?= esc($patientNameWithRelation !== '' ? $patientNameWithRelation : '-') ?></div>
|
||||||
<div><strong>Gender:</strong> <?= esc($patient_master[0]->xgender ?? '') ?></div>
|
<div class="rx-meta-line"><strong>UHID:</strong> <?= esc($patient_master[0]->p_code ?? '') ?></div>
|
||||||
<div class="mt-2">
|
<div class="rx-meta-line"><strong>Age:</strong> <?= esc($patient_master[0]->str_age ?? '') ?></div>
|
||||||
<label class="form-label mb-1"><strong>ABHA Address:</strong></label>
|
<div class="rx-meta-line"><strong>Gender:</strong> <?= esc($patient_master[0]->xgender ?? '') ?></div>
|
||||||
<input type="text" class="form-control form-control-sm" id="abha_address" maxlength="18"
|
<?php if ($patientAbhaAddress !== '') { ?>
|
||||||
value="<?= esc($patient_master[0]->abha_address ?? $patient_master[0]->abha ?? '') ?>"
|
<div class="rx-meta-line"><strong>ABHA Address:</strong> <?= esc($patientAbhaAddress) ?></div>
|
||||||
placeholder="Enter ABHA Address">
|
<?php } ?>
|
||||||
|
<div class="rx-meta-line"><strong>OPD:</strong> <?= esc($opd_master[0]->opd_code ?? '') ?></div>
|
||||||
|
<div class="rx-meta-line"><strong>Date:</strong> <?= esc($opd_master[0]->apointment_date ?? '') ?></div>
|
||||||
|
<div class="rx-meta-line mb-0"><strong>Doctor:</strong> <?= esc($opd_master[0]->doc_name ?? '') ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 rx-meta-photo-col">
|
||||||
|
<img src="<?= esc($patient_profile_file_path ?? '/assets/images/no_image.svg') ?>" alt="Patient Profile" class="rx-patient-photo">
|
||||||
|
<div class="rx-meta-photo-actions">
|
||||||
|
<a href="javascript:load_form('<?= esc($profileImageUrl, 'js') ?>','Profile Image');" class="btn btn-success btn-sm">
|
||||||
|
<i class="bi bi-camera-fill"></i> Edit Picture
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div><strong>OPD:</strong> <?= esc($opd_master[0]->opd_code ?? '') ?></div>
|
|
||||||
<div><strong>Date:</strong> <?= esc($opd_master[0]->apointment_date ?? '') ?></div>
|
|
||||||
<div><strong>Doctor:</strong> <?= esc($opd_master[0]->doc_name ?? '') ?></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header"><strong>Quick Tips</strong></div>
|
<div class="card-header"><strong>Quick Tips</strong></div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user