diff --git a/admin_doctors.php b/admin_doctors.php index 356e4ab..9c495b8 100644 --- a/admin_doctors.php +++ b/admin_doctors.php @@ -12,7 +12,7 @@ $editId = (int) ($_GET['edit'] ?? 0); $editDoctor = $editId > 0 ? qh_fetch_doctor($editId) : null; if ($editId > 0 && $editDoctor === null) { - qh_set_flash('warning', qh_t('The requested doctor record was not found.', 'لم يتم العثور على سجل الطبيب المطلوب.')); + qh_set_flash('warning', qh_t('The requested room record was not found.', 'لم يتم العثور على سجل الغرفة المطلوب.')); qh_redirect('admin_doctors.php'); } @@ -36,8 +36,8 @@ if ($search !== '') { qh_page_start( 'admin', - qh_t('Doctor management', 'إدارة الأطباء'), - qh_t('Professional doctor directory with search, edit, and delete actions.', 'دليل احترافي للأطباء مع البحث وخيارات التعديل والحذف.') + qh_t('Room management', 'إدارة الغرف'), + qh_t('Professional room directory with search, edit, and delete actions.', 'دليل احترافي للغرف مع البحث وخيارات التعديل والحذف.') ); ?>
@@ -49,10 +49,10 @@ qh_page_start(
- +
-

-

+

+

@@ -60,15 +60,15 @@ qh_page_start(
- +
@@ -77,25 +77,24 @@ qh_page_start(
- -

+ +

+ : qh_t('All configured room records.', 'جميع سجلات الغرف المهيأة.')) ?>

-
+
- - + @@ -104,17 +103,13 @@ qh_page_start( (int) $doctor['id'], 'q' => $search]); ?> - - + @@ -117,8 +114,8 @@ qh_page_start( - + @@ -144,12 +141,8 @@ qh_page_start(
-
- -
-
- : + :
:
-
-
-
-
+ @@ -133,14 +128,14 @@ qh_page_start(
- -

-

+ +

+

-
+
@@ -150,16 +145,6 @@ qh_page_start( -
- - -
- -
- - -
-
- +
-
-
+
+
@@ -61,7 +61,7 @@ qh_page_start(
-

+

@@ -100,12 +100,12 @@ qh_page_start(
-
diff --git a/queue_bootstrap.php b/queue_bootstrap.php index 7242ea8..716c042 100644 --- a/queue_bootstrap.php +++ b/queue_bootstrap.php @@ -270,7 +270,7 @@ function qh_project_name(string $fallback = 'Hospital Queue'): string return $candidate !== '' ? $candidate : $fallback; } -function qh_project_description(string $fallback = 'Bilingual hospital queue workflow for reception, nursing, doctors, and the public display.'): string +function qh_project_description(string $fallback = 'Bilingual hospital queue workflow for reception, nursing, rooms, and the public display.'): string { $candidate = trim((string) ($_SERVER['PROJECT_DESCRIPTION'] ?? '')); return $candidate !== '' ? $candidate : $fallback; @@ -487,8 +487,8 @@ function qh_admin_sections(): array 'icon' => 'users', ], 'admin_doctors.php' => [ - 'label' => qh_t('Doctors', 'الأطباء'), - 'description' => qh_t('Manage doctors, rooms, and assignments.', 'إدارة الأطباء والغرف والتعيينات.'), + 'label' => qh_t('Rooms', 'الغرف'), + 'description' => qh_t('Manage rooms, clinic mapping, and assignments.', 'إدارة الغرف وربطها بالعيادات والتعيينات.'), 'icon' => 'doctor', ], ]; @@ -556,7 +556,7 @@ function qh_render_admin_sidebar(string $activePage, array $stats = []): void echo ' '; echo '
'; echo '
' . qh_h((string) ($stats['clinics'] ?? 0)) . '' . qh_h(qh_t('Clinics', 'العيادات')) . '
'; - echo '
' . qh_h((string) ($stats['doctors'] ?? 0)) . '' . qh_h(qh_t('Doctors', 'الأطباء')) . '
'; + echo '
' . qh_h((string) ($stats['doctors'] ?? 0)) . '' . qh_h(qh_t('Rooms', 'الغرف')) . '
'; echo '
' . qh_h((string) ($stats['vitals_clinics'] ?? 0)) . '' . qh_h(qh_t('Vitals-first clinics', 'العيادات التي تبدأ بالعلامات')) . '
'; echo '
'; echo '
'; @@ -566,15 +566,15 @@ function qh_ticket_next_stop(array $ticket): string { return (int) ($ticket['clinic_requires_vitals'] ?? 0) === 1 ? qh_t('Nursing vitals', 'العلامات الحيوية في التمريض') - : qh_t('Doctor waiting area', 'منطقة انتظار الطبيب'); + : qh_t('Room waiting area', 'منطقة انتظار الغرفة'); } function qh_ticket_last_note(array $ticket): string { return match ((string) ($ticket['status'] ?? '')) { 'waiting_vitals' => qh_t('Proceed to nursing vitals first.', 'يرجى التوجه أولاً إلى العلامات الحيوية في التمريض.'), - 'ready_for_doctor' => qh_t('Wait for the doctor call on the public display.', 'انتظر نداء الطبيب على الشاشة العامة.'), - 'called' => qh_t('The patient has been called to the doctor room.', 'تم نداء المريض إلى غرفة الطبيب.'), + 'ready_for_doctor' => qh_t('Wait for the room call on the public display.', 'انتظر نداء الغرفة على الشاشة العامة.'), + 'called' => qh_t('The ticket has been called to the room.', 'تم نداء التذكرة إلى الغرفة.'), 'in_progress' => qh_t('The consultation is currently in progress.', 'الاستشارة جارية حالياً.'), 'done' => qh_t('The visit has been completed.', 'تم إكمال الزيارة.'), 'no_show' => qh_t('The patient was marked as no-show.', 'تم تسجيل المريض كحالة عدم حضور.'), @@ -666,7 +666,7 @@ function qh_render_nav(string $activePage): void 'admin' => ['href' => qh_url('admin.php'), 'label' => qh_t('Admin', 'الإدارة')], 'reception' => ['href' => qh_url('reception.php'), 'label' => qh_t('Reception', 'الاستقبال')], 'nursing' => ['href' => qh_url('nursing.php'), 'label' => qh_t('Nursing', 'التمريض')], - 'doctor' => ['href' => qh_url('doctor.php'), 'label' => qh_t('Doctor', 'الطبيب')], + 'doctor' => ['href' => qh_url('doctor.php'), 'label' => qh_t('Room', 'الغرفة')], 'users' => ['href' => qh_url('admin_users.php'), 'label' => qh_t('Users', 'المستخدمون')], 'display' => ['href' => qh_url('display.php'), 'label' => qh_t('Display', 'الشاشة')], ]; @@ -917,10 +917,11 @@ function qh_create_ticket(string $patientName, int $clinicId, int $doctorId, str $doctor = qh_fetch_doctor($doctorId); if (!$doctor || (int) $doctor['clinic_id'] !== $clinicId) { - throw new RuntimeException(qh_t('The doctor does not belong to the selected clinic.', 'الطبيب لا يتبع العيادة المحددة.')); + throw new RuntimeException(qh_t('The selected room does not belong to the selected clinic.', 'الغرفة المحددة لا تتبع العيادة المحددة.')); } $ticketNumber = qh_generate_ticket_number($clinic['code']); + $patientName = trim($patientName) !== '' ? trim($patientName) : $ticketNumber; $status = $forcedStatus ?: ((int) $clinic['requires_vitals'] === 1 ? 'waiting_vitals' : 'ready_for_doctor'); $stmt = $pdo->prepare( @@ -938,7 +939,7 @@ function qh_create_ticket(string $patientName, int $clinicId, int $doctorId, str 'status' => $status, 'display_note' => $status === 'waiting_vitals' ? 'Proceed to nursing vitals first.' - : 'Wait for your doctor call on the public screen.', + : 'Wait for your room call on the public screen.', ]); return (int) $pdo->lastInsertId(); @@ -980,7 +981,7 @@ function qh_status_meta(string $status): array $map = [ 'waiting_vitals' => ['class' => 'warning', 'en' => 'Waiting for vitals', 'ar' => 'بانتظار العلامات الحيوية'], 'nursing_called' => ['class' => 'primary', 'en' => 'Nursing Call', 'ar' => 'نداء التمريض'], - 'ready_for_doctor' => ['class' => 'info', 'en' => 'Ready for doctor', 'ar' => 'جاهز للطبيب'], + 'ready_for_doctor' => ['class' => 'info', 'en' => 'Ready for room', 'ar' => 'جاهز للغرفة'], 'called' => ['class' => 'primary', 'en' => 'Called', 'ar' => 'تم النداء'], 'in_progress' => ['class' => 'secondary', 'en' => 'In consultation', 'ar' => 'داخل العيادة'], 'done' => ['class' => 'success', 'en' => 'Completed', 'ar' => 'مكتمل'], @@ -1022,15 +1023,13 @@ function qh_call_message(array $ticket): array ]; } - $doctorNameEn = $ticket['doctor_name_en'] ?? 'Doctor'; - $doctorNameAr = $ticket['doctor_name_ar'] ?? 'الطبيب'; $room = $ticket['doctor_room'] ?? '--'; return [ - 'en' => sprintf('Ticket %s, please proceed to room %s for %s.', $ticketNumber, $room, $doctorNameEn), - 'ar' => sprintf('رقم التذكرة %s، يرجى التوجه إلى الغرفة %s إلى %s.', $ticketNumber, $room, $doctorNameAr), - 'speech_en' => sprintf('Ticket %s, please proceed to room %s for %s.', $speechEn, $room, $doctorNameEn), - 'speech_ar' => sprintf('رقم التذكرة %s، يرجى التوجه إلى الغرفة %s إلى %s.', $speechAr, $room, $doctorNameAr), + 'en' => sprintf('Ticket %s, please proceed to room %s.', $ticketNumber, $room), + 'ar' => sprintf('رقم التذكرة %s، يرجى التوجه إلى الغرفة %s.', $ticketNumber, $room), + 'speech_en' => sprintf('Ticket %s, please proceed to room %s.', $speechEn, $room), + 'speech_ar' => sprintf('رقم التذكرة %s، يرجى التوجه إلى الغرفة %s.', $speechAr, $room), ]; } @@ -1173,39 +1172,37 @@ function qh_admin_handle_request(): void $ticketCountStmt->execute(['clinic_id' => $clinicId]); $ticketCount = (int) $ticketCountStmt->fetchColumn(); if ($doctorCount > 0 || $ticketCount > 0) { - throw new InvalidArgumentException(qh_t('This clinic cannot be deleted because it is linked to doctors or patient tickets.', 'لا يمكن حذف هذه العيادة لأنها مرتبطة بأطباء أو تذاكر مرضى.')); + throw new InvalidArgumentException(qh_t('This clinic cannot be deleted because it is linked to rooms or patient tickets.', 'لا يمكن حذف هذه العيادة لأنها مرتبطة بغرف أو تذاكر مرضى.')); } $stmt = $pdo->prepare("DELETE FROM hospital_queue_records WHERE item_type = 'clinic' AND id = :clinic_id"); $stmt->execute(['clinic_id' => $clinicId]); qh_set_flash('success', qh_t('Clinic deleted successfully.', 'تم حذف العيادة بنجاح.')); } elseif ($action === 'add_doctor') { - $nameEn = trim((string) ($_POST['name_en'] ?? '')); - $nameAr = trim((string) ($_POST['name_ar'] ?? '')); $clinicId = (int) ($_POST['clinic_id'] ?? 0); $roomNumber = trim((string) ($_POST['room_number'] ?? '')); - if ($nameEn === '' || $nameAr === '' || $clinicId <= 0 || $roomNumber === '') { - throw new InvalidArgumentException(qh_t('Please complete the doctor form before saving.', 'يرجى إكمال بيانات الطبيب قبل الحفظ.')); + $roomLabel = $roomNumber; + if ($clinicId <= 0 || $roomNumber === '') { + throw new InvalidArgumentException(qh_t('Please complete the room form before saving.', 'يرجى إكمال بيانات الغرفة قبل الحفظ.')); } $stmt = $pdo->prepare( "INSERT INTO hospital_queue_records (item_type, name_en, name_ar, clinic_id, room_number, sort_order, status) VALUES ('doctor', :name_en, :name_ar, :clinic_id, :room_number, :sort_order, 'active')" ); $stmt->execute([ - 'name_en' => $nameEn, - 'name_ar' => $nameAr, + 'name_en' => $roomLabel, + 'name_ar' => $roomLabel, 'clinic_id' => $clinicId, 'room_number' => $roomNumber, 'sort_order' => max((int) ($_POST['sort_order'] ?? 50), 1), ]); - qh_set_flash('success', qh_t('Doctor profile saved.', 'تم حفظ ملف الطبيب.')); + qh_set_flash('success', qh_t('Room saved.', 'تم حفظ الغرفة.')); } elseif ($action === 'update_doctor') { $doctorId = (int) ($_POST['doctor_id'] ?? 0); - $nameEn = trim((string) ($_POST['name_en'] ?? '')); - $nameAr = trim((string) ($_POST['name_ar'] ?? '')); $clinicId = (int) ($_POST['clinic_id'] ?? 0); $roomNumber = trim((string) ($_POST['room_number'] ?? '')); - if ($doctorId <= 0 || $nameEn === '' || $nameAr === '' || $clinicId <= 0 || $roomNumber === '') { - throw new InvalidArgumentException(qh_t('Please complete the doctor details before updating.', 'يرجى إكمال بيانات الطبيب قبل التحديث.')); + $roomLabel = $roomNumber; + if ($doctorId <= 0 || $clinicId <= 0 || $roomNumber === '') { + throw new InvalidArgumentException(qh_t('Please complete the room details before updating.', 'يرجى إكمال بيانات الغرفة قبل التحديث.')); } $stmt = $pdo->prepare( "UPDATE hospital_queue_records @@ -1217,28 +1214,28 @@ function qh_admin_handle_request(): void WHERE item_type = 'doctor' AND id = :doctor_id" ); $stmt->execute([ - 'name_en' => $nameEn, - 'name_ar' => $nameAr, + 'name_en' => $roomLabel, + 'name_ar' => $roomLabel, 'clinic_id' => $clinicId, 'room_number' => $roomNumber, 'sort_order' => max((int) ($_POST['sort_order'] ?? 50), 1), 'doctor_id' => $doctorId, ]); - qh_set_flash('success', qh_t('Doctor assignment updated.', 'تم تحديث تعيين الطبيب.')); + qh_set_flash('success', qh_t('Room updated.', 'تم تحديث الغرفة.')); } elseif ($action === 'delete_doctor') { $doctorId = (int) ($_POST['doctor_id'] ?? 0); if ($doctorId <= 0) { - throw new InvalidArgumentException(qh_t('Invalid doctor selected.', 'تم اختيار طبيب غير صالح.')); + throw new InvalidArgumentException(qh_t('Invalid room selected.', 'تم اختيار غرفة غير صالحة.')); } $ticketCountStmt = $pdo->prepare("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket' AND doctor_id = :doctor_id"); $ticketCountStmt->execute(['doctor_id' => $doctorId]); $ticketCount = (int) $ticketCountStmt->fetchColumn(); if ($ticketCount > 0) { - throw new InvalidArgumentException(qh_t('This doctor cannot be deleted because patient tickets are linked to the profile.', 'لا يمكن حذف هذا الطبيب لأن هناك تذاكر مرضى مرتبطة بالملف.')); + throw new InvalidArgumentException(qh_t('This room cannot be deleted because tickets are linked to the profile.', 'لا يمكن حذف هذه الغرفة لأن هناك تذاكر مرتبطة بالملف.')); } $stmt = $pdo->prepare("DELETE FROM hospital_queue_records WHERE item_type = 'doctor' AND id = :doctor_id"); $stmt->execute(['doctor_id' => $doctorId]); - qh_set_flash('success', qh_t('Doctor deleted successfully.', 'تم حذف الطبيب بنجاح.')); + qh_set_flash('success', qh_t('Room deleted successfully.', 'تم حذف الغرفة بنجاح.')); } elseif ($action === 'save_hospital_profile') { $nameEn = trim((string) ($_POST['name_en'] ?? '')); $nameAr = trim((string) ($_POST['name_ar'] ?? '')); @@ -1356,8 +1353,8 @@ function qh_reception_handle_request(): void $doctorId = (int) ($_POST['doctor_id'] ?? 0); $languagePref = trim((string) ($_POST['language_pref'] ?? 'en')); - if ($patientName === '' || $clinicId <= 0 || $doctorId <= 0) { - throw new InvalidArgumentException(qh_t('Please complete the patient name, clinic, and doctor.', 'يرجى إدخال اسم المريض والعيادة والطبيب.')); + if ($clinicId <= 0 || $doctorId <= 0) { + throw new InvalidArgumentException(qh_t('Please choose the clinic and room.', 'يرجى اختيار العيادة والغرفة.')); } $ticketId = qh_create_ticket($patientName, $clinicId, $doctorId, $languagePref); @@ -1401,14 +1398,14 @@ function qh_nursing_handle_request(): void "UPDATE hospital_queue_records SET vitals_notes = :vitals_notes, status = 'ready_for_doctor', - display_note = 'Vitals completed. Wait for doctor call.' + display_note = 'Vitals completed. Wait for room call.' WHERE item_type = 'ticket' AND id = :ticket_id AND status IN ('waiting_vitals', 'nursing_called')" ); $stmt->execute([ 'vitals_notes' => $vitalsNotes, 'ticket_id' => $ticketId, ]); - qh_set_flash('success', qh_t('Vitals captured and patient moved to the doctor queue.', 'تم حفظ العلامات الحيوية ونقل المريض إلى طابور الطبيب.')); + qh_set_flash('success', qh_t('Vitals captured and patient moved to the room queue.', 'تم حفظ العلامات الحيوية ونقل المريض إلى طابور الغرفة.')); } } catch (Throwable $exception) { qh_set_flash('danger', $exception->getMessage()); @@ -1429,7 +1426,7 @@ function qh_doctor_handle_request(): void $action = trim((string) ($_POST['action'] ?? '')); $ticket = qh_fetch_ticket($ticketId); if (!$ticket || $doctorId <= 0 || (int) $ticket['doctor_id'] !== $doctorId) { - throw new InvalidArgumentException(qh_t('That ticket is not available for the selected doctor.', 'هذه التذكرة غير متاحة للطبيب المحدد.')); + throw new InvalidArgumentException(qh_t('That ticket is not available for the selected room.', 'هذه التذكرة غير متاحة للغرفة المحددة.')); } if ($action === 'call_ticket') { @@ -1463,7 +1460,7 @@ function qh_doctor_handle_request(): void } elseif ($action === 'refer_ticket') { $referToDoctorId = (int) ($_POST["refer_to_doctor_id"] ?? 0); if ($referToDoctorId <= 0 || $referToDoctorId === $doctorId) { - throw new InvalidArgumentException(qh_t('Please select a valid doctor to refer the patient to.', 'يرجى اختيار طبيب صالح لتحويل المريض إليه.')); + throw new InvalidArgumentException(qh_t('Please select a valid room to move the ticket to.', 'يرجى اختيار غرفة صالحة لنقل التذكرة إليها.')); } $stmt = db()->prepare( "UPDATE hospital_queue_records @@ -1474,7 +1471,7 @@ function qh_doctor_handle_request(): void 'refer_to_doctor_id' => $referToDoctorId, 'ticket_id' => $ticketId, ]); - qh_set_flash('success', qh_t('Patient referred successfully.', 'تم تحويل المريض بنجاح.')); + qh_set_flash('success', qh_t('Ticket moved successfully.', 'تم نقل التذكرة بنجاح.')); } elseif ($action === 'mark_no_show') { $stmt = db()->prepare( "UPDATE hospital_queue_records diff --git a/reception.php b/reception.php index 8386c9c..1bdc584 100644 --- a/reception.php +++ b/reception.php @@ -12,14 +12,14 @@ $todayTickets = qh_fetch_tickets(['waiting_vitals', 'nursing_called', 'ready_for qh_page_start( 'reception', qh_t('Reception', 'الاستقبال'), - qh_t('Issue new tickets and manage patient flow.', 'إصدار التذاكر الجديدة وإدارة حركة المرضى.') + qh_t('Issue privacy-friendly tickets by clinic and room.', 'إصدار تذاكر تراعي الخصوصية حسب العيادة والغرفة.') ); ?>

-

+

@@ -31,10 +31,6 @@ qh_page_start(
-
- - -
- + +
@@ -74,8 +71,8 @@ qh_page_start(
-
-
+
+
@@ -107,8 +104,8 @@ qh_page_start(