fix(hms): resolve base64 photo upload decoding and add professional image thumbnails to HMS Nursing Workspace Treatment Notes

This commit is contained in:
DevSoftTech Server 2026-08-29 04:04:03 +05:30
parent e32feeb8d6
commit e4c6b398cc
3 changed files with 112 additions and 63 deletions

View File

@ -337,39 +337,44 @@ class DoctorApi extends BaseController
public function saveOpdPrescription(int $opdId)
{
$db = db_connect();
$post = $this->request->getPost() ?: ($this->request->getJSON(true) ?? []);
$json = $this->request->getJSON(true);
$post = $this->request->getPost() ?: [];
$dataInput = ! empty($json) ? $json : $post;
$opdRow = $db->table('opd_master')->where('opd_id', $opdId)->get()->getRowArray();
if (! $opdRow) {
return $this->response->setStatusCode(404)->setJSON(['status' => 0, 'message' => 'OPD record not found']);
}
$docId = (int) ($post['doctor_id'] ?? $opdRow['doc_id']);
$docId = (int) ($dataInput['doctor_id'] ?? $opdRow['doc_id']);
$pId = (int) ($opdRow['p_id'] ?? 0);
$imageBase64 = $post['image_base64'] ?? null;
$imageBase64 = $dataInput['image_base64'] ?? null;
$imageUrl = '';
if (! empty($imageBase64)) {
$imgData = $imageBase64;
$ext = 'jpg';
if (preg_match('/^data:image\/(\w+);base64,/', $imageBase64, $type)) {
$imgData = substr($imageBase64, strpos($imageBase64, ',') + 1);
$ext = strtolower($type[1]);
if ($ext === 'jpeg') $ext = 'jpg';
$binary = base64_decode($imgData);
if ($binary !== false) {
$uploadDir = FCPATH . 'uploads/doctor_notes/';
if (! is_dir($uploadDir)) {
@mkdir($uploadDir, 0777, true);
}
$filename = 'opd_rx_' . $opdId . '_' . time() . '_' . rand(100, 999) . '.' . $ext;
file_put_contents($uploadDir . $filename, $binary);
$imageUrl = '/uploads/doctor_notes/' . $filename;
}
$imgData = str_replace(' ', '+', $imgData);
$binary = base64_decode($imgData);
if ($binary !== false && strlen($binary) > 0) {
$uploadDir = FCPATH . 'uploads/doctor_notes/';
if (! is_dir($uploadDir)) {
@mkdir($uploadDir, 0777, true);
}
$filename = 'opd_rx_' . $opdId . '_' . time() . '_' . rand(100, 999) . '.' . $ext;
file_put_contents($uploadDir . $filename, $binary);
$imageUrl = '/uploads/doctor_notes/' . $filename;
}
}
$adviceText = trim((string) ($post['advice'] ?? ''));
$adviceText = trim((string) ($dataInput['advice'] ?? ''));
if (! empty($imageUrl)) {
$adviceText .= ($adviceText ? "\n" : '') . '[Handwritten Rx Photo: ' . $imageUrl . ']';
$adviceText .= ($adviceText ? "\n" : '') . '[IMAGE_ATTACHMENT:' . $imageUrl . ']';
}
$prescData = [
@ -377,20 +382,20 @@ class DoctorApi extends BaseController
'doc_id' => $docId,
'p_id' => $pId,
'date_opd_visit' => date('Y-m-d'),
'temp' => trim((string) ($post['temp'] ?? '')),
'pulse' => trim((string) ($post['pulse'] ?? '')),
'bp' => trim((string) ($post['bp_systolic'] ?? '')),
'diastolic' => trim((string) ($post['bp_diastolic'] ?? '')),
'spo2' => trim((string) ($post['spo2'] ?? '')),
'weight' => trim((string) ($post['weight'] ?? '')),
'height' => trim((string) ($post['height'] ?? '')),
'rr_min' => trim((string) ($post['rr_min'] ?? '')),
'complaints' => trim((string) ($post['chief_complaints'] ?? '')),
'Finding_Examinations' => trim((string) ($post['finding_examinations'] ?? '')),
'diagnosis' => trim((string) ($post['provisional_diagnosis'] ?? '')),
'temp' => trim((string) ($dataInput['temp'] ?? '')),
'pulse' => trim((string) ($dataInput['pulse'] ?? '')),
'bp' => trim((string) ($dataInput['bp_systolic'] ?? '')),
'diastolic' => trim((string) ($dataInput['bp_diastolic'] ?? '')),
'spo2' => trim((string) ($dataInput['spo2'] ?? '')),
'weight' => trim((string) ($dataInput['weight'] ?? '')),
'height' => trim((string) ($dataInput['height'] ?? '')),
'rr_min' => trim((string) ($dataInput['rr_min'] ?? '')),
'complaints' => trim((string) ($dataInput['chief_complaints'] ?? '')),
'Finding_Examinations' => trim((string) ($dataInput['finding_examinations'] ?? '')),
'diagnosis' => trim((string) ($dataInput['provisional_diagnosis'] ?? '')),
'advice' => $adviceText,
'investigation' => trim((string) ($post['investigation'] ?? '')),
'next_visit' => trim((string) ($post['next_visit'] ?? '')),
'investigation' => trim((string) ($dataInput['investigation'] ?? '')),
'next_visit' => trim((string) ($dataInput['next_visit'] ?? '')),
];
$existing = $db->table('opd_prescription')->where('opd_id', $opdId)->get()->getRowArray();
@ -403,7 +408,7 @@ class DoctorApi extends BaseController
$opdPreId = (int) $db->insertID();
}
$medRaw = $post['medicines'] ?? null;
$medRaw = $dataInput['medicines'] ?? null;
$medArray = is_array($medRaw) ? $medRaw : json_decode((string)$medRaw, true);
if ($opdPreId > 0 && is_array($medArray)) {
$db->table('opd_prescrption_prescribed')->where('opd_pre_id', $opdPreId)->delete();
@ -428,6 +433,7 @@ class DoctorApi extends BaseController
return $this->response->setJSON([
'status' => 1,
'message' => 'Prescription saved & OPD Visit completed successfully',
'image_url' => $imageUrl,
]);
}
@ -523,26 +529,32 @@ class DoctorApi extends BaseController
*/
public function saveTreatmentNote(int $ipdId)
{
$post = $this->request->getPost() ?: ($this->request->getJSON(true) ?? []);
$treatmentText = trim((string) ($post['treatment_text'] ?? ''));
$imageBase64 = $post['image_base64'] ?? null;
$json = $this->request->getJSON(true);
$post = $this->request->getPost() ?: [];
$dataInput = ! empty($json) ? $json : $post;
$treatmentText = trim((string) ($dataInput['treatment_text'] ?? ''));
$imageBase64 = $dataInput['image_base64'] ?? null;
$imageUrl = '';
if (! empty($imageBase64)) {
$imgData = $imageBase64;
$ext = 'jpg';
if (preg_match('/^data:image\/(\w+);base64,/', $imageBase64, $type)) {
$imgData = substr($imageBase64, strpos($imageBase64, ',') + 1);
$ext = strtolower($type[1]);
if ($ext === 'jpeg') $ext = 'jpg';
$binary = base64_decode($imgData);
if ($binary !== false) {
$uploadDir = FCPATH . 'uploads/doctor_notes/';
if (! is_dir($uploadDir)) {
@mkdir($uploadDir, 0777, true);
}
$filename = 'note_ipd_' . $ipdId . '_' . time() . '_' . rand(100, 999) . '.' . $ext;
file_put_contents($uploadDir . $filename, $binary);
$imageUrl = '/uploads/doctor_notes/' . $filename;
}
$imgData = str_replace(' ', '+', $imgData);
$binary = base64_decode($imgData);
if ($binary !== false && strlen($binary) > 0) {
$uploadDir = FCPATH . 'uploads/doctor_notes/';
if (! is_dir($uploadDir)) {
@mkdir($uploadDir, 0777, true);
}
$filename = 'note_ipd_' . $ipdId . '_' . time() . '_' . rand(100, 999) . '.' . $ext;
file_put_contents($uploadDir . $filename, $binary);
$imageUrl = '/uploads/doctor_notes/' . $filename;
}
}
@ -550,8 +562,8 @@ class DoctorApi extends BaseController
return $this->response->setStatusCode(422)->setJSON(['status' => 0, 'message' => 'Please enter clinical notes or attach a handwritten note photo']);
}
$docId = (int) ($post['doctor_id'] ?? 0);
$doctorName = trim((string) ($post['doctor_name'] ?? 'Doctor'));
$docId = (int) ($dataInput['doctor_id'] ?? 0);
$doctorName = trim((string) ($dataInput['doctor_name'] ?? 'Doctor'));
$fullNoteText = '[Doctor Note] ' . $treatmentText;
if (! empty($imageUrl)) {
@ -563,7 +575,7 @@ class DoctorApi extends BaseController
'entry_type' => 'treatment',
'recorded_at' => date('Y-m-d H:i:s'),
'treatment_text' => $fullNoteText,
'general_note' => $imageUrl ? ('Attachment: ' . $imageUrl) : (string) ($post['general_note'] ?? ''),
'general_note' => $imageUrl ? ('Attachment: ' . $imageUrl) : (string) ($dataInput['general_note'] ?? ''),
'recorded_by' => 'Dr. ' . $doctorName,
'recorded_by_id' => $docId,
'created_at' => date('Y-m-d H:i:s'),

View File

@ -792,11 +792,50 @@ $treatmentEntries = array_values(array_filter($entries, static fn ($r) => ($r['e
<tr><td colspan="5" class="text-center text-muted py-3">No treatment notes recorded yet.</td></tr>
<?php else: ?>
<?php foreach ($treatmentEntries as $trRow): ?>
<?php
$tText = (string)($trRow['treatment_text'] ?? '');
$gNote = (string)($trRow['general_note'] ?? '');
$imgUrl = '';
if (preg_match('/\[IMAGE_ATTACHMENT:(.*?)\]/', $tText, $m)) {
$imgUrl = trim($m[1]);
$tText = trim(str_replace(['[Doctor Note]', $m[0]], '', $tText));
} elseif (preg_match('/\/uploads\/doctor_notes\/[^\s\]]+/', $tText, $m)) {
$imgUrl = trim($m[0]);
} elseif (preg_match('/\/uploads\/doctor_notes\/[^\s\]]+/', $gNote, $m)) {
$imgUrl = trim($m[0]);
}
$tTextClean = str_replace(['[Doctor Note]', '[Doctor Note] '], '', $tText);
?>
<tr>
<td><small><?= esc($trRow['recorded_at']) ?></small></td>
<td><small><?= esc($trRow['recorded_by'] ?? 'Staff') ?></small></td>
<td><?= esc((string)($trRow['treatment_text'] ?? '')) ?></td>
<td><small><?= esc((string)($trRow['general_note'] ?? '')) ?></small></td>
<td>
<?php if ($tTextClean !== ''): ?>
<div class="fw-semibold text-dark"><?= esc($tTextClean) ?></div>
<?php endif; ?>
<?php if ($imgUrl !== ''): ?>
<div class="mt-1">
<span class="badge bg-primary mb-1 d-inline-flex align-items-center gap-1" style="font-size: 10px;">
<i class="bi bi-paperclip"></i> Doctor Handwritten Note
</span><br>
<a href="<?= esc($imgUrl) ?>" target="_blank" class="d-inline-block border rounded p-1 bg-white shadow-sm me-2">
<img src="<?= esc($imgUrl) ?>" alt="Doctor Handwritten Note" style="max-height: 120px; max-width: 260px; border-radius: 6px; object-fit: contain;">
</a>
</div>
<?php endif; ?>
</td>
<td>
<?php if ($imgUrl !== ''): ?>
<a href="<?= esc($imgUrl) ?>" target="_blank" class="btn btn-xs btn-outline-primary py-0 px-2 fw-bold" style="font-size: 11px;">
<i class="bi bi-file-earmark-image me-1"></i> View Photo
</a>
<?php else: ?>
<small><?= esc($gNote) ?></small>
<?php endif; ?>
</td>
<td class="text-center">
<button type="button" class="btn btn-outline-primary btn-sm py-0 px-2 btn-edit-treatment"
data-id="<?= esc($trRow['id']) ?>"

View File

@ -549,18 +549,17 @@
}
try {
const body = new URLSearchParams();
body.append('doctor_id', authenticatedDoctor ? authenticatedDoctor.id : 0);
body.append('doctor_name', authenticatedDoctor ? authenticatedDoctor.name : 'Doctor');
body.append('treatment_text', newTreatmentNote);
if (noteImageBase64) {
body.append('image_base64', noteImageBase64);
}
const payload = {
doctor_id: authenticatedDoctor ? authenticatedDoctor.id : 0,
doctor_name: authenticatedDoctor ? authenticatedDoctor.name : 'Doctor',
treatment_text: newTreatmentNote,
image_base64: noteImageBase64 || ''
};
const res = await fetch(`/api/v1/doctor/ipd/treatment/save/${activePatient.ipd_id}`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString()
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const json = await res.json();
if (json.status === 1) {
@ -626,18 +625,17 @@
if (!activeOpd) return;
try {
const body = new URLSearchParams();
body.append('doctor_id', authenticatedDoctor ? authenticatedDoctor.id : 0);
Object.keys(prescForm).forEach(k => body.append(k, prescForm[k]));
body.append('medicines', JSON.stringify(medicines));
if (noteImageBase64) {
body.append('image_base64', noteImageBase64);
}
const payload = {
doctor_id: authenticatedDoctor ? authenticatedDoctor.id : 0,
...prescForm,
medicines: medicines,
image_base64: noteImageBase64 || ''
};
const res = await fetch(`/api/v1/doctor/opd/prescription/save/${activeOpd.opd_id}`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body.toString()
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const json = await res.json();
if (json.status === 1) {