Autosave: 20260331-163419
This commit is contained in:
parent
0c7ba978a8
commit
9ef45e15f2
254
admin.php
254
admin.php
@ -17,147 +17,137 @@ qh_page_start(
|
|||||||
qh_t('Structured admin overview with separate pages for clinics and doctors.', 'نظرة عامة منظمة للإدارة مع صفحات مستقلة للعيادات والأطباء.')
|
qh_t('Structured admin overview with separate pages for clinics and doctors.', 'نظرة عامة منظمة للإدارة مع صفحات مستقلة للعيادات والأطباء.')
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<div class="container-xxl px-3 px-lg-4">
|
<div class="container-fluid container-xxl px-3 px-lg-4">
|
||||||
<div class="admin-layout">
|
<div class="admin-layout">
|
||||||
<aside class="admin-sidebar-column">
|
<aside class="admin-sidebar-column">
|
||||||
<?php qh_render_admin_sidebar('admin.php', $stats); ?>
|
<?php qh_render_admin_sidebar('admin.php', $stats); ?>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div class="admin-content-stack">
|
<div class="admin-content-stack">
|
||||||
<section class="page-header-panel admin-hero-panel mb-0">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<div>
|
<div>
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Admin overview', 'نظرة عامة للإدارة')) ?></span>
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Admin Dashboard', 'لوحة تحكم الإدارة')) ?></h1>
|
||||||
<div class="locale-chip mt-3"><?= qh_h(qh_current_language_badge()) ?></div>
|
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('System configuration and management.', 'إعدادات وإدارة النظام.')) ?></p>
|
||||||
<h1 class="section-title-xl mt-2 mb-2"><?= qh_h(qh_t('A cleaner admin center with separate management pages.', 'مركز إدارة أنظف مع صفحات إدارة منفصلة.')) ?></h1>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Use the left sidebar to open focused pages for clinics and doctors instead of working in one long page.', 'استخدم الشريط الجانبي لفتح صفحات مركزة للعيادات والأطباء بدلاً من العمل داخل صفحة واحدة طويلة.')) ?></p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="panel-card admin-section-card">
|
|
||||||
<div class="admin-section-head">
|
|
||||||
<div>
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('System snapshot', 'ملخص النظام')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_t('Current queue configuration', 'إعداد نظام الطوابير الحالي')) ?></h2>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Quick numbers and shortcuts for the main admin tasks.', 'أرقام سريعة واختصارات لمهام الإدارة الأساسية.')) ?></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin-overview-grid mt-4">
|
|
||||||
<article class="mini-overview-card admin-overview-card"><span class="workflow-step"><?= qh_h(qh_t('Clinics', 'العيادات')) ?></span><strong><?= qh_h((string) $stats['clinics']) ?></strong><p><?= qh_h(qh_t('Configured destinations used by reception and routing.', 'الوجهات المهيأة التي يستخدمها الاستقبال ومسار المريض.')) ?></p></article>
|
|
||||||
<article class="mini-overview-card admin-overview-card"><span class="workflow-step"><?= qh_h(qh_t('Doctors', 'الأطباء')) ?></span><strong><?= qh_h((string) $stats['doctors']) ?></strong><p><?= qh_h(qh_t('Doctors currently assigned to rooms and clinics.', 'الأطباء المعينون حالياً للغرف والعيادات.')) ?></p></article>
|
|
||||||
<article class="mini-overview-card admin-overview-card"><span class="workflow-step"><?= qh_h(qh_t('Vitals first', 'العلامات أولاً')) ?></span><strong><?= qh_h((string) $stats['vitals_clinics']) ?></strong><p><?= qh_h(qh_t('Clinics that must pass through nursing before the doctor.', 'العيادات التي تمر على التمريض قبل الطبيب.')) ?></p></article>
|
|
||||||
<article class="mini-overview-card admin-overview-card"><span class="workflow-step"><?= qh_h(qh_t('Direct doctor', 'الطبيب مباشرة')) ?></span><strong><?= qh_h((string) $stats['direct_clinics']) ?></strong><p><?= qh_h(qh_t('Clinics that route patients directly to the doctor queue.', 'العيادات التي توجه المرضى مباشرة إلى انتظار الطبيب.')) ?></p></article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="admin-card-grid admin-card-grid-triple">
|
|
||||||
<article class="panel-card admin-link-card">
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Branding', 'الهوية')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-2"><?= qh_h(qh_t('Hospital profile', 'ملف المستشفى')) ?></h2>
|
|
||||||
<p class="section-copy mb-4"><?= qh_h(qh_t('Update the hospital logo, favicon, contact details, working hours, and brand colors from a dedicated profile page.', 'حدّث شعار المستشفى والأيقونة وبيانات التواصل وساعات العمل وألوان الهوية من صفحة ملف مستقلة.')) ?></p>
|
|
||||||
<a class="btn btn-dark" href="<?= qh_h(qh_url('admin_hospital.php')) ?>"><?= qh_h(qh_t('Open profile page', 'فتح صفحة الملف')) ?></a>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="panel-card admin-link-card">
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Directory', 'الدليل')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-2"><?= qh_h(qh_t('Manage clinics', 'إدارة العيادات')) ?></h2>
|
|
||||||
<p class="section-copy mb-4"><?= qh_h(qh_t('Create clinics, define codes, set routing, and edit the display order from a dedicated page.', 'أنشئ العيادات وحدد الرموز ومسار العمل وعدّل ترتيب العرض من صفحة مستقلة.')) ?></p>
|
|
||||||
<a class="btn btn-dark" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Open clinics page', 'فتح صفحة العيادات')) ?></a>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="panel-card admin-link-card">
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Directory', 'الدليل')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-2"><?= qh_h(qh_t('Manage doctors', 'إدارة الأطباء')) ?></h2>
|
|
||||||
<p class="section-copy mb-4"><?= qh_h(qh_t('Search the doctor list, update room assignments, and manage records with edit and delete actions.', 'ابحث في قائمة الأطباء وحدّث الغرف والتعيينات وأدر السجلات بخيارات التعديل والحذف.')) ?></p>
|
|
||||||
<a class="btn btn-dark" href="<?= qh_h(qh_url('admin_doctors.php')) ?>"><?= qh_h(qh_t('Open doctors page', 'فتح صفحة الأطباء')) ?></a>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="admin-card-grid admin-card-grid-secondary admin-card-grid-triple">
|
|
||||||
<section class="panel-card admin-section-card hospital-overview-card">
|
|
||||||
<div class="admin-section-head">
|
|
||||||
<div>
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Hospital profile', 'ملف المستشفى')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_hospital_name()) ?></h2>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('admin_hospital.php')) ?>"><?= qh_h(qh_t('Edit profile', 'تعديل الملف')) ?></a>
|
|
||||||
</div>
|
|
||||||
<div class="vstack gap-3 mt-4">
|
|
||||||
<div class="admin-summary-row">
|
|
||||||
<div>
|
|
||||||
<div class="fw-semibold"><?= qh_h(qh_t('Short name', 'الاسم المختصر')) ?></div>
|
|
||||||
<div class="small text-secondary"><?= qh_h((string) ($profile['short_name'] ?? qh_t('Not set yet', 'غير محدد بعد'))) ?></div>
|
|
||||||
</div>
|
|
||||||
<span class="table-pill info"><?= qh_h(qh_t('Brand ready', 'الهوية جاهزة')) ?></span>
|
|
||||||
</div>
|
|
||||||
<div class="admin-summary-row">
|
|
||||||
<div>
|
|
||||||
<div class="fw-semibold"><?= qh_h(qh_t('Phone', 'الهاتف')) ?></div>
|
|
||||||
<div class="small text-secondary"><?= qh_h((string) (($profile['phone'] ?? '') !== '' ? $profile['phone'] : qh_t('Add a contact number', 'أضف رقم تواصل'))) ?></div>
|
|
||||||
</div>
|
|
||||||
<span class="room-badge"><?= qh_h(qh_t('Hours', 'الساعات')) ?></span>
|
|
||||||
</div>
|
|
||||||
<div class="admin-summary-row">
|
|
||||||
<div>
|
|
||||||
<div class="fw-semibold"><?= qh_h(qh_t('Working hours', 'ساعات العمل')) ?></div>
|
|
||||||
<div class="small text-secondary"><?= qh_h(qh_name($profile, 'working_hours', qh_t('Set your opening hours', 'حدّد ساعات العمل'))) ?></div>
|
|
||||||
</div>
|
|
||||||
<span class="table-pill dark"><?= qh_h(qh_t('Shared app branding', 'هوية مشتركة للتطبيق')) ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="panel-card admin-section-card">
|
|
||||||
<div class="admin-section-head">
|
|
||||||
<div>
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Clinic preview', 'معاينة العيادات')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_t('Latest clinic setup', 'أحدث إعدادات العيادات')) ?></h2>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('View all', 'عرض الكل')) ?></a>
|
|
||||||
</div>
|
|
||||||
<?php if ($recentClinics === []): ?>
|
|
||||||
<div class="empty-state compact mt-4"><strong><?= qh_h(qh_t('No clinics configured yet.', 'لم يتم إعداد أي عيادات بعد.')) ?></strong><span><?= qh_h(qh_t('Open the clinics page to add your first clinic.', 'افتح صفحة العيادات لإضافة أول عيادة.')) ?></span></div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="vstack gap-3 mt-4">
|
|
||||||
<?php foreach ($recentClinics as $clinic): ?>
|
|
||||||
<div class="admin-summary-row">
|
|
||||||
<div>
|
|
||||||
<div class="fw-semibold"><?= qh_h(qh_name($clinic)) ?></div>
|
|
||||||
<div class="small text-secondary"><?= qh_h(qh_t('Code', 'الرمز')) ?>: <?= qh_h((string) $clinic['code']) ?></div>
|
|
||||||
</div>
|
|
||||||
<span class="table-pill <?= (int) $clinic['requires_vitals'] === 1 ? 'warning' : 'info' ?>"><?= qh_h((int) $clinic['requires_vitals'] === 1 ? qh_t('Vitals first', 'العلامات أولاً') : qh_t('Direct doctor', 'الطبيب مباشرة')) ?></span>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="panel-card admin-section-card">
|
|
||||||
<div class="admin-section-head">
|
|
||||||
<div>
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Doctor preview', 'معاينة الأطباء')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_t('Latest doctor assignments', 'أحدث تعيينات الأطباء')) ?></h2>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('admin_doctors.php')) ?>"><?= qh_h(qh_t('View all', 'عرض الكل')) ?></a>
|
|
||||||
</div>
|
|
||||||
<?php if ($recentDoctors === []): ?>
|
|
||||||
<div class="empty-state compact mt-4"><strong><?= qh_h(qh_t('No doctors configured yet.', 'لم يتم إعداد أي أطباء بعد.')) ?></strong><span><?= qh_h(qh_t('Open the doctors page to add your first doctor profile.', 'افتح صفحة الأطباء لإضافة أول ملف طبيب.')) ?></span></div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="vstack gap-3 mt-4">
|
|
||||||
<?php foreach ($recentDoctors as $doctor): ?>
|
|
||||||
<div class="admin-summary-row">
|
|
||||||
<div>
|
|
||||||
<div class="fw-semibold"><?= qh_h(qh_name($doctor)) ?></div>
|
|
||||||
<div class="small text-secondary"><?= qh_h(qh_name($doctor, 'clinic_name', qh_t('Unassigned clinic', 'عيادة غير محددة'))) ?></div>
|
|
||||||
</div>
|
|
||||||
<span class="room-badge"><?= qh_h(qh_t('Room', 'الغرفة')) ?> <?= qh_h((string) $doctor['room_number']) ?></span>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-4 mb-4">
|
||||||
|
<div class="col-md-3 col-sm-6">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1 small"><?= qh_h(qh_t('Clinics', 'العيادات')) ?></div>
|
||||||
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['clinics']) ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-sm-6">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-success text-uppercase mb-1 small"><?= qh_h(qh_t('Doctors', 'الأطباء')) ?></div>
|
||||||
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['doctors']) ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-sm-6">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1 small"><?= qh_h(qh_t('Vitals Clinics', 'عيادات العلامات')) ?></div>
|
||||||
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['vitals_clinics']) ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-sm-6">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-info text-uppercase mb-1 small"><?= qh_h(qh_t('Direct Clinics', 'عيادات مباشرة')) ?></div>
|
||||||
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['direct_clinics']) ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-4 mb-4">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Hospital Profile', 'ملف المستشفى')) ?></h5>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary shadow-sm" href="<?= qh_h(qh_url('admin_hospital.php')) ?>"><?= qh_h(qh_t('Edit', 'تعديل')) ?></a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex flex-column gap-3">
|
||||||
|
<div>
|
||||||
|
<div class="fw-semibold text-dark"><?= qh_h(qh_hospital_name()) ?></div>
|
||||||
|
<div class="small text-muted"><?= qh_h((string) ($profile['short_name'] ?? qh_t('No short name', 'لا يوجد اسم مختصر'))) ?></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="small text-muted mb-1"><?= qh_h(qh_t('Phone', 'الهاتف')) ?></div>
|
||||||
|
<div class="fw-semibold text-dark"><?= qh_h((string) (($profile['phone'] ?? '') !== '' ? $profile['phone'] : '--')) ?></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="small text-muted mb-1"><?= qh_h(qh_t('Working hours', 'ساعات العمل')) ?></div>
|
||||||
|
<div class="fw-semibold text-dark"><?= qh_h(qh_name($profile, 'working_hours', '--')) ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Recent Clinics', 'أحدث العيادات')) ?></h5>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary shadow-sm" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Manage', 'إدارة')) ?></a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<?php if ($recentClinics === []): ?>
|
||||||
|
<div class="text-center py-4 text-muted"><p class="mb-0 small"><?= qh_h(qh_t('No clinics yet.', 'لا توجد عيادات.')) ?></p></div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
<?php foreach ($recentClinics as $clinic): ?>
|
||||||
|
<div class="list-group-item d-flex justify-content-between align-items-center py-3 border-0 border-bottom">
|
||||||
|
<div>
|
||||||
|
<div class="fw-semibold text-dark mb-1"><?= qh_h(qh_name($clinic)) ?></div>
|
||||||
|
<div class="small text-muted"><?= qh_h(qh_t('Code', 'الرمز')) ?>: <?= qh_h((string) $clinic['code']) ?></div>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-<?= (int) $clinic['requires_vitals'] === 1 ? 'warning text-dark' : 'info text-dark' ?> rounded-pill px-2 py-1">
|
||||||
|
<?= qh_h((int) $clinic['requires_vitals'] === 1 ? qh_t('Vitals', 'علامات') : qh_t('Direct', 'مباشر')) ?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Recent Doctors', 'أحدث الأطباء')) ?></h5>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary shadow-sm" href="<?= qh_h(qh_url('admin_doctors.php')) ?>"><?= qh_h(qh_t('Manage', 'إدارة')) ?></a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<?php if ($recentDoctors === []): ?>
|
||||||
|
<div class="text-center py-4 text-muted"><p class="mb-0 small"><?= qh_h(qh_t('No doctors yet.', 'لا يوجد أطباء.')) ?></p></div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
<?php foreach ($recentDoctors as $doctor): ?>
|
||||||
|
<div class="list-group-item d-flex justify-content-between align-items-center py-3 border-0 border-bottom">
|
||||||
|
<div>
|
||||||
|
<div class="fw-semibold text-dark mb-1"><?= qh_h(qh_name($doctor)) ?></div>
|
||||||
|
<div class="small text-muted"><?= qh_h(qh_name($doctor, 'clinic_name', qh_t('Unassigned', 'غير محدد'))) ?></div>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-light text-dark border px-2 py-1"><?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h((string) $doctor['room_number']) ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
197
admin_ads.php
Normal file
197
admin_ads.php
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
require_once __DIR__ . '/queue_bootstrap.php';
|
||||||
|
qh_boot();
|
||||||
|
qh_admin_handle_request();
|
||||||
|
|
||||||
|
// Ensure the table exists
|
||||||
|
$pdo = db();
|
||||||
|
$pdo->exec("CREATE TABLE IF NOT EXISTS hospital_ads (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
title VARCHAR(255) NULL,
|
||||||
|
video_path VARCHAR(255) NOT NULL,
|
||||||
|
is_active TINYINT(1) DEFAULT 1,
|
||||||
|
sort_order INT DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Handle Form Submissions
|
||||||
|
$message = '';
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
if (isset($_POST['action']) && $_POST['action'] === 'add_video' && !empty($_FILES['video']['name'])) {
|
||||||
|
$uploadDir = __DIR__ . '/assets/videos/uploads/';
|
||||||
|
if (!is_dir($uploadDir)) {
|
||||||
|
mkdir($uploadDir, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ext = strtolower(pathinfo($_FILES['video']['name'], PATHINFO_EXTENSION));
|
||||||
|
$allowed = ['mp4', 'webm', 'ogg'];
|
||||||
|
|
||||||
|
if (!in_array($ext, $allowed)) {
|
||||||
|
$message = qh_t("Invalid video format (.$ext). Allowed formats: MP4, WebM, OGG.", "تنسيق فيديو غير صالح (.$ext). الصيغ المسموحة: MP4, WebM, OGG.");
|
||||||
|
} elseif ($_FILES['video']['error'] !== UPLOAD_ERR_OK) {
|
||||||
|
$errorMap = [
|
||||||
|
UPLOAD_ERR_INI_SIZE => 'File exceeds max size in php.ini.',
|
||||||
|
UPLOAD_ERR_FORM_SIZE => 'File exceeds max size in HTML form.',
|
||||||
|
UPLOAD_ERR_PARTIAL => 'File was partially uploaded.',
|
||||||
|
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
|
||||||
|
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder.',
|
||||||
|
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.',
|
||||||
|
UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the upload.'
|
||||||
|
];
|
||||||
|
$errMsg = $errorMap[$_FILES['video']['error']] ?? 'Unknown error';
|
||||||
|
$message = qh_t('Upload error: ' . $errMsg, 'خطأ في الرفع: ' . $errMsg);
|
||||||
|
} else {
|
||||||
|
$filename = uniqid('vid_') . '.' . $ext;
|
||||||
|
$dest = $uploadDir . $filename;
|
||||||
|
|
||||||
|
if (move_uploaded_file($_FILES['video']['tmp_name'], $dest)) {
|
||||||
|
$title = $_POST['title'] ?? '';
|
||||||
|
$path = 'assets/videos/uploads/' . $filename;
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO hospital_ads (title, video_path, is_active) VALUES (?, ?, 1)");
|
||||||
|
$stmt->execute([$title, $path]);
|
||||||
|
$message = qh_t('Video uploaded successfully.', 'تم رفع الفيديو بنجاح.');
|
||||||
|
} else {
|
||||||
|
$message = qh_t('Failed to save uploaded file.', 'فشل في حفظ الملف المرفوع.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (isset($_POST['action']) && $_POST['action'] === 'delete_video' && !empty($_POST['video_id'])) {
|
||||||
|
$id = (int) $_POST['video_id'];
|
||||||
|
$stmt = $pdo->prepare("SELECT video_path FROM hospital_ads WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$video = $stmt->fetch();
|
||||||
|
if ($video) {
|
||||||
|
$fullPath = __DIR__ . '/' . $video['video_path'];
|
||||||
|
if (file_exists($fullPath)) {
|
||||||
|
unlink($fullPath);
|
||||||
|
}
|
||||||
|
$pdo->prepare("DELETE FROM hospital_ads WHERE id = ?")->execute([$id]);
|
||||||
|
$message = qh_t('Video deleted successfully.', 'تم حذف الفيديو بنجاح.');
|
||||||
|
}
|
||||||
|
} elseif (isset($_POST['action']) && $_POST['action'] === 'toggle_status' && !empty($_POST['video_id'])) {
|
||||||
|
$id = (int) $_POST['video_id'];
|
||||||
|
$stmt = $pdo->prepare("UPDATE hospital_ads SET is_active = NOT is_active WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$message = qh_t('Video status updated.', 'تم تحديث حالة الفيديو.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch existing ads
|
||||||
|
$stmt = $pdo->query("SELECT * FROM hospital_ads ORDER BY sort_order ASC, id DESC");
|
||||||
|
$ads = $stmt->fetchAll();
|
||||||
|
|
||||||
|
qh_page_start(
|
||||||
|
'admin_ads',
|
||||||
|
qh_t('Manage Advertisements', 'إدارة الإعلانات'),
|
||||||
|
qh_t('Upload and manage videos to display on the queue screen.', 'رفع وإدارة الفيديوهات لعرضها على شاشة الطابور.')
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<div class="container-fluid container-xxl px-3 px-lg-4">
|
||||||
|
<div class="admin-layout">
|
||||||
|
<aside class="admin-sidebar-column">
|
||||||
|
<?php qh_render_admin_sidebar('admin_ads.php'); ?>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class="admin-content-stack">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Advertisements (Videos)', 'الإعلانات (فيديوهات)')) ?></h1>
|
||||||
|
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('These videos will be played sequentially on the display screen.', 'سيتم تشغيل هذه الفيديوهات بالتتابع على شاشة العرض.')) ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="alert alert-info shadow-sm"><?= qh_h($message) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Upload New Video', 'رفع فيديو جديد')) ?></h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" enctype="multipart/form-data" class="row g-3">
|
||||||
|
<input type="hidden" name="action" value="add_video">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Video Title (Optional)', 'عنوان الفيديو (اختياري)')) ?></label>
|
||||||
|
<input type="text" name="title" class="form-control" placeholder="<?= qh_h(qh_t('e.g. Summer Promo', 'مثال: عرض الصيف')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Video File', 'ملف الفيديو')) ?></label>
|
||||||
|
<input type="file" name="video" class="form-control" accept="video/mp4,video/webm,video/ogg" required>
|
||||||
|
<div class="form-text"><?= qh_h(qh_t('Max 5 videos is recommended. Formats: MP4, WebM, OGG.', 'يُنصح بحد أقصى 5 فيديوهات. الصيغ: MP4, WebM, OGG.')) ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 mt-3">
|
||||||
|
<button type="submit" class="btn btn-primary px-4"><?= qh_h(qh_t('Upload Video', 'رفع الفيديو')) ?></button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Uploaded Videos', 'الفيديوهات المرفوعة')) ?></h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Preview', 'معاينة')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Title', 'العنوان')) ?></th>
|
||||||
|
<th class="border-0 py-3 text-center"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
|
||||||
|
<th class="border-0 px-4 py-3 text-end"><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($ads)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="text-center py-4 text-muted"><?= qh_h(qh_t('No videos uploaded yet.', 'لم يتم رفع فيديوهات بعد.')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($ads as $ad): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="px-4 py-3">
|
||||||
|
<video width="120" class="rounded shadow-sm border" muted>
|
||||||
|
<source src="<?= qh_h($ad['video_path']) ?>" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 fw-semibold text-dark">
|
||||||
|
<?= qh_h($ad['title'] ?: qh_t('Untitled', 'بدون عنوان')) ?>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 text-center">
|
||||||
|
<?php if ($ad['is_active']): ?>
|
||||||
|
<span class="badge bg-success rounded-pill px-3"><?= qh_h(qh_t('Active', 'مفعل')) ?></span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="badge bg-secondary rounded-pill px-3"><?= qh_h(qh_t('Inactive', 'غير مفعل')) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td class="px-4 py-3 text-end">
|
||||||
|
<div class="d-flex justify-content-end gap-2">
|
||||||
|
<form method="post" class="m-0 p-0">
|
||||||
|
<input type="hidden" name="action" value="toggle_status">
|
||||||
|
<input type="hidden" name="video_id" value="<?= (int) $ad['id'] ?>">
|
||||||
|
<button type="submit" class="btn btn-sm <?= $ad['is_active'] ? 'btn-outline-secondary' : 'btn-outline-success' ?>">
|
||||||
|
<?= qh_h($ad['is_active'] ? qh_t('Disable', 'تعطيل') : qh_t('Enable', 'تفعيل')) ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<form method="post" class="m-0 p-0" onsubmit="return confirm('<?= qh_h(qh_t('Are you sure you want to delete this video?', 'هل أنت متأكد أنك تريد حذف هذا الفيديو؟')) ?>');">
|
||||||
|
<input type="hidden" name="action" value="delete_video">
|
||||||
|
<input type="hidden" name="video_id" value="<?= (int) $ad['id'] ?>">
|
||||||
|
<button type="submit" class="btn btn-sm btn-outline-danger"><?= qh_h(qh_t('Delete', 'حذف')) ?></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php qh_page_end(); ?>
|
||||||
@ -38,151 +38,148 @@ qh_page_start(
|
|||||||
qh_t('Professional clinic directory with search, edit, and delete actions.', 'دليل احترافي للعيادات مع البحث وخيارات التعديل والحذف.')
|
qh_t('Professional clinic directory with search, edit, and delete actions.', 'دليل احترافي للعيادات مع البحث وخيارات التعديل والحذف.')
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<div class="container-xxl px-3 px-lg-4">
|
<div class="container-fluid container-xxl px-3 px-lg-4">
|
||||||
<div class="admin-layout">
|
<div class="admin-layout">
|
||||||
<aside class="admin-sidebar-column">
|
<aside class="admin-sidebar-column">
|
||||||
<?php qh_render_admin_sidebar('admin_clinics.php', $stats); ?>
|
<?php qh_render_admin_sidebar('admin_clinics.php', $stats); ?>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div class="admin-content-stack">
|
<div class="admin-content-stack">
|
||||||
<section class="page-header-panel admin-hero-panel mb-0">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<div>
|
<div>
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Clinic directory', 'دليل العيادات')) ?></span>
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Clinic Directory', 'دليل العيادات')) ?></h1>
|
||||||
<div class="locale-chip mt-3"><?= qh_h(qh_current_language_badge()) ?></div>
|
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('Manage clinics and routing rules.', 'إدارة العيادات وقواعد التوجيه.')) ?></p>
|
||||||
<h1 class="section-title-xl mt-2 mb-2"><?= qh_h(qh_t('Dedicated clinic management with cleaner routing control.', 'إدارة مستقلة للعيادات مع تحكم أنظف في مسار العمل.')) ?></h1>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Search clinics, adjust codes and routing, and keep the front desk workflow organized.', 'ابحث في العيادات وعدّل الرموز ومسار العمل وحافظ على تنظيم سير العمل في الاستقبال.')) ?></p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<?php if ($editClinic !== null): ?>
|
||||||
|
<a class="btn btn-primary shadow-sm" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><i class="bi bi-plus-lg me-1"></i><?= qh_h(qh_t('Add New', 'إضافة جديد')) ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<section class="panel-card admin-toolbar-card">
|
<div class="card shadow-sm border-0 mb-4 bg-white">
|
||||||
<div class="admin-toolbar">
|
<div class="card-body p-3">
|
||||||
<form method="get" class="admin-search-form" role="search">
|
<form method="get" class="d-flex gap-2 align-items-center flex-wrap" role="search">
|
||||||
<input type="hidden" name="lang" value="<?= qh_h(qh_locale()) ?>">
|
<input type="hidden" name="lang" value="<?= qh_h(qh_locale()) ?>">
|
||||||
<label class="visually-hidden" for="clinicSearch"><?= qh_h(qh_t('Search clinics', 'البحث عن العيادات')) ?></label>
|
<div class="input-group">
|
||||||
<input id="clinicSearch" class="form-control" type="search" name="q" value="<?= qh_h($search) ?>" placeholder="<?= qh_h(qh_t('Search by clinic name or code', 'ابحث باسم العيادة أو الرمز')) ?>">
|
<span class="input-group-text bg-light border-end-0"><i class="bi bi-search text-muted"></i></span>
|
||||||
<button class="btn btn-dark" type="submit"><?= qh_h(qh_t('Search', 'بحث')) ?></button>
|
<input id="clinicSearch" class="form-control border-start-0 ps-0 bg-light" type="search" name="q" value="<?= qh_h($search) ?>" placeholder="<?= qh_h(qh_t('Search by clinic name or code...', 'ابحث باسم العيادة أو الرمز...')) ?>">
|
||||||
|
<button class="btn btn-primary px-4" type="submit"><?= qh_h(qh_t('Search', 'بحث')) ?></button>
|
||||||
|
</div>
|
||||||
<?php if ($search !== ''): ?>
|
<?php if ($search !== ''): ?>
|
||||||
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Reset', 'إعادة ضبط')) ?></a>
|
<a class="btn btn-outline-secondary bg-white" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Clear', 'مسح')) ?></a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</form>
|
</form>
|
||||||
<?php if ($editClinic !== null): ?>
|
|
||||||
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Add new clinic', 'إضافة عيادة جديدة')) ?></a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<div class="admin-directory-layout">
|
<div class="row g-4">
|
||||||
<section class="panel-card admin-table-card">
|
<div class="col-xl-7 col-lg-6">
|
||||||
<div class="admin-section-head">
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<div>
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Clinic list', 'قائمة العيادات')) ?></span>
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Clinics', 'العيادات')) ?></h5>
|
||||||
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_t('Clinics and routing rules', 'العيادات وقواعد مسار العمل')) ?></h2>
|
<span class="badge bg-light text-dark border px-2 py-1"><?= qh_h((string) count($clinics)) ?> <?= qh_h(qh_t('records', 'سجلات')) ?></span>
|
||||||
<p class="section-copy mb-0"><?= qh_h($search !== ''
|
|
||||||
? qh_t('Filtered results based on your search.', 'نتائج مفلترة بناءً على البحث.')
|
|
||||||
: qh_t('All configured clinic records.', 'جميع سجلات العيادات المهيأة.')) ?></p>
|
|
||||||
</div>
|
|
||||||
<div class="admin-count-chip"><?= qh_h((string) count($clinics)) ?> <?= qh_h(qh_t('records', 'سجلات')) ?></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if ($clinics === []): ?>
|
|
||||||
<div class="empty-state compact mt-4"><strong><?= qh_h(qh_t('No clinic records match this view.', 'لا توجد سجلات عيادات مطابقة لهذا العرض.')) ?></strong><span><?= qh_h(qh_t('Try another search or create a new clinic.', 'جرّب بحثاً آخر أو أنشئ عيادة جديدة.')) ?></span></div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="table-responsive mt-4">
|
|
||||||
<table class="table align-middle admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th><?= qh_h(qh_t('Code', 'الرمز')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Routing', 'المسار')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Order', 'الترتيب')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($clinics as $clinic): ?>
|
|
||||||
<?php $editUrl = qh_url('admin_clinics.php', ['edit' => (int) $clinic['id'], 'q' => $search]); ?>
|
|
||||||
<tr>
|
|
||||||
<td><span class="room-badge"><?= qh_h((string) $clinic['code']) ?></span></td>
|
|
||||||
<td>
|
|
||||||
<div class="fw-semibold"><?= qh_h(qh_name($clinic)) ?></div>
|
|
||||||
<div class="small text-secondary"><?= qh_h(qh_is_ar() ? (string) ($clinic['name_en'] ?? '') : (string) ($clinic['name_ar'] ?? '')) ?></div>
|
|
||||||
</td>
|
|
||||||
<td><span class="table-pill <?= (int) $clinic['requires_vitals'] === 1 ? 'warning' : 'info' ?>"><?= qh_h((int) $clinic['requires_vitals'] === 1 ? qh_t('Vitals first', 'العلامات أولاً') : qh_t('Direct doctor', 'الطبيب مباشرة')) ?></span></td>
|
|
||||||
<td><?= qh_h((string) $clinic['sort_order']) ?></td>
|
|
||||||
<td>
|
|
||||||
<div class="admin-table-actions">
|
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h($editUrl) ?>"><?= qh_h(qh_t('Edit', 'تعديل')) ?></a>
|
|
||||||
<form method="post" onsubmit="return confirm('<?= qh_h(qh_t('Delete this clinic record?', 'هل تريد حذف سجل هذه العيادة؟')) ?>');">
|
|
||||||
<input type="hidden" name="action" value="delete_clinic">
|
|
||||||
<input type="hidden" name="clinic_id" value="<?= qh_h((string) $clinic['id']) ?>">
|
|
||||||
<input type="hidden" name="return_to" value="admin_clinics.php">
|
|
||||||
<button class="btn btn-sm btn-outline-danger" type="submit"><?= qh_h(qh_t('Delete', 'حذف')) ?></button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="panel-card admin-form-card">
|
|
||||||
<div class="admin-section-head">
|
|
||||||
<div>
|
|
||||||
<span class="section-kicker"><?= qh_h($editClinic ? qh_t('Edit clinic', 'تعديل العيادة') : qh_t('New clinic', 'عيادة جديدة')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-1"><?= qh_h($editClinic ? qh_t('Update clinic profile', 'تحديث ملف العيادة') : qh_t('Create clinic profile', 'إنشاء ملف العيادة')) ?></h2>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Define the code, both names, routing type, and display order in one focused form.', 'حدّد الرمز والاسمين ونوع المسار وترتيب العرض في نموذج واحد مركز.')) ?></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="post" class="vstack gap-3 mt-4">
|
|
||||||
<input type="hidden" name="action" value="<?= qh_h($editClinic ? 'update_clinic' : 'add_clinic') ?>">
|
|
||||||
<input type="hidden" name="return_to" value="admin_clinics.php">
|
|
||||||
<?php if ($editClinic): ?>
|
|
||||||
<input type="hidden" name="clinic_id" value="<?= qh_h((string) $editClinic['id']) ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div class="row g-3">
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<label class="form-label" for="clinicCode"><?= qh_h(qh_t('Clinic code', 'رمز العيادة')) ?></label>
|
|
||||||
<input id="clinicCode" class="form-control" type="text" maxlength="10" name="code" value="<?= qh_h((string) ($editClinic['code'] ?? '')) ?>" required>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<label class="form-label" for="clinicOrder"><?= qh_h(qh_t('Display order', 'ترتيب العرض')) ?></label>
|
|
||||||
<input id="clinicOrder" class="form-control" type="number" min="1" name="sort_order" value="<?= qh_h((string) ($editClinic['sort_order'] ?? 50)) ?>" required>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="card-body p-0">
|
||||||
<label class="form-label" for="clinicNameEn"><?= qh_h(qh_t('Clinic name (English)', 'اسم العيادة بالإنجليزية')) ?></label>
|
<?php if ($clinics === []): ?>
|
||||||
<input id="clinicNameEn" class="form-control" type="text" name="name_en" value="<?= qh_h((string) ($editClinic['name_en'] ?? '')) ?>" required>
|
<div class="text-center py-5 text-muted">
|
||||||
</div>
|
<p class="mb-0"><?= qh_h(qh_t('No clinics found.', 'لم يتم العثور على عيادات.')) ?></p>
|
||||||
|
</div>
|
||||||
<div>
|
<?php else: ?>
|
||||||
<label class="form-label" for="clinicNameAr"><?= qh_h(qh_t('Clinic name (Arabic)', 'اسم العيادة بالعربية')) ?></label>
|
<div class="table-responsive">
|
||||||
<input id="clinicNameAr" class="form-control" type="text" name="name_ar" value="<?= qh_h((string) ($editClinic['name_ar'] ?? '')) ?>" required>
|
<table class="table table-hover align-middle mb-0">
|
||||||
</div>
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
<div class="admin-switch-card">
|
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Code', 'الرمز')) ?></th>
|
||||||
<div class="form-check form-switch mb-0">
|
<th class="border-0 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="clinicVitals" name="requires_vitals" <?= (int) ($editClinic['requires_vitals'] ?? 0) === 1 ? 'checked' : '' ?>>
|
<th class="border-0 py-3"><?= qh_h(qh_t('Routing', 'المسار')) ?></th>
|
||||||
<label class="form-check-label" for="clinicVitals"><?= qh_h(qh_t('Require nursing vitals before doctor', 'يتطلب العلامات الحيوية في التمريض قبل الطبيب')) ?></label>
|
<th class="border-0 px-4 py-3 text-end"><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
|
||||||
</div>
|
</tr>
|
||||||
<div class="admin-form-note mt-2"><?= qh_h(qh_t('Turn this on for clinics that must stop at nursing before the doctor queue.', 'فعّل هذا الخيار للعيادات التي يجب أن تمر على التمريض قبل قائمة انتظار الطبيب.')) ?></div>
|
</thead>
|
||||||
</div>
|
<tbody>
|
||||||
|
<?php foreach ($clinics as $clinic): ?>
|
||||||
<div class="d-flex flex-wrap gap-2 pt-2">
|
<?php $editUrl = qh_url('admin_clinics.php', ['edit' => (int) $clinic['id'], 'q' => $search]); ?>
|
||||||
<button class="btn btn-dark" type="submit"><?= qh_h($editClinic ? qh_t('Save changes', 'حفظ التعديلات') : qh_t('Add clinic', 'إضافة عيادة')) ?></button>
|
<tr>
|
||||||
<?php if ($editClinic): ?>
|
<td class="px-4 py-3"><span class="badge bg-light text-dark border"><?= qh_h((string) $clinic['code']) ?></span></td>
|
||||||
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Cancel edit', 'إلغاء التعديل')) ?></a>
|
<td class="py-3">
|
||||||
|
<div class="fw-semibold text-dark"><?= qh_h(qh_name($clinic)) ?></div>
|
||||||
|
<div class="small text-muted"><?= qh_h(qh_is_ar() ? (string) ($clinic['name_en'] ?? '') : (string) ($clinic['name_ar'] ?? '')) ?></div>
|
||||||
|
</td>
|
||||||
|
<td class="py-3"><span class="badge bg-<?= (int) $clinic['requires_vitals'] === 1 ? 'warning text-dark' : 'info text-dark' ?> rounded-pill px-2 py-1"><?= qh_h((int) $clinic['requires_vitals'] === 1 ? qh_t('Vitals', 'علامات') : qh_t('Direct', 'مباشر')) ?></span></td>
|
||||||
|
<td class="text-end px-4 py-3">
|
||||||
|
<div class="d-inline-flex gap-2">
|
||||||
|
<a class="btn btn-sm btn-light border shadow-sm" href="<?= qh_h($editUrl) ?>"><i class="bi bi-pencil"></i></a>
|
||||||
|
<form method="post" class="m-0" onsubmit="return confirm('<?= qh_h(qh_t('Delete this clinic record?', 'هل تريد حذف سجل هذه العيادة؟')) ?>');">
|
||||||
|
<input type="hidden" name="action" value="delete_clinic">
|
||||||
|
<input type="hidden" name="clinic_id" value="<?= qh_h((string) $clinic['id']) ?>">
|
||||||
|
<input type="hidden" name="return_to" value="admin_clinics.php">
|
||||||
|
<button class="btn btn-sm btn-outline-danger shadow-sm bg-white" type="submit"><i class="bi bi-trash"></i></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-5 col-lg-6">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h($editClinic ? qh_t('Edit Clinic', 'تعديل العيادة') : qh_t('New Clinic', 'عيادة جديدة')) ?></h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" class="vstack gap-3">
|
||||||
|
<input type="hidden" name="action" value="<?= qh_h($editClinic ? 'update_clinic' : 'add_clinic') ?>">
|
||||||
|
<input type="hidden" name="return_to" value="admin_clinics.php">
|
||||||
|
<?php if ($editClinic): ?>
|
||||||
|
<input type="hidden" name="clinic_id" value="<?= qh_h((string) $editClinic['id']) ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<label class="form-label text-dark fw-semibold small" for="clinicCode"><?= qh_h(qh_t('Code', 'الرمز')) ?></label>
|
||||||
|
<input id="clinicCode" class="form-control bg-light" type="text" maxlength="10" name="code" value="<?= qh_h((string) ($editClinic['code'] ?? '')) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<label class="form-label text-dark fw-semibold small" for="clinicOrder"><?= qh_h(qh_t('Order', 'الترتيب')) ?></label>
|
||||||
|
<input id="clinicOrder" class="form-control bg-light" type="number" min="1" name="sort_order" value="<?= qh_h((string) ($editClinic['sort_order'] ?? 50)) ?>" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label text-dark fw-semibold small" for="clinicNameEn"><?= qh_h(qh_t('Name (English)', 'الاسم بالإنجليزية')) ?></label>
|
||||||
|
<input id="clinicNameEn" class="form-control bg-light" type="text" name="name_en" value="<?= qh_h((string) ($editClinic['name_en'] ?? '')) ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label text-dark fw-semibold small" for="clinicNameAr"><?= qh_h(qh_t('Name (Arabic)', 'الاسم بالعربية')) ?></label>
|
||||||
|
<input id="clinicNameAr" class="form-control bg-light" type="text" name="name_ar" value="<?= qh_h((string) ($editClinic['name_ar'] ?? '')) ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-3 border rounded bg-light mt-2">
|
||||||
|
<div class="form-check form-switch mb-0">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="clinicVitals" name="requires_vitals" <?= (int) ($editClinic['requires_vitals'] ?? 0) === 1 ? 'checked' : '' ?>>
|
||||||
|
<label class="form-check-label ms-2 text-dark" for="clinicVitals"><?= qh_h(qh_t('Require vitals before doctor', 'يتطلب علامات حيوية')) ?></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 pt-3 border-top mt-2">
|
||||||
|
<button class="btn btn-primary shadow-sm" type="submit"><?= qh_h($editClinic ? qh_t('Save Changes', 'حفظ التعديلات') : qh_t('Add Clinic', 'إضافة عيادة')) ?></button>
|
||||||
|
<?php if ($editClinic): ?>
|
||||||
|
<a class="btn btn-outline-secondary bg-white shadow-sm" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Cancel', 'إلغاء')) ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
@ -57,106 +57,141 @@ qh_page_start(
|
|||||||
|
|
||||||
<div class="admin-directory-layout hospital-profile-layout">
|
<div class="admin-directory-layout hospital-profile-layout">
|
||||||
<section class="panel-card admin-form-card">
|
<section class="panel-card admin-form-card">
|
||||||
<div class="admin-section-head">
|
<form method="post" enctype="multipart/form-data" class="vstack gap-5">
|
||||||
<div>
|
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Profile editor', 'تحرير الملف')) ?></span>
|
|
||||||
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_t('Update hospital details', 'تحديث بيانات المستشفى')) ?></h2>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Use image URLs for the logo and favicon. The saved branding is reused in the top navigation and browser tab.', 'استخدم روابط صور للشعار والأيقونة. سيتم استخدام الهوية المحفوظة في شريط التنقل العلوي وتبويب المتصفح.')) ?></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="post" class="vstack gap-4 mt-4">
|
|
||||||
<input type="hidden" name="action" value="save_hospital_profile">
|
<input type="hidden" name="action" value="save_hospital_profile">
|
||||||
<input type="hidden" name="return_to" value="admin_hospital.php">
|
<input type="hidden" name="return_to" value="admin_hospital.php">
|
||||||
|
|
||||||
<div class="row g-3">
|
<!-- Basic Info -->
|
||||||
<div class="col-lg-6">
|
<div>
|
||||||
<label class="form-label" for="hospitalNameEn"><?= qh_h(qh_t('Hospital name (English)', 'اسم المستشفى بالإنجليزية')) ?></label>
|
<div class="admin-section-head mb-4">
|
||||||
<input id="hospitalNameEn" class="form-control" type="text" name="name_en" value="<?= qh_h((string) ($profile['name_en'] ?? '')) ?>" required>
|
<div>
|
||||||
|
<h2 class="section-title mb-1"><?= qh_h(qh_t('Basic Information', 'المعلومات الأساسية')) ?></h2>
|
||||||
|
<p class="section-copy mb-0"><?= qh_h(qh_t('Core naming used across the system.', 'التسمية الأساسية المستخدمة في النظام.')) ?></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="row g-3">
|
||||||
<label class="form-label" for="hospitalNameAr"><?= qh_h(qh_t('Hospital name (Arabic)', 'اسم المستشفى بالعربية')) ?></label>
|
<div class="col-lg-6">
|
||||||
<input id="hospitalNameAr" class="form-control" type="text" name="name_ar" value="<?= qh_h((string) ($profile['name_ar'] ?? '')) ?>" required>
|
<label class="form-label fw-bold" for="hospitalNameEn"><?= qh_h(qh_t('Hospital name (English)', 'اسم المستشفى بالإنجليزية')) ?></label>
|
||||||
|
<input id="hospitalNameEn" class="form-control" type="text" name="name_en" value="<?= qh_h((string) ($profile['name_en'] ?? '')) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-label fw-bold" for="hospitalNameAr"><?= qh_h(qh_t('Hospital name (Arabic)', 'اسم المستشفى بالعربية')) ?></label>
|
||||||
|
<input id="hospitalNameAr" class="form-control" type="text" name="name_ar" value="<?= qh_h((string) ($profile['name_ar'] ?? '')) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<label class="form-label fw-bold" for="hospitalShortName"><?= qh_h(qh_t('Short name', 'الاسم المختصر')) ?></label>
|
||||||
|
<input id="hospitalShortName" class="form-control" type="text" maxlength="40" name="short_name" value="<?= qh_h((string) ($profile['short_name'] ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<label class="form-label fw-bold" for="hospitalTaglineEn"><?= qh_h(qh_t('Tagline (English)', 'الشعار النصي بالإنجليزية')) ?></label>
|
||||||
|
<input id="hospitalTaglineEn" class="form-control" type="text" name="tagline_en" value="<?= qh_h((string) ($profile['tagline_en'] ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<label class="form-label fw-bold" for="hospitalTaglineAr"><?= qh_h(qh_t('Tagline (Arabic)', 'الشعار النصي بالعربية')) ?></label>
|
||||||
|
<input id="hospitalTaglineAr" class="form-control" type="text" name="tagline_ar" value="<?= qh_h((string) ($profile['tagline_ar'] ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-label fw-bold" for="newsTickerEn"><?= qh_h(qh_t('News Ticker (English)', 'شريط الأخبار بالإنجليزية')) ?></label>
|
||||||
|
<input id="newsTickerEn" class="form-control" type="text" name="news_ticker_en" value="<?= qh_h((string) ($profile['news_ticker_en'] ?? '')) ?>" placeholder="<?= qh_h(qh_t('Scrolling text for display board', 'نص متحرك لشاشة العرض')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-label fw-bold" for="newsTickerAr"><?= qh_h(qh_t('News Ticker (Arabic)', 'شريط الأخبار بالعربية')) ?></label>
|
||||||
|
<input id="newsTickerAr" class="form-control" type="text" name="news_ticker_ar" value="<?= qh_h((string) ($profile['news_ticker_ar'] ?? '')) ?>" placeholder="<?= qh_h(qh_t('Scrolling text for display board', 'نص متحرك لشاشة العرض')) ?>">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-3">
|
<hr class="m-0 text-muted">
|
||||||
<div class="col-lg-4">
|
|
||||||
<label class="form-label" for="hospitalShortName"><?= qh_h(qh_t('Short name', 'الاسم المختصر')) ?></label>
|
<!-- Contact & Location -->
|
||||||
<input id="hospitalShortName" class="form-control" type="text" maxlength="40" name="short_name" value="<?= qh_h((string) ($profile['short_name'] ?? '')) ?>">
|
<div>
|
||||||
|
<div class="admin-section-head mb-4">
|
||||||
|
<div>
|
||||||
|
<h2 class="section-title mb-1"><?= qh_h(qh_t('Contact & Location', 'التواصل والموقع')) ?></h2>
|
||||||
|
<p class="section-copy mb-0"><?= qh_h(qh_t('Public details shown to patients or staff.', 'التفاصيل العامة المعروضة للمرضى أو الموظفين.')) ?></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="row g-3">
|
||||||
<label class="form-label" for="hospitalPhone"><?= qh_h(qh_t('Phone number', 'رقم الهاتف')) ?></label>
|
<div class="col-lg-4">
|
||||||
<input id="hospitalPhone" class="form-control" type="text" name="phone" value="<?= qh_h((string) ($profile['phone'] ?? '')) ?>">
|
<label class="form-label fw-bold" for="hospitalPhone"><?= qh_h(qh_t('Phone number', 'رقم الهاتف')) ?></label>
|
||||||
</div>
|
<input id="hospitalPhone" class="form-control" type="text" name="phone" value="<?= qh_h((string) ($profile['phone'] ?? '')) ?>">
|
||||||
<div class="col-lg-4">
|
</div>
|
||||||
<label class="form-label" for="hospitalEmail"><?= qh_h(qh_t('Email address', 'البريد الإلكتروني')) ?></label>
|
<div class="col-lg-4">
|
||||||
<input id="hospitalEmail" class="form-control" type="email" name="email" value="<?= qh_h((string) ($profile['email'] ?? '')) ?>">
|
<label class="form-label fw-bold" for="hospitalEmail"><?= qh_h(qh_t('Email address', 'البريد الإلكتروني')) ?></label>
|
||||||
|
<input id="hospitalEmail" class="form-control" type="email" name="email" value="<?= qh_h((string) ($profile['email'] ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<label class="form-label fw-bold" for="hospitalWebsite"><?= qh_h(qh_t('Website URL', 'رابط الموقع الإلكتروني')) ?></label>
|
||||||
|
<input id="hospitalWebsite" class="form-control" type="url" name="website" placeholder="https://example.com" value="<?= qh_h((string) ($profile['website'] ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-label fw-bold" for="hospitalAddressEn"><?= qh_h(qh_t('Address (English)', 'العنوان بالإنجليزية')) ?></label>
|
||||||
|
<textarea id="hospitalAddressEn" class="form-control" name="address_en" rows="3"><?= qh_h((string) ($profile['address_en'] ?? '')) ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-label fw-bold" for="hospitalAddressAr"><?= qh_h(qh_t('Address (Arabic)', 'العنوان بالعربية')) ?></label>
|
||||||
|
<textarea id="hospitalAddressAr" class="form-control" name="address_ar" rows="3"><?= qh_h((string) ($profile['address_ar'] ?? '')) ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-label fw-bold" for="hospitalHoursEn"><?= qh_h(qh_t('Working hours (English)', 'ساعات العمل بالإنجليزية')) ?></label>
|
||||||
|
<input id="hospitalHoursEn" class="form-control" type="text" name="working_hours_en" value="<?= qh_h((string) ($profile['working_hours_en'] ?? '')) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-label fw-bold" for="hospitalHoursAr"><?= qh_h(qh_t('Working hours (Arabic)', 'ساعات العمل بالعربية')) ?></label>
|
||||||
|
<input id="hospitalHoursAr" class="form-control" type="text" name="working_hours_ar" value="<?= qh_h((string) ($profile['working_hours_ar'] ?? '')) ?>">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-3">
|
<hr class="m-0 text-muted">
|
||||||
<div class="col-lg-6">
|
|
||||||
<label class="form-label" for="hospitalTaglineEn"><?= qh_h(qh_t('Tagline (English)', 'الشعار النصي بالإنجليزية')) ?></label>
|
|
||||||
<input id="hospitalTaglineEn" class="form-control" type="text" name="tagline_en" value="<?= qh_h((string) ($profile['tagline_en'] ?? '')) ?>">
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<label class="form-label" for="hospitalTaglineAr"><?= qh_h(qh_t('Tagline (Arabic)', 'الشعار النصي بالعربية')) ?></label>
|
|
||||||
<input id="hospitalTaglineAr" class="form-control" type="text" name="tagline_ar" value="<?= qh_h((string) ($profile['tagline_ar'] ?? '')) ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row g-3">
|
<!-- Branding -->
|
||||||
<div class="col-lg-6">
|
<div>
|
||||||
<label class="form-label" for="hospitalAddressEn"><?= qh_h(qh_t('Address (English)', 'العنوان بالإنجليزية')) ?></label>
|
<div class="admin-section-head mb-4">
|
||||||
<textarea id="hospitalAddressEn" class="form-control" name="address_en" rows="3"><?= qh_h((string) ($profile['address_en'] ?? '')) ?></textarea>
|
<div>
|
||||||
|
<h2 class="section-title mb-1"><?= qh_h(qh_t('Branding & Assets', 'الهوية والأصول')) ?></h2>
|
||||||
|
<p class="section-copy mb-0"><?= qh_h(qh_t('Upload logo/favicon and choose brand colors.', 'قم برفع الشعار/الأيقونة واختر ألوان الهوية.')) ?></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="row g-4">
|
||||||
<label class="form-label" for="hospitalAddressAr"><?= qh_h(qh_t('Address (Arabic)', 'العنوان بالعربية')) ?></label>
|
<div class="col-lg-6">
|
||||||
<textarea id="hospitalAddressAr" class="form-control" name="address_ar" rows="3"><?= qh_h((string) ($profile['address_ar'] ?? '')) ?></textarea>
|
<label class="form-label fw-bold" for="hospitalLogoUpload"><?= qh_h(qh_t('Upload Logo', 'رفع الشعار')) ?></label>
|
||||||
</div>
|
<input id="hospitalLogoUpload" class="form-control" type="file" name="logo_upload" accept="image/*">
|
||||||
</div>
|
<?php if ($logoUrl !== ''): ?>
|
||||||
|
<div class="form-check mt-2">
|
||||||
<div class="row g-3">
|
<input class="form-check-input" type="checkbox" name="remove_logo" value="1" id="removeLogo">
|
||||||
<div class="col-lg-6">
|
<label class="form-check-label text-danger" for="removeLogo">
|
||||||
<label class="form-label" for="hospitalHoursEn"><?= qh_h(qh_t('Working hours (English)', 'ساعات العمل بالإنجليزية')) ?></label>
|
<?= qh_h(qh_t('Remove current logo', 'إزالة الشعار الحالي')) ?>
|
||||||
<input id="hospitalHoursEn" class="form-control" type="text" name="working_hours_en" value="<?= qh_h((string) ($profile['working_hours_en'] ?? '')) ?>">
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<?php endif; ?>
|
||||||
<label class="form-label" for="hospitalHoursAr"><?= qh_h(qh_t('Working hours (Arabic)', 'ساعات العمل بالعربية')) ?></label>
|
</div>
|
||||||
<input id="hospitalHoursAr" class="form-control" type="text" name="working_hours_ar" value="<?= qh_h((string) ($profile['working_hours_ar'] ?? '')) ?>">
|
<div class="col-lg-6">
|
||||||
</div>
|
<label class="form-label fw-bold" for="hospitalFaviconUpload"><?= qh_h(qh_t('Upload Favicon', 'رفع الأيقونة')) ?></label>
|
||||||
</div>
|
<input id="hospitalFaviconUpload" class="form-control" type="file" name="favicon_upload" accept="image/*">
|
||||||
|
<?php if ($faviconUrl !== ''): ?>
|
||||||
<div class="row g-3">
|
<div class="form-check mt-2">
|
||||||
<div class="col-lg-6">
|
<input class="form-check-input" type="checkbox" name="remove_favicon" value="1" id="removeFavicon">
|
||||||
<label class="form-label" for="hospitalWebsite"><?= qh_h(qh_t('Website URL', 'رابط الموقع الإلكتروني')) ?></label>
|
<label class="form-check-label text-danger" for="removeFavicon">
|
||||||
<input id="hospitalWebsite" class="form-control" type="url" name="website" placeholder="https://example.com" value="<?= qh_h((string) ($profile['website'] ?? '')) ?>">
|
<?= qh_h(qh_t('Remove current favicon', 'إزالة الأيقونة الحالية')) ?>
|
||||||
</div>
|
</label>
|
||||||
<div class="col-lg-6">
|
</div>
|
||||||
<label class="form-label" for="hospitalLogoUrl"><?= qh_h(qh_t('Logo image URL', 'رابط صورة الشعار')) ?></label>
|
<?php endif; ?>
|
||||||
<input id="hospitalLogoUrl" class="form-control" type="url" name="logo_url" placeholder="https://.../logo.png" value="<?= qh_h((string) ($profile['logo_url'] ?? '')) ?>">
|
</div>
|
||||||
</div>
|
<div class="col-lg-3">
|
||||||
</div>
|
<label class="form-label fw-bold" for="hospitalPrimaryColor"><?= qh_h(qh_t('Primary color', 'اللون الأساسي')) ?></label>
|
||||||
|
<input id="hospitalPrimaryColor" class="form-control form-control-color w-100" type="color" name="primary_color" value="<?= qh_h(qh_hospital_primary_color()) ?>">
|
||||||
<div class="row g-3">
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-3">
|
||||||
<label class="form-label" for="hospitalFaviconUrl"><?= qh_h(qh_t('Favicon URL', 'رابط الأيقونة')) ?></label>
|
<label class="form-label fw-bold" for="hospitalSecondaryColor"><?= qh_h(qh_t('Secondary color', 'اللون الثانوي')) ?></label>
|
||||||
<input id="hospitalFaviconUrl" class="form-control" type="url" name="favicon_url" placeholder="https://.../favicon.png" value="<?= qh_h((string) ($profile['favicon_url'] ?? '')) ?>">
|
<input id="hospitalSecondaryColor" class="form-control form-control-color w-100" type="color" name="secondary_color" value="<?= qh_h(qh_hospital_secondary_color()) ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3">
|
|
||||||
<label class="form-label" for="hospitalPrimaryColor"><?= qh_h(qh_t('Primary color', 'اللون الأساسي')) ?></label>
|
|
||||||
<input id="hospitalPrimaryColor" class="form-control form-control-color w-100" type="color" name="primary_color" value="<?= qh_h(qh_hospital_primary_color()) ?>">
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-3">
|
|
||||||
<label class="form-label" for="hospitalSecondaryColor"><?= qh_h(qh_t('Secondary color', 'اللون الثانوي')) ?></label>
|
|
||||||
<input id="hospitalSecondaryColor" class="form-control form-control-color w-100" type="color" name="secondary_color" value="<?= qh_h(qh_hospital_secondary_color()) ?>">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex flex-wrap gap-2 pt-2">
|
<div class="d-flex flex-wrap gap-2 pt-2">
|
||||||
<button class="btn btn-dark" type="submit"><?= qh_h(qh_t('Save hospital profile', 'حفظ ملف المستشفى')) ?></button>
|
<button class="btn btn-dark btn-lg px-4" type="submit"><?= qh_h(qh_t('Save hospital profile', 'حفظ ملف المستشفى')) ?></button>
|
||||||
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('admin.php')) ?>"><?= qh_h(qh_t('Back to overview', 'العودة إلى النظرة العامة')) ?></a>
|
<a class="btn btn-outline-secondary btn-lg px-4" href="<?= qh_h(qh_url('admin.php')) ?>"><?= qh_h(qh_t('Cancel', 'إلغاء')) ?></a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
@ -222,7 +257,7 @@ qh_page_start(
|
|||||||
<?php if ($logoUrl !== ''): ?>
|
<?php if ($logoUrl !== ''): ?>
|
||||||
<img class="hospital-asset-image" src="<?= qh_h($logoUrl) ?>" alt="<?= qh_h(qh_t('Hospital logo preview', 'معاينة شعار المستشفى')) ?>">
|
<img class="hospital-asset-image" src="<?= qh_h($logoUrl) ?>" alt="<?= qh_h(qh_t('Hospital logo preview', 'معاينة شعار المستشفى')) ?>">
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="empty-state compact mt-3"><strong><?= qh_h(qh_t('No logo yet.', 'لا يوجد شعار حتى الآن.')) ?></strong><span><?= qh_h(qh_t('Add a logo URL to replace the initials badge in the top header.', 'أضف رابط شعار لاستبدال شارة الأحرف في الترويسة العلوية.')) ?></span></div>
|
<div class="empty-state compact mt-3"><strong><?= qh_h(qh_t('No logo yet.', 'لا يوجد شعار حتى الآن.')) ?></strong><span><?= qh_h(qh_t('Upload a logo to replace the initials badge in the top header.', 'ارفع شعاراً لاستبدال شارة الأحرف في الترويسة العلوية.')) ?></span></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="hospital-asset-box">
|
<div class="hospital-asset-box">
|
||||||
@ -230,7 +265,7 @@ qh_page_start(
|
|||||||
<?php if ($faviconUrl !== ''): ?>
|
<?php if ($faviconUrl !== ''): ?>
|
||||||
<img class="hospital-asset-image hospital-asset-favicon" src="<?= qh_h($faviconUrl) ?>" alt="<?= qh_h(qh_t('Favicon preview', 'معاينة الأيقونة')) ?>">
|
<img class="hospital-asset-image hospital-asset-favicon" src="<?= qh_h($faviconUrl) ?>" alt="<?= qh_h(qh_t('Favicon preview', 'معاينة الأيقونة')) ?>">
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="empty-state compact mt-3"><strong><?= qh_h(qh_t('No custom favicon yet.', 'لا توجد أيقونة مخصصة حتى الآن.')) ?></strong><span><?= qh_h(qh_t('If left empty, the app will reuse the logo as the browser icon when available.', 'إذا تُرك الحقل فارغاً، سيعيد التطبيق استخدام الشعار كأيقونة للمتصفح عند توفره.')) ?></span></div>
|
<div class="empty-state compact mt-3"><strong><?= qh_h(qh_t('No custom favicon yet.', 'لا توجد أيقونة مخصصة حتى الآن.')) ?></strong><span><?= qh_h(qh_t('If missing, the app will reuse the logo as the browser icon when available.', 'إذا كانت مفقودة، سيعيد التطبيق استخدام الشعار كأيقونة للمتصفح عند توفره.')) ?></span></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -240,4 +275,4 @@ qh_page_start(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
@ -1159,3 +1159,103 @@ html[dir="rtl"] .hospital-detail-list dt,
|
|||||||
html[dir="rtl"] .hospital-detail-list dd {
|
html[dir="rtl"] .hospital-detail-list dd {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print Ticket Styling (80mm Thermal Printer) */
|
||||||
|
@media print {
|
||||||
|
body * {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.app-header,
|
||||||
|
main.app-shell > .container-fluid,
|
||||||
|
main.app-shell > .container,
|
||||||
|
main.app-shell > .container-xxl,
|
||||||
|
div[style*="position: fixed; bottom: 0"] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#print-ticket, #print-ticket * {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#print-ticket {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 80mm;
|
||||||
|
margin: 0;
|
||||||
|
padding: 5mm;
|
||||||
|
display: block !important;
|
||||||
|
background: white;
|
||||||
|
color: black;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-ticket-inner {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-header {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-token-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-token-number {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 5px 0 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-patient-name {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-divider {
|
||||||
|
border-top: 1px dashed black;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-detail {
|
||||||
|
font-size: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[dir="rtl"] .pt-detail {
|
||||||
|
text-align: right;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-datetime {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-footer {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@page {
|
||||||
|
margin: 0;
|
||||||
|
size: 80mm auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
BIN
assets/images/uploads/img_69cbc7b59b0785.42983933.png
Normal file
BIN
assets/images/uploads/img_69cbc7b59b0785.42983933.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
assets/images/uploads/img_69cbc7b59c3a98.53445034.png
Normal file
BIN
assets/images/uploads/img_69cbc7b59c3a98.53445034.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
assets/videos/uploads/test_video_1.mp4
Normal file
BIN
assets/videos/uploads/test_video_1.mp4
Normal file
Binary file not shown.
BIN
assets/videos/uploads/test_video_2.mp4
Normal file
BIN
assets/videos/uploads/test_video_2.mp4
Normal file
Binary file not shown.
BIN
assets/videos/uploads/vid_69cbe51070e11.mp4
Normal file
BIN
assets/videos/uploads/vid_69cbe51070e11.mp4
Normal file
Binary file not shown.
204
display.php
204
display.php
@ -3,95 +3,155 @@ declare(strict_types=1);
|
|||||||
require_once __DIR__ . '/queue_bootstrap.php';
|
require_once __DIR__ . '/queue_bootstrap.php';
|
||||||
qh_boot();
|
qh_boot();
|
||||||
|
|
||||||
$activeCalls = qh_fetch_tickets(['called', 'in_progress'], null, 6);
|
$activeCalls = qh_fetch_tickets(['called', 'in_progress'], null, 8);
|
||||||
$queueOverview = qh_queue_overview();
|
$queueOverview = qh_queue_overview();
|
||||||
|
|
||||||
|
$activeVideos = [];
|
||||||
|
try {
|
||||||
|
$stmt = db()->query("SELECT video_path FROM hospital_ads WHERE is_active = 1 ORDER BY sort_order ASC, id DESC LIMIT 10");
|
||||||
|
if ($stmt) {
|
||||||
|
$activeVideos = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
// Table might not exist yet, safe to ignore
|
||||||
|
}
|
||||||
|
|
||||||
qh_page_start(
|
qh_page_start(
|
||||||
'display',
|
'display',
|
||||||
qh_t('General display board', 'لوحة العرض العامة'),
|
qh_t('General display board', 'لوحة العرض العامة'),
|
||||||
qh_t('Public queue display with separated English and Arabic modes.', 'شاشة طوابير عامة مع وضعي عرض منفصلين بالعربية والإنجليزية.')
|
qh_t('Public queue display.', 'شاشة طوابير عامة.')
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<div class="container-fluid px-3 px-lg-4 py-2" data-auto-refresh="20">
|
<div class="container-fluid px-3 px-lg-4 py-3" data-auto-refresh="20">
|
||||||
<section class="display-shell">
|
<div class="row g-4 h-100">
|
||||||
<div class="display-main panel-card p-4">
|
<div class="col-xl-8 col-lg-7 d-flex flex-column gap-4">
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-4">
|
<div class="card shadow-sm border-0 flex-grow-1">
|
||||||
<div>
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
<div class="section-kicker"><?= qh_h(qh_t('General display', 'الشاشة العامة')) ?></div>
|
<div>
|
||||||
<h1 class="section-title-xl mt-2 mb-1"><?= qh_h(qh_t('Now serving', 'يتم الآن النداء')) ?></h1>
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Now Serving', 'يتم الآن النداء')) ?></h1>
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('This page shows a single language view. Use the language switch in the main app to open the English or Arabic display mode.', 'تعرض هذه الصفحة لغة واحدة فقط. استخدم مبدل اللغة في التطبيق لفتح وضع العرض العربي أو الإنجليزي.')) ?></p>
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="fs-4 fw-bold text-dark js-live-clock"><?= qh_h(date('H:i')) ?></div>
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm shadow-sm js-fullscreen-toggle" aria-pressed="false" data-label-enter="<?= qh_h(qh_t('Fullscreen', 'ملء الشاشة')) ?>" data-label-exit="<?= qh_h(qh_t('Exit fullscreen', 'إنهاء ملء الشاشة')) ?>"><i class="bi bi-arrows-fullscreen"></i></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center gap-2 flex-wrap justify-content-end">
|
|
||||||
<button type="button" class="btn btn-dark btn-sm js-fullscreen-toggle" aria-pressed="false" data-label-enter="<?= qh_h(qh_t('Full display', 'عرض كامل')) ?>" data-label-exit="<?= qh_h(qh_t('Exit full display', 'إنهاء العرض الكامل')) ?>"><?= qh_h(qh_t('Full display', 'عرض كامل')) ?></button>
|
<div class="card-body p-4 bg-light">
|
||||||
<div class="live-clock js-live-clock"><?= qh_h(date('H:i')) ?></div>
|
<?php if ($activeCalls): ?>
|
||||||
|
<div class="row g-3">
|
||||||
|
<?php foreach ($activeCalls as $ticket): $speech = qh_call_message($ticket); ?>
|
||||||
|
<div class="col-6 col-lg-3">
|
||||||
|
<div class="card border-0 shadow-sm h-100" data-announcement-key="<?= qh_h((string) $ticket['id']) ?>-<?= qh_h((string) strtotime((string) $ticket['called_at'])) ?>" data-announcement-en="<?= qh_h($speech['en']) ?>" data-announcement-ar="<?= qh_h($speech['ar']) ?>">
|
||||||
|
<div class="card-body text-center p-3">
|
||||||
|
<div class="display-5 fw-bold text-primary mb-1"><?= qh_h($ticket['ticket_number']) ?></div>
|
||||||
|
<div class="fs-6 text-muted mb-1 text-truncate" style="max-width: 100%;" title="<?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Doctor', 'الطبيب'))) ?>"><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Doctor', 'الطبيب'))) ?></div>
|
||||||
|
<div class="d-inline-block bg-primary text-white rounded px-2 py-1 fs-6 fw-bold mt-1">
|
||||||
|
<?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer bg-white text-center text-muted small py-1" style="font-size: 0.75rem;">
|
||||||
|
<?= qh_format_datetime($ticket['called_at'] ?? $ticket['updated_at']) ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="d-flex align-items-center justify-content-center h-100 min-vh-50 text-muted">
|
||||||
|
<div class="text-center">
|
||||||
|
<i class="bi bi-display display-1 mb-3 opacity-50"></i>
|
||||||
|
<h2><?= qh_h(qh_t('No active calls right now.', 'لا توجد نداءات نشطة حالياً.')) ?></h2>
|
||||||
|
<p class="lead opacity-75"><?= qh_h(qh_t('Please wait for your ticket number.', 'يرجى الانتظار حتى يتم نداء رقم تذكرتك.')) ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($activeCalls): ?>
|
<div class="card shadow-sm border-0">
|
||||||
<div class="vstack gap-3">
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
<?php foreach ($activeCalls as $ticket): $speech = qh_call_message($ticket); ?>
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Queue by Clinic', 'الطابور حسب العيادة')) ?></h5>
|
||||||
<article class="announcement-card" data-announcement-key="<?= qh_h((string) $ticket['id']) ?>-<?= qh_h((string) strtotime((string) $ticket['called_at'])) ?>" data-announcement-en="<?= qh_h($speech['en']) ?>" data-announcement-ar="<?= qh_h($speech['ar']) ?>">
|
|
||||||
<div>
|
|
||||||
<div class="ticket-number large"><?= qh_h($ticket['ticket_number']) ?></div>
|
|
||||||
<div class="display-meta"><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Doctor', 'الطبيب'))) ?> · <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?></div>
|
|
||||||
</div>
|
|
||||||
<div class="text-end">
|
|
||||||
<?= qh_status_badge($ticket['status']) ?>
|
|
||||||
<div class="small text-secondary mt-2"><?= qh_format_datetime($ticket['called_at'] ?? $ticket['updated_at']) ?></div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<div class="card-body p-0">
|
||||||
<div class="empty-state display-empty">
|
<div class="table-responsive">
|
||||||
<strong><?= qh_h(qh_t('No live calls right now.', 'لا توجد نداءات مباشرة حالياً.')) ?></strong>
|
<table class="table table-hover align-middle mb-0">
|
||||||
<span><?= qh_h(qh_t('When a doctor presses “Call patient”, the ticket will appear here.', 'عندما يضغط الطبيب على زر نداء المريض ستظهر التذكرة هنا.')) ?></span>
|
<thead class="table-light">
|
||||||
</div>
|
<tr>
|
||||||
<?php endif; ?>
|
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
||||||
|
<th class="border-0 py-3 text-center"><?= qh_h(qh_t('Vitals Wait', 'انتظار العلامات')) ?></th>
|
||||||
<div class="panel-subsection mt-4">
|
<th class="border-0 px-4 py-3 text-center"><?= qh_h(qh_t('Doctor Wait', 'انتظار الطبيب')) ?></th>
|
||||||
<h2 class="section-title mb-3"><?= qh_h(qh_t('Queue by clinic', 'الطابور حسب العيادة')) ?></h2>
|
</tr>
|
||||||
<div class="row g-3">
|
</thead>
|
||||||
<?php foreach ($queueOverview as $row): ?>
|
<tbody>
|
||||||
<div class="col-md-6 col-xl-4">
|
<?php foreach ($queueOverview as $row): ?>
|
||||||
<div class="mini-overview-card">
|
<tr>
|
||||||
<div class="fw-semibold"><?= qh_h(qh_name($row)) ?></div>
|
<td class="fw-semibold px-4 py-3 text-dark"><?= qh_h(qh_name($row)) ?></td>
|
||||||
<div class="d-flex gap-2 mt-3 flex-wrap">
|
<td class="py-3 text-center"><span class="badge bg-warning text-dark rounded-pill px-3"><?= qh_h((string) $row['vitals_waiting']) ?></span></td>
|
||||||
<span class="table-pill warning"><?= qh_h(qh_t('Vitals', 'العلامات')) ?> <?= qh_h((string) $row['vitals_waiting']) ?></span>
|
<td class="py-3 text-center px-4"><span class="badge bg-info text-dark rounded-pill px-3"><?= qh_h((string) $row['doctor_waiting']) ?></span></td>
|
||||||
<span class="table-pill info"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?> <?= qh_h((string) $row['doctor_waiting']) ?></span>
|
</tr>
|
||||||
</div>
|
<?php endforeach; ?>
|
||||||
</div>
|
</tbody>
|
||||||
</div>
|
</table>
|
||||||
<?php endforeach; ?>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside class="display-ads panel-card p-4">
|
<div class="col-xl-4 col-lg-5">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="sticky-top" style="top: 1rem; height: 60vh;">
|
||||||
<div>
|
<?php if (!empty($activeVideos)): ?>
|
||||||
<div class="section-kicker"><?= qh_h(qh_t('Ads and notices', 'الإعلانات والتنبيهات')) ?></div>
|
<div class="card shadow-sm border-0 h-100 bg-black overflow-hidden d-flex justify-content-center align-items-center">
|
||||||
<h2 class="section-title mb-1"><?= qh_h(qh_t('Patient information', 'معلومات المرضى')) ?></h2>
|
<video id="adsVideoPlayer" class="w-100 h-100 object-fit-contain" autoplay muted playsinline>
|
||||||
|
<source src="<?= qh_h($activeVideos[0]) ?>" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const videos = <?= json_encode($activeVideos, JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
let currentIdx = 0;
|
||||||
|
const player = document.getElementById('adsVideoPlayer');
|
||||||
|
|
||||||
|
if (videos.length > 1) {
|
||||||
|
player.addEventListener('ended', function() {
|
||||||
|
currentIdx = (currentIdx + 1) % videos.length;
|
||||||
|
player.src = videos[currentIdx];
|
||||||
|
player.play().catch(function(e) { console.error("Error playing video:", e); });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If only one video, loop it
|
||||||
|
player.loop = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge rounded-pill text-bg-dark"><?= qh_h(qh_t('Looping', 'متكرر')) ?></span>
|
<?php else: ?>
|
||||||
</div>
|
<div class="card shadow-sm border-0 h-100 bg-primary text-white">
|
||||||
|
<div class="card-header border-bottom border-light border-opacity-25 py-3 bg-transparent d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-white"><i class="bi bi-info-circle me-2"></i><?= qh_h(qh_t('Information', 'معلومات')) ?></h5>
|
||||||
|
<span class="badge bg-white text-primary rounded-pill small"><?= qh_h(qh_t('Notices', 'تنبيهات')) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-4 d-flex flex-column gap-4">
|
||||||
|
<div class="border border-light border-opacity-25 rounded p-4 bg-white bg-opacity-10">
|
||||||
|
<div class="badge bg-light text-primary mb-2 px-2 py-1"><?= qh_h(qh_t('Service', 'خدمة')) ?></div>
|
||||||
|
<h4 class="text-white fw-bold"><?= qh_h(qh_t('Lab packages & checks', 'باقات المختبر والفحوصات')) ?></h4>
|
||||||
|
<p class="mb-0 text-white text-opacity-75"><?= qh_h(qh_t('Ask reception about bundled blood tests, diabetes follow-up, and annual screenings.', 'اسأل الاستقبال عن باقات تحاليل الدم، ومتابعة السكري، والفحوصات السنوية.')) ?></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border border-light border-opacity-25 rounded p-4 bg-white bg-opacity-10">
|
||||||
|
<div class="badge bg-light text-primary mb-2 px-2 py-1"><?= qh_h(qh_t('Reminder', 'تذكير')) ?></div>
|
||||||
|
<h4 class="text-white fw-bold"><?= qh_h(qh_t('Keep your ticket visible', 'احتفظ بتذكرتك ظاهرة')) ?></h4>
|
||||||
|
<p class="mb-0 text-white text-opacity-75"><?= qh_h(qh_t('We announce ticket numbers on this screen and by voice. Stay near your department area.', 'نعلن أرقام التذاكر على هذه الشاشة وبالصوت. يرجى البقاء قرب منطقة القسم الخاص بك.')) ?></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="ad-card mb-3">
|
<div class="border border-light border-opacity-25 rounded p-4 bg-white bg-opacity-10">
|
||||||
<div class="ad-tag"><?= qh_h(qh_t('Service', 'خدمة')) ?></div>
|
<div class="badge bg-light text-primary mb-2 px-2 py-1"><?= qh_h(qh_t('Wayfinding', 'الإرشاد')) ?></div>
|
||||||
<h3><?= qh_h(qh_t('Lab packages and wellness checks', 'باقات المختبر والفحوصات الوقائية')) ?></h3>
|
<h4 class="text-white fw-bold"><?= qh_h(qh_t('Pharmacy & billing', 'الصيدلية والمحاسبة')) ?></h4>
|
||||||
<p><?= qh_h(qh_t('Ask reception about bundled blood tests, diabetes follow-up, and annual screenings.', 'اسأل الاستقبال عن باقات تحاليل الدم، ومتابعة السكري، والفحوصات السنوية.')) ?></p>
|
<p class="mb-0 text-white text-opacity-75"><?= qh_h(qh_t('Completed visits can proceed to the pharmacy and billing desk near the main exit.', 'بعد انتهاء الزيارة يمكن التوجه إلى الصيدلية ومكتب المحاسبة قرب المخرج الرئيسي.')) ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="ad-card mb-3">
|
</div>
|
||||||
<div class="ad-tag"><?= qh_h(qh_t('Reminder', 'تذكير')) ?></div>
|
</div>
|
||||||
<h3><?= qh_h(qh_t('Keep your ticket visible', 'احتفظ بتذكرتك ظاهرة')) ?></h3>
|
|
||||||
<p><?= qh_h(qh_t('We announce ticket numbers on this screen and by voice. Stay near your department area.', 'نعلن أرقام التذاكر على هذه الشاشة وبالصوت. يرجى البقاء قرب منطقة القسم الخاص بك.')) ?></p>
|
|
||||||
</div>
|
|
||||||
<div class="ad-card">
|
|
||||||
<div class="ad-tag"><?= qh_h(qh_t('Wayfinding', 'الإرشاد')) ?></div>
|
|
||||||
<h3><?= qh_h(qh_t('Pharmacy and billing', 'الصيدلية والمحاسبة')) ?></h3>
|
|
||||||
<p><?= qh_h(qh_t('Completed visits can proceed to the pharmacy and billing desk near the main exit.', 'بعد انتهاء الزيارة يمكن التوجه إلى الصيدلية ومكتب المحاسبة قرب المخرج الرئيسي.')) ?></p>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
145
doctor.php
145
doctor.php
@ -11,86 +11,101 @@ $doctorQueue = $selectedDoctorId > 0 ? qh_fetch_tickets(['ready_for_doctor', 'ca
|
|||||||
|
|
||||||
qh_page_start(
|
qh_page_start(
|
||||||
'doctor',
|
'doctor',
|
||||||
qh_t('Doctor room queue', 'طابور غرفة الطبيب'),
|
qh_t('Doctor queue', 'طابور الطبيب'),
|
||||||
qh_t('Doctor workspace with separate English and Arabic page views.', 'مساحة عمل الطبيب مع صفحات عربية وإنجليزية منفصلة.')
|
qh_t('Doctor workspace.', 'مساحة عمل الطبيب.')
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<div class="container-xxl px-3 px-lg-4">
|
<div class="container-fluid container-xxl px-3 px-lg-4">
|
||||||
<section class="page-header-panel mb-4">
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></span>
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Doctor Workspace', 'مساحة عمل الطبيب')) ?></h1>
|
||||||
<div class="locale-chip mt-3"><?= qh_h(qh_current_language_badge()) ?></div>
|
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('Call the next patient and manage room flow.', 'نادِ المريض التالي وأدر حركة الغرفة.')) ?></p>
|
||||||
<h1 class="section-title-xl mt-2"><?= qh_h(qh_t('Call the next patient and manage room flow.', 'نادِ المريض التالي وأدر حركة الغرفة.')) ?></h1>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Select a doctor room, then call, start, complete, or mark no-show for the assigned patients.', 'اختر غرفة الطبيب ثم قم بنداء المرضى المخصصين أو بدء الزيارة أو إنهائها أو تسجيل عدم الحضور.')) ?></p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-xl-4">
|
<div class="col-xl-4">
|
||||||
<div class="panel-card h-100">
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<label class="form-label"><?= qh_h(qh_t('Doctor room', 'غرفة الطبيب')) ?></label>
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
<form method="get" class="vstack gap-3">
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Select Room', 'اختيار الغرفة')) ?></h5>
|
||||||
<input type="hidden" name="lang" value="<?= qh_h(qh_locale()) ?>">
|
</div>
|
||||||
<select class="form-select" name="doctor_id" onchange="this.form.submit()">
|
<div class="card-body">
|
||||||
<?php foreach ($doctors as $doctor): ?>
|
<form method="get" class="vstack gap-3">
|
||||||
<option value="<?= qh_h((string) $doctor['id']) ?>" <?= (int) $doctor['id'] === $selectedDoctorId ? 'selected' : '' ?>><?= qh_h(qh_name($doctor)) ?> · <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($doctor['room_number']) ?></option>
|
<input type="hidden" name="lang" value="<?= qh_h(qh_locale()) ?>">
|
||||||
<?php endforeach; ?>
|
<div>
|
||||||
</select>
|
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Doctor room', 'غرفة الطبيب')) ?></label>
|
||||||
<?php if ($selectedDoctor): ?>
|
<select class="form-select bg-light" name="doctor_id" onchange="this.form.submit()">
|
||||||
<div class="mini-overview-card">
|
<?php foreach ($doctors as $doctor): ?>
|
||||||
<div class="fw-semibold"><?= qh_h(qh_name($selectedDoctor)) ?></div>
|
<option value="<?= qh_h((string) $doctor['id']) ?>" <?= (int) $doctor['id'] === $selectedDoctorId ? 'selected' : '' ?>><?= qh_h(qh_name($doctor)) ?> · <?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h($doctor['room_number']) ?></option>
|
||||||
<div class="small text-secondary mt-1"><?= qh_h(qh_t('Room', 'الغرفة')) ?> <?= qh_h($selectedDoctor['room_number'] ?? '--') ?></div>
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php if ($selectedDoctor): ?>
|
||||||
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('display.php')) ?>" target="_blank" rel="noopener"><?= qh_h(qh_t('Preview public display', 'معاينة الشاشة العامة')) ?></a>
|
<div class="p-3 bg-light rounded border mt-2">
|
||||||
</form>
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1 small"><?= qh_h(qh_t('Active Doctor', 'الطبيب النشط')) ?></div>
|
||||||
|
<div class="fw-bold text-dark fs-5 mb-1"><?= qh_h(qh_name($selectedDoctor)) ?></div>
|
||||||
|
<div class="text-muted small"><i class="bi bi-door-open me-1"></i><?= qh_h(qh_t('Room', 'الغرفة')) ?> <?= qh_h($selectedDoctor['room_number'] ?? '--') ?></div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a class="btn btn-outline-secondary bg-white shadow-sm mt-3 w-100" href="<?= qh_h(qh_url('display.php')) ?>" target="_blank" rel="noopener"><i class="bi bi-display me-2"></i><?= qh_h(qh_t('Open Display', 'فتح الشاشة العامة')) ?></a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xl-8">
|
<div class="col-xl-8">
|
||||||
<div class="panel-card h-100">
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Room Queue', 'طابور الغرفة')) ?></h5>
|
||||||
<h2 class="section-title mb-1"><?= qh_h(qh_t('Doctor queue', 'طابور الطبيب')) ?></h2>
|
</div>
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Patients currently assigned to this room.', 'المرضى المخصصون حالياً لهذه الغرفة.')) ?></p>
|
<div class="card-body p-0">
|
||||||
</div>
|
<?php if (!$selectedDoctor): ?>
|
||||||
<?php if ($selectedDoctor): ?>
|
<div class="text-center py-5 text-muted">
|
||||||
<span class="room-badge"><?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($selectedDoctor['room_number'] ?? '--') ?></span>
|
<p class="mb-0"><?= qh_h(qh_t('Please select a doctor to view the queue.', 'يرجى اختيار طبيب لعرض الطابور.')) ?></p>
|
||||||
|
</div>
|
||||||
|
<?php elseif (empty($doctorQueue)): ?>
|
||||||
|
<div class="text-center py-5 text-muted">
|
||||||
|
<p class="mb-0"><?= qh_h(qh_t('No patients waiting for this room.', 'لا يوجد مرضى في انتظار هذه الغرفة.')) ?></p>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Patient', 'المريض')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
|
||||||
|
<th class="border-0 px-4 py-3 text-end"><?= qh_h(qh_t('Action', 'الإجراء')) ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($doctorQueue as $ticket): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="fw-bold px-4 py-3 text-dark"><?= qh_h($ticket['ticket_number']) ?></td>
|
||||||
|
<td class="py-3 fw-semibold text-dark"><?= qh_h($ticket['patient_name']) ?></td>
|
||||||
|
<td class="py-3"><?= qh_status_badge($ticket['status']) ?></td>
|
||||||
|
<td class="text-end px-4 py-3">
|
||||||
|
<form method="post" class="d-inline-flex gap-2">
|
||||||
|
<input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>"> <input type="hidden" name="doctor_id" value="<?= qh_h((string) $selectedDoctorId) ?>">
|
||||||
|
<?php if ($ticket['status'] === 'ready_for_doctor'): ?>
|
||||||
|
<button class="btn btn-sm btn-primary shadow-sm" type="submit" name="action" value="call_ticket"><?= qh_h(qh_t('Call', 'نداء')) ?></button>
|
||||||
|
<?php elseif ($ticket['status'] === 'called'): ?>
|
||||||
|
<button class="btn btn-sm btn-success shadow-sm" type="submit" name="action" value="start_visit"><?= qh_h(qh_t('Start', 'بدء')) ?></button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger shadow-sm bg-white" type="submit" name="action" value="mark_no_show"><?= qh_h(qh_t('No-show', 'غائب')) ?></button>
|
||||||
|
<?php elseif ($ticket['status'] === 'in_progress'): ?>
|
||||||
|
<button class="btn btn-sm btn-dark shadow-sm" type="submit" name="action" value="complete_ticket"><?= qh_h(qh_t('Complete', 'إنهاء')) ?></button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($doctorQueue): ?>
|
|
||||||
<div class="vstack gap-3">
|
|
||||||
<?php foreach ($doctorQueue as $ticket): ?>
|
|
||||||
<article class="list-row-form">
|
|
||||||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
|
|
||||||
<div>
|
|
||||||
<div class="ticket-code"><?= qh_h($ticket['ticket_number']) ?></div>
|
|
||||||
<div class="fw-semibold mt-1"><?= qh_h($ticket['patient_name']) ?></div>
|
|
||||||
<div class="small text-secondary mt-1"><?= qh_h(qh_name($ticket, 'clinic_name')) ?> · <?= qh_h(qh_t('Created', 'أُنشئت')) ?> <?= qh_format_datetime($ticket['created_at']) ?></div>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex gap-2 flex-wrap align-items-center">
|
|
||||||
<?= qh_status_badge($ticket['status']) ?>
|
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('ticket.php', ['id' => (int) $ticket['id']])) ?>"><?= qh_h(qh_t('Ticket detail', 'تفاصيل التذكرة')) ?></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex flex-wrap gap-2 mt-3">
|
|
||||||
<form method="post"><input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>"><input type="hidden" name="doctor_id" value="<?= qh_h((string) $selectedDoctorId) ?>"><input type="hidden" name="action" value="call_ticket"><button class="btn btn-dark btn-sm" type="submit"><?= qh_h(qh_t('Call patient', 'نداء المريض')) ?></button></form>
|
|
||||||
<form method="post"><input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>"><input type="hidden" name="doctor_id" value="<?= qh_h((string) $selectedDoctorId) ?>"><input type="hidden" name="action" value="start_visit"><button class="btn btn-outline-dark btn-sm" type="submit"><?= qh_h(qh_t('Start visit', 'بدء الزيارة')) ?></button></form>
|
|
||||||
<form method="post"><input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>"><input type="hidden" name="doctor_id" value="<?= qh_h((string) $selectedDoctorId) ?>"><input type="hidden" name="action" value="complete_ticket"><button class="btn btn-outline-success btn-sm" type="submit"><?= qh_h(qh_t('Mark done', 'إنهاء الزيارة')) ?></button></form>
|
|
||||||
<form method="post"><input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>"><input type="hidden" name="doctor_id" value="<?= qh_h((string) $selectedDoctorId) ?>"><input type="hidden" name="action" value="mark_no_show"><button class="btn btn-outline-danger btn-sm" type="submit"><?= qh_h(qh_t('No-show', 'عدم حضور')) ?></button></form>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="empty-state">
|
|
||||||
<strong><?= qh_h(qh_t('No patients are in this doctor queue.', 'لا يوجد مرضى في طابور هذا الطبيب.')) ?></strong>
|
|
||||||
<span><?= qh_h(qh_t('Issue a ticket at reception or complete vitals in nursing to fill this room.', 'أصدر تذكرة من الاستقبال أو أكمل العلامات الحيوية في التمريض لإضافة مرضى لهذه الغرفة.')) ?></span>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
271
index.php
271
index.php
@ -14,156 +14,153 @@ qh_page_start(
|
|||||||
qh_t('English operations dashboard for the hospital queue workflow.', 'لوحة عمليات عربية لمسار طابور المستشفى.')
|
qh_t('English operations dashboard for the hospital queue workflow.', 'لوحة عمليات عربية لمسار طابور المستشفى.')
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<div class="container-xxl px-3 px-lg-4">
|
<div class="container-fluid container-xxl px-3 px-lg-4">
|
||||||
<section class="hero-panel mb-4 mb-lg-5">
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-4">
|
||||||
<div class="row g-4 align-items-center">
|
<div>
|
||||||
<div class="col-lg-7">
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Operations Dashboard', 'لوحة عمليات المستشفى')) ?></h1>
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Hospital queue system', 'نظام طوابير المستشفى')) ?></span>
|
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('Real-time hospital queue system metrics.', 'إحصائيات لحظية لنظام طوابير المستشفى.')) ?></p>
|
||||||
<div class="locale-chip mt-3"><?= qh_h(qh_current_language_badge()) ?></div>
|
</div>
|
||||||
<h1 class="display-title mt-3 mb-3"><?= qh_h(qh_t('One clear workflow for reception, nursing, doctors, and the public display.', 'مسار واضح للاستقبال والتمريض والأطباء والشاشة العامة.')) ?></h1>
|
<div class="d-flex gap-2">
|
||||||
<p class="lead text-secondary mb-4"><?= qh_h(qh_t('Track each patient with one ticket, route them to vitals only when needed, and keep staff and patients aligned in real time.', 'تابع كل مريض بتذكرة واحدة، ووجّهه إلى العلامات الحيوية عند الحاجة فقط، وحافظ على تنسيق العمل بين الطاقم والمرضى لحظياً.')) ?></p>
|
<a class="btn btn-primary shadow-sm" href="<?= qh_h(qh_url('reception.php')) ?>"><?= qh_h(qh_t('Issue ticket', 'إصدار تذكرة')) ?></a>
|
||||||
<div class="d-flex flex-wrap gap-2">
|
<a class="btn btn-outline-secondary shadow-sm bg-white" href="<?= qh_h(qh_url('display.php')) ?>" target="_blank"><?= qh_h(qh_t('Public display', 'الشاشة العامة')) ?></a>
|
||||||
<a class="btn btn-dark" href="<?= qh_h(qh_url('reception.php')) ?>"><?= qh_h(qh_t('Issue ticket', 'إصدار تذكرة')) ?></a>
|
</div>
|
||||||
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('display.php')) ?>"><?= qh_h(qh_t('Open public display', 'فتح الشاشة العامة')) ?></a>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<div class="row g-4 mb-4">
|
||||||
<div class="col-lg-5">
|
<div class="col-md-3 col-sm-6">
|
||||||
<div class="hero-card stack-card h-100">
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<div class="small text-uppercase text-secondary fw-semibold mb-2"><?= qh_h(qh_t('Today', 'اليوم')) ?></div>
|
<div class="card-body">
|
||||||
<div class="row g-3">
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1 small"><?= qh_h(qh_t('Issued today', 'التذاكر الصادرة اليوم')) ?></div>
|
||||||
<div class="col-6"><div class="metric-card"><div class="metric-value"><?= qh_h((string) $stats['issued_today']) ?></div><div class="metric-label"><?= qh_h(qh_t('Issued tickets', 'التذاكر الصادرة')) ?></div></div></div>
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['issued_today']) ?></div>
|
||||||
<div class="col-6"><div class="metric-card"><div class="metric-value"><?= qh_h((string) $stats['waiting_vitals']) ?></div><div class="metric-label"><?= qh_h(qh_t('Waiting vitals', 'بانتظار العلامات')) ?></div></div></div>
|
|
||||||
<div class="col-6"><div class="metric-card"><div class="metric-value"><?= qh_h((string) $stats['ready_for_doctor']) ?></div><div class="metric-label"><?= qh_h(qh_t('Ready for doctor', 'جاهز للطبيب')) ?></div></div></div>
|
|
||||||
<div class="col-6"><div class="metric-card"><div class="metric-value"><?= qh_h((string) $stats['active_rooms']) ?></div><div class="metric-label"><?= qh_h(qh_t('Active rooms', 'الغرف النشطة')) ?></div></div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<div class="col-md-3 col-sm-6">
|
||||||
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<section class="row g-4 mb-4 mb-lg-5">
|
<div class="card-body">
|
||||||
<div class="col-xl-8">
|
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1 small"><?= qh_h(qh_t('Waiting vitals', 'بانتظار العلامات')) ?></div>
|
||||||
<div class="panel-card h-100">
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['waiting_vitals']) ?></div>
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
|
||||||
<div>
|
|
||||||
<h2 class="section-title mb-1"><?= qh_h(qh_t('Live queue overview', 'نظرة مباشرة على الطابور')) ?></h2>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('See clinic demand, waiting load, and active calls at a glance.', 'اطلع بسرعة على ضغط العيادات والانتظار والنداءات النشطة.')) ?></p>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('admin.php')) ?>"><?= qh_h(qh_t('Manage clinics and doctors', 'إدارة العيادات والأطباء')) ?></a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="table-responsive">
|
</div>
|
||||||
<table class="table align-middle mb-0">
|
</div>
|
||||||
<thead>
|
<div class="col-md-3 col-sm-6">
|
||||||
<tr>
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
<div class="card-body">
|
||||||
<th><?= qh_h(qh_t('Vitals wait', 'انتظار العلامات')) ?></th>
|
<div class="text-xs font-weight-bold text-info text-uppercase mb-1 small"><?= qh_h(qh_t('Ready for doctor', 'جاهز للطبيب')) ?></div>
|
||||||
<th><?= qh_h(qh_t('Doctor wait', 'انتظار الطبيب')) ?></th>
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['ready_for_doctor']) ?></div>
|
||||||
<th><?= qh_h(qh_t('Active calls', 'النداءات النشطة')) ?></th>
|
</div>
|
||||||
<th><?= qh_h(qh_t('Today total', 'إجمالي اليوم')) ?></th>
|
</div>
|
||||||
</tr>
|
</div>
|
||||||
</thead>
|
<div class="col-md-3 col-sm-6">
|
||||||
<tbody>
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<?php foreach ($overview as $row): ?>
|
<div class="card-body">
|
||||||
<tr>
|
<div class="text-xs font-weight-bold text-success text-uppercase mb-1 small"><?= qh_h(qh_t('Active rooms', 'الغرف النشطة')) ?></div>
|
||||||
<td class="fw-semibold"><?= qh_h(qh_name($row)) ?></td>
|
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['active_rooms']) ?></div>
|
||||||
<td><span class="table-pill warning"><?= qh_h((string) $row['vitals_waiting']) ?></span></td>
|
</div>
|
||||||
<td><span class="table-pill info"><?= qh_h((string) $row['doctor_waiting']) ?></span></td>
|
</div>
|
||||||
<td><span class="table-pill dark"><?= qh_h((string) $row['active_calls']) ?></span></td>
|
</div>
|
||||||
<td><?= qh_h((string) $row['total_today']) ?></td>
|
</div>
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
<div class="row g-4 mb-4">
|
||||||
</tbody>
|
<div class="col-xl-8">
|
||||||
</table>
|
<div class="card shadow-sm border-0 h-100">
|
||||||
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Live queue overview', 'نظرة مباشرة على الطابور')) ?></h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Vitals wait', 'انتظار العلامات')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Doctor wait', 'انتظار الطبيب')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Active calls', 'النداءات النشطة')) ?></th>
|
||||||
|
<th class="border-0 text-end px-4 py-3"><?= qh_h(qh_t('Today total', 'إجمالي اليوم')) ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($overview as $row): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="fw-semibold px-4 py-3 text-dark"><?= qh_h(qh_name($row)) ?></td>
|
||||||
|
<td class="py-3"><span class="badge bg-warning text-dark px-2 py-1 rounded-pill"><?= qh_h((string) $row['vitals_waiting']) ?></span></td>
|
||||||
|
<td class="py-3"><span class="badge bg-info text-dark px-2 py-1 rounded-pill"><?= qh_h((string) $row['doctor_waiting']) ?></span></td>
|
||||||
|
<td class="py-3"><span class="badge bg-dark px-2 py-1 rounded-pill"><?= qh_h((string) $row['active_calls']) ?></span></td>
|
||||||
|
<td class="text-end px-4 py-3 text-muted"><?= qh_h((string) $row['total_today']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-4">
|
<div class="col-xl-4">
|
||||||
<div class="panel-card h-100">
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<h2 class="section-title mb-1"><?= qh_h(qh_t('Current calls', 'النداءات الحالية')) ?></h2>
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
<p class="section-copy mb-3"><?= qh_h(qh_t('Patients already called into active doctor rooms.', 'المرضى الذين تم استدعاؤهم بالفعل إلى غرف الأطباء.')) ?></p>
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Current calls', 'النداءات الحالية')) ?></h5>
|
||||||
<?php if ($calledTickets): ?>
|
</div>
|
||||||
<div class="vstack gap-3">
|
<div class="card-body">
|
||||||
<?php foreach ($calledTickets as $ticket): ?>
|
<?php if ($calledTickets): ?>
|
||||||
<div class="call-strip">
|
<div class="vstack gap-3">
|
||||||
<div>
|
<?php foreach ($calledTickets as $ticket): ?>
|
||||||
<div class="ticket-code"><?= qh_h($ticket['ticket_number']) ?></div>
|
<div class="d-flex justify-content-between align-items-center p-3 border rounded bg-light">
|
||||||
<div class="small text-secondary"><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Unassigned', 'غير محدد'))) ?> · <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?></div>
|
<div>
|
||||||
|
<div class="fw-bold h5 mb-1 text-dark"><?= qh_h($ticket['ticket_number']) ?></div>
|
||||||
|
<div class="small text-muted"><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Unassigned', 'غير محدد'))) ?> • <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?></div>
|
||||||
|
</div>
|
||||||
|
<?= qh_status_badge($ticket['status']) ?>
|
||||||
</div>
|
</div>
|
||||||
<?= qh_status_badge($ticket['status']) ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php else: ?>
|
||||||
</div>
|
<div class="text-center py-4 text-muted">
|
||||||
<?php else: ?>
|
<p class="mb-0"><?= qh_h(qh_t('No active calls yet.', 'لا توجد نداءات نشطة حالياً.')) ?></p>
|
||||||
<div class="empty-state compact">
|
</div>
|
||||||
<strong><?= qh_h(qh_t('No active calls yet.', 'لا توجد نداءات نشطة حالياً.')) ?></strong>
|
<?php endif; ?>
|
||||||
<span><?= qh_h(qh_t('Use the doctor page to call the next patient.', 'استخدم صفحة الطبيب لنداء المريض التالي.')) ?></span>
|
</div>
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<section class="row g-4 mb-4 mb-lg-5">
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
<?php
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
$workflow = [
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Recent patient flow', 'آخر حركة للمرضى')) ?></h5>
|
||||||
['step' => '01', 'title' => qh_t('Reception issues one ticket', 'الاستقبال يصدر تذكرة واحدة'), 'copy' => qh_t('Choose the clinic and doctor once at the front desk.', 'يتم اختيار العيادة والطبيب مرة واحدة من مكتب الاستقبال.')],
|
|
||||||
['step' => '02', 'title' => qh_t('Optional vitals step', 'خطوة العلامات الحيوية عند الحاجة'), 'copy' => qh_t('Only clinics that require vitals route through nursing first.', 'فقط العيادات التي تتطلب العلامات الحيوية تمر أولاً على التمريض.')],
|
|
||||||
['step' => '03', 'title' => qh_t('Doctor calls the patient', 'الطبيب ينادي المريض'), 'copy' => qh_t('The doctor room page pushes the next call to the display.', 'صفحة غرفة الطبيب ترسل النداء التالي إلى الشاشة.')],
|
|
||||||
['step' => '04', 'title' => qh_t('Display announces the call', 'الشاشة تعلن النداء'), 'copy' => qh_t('Patients see the latest ticket and room assignment immediately.', 'يرى المرضى آخر تذكرة ورقم الغرفة مباشرة.')],
|
|
||||||
];
|
|
||||||
?>
|
|
||||||
<?php foreach ($workflow as $card): ?>
|
|
||||||
<div class="col-md-6 col-xl-3">
|
|
||||||
<article class="panel-card workflow-card h-100">
|
|
||||||
<span class="workflow-step"><?= qh_h($card['step']) ?></span>
|
|
||||||
<h3><?= qh_h($card['title']) ?></h3>
|
|
||||||
<p><?= qh_h($card['copy']) ?></p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="panel-card">
|
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
|
||||||
<div>
|
|
||||||
<h2 class="section-title mb-1"><?= qh_h(qh_t('Recent patient flow', 'آخر حركة للمرضى')) ?></h2>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('The newest in-progress tickets across the workflow.', 'أحدث التذاكر النشطة عبر مسار العمل.')) ?></p>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('reception.php')) ?>"><?= qh_h(qh_t('Create new ticket', 'إنشاء تذكرة جديدة')) ?></a>
|
|
||||||
</div>
|
</div>
|
||||||
<?php if ($recentTickets): ?>
|
<div class="card-body p-0">
|
||||||
<div class="table-responsive">
|
<?php if ($recentTickets): ?>
|
||||||
<table class="table align-middle mb-0">
|
<div class="table-responsive">
|
||||||
<thead>
|
<table class="table table-hover align-middle mb-0">
|
||||||
<tr>
|
<thead class="table-light">
|
||||||
<th><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Patient', 'المريض')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($recentTickets as $ticket): ?>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="fw-semibold"><?= qh_h($ticket['ticket_number']) ?></td>
|
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th>
|
||||||
<td><?= qh_h($ticket['patient_name']) ?></td>
|
<th class="border-0 py-3"><?= qh_h(qh_t('Patient', 'المريض')) ?></th>
|
||||||
<td><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td>
|
<th class="border-0 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
||||||
<td><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Unassigned', 'غير محدد'))) ?></td>
|
<th class="border-0 py-3"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></th>
|
||||||
<td><?= qh_status_badge($ticket['status']) ?></td>
|
<th class="border-0 py-3"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
|
||||||
<td class="text-end"><a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('ticket.php', ['id' => (int) $ticket['id']])) ?>"><?= qh_h(qh_t('View detail', 'عرض التفاصيل')) ?></a></td>
|
<th class="border-0 px-4 py-3 text-end"></th>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
<?php foreach ($recentTickets as $ticket): ?>
|
||||||
</div>
|
<tr>
|
||||||
<?php else: ?>
|
<td class="fw-bold px-4 py-3 text-dark"><?= qh_h($ticket['ticket_number']) ?></td>
|
||||||
<div class="empty-state compact">
|
<td class="py-3"><?= qh_h($ticket['patient_name']) ?></td>
|
||||||
<strong><?= qh_h(qh_t('No recent tickets yet.', 'لا توجد تذاكر حديثة حتى الآن.')) ?></strong>
|
<td class="py-3 text-muted"><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td>
|
||||||
<span><?= qh_h(qh_t('Start from reception to populate the workflow.', 'ابدأ من صفحة الاستقبال لبدء تعبئة مسار العمل.')) ?></span>
|
<td class="py-3 text-muted"><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Unassigned', 'غير محدد'))) ?></td>
|
||||||
</div>
|
<td class="py-3"><?= qh_status_badge($ticket['status']) ?></td>
|
||||||
<?php endif; ?>
|
<td class="text-end px-4 py-3"><a class="btn btn-sm btn-light border" href="<?= qh_h(qh_url('ticket.php', ['id' => (int) $ticket['id']])) ?>"><?= qh_h(qh_t('View', 'عرض')) ?></a></td>
|
||||||
</section>
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="text-center py-5 text-muted">
|
||||||
|
<p class="mb-0"><?= qh_h(qh_t('No recent tickets yet.', 'لا توجد تذاكر حديثة حتى الآن.')) ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
94
nursing.php
94
nursing.php
@ -8,63 +8,59 @@ $waitingTickets = qh_fetch_tickets(['waiting_vitals'], null, 20);
|
|||||||
|
|
||||||
qh_page_start(
|
qh_page_start(
|
||||||
'nursing',
|
'nursing',
|
||||||
qh_t('Nursing vitals queue', 'طابور التمريض للعلامات الحيوية'),
|
qh_t('Nursing vitals', 'علامات التمريض'),
|
||||||
qh_t('Nursing workspace with separate English and Arabic views.', 'مساحة عمل التمريض مع فصل بين الواجهة العربية والإنجليزية.')
|
qh_t('Nursing workspace.', 'مساحة عمل التمريض.')
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<div class="container-xxl px-3 px-lg-4">
|
<div class="container-fluid container-xxl px-3 px-lg-4">
|
||||||
<section class="page-header-panel mb-4">
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Nursing', 'التمريض')) ?></span>
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Nursing Workspace', 'مساحة عمل التمريض')) ?></h1>
|
||||||
<div class="locale-chip mt-3"><?= qh_h(qh_current_language_badge()) ?></div>
|
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('Capture vitals and release patients to doctors.', 'سجّل العلامات الحيوية ثم حوّل المرضى إلى الأطباء.')) ?></p>
|
||||||
<h1 class="section-title-xl mt-2"><?= qh_h(qh_t('Capture vitals and release patients to doctors.', 'سجّل العلامات الحيوية ثم حوّل المرضى إلى الأطباء.')) ?></h1>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Only clinics configured for vitals-first appear here.', 'تظهر هنا فقط العيادات التي تم إعدادها لتبدأ بالعلامات الحيوية.')) ?></p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<div class="panel-card">
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Waiting for Vitals', 'بانتظار العلامات الحيوية')) ?></h5>
|
||||||
<h2 class="section-title mb-1"><?= qh_h(qh_t('Waiting for vitals', 'بانتظار العلامات الحيوية')) ?></h2>
|
<span class="badge bg-secondary rounded-pill"><?= qh_h(count($waitingTickets) . ' ' . qh_t('patients', 'مرضى')) ?></span>
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Add a short note, then send the patient to the doctor queue.', 'أضف ملاحظة قصيرة ثم أرسل المريض إلى طابور الطبيب.')) ?></p>
|
|
||||||
</div>
|
|
||||||
<span class="badge rounded-pill text-bg-dark"><?= qh_h(count($waitingTickets) . ' ' . qh_t('patients', 'مرضى')) ?></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($waitingTickets): ?>
|
<div class="card-body">
|
||||||
<div class="vstack gap-3">
|
<?php if ($waitingTickets): ?>
|
||||||
<?php foreach ($waitingTickets as $ticket): ?>
|
<div class="vstack gap-3">
|
||||||
<article class="list-row-form">
|
<?php foreach ($waitingTickets as $ticket): ?>
|
||||||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
|
<div class="border rounded p-3 bg-light">
|
||||||
<div>
|
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-3">
|
||||||
<div class="ticket-code"><?= qh_h($ticket['ticket_number']) ?></div>
|
<div>
|
||||||
<div class="fw-semibold mt-1"><?= qh_h($ticket['patient_name']) ?></div>
|
<div class="fw-bold fs-5 text-dark"><?= qh_h($ticket['ticket_number']) ?></div>
|
||||||
<div class="small text-secondary mt-1"><?= qh_h(qh_name($ticket, 'clinic_name')) ?> · <?= qh_h(qh_name($ticket, 'doctor_name')) ?> · <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?></div>
|
<div class="fw-semibold mt-1 text-dark"><?= qh_h($ticket['patient_name']) ?></div>
|
||||||
</div>
|
<div class="small text-muted mt-1"><?= qh_h(qh_name($ticket, 'clinic_name')) ?> • <?= qh_h(qh_name($ticket, 'doctor_name')) ?> • <?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?></div>
|
||||||
<div class="d-flex gap-2 flex-wrap align-items-center">
|
</div>
|
||||||
<?= qh_status_badge($ticket['status']) ?>
|
<div class="d-flex gap-2 flex-wrap align-items-center">
|
||||||
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('ticket.php', ['id' => (int) $ticket['id']])) ?>"><?= qh_h(qh_t('Ticket detail', 'تفاصيل التذكرة')) ?></a>
|
<?= qh_status_badge($ticket['status']) ?>
|
||||||
|
<a class="btn btn-sm btn-outline-secondary bg-white shadow-sm" href="<?= qh_h(qh_url('ticket.php', ['id' => (int) $ticket['id']])) ?>"><?= qh_h(qh_t('Details', 'تفاصيل')) ?></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<form method="post" class="row g-3 align-items-end">
|
||||||
|
<input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>">
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<label class="form-label text-dark fw-semibold small"><?= qh_h(qh_t('Vitals note', 'ملاحظة العلامات الحيوية')) ?></label>
|
||||||
|
<textarea class="form-control bg-white" name="vitals_notes" rows="2" placeholder="<?= qh_h(qh_t('Blood pressure, pulse, temperature...', 'الضغط والنبض والحرارة...')) ?>" required></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3 d-grid">
|
||||||
|
<button class="btn btn-success shadow-sm" type="submit"><i class="bi bi-check2-circle me-1"></i><?= qh_h(qh_t('Send to Doctor', 'إرسال للطبيب')) ?></button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" class="row g-3 align-items-end mt-1">
|
<?php endforeach; ?>
|
||||||
<input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>">
|
</div>
|
||||||
<div class="col-lg-9">
|
<?php else: ?>
|
||||||
<label class="form-label"><?= qh_h(qh_t('Vitals note', 'ملاحظة العلامات الحيوية')) ?></label>
|
<div class="text-center py-5 text-muted">
|
||||||
<textarea class="form-control" name="vitals_notes" rows="2" placeholder="<?= qh_h(qh_t('Blood pressure, pulse, temperature...', 'الضغط والنبض والحرارة...')) ?>" required></textarea>
|
<p class="mb-0"><?= qh_h(qh_t('No patients are waiting for vitals.', 'لا يوجد مرضى بانتظار العلامات الحيوية.')) ?></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3 d-grid">
|
<?php endif; ?>
|
||||||
<button class="btn btn-dark" type="submit"><?= qh_h(qh_t('Send to doctor', 'إرسال إلى الطبيب')) ?></button>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="empty-state">
|
|
||||||
<strong><?= qh_h(qh_t('No patients are waiting for vitals.', 'لا يوجد مرضى بانتظار العلامات الحيوية.')) ?></strong>
|
|
||||||
<span><?= qh_h(qh_t('New vitals-first tickets from reception will appear here.', 'ستظهر هنا التذاكر الجديدة التي تتطلب العلامات الحيوية أولاً.')) ?></span>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
0
output.html
Normal file
0
output.html
Normal file
@ -72,11 +72,15 @@ CREATE TABLE IF NOT EXISTS hospital_profile_settings (
|
|||||||
favicon_url VARCHAR(255) DEFAULT NULL,
|
favicon_url VARCHAR(255) DEFAULT NULL,
|
||||||
primary_color VARCHAR(7) DEFAULT NULL,
|
primary_color VARCHAR(7) DEFAULT NULL,
|
||||||
secondary_color VARCHAR(7) DEFAULT NULL,
|
secondary_color VARCHAR(7) DEFAULT NULL,
|
||||||
|
news_ticker_en VARCHAR(1000) DEFAULT NULL,
|
||||||
|
news_ticker_ar VARCHAR(1000) DEFAULT NULL,
|
||||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||||
SQL;
|
SQL;
|
||||||
|
|
||||||
db()->exec($profileSql);
|
db()->exec($profileSql);
|
||||||
|
try { db()->exec("ALTER TABLE hospital_profile_settings ADD COLUMN news_ticker_en VARCHAR(1000) DEFAULT NULL"); } catch (\Throwable $e) {}
|
||||||
|
try { db()->exec("ALTER TABLE hospital_profile_settings ADD COLUMN news_ticker_ar VARCHAR(1000) DEFAULT NULL"); } catch (\Throwable $e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function qh_seed_demo_data(): void
|
function qh_seed_demo_data(): void
|
||||||
@ -163,9 +167,9 @@ function qh_seed_hospital_profile(): void
|
|||||||
|
|
||||||
$stmt = db()->prepare(
|
$stmt = db()->prepare(
|
||||||
'INSERT INTO hospital_profile_settings
|
'INSERT INTO hospital_profile_settings
|
||||||
(id, name_en, name_ar, short_name, tagline_en, tagline_ar, phone, email, website, address_en, address_ar, working_hours_en, working_hours_ar, logo_url, favicon_url, primary_color, secondary_color)
|
(id, name_en, name_ar, short_name, tagline_en, tagline_ar, phone, email, website, address_en, address_ar, working_hours_en, working_hours_ar, logo_url, favicon_url, primary_color, secondary_color, news_ticker_en, news_ticker_ar)
|
||||||
VALUES
|
VALUES
|
||||||
(1, :name_en, :name_ar, :short_name, :tagline_en, :tagline_ar, :phone, :email, :website, :address_en, :address_ar, :working_hours_en, :working_hours_ar, :logo_url, :favicon_url, :primary_color, :secondary_color)'
|
(1, :name_en, :name_ar, :short_name, :tagline_en, :tagline_ar, :phone, :email, :website, :address_en, :address_ar, :working_hours_en, :working_hours_ar, :logo_url, :favicon_url, :primary_color, :secondary_color, :news_ticker_en, :news_ticker_ar)'
|
||||||
);
|
);
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
@ -185,6 +189,8 @@ function qh_seed_hospital_profile(): void
|
|||||||
'favicon_url' => '',
|
'favicon_url' => '',
|
||||||
'primary_color' => '#0f8b8d',
|
'primary_color' => '#0f8b8d',
|
||||||
'secondary_color' => '#16697a',
|
'secondary_color' => '#16697a',
|
||||||
|
'news_ticker_en' => 'Welcome to our hospital.',
|
||||||
|
'news_ticker_ar' => 'مرحباً بكم في مستشفانا.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,6 +395,11 @@ function qh_admin_sections(): array
|
|||||||
'description' => qh_t('Admin home and setup summary.', 'الصفحة الرئيسية وملخص الإعدادات.'),
|
'description' => qh_t('Admin home and setup summary.', 'الصفحة الرئيسية وملخص الإعدادات.'),
|
||||||
'icon' => 'overview',
|
'icon' => 'overview',
|
||||||
],
|
],
|
||||||
|
'admin_ads.php' => [
|
||||||
|
'label' => qh_t('Advertisements', 'الإعلانات'),
|
||||||
|
'description' => qh_t('Manage videos shown on the queue display.', 'إدارة الفيديوهات المعروضة على شاشة الطابور.'),
|
||||||
|
'icon' => 'display',
|
||||||
|
],
|
||||||
'admin_hospital.php' => [
|
'admin_hospital.php' => [
|
||||||
'label' => qh_t('Hospital profile', 'ملف المستشفى'),
|
'label' => qh_t('Hospital profile', 'ملف المستشفى'),
|
||||||
'description' => qh_t('Manage logo, favicon, contact details, and brand colors.', 'إدارة الشعار والأيقونة وبيانات التواصل وألوان الهوية.'),
|
'description' => qh_t('Manage logo, favicon, contact details, and brand colors.', 'إدارة الشعار والأيقونة وبيانات التواصل وألوان الهوية.'),
|
||||||
@ -552,6 +563,15 @@ function qh_page_start(string $activePage, string $pageTitle, string $metaDescri
|
|||||||
|
|
||||||
function qh_page_end(): void
|
function qh_page_end(): void
|
||||||
{
|
{
|
||||||
|
$profile = qh_fetch_hospital_profile();
|
||||||
|
$newsTicker = trim((string)($profile['news_ticker'] ?? ''));
|
||||||
|
|
||||||
|
if ($newsTicker !== '') {
|
||||||
|
echo '<div style="position: fixed; bottom: 0; left: 0; width: 100%; height: 40px; line-height: 40px; background: var(--accent-strong, #16697A); color: #fff; z-index: 9999; font-size: 1.25rem; font-weight: bold; overflow: hidden; box-shadow: 0 -2px 10px rgba(0,0,0,0.1);">';
|
||||||
|
echo '<marquee direction="right" scrollamount="6" scrolldelay="85">' . qh_h($newsTicker) . '</marquee>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
$assetVersionJs = qh_asset_version('assets/js/main.js');
|
$assetVersionJs = qh_asset_version('assets/js/main.js');
|
||||||
echo '</main>';
|
echo '</main>';
|
||||||
echo '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>';
|
echo '<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>';
|
||||||
@ -894,6 +914,53 @@ function qh_require_post(): void
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function qh_handle_image_upload(string $inputName): string
|
||||||
|
{
|
||||||
|
if (!isset($_FILES[$inputName]) || $_FILES[$inputName]['error'] !== UPLOAD_ERR_OK) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmpName = $_FILES[$inputName]['tmp_name'];
|
||||||
|
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||||
|
if ($finfo === false) return '';
|
||||||
|
$mime = finfo_file($finfo, $tmpName);
|
||||||
|
finfo_close($finfo);
|
||||||
|
|
||||||
|
if (!is_string($mime) || !str_starts_with($mime, 'image/')) {
|
||||||
|
throw new InvalidArgumentException(qh_t('Invalid image format.', 'تنسيق صورة غير صالح.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$ext = match ($mime) {
|
||||||
|
'image/jpeg' => '.jpg',
|
||||||
|
'image/png' => '.png',
|
||||||
|
'image/gif' => '.gif',
|
||||||
|
'image/webp' => '.webp',
|
||||||
|
'image/svg+xml' => '.svg',
|
||||||
|
'image/x-icon' => '.ico',
|
||||||
|
'image/vnd.microsoft.icon' => '.ico',
|
||||||
|
default => '.bin'
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($ext === '.bin') {
|
||||||
|
throw new InvalidArgumentException(qh_t('Unsupported image type.', 'نوع الصورة غير مدعوم.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$uploadDir = __DIR__ . '/assets/images/uploads';
|
||||||
|
if (!is_dir($uploadDir)) {
|
||||||
|
if (!mkdir($uploadDir, 0775, true)) {
|
||||||
|
throw new RuntimeException(qh_t('Failed to create upload directory.', 'فشل إنشاء مجلد الرفع.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = uniqid('img_', true) . $ext;
|
||||||
|
$dest = $uploadDir . '/' . $filename;
|
||||||
|
|
||||||
|
if (!move_uploaded_file($tmpName, $dest)) {
|
||||||
|
throw new RuntimeException(qh_t('Failed to save uploaded file.', 'فشل حفظ الملف المرفوع.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'assets/images/uploads/' . $filename;
|
||||||
|
}
|
||||||
function qh_admin_handle_request(): void
|
function qh_admin_handle_request(): void
|
||||||
{
|
{
|
||||||
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
|
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
|
||||||
@ -901,7 +968,7 @@ function qh_admin_handle_request(): void
|
|||||||
}
|
}
|
||||||
|
|
||||||
$action = trim((string) ($_POST['action'] ?? ''));
|
$action = trim((string) ($_POST['action'] ?? ''));
|
||||||
if ($action === '') {
|
if ($action === '' || in_array($action, ['add_video', 'delete_video', 'toggle_status'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1046,8 +1113,25 @@ function qh_admin_handle_request(): void
|
|||||||
$addressAr = trim((string) ($_POST['address_ar'] ?? ''));
|
$addressAr = trim((string) ($_POST['address_ar'] ?? ''));
|
||||||
$workingHoursEn = trim((string) ($_POST['working_hours_en'] ?? ''));
|
$workingHoursEn = trim((string) ($_POST['working_hours_en'] ?? ''));
|
||||||
$workingHoursAr = trim((string) ($_POST['working_hours_ar'] ?? ''));
|
$workingHoursAr = trim((string) ($_POST['working_hours_ar'] ?? ''));
|
||||||
$logoUrl = trim((string) ($_POST['logo_url'] ?? ''));
|
$newsTickerEn = trim((string) ($_POST['news_ticker_en'] ?? ''));
|
||||||
$faviconUrl = trim((string) ($_POST['favicon_url'] ?? ''));
|
$newsTickerAr = trim((string) ($_POST['news_ticker_ar'] ?? ''));
|
||||||
|
$profile = qh_fetch_hospital_profile();
|
||||||
|
$logoUrl = $profile['logo_url'] ?? '';
|
||||||
|
$faviconUrl = $profile['favicon_url'] ?? '';
|
||||||
|
|
||||||
|
if (!empty($_POST['remove_logo'])) {
|
||||||
|
$logoUrl = '';
|
||||||
|
} else {
|
||||||
|
$newLogo = qh_handle_image_upload('logo_upload');
|
||||||
|
if ($newLogo !== '') $logoUrl = $newLogo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($_POST['remove_favicon'])) {
|
||||||
|
$faviconUrl = '';
|
||||||
|
} else {
|
||||||
|
$newFavicon = qh_handle_image_upload('favicon_upload');
|
||||||
|
if ($newFavicon !== '') $faviconUrl = $newFavicon;
|
||||||
|
}
|
||||||
$primaryColor = qh_sanitize_hex_color($_POST['primary_color'] ?? '', '#0F8B8D');
|
$primaryColor = qh_sanitize_hex_color($_POST['primary_color'] ?? '', '#0F8B8D');
|
||||||
$secondaryColor = qh_sanitize_hex_color($_POST['secondary_color'] ?? '', '#16697A');
|
$secondaryColor = qh_sanitize_hex_color($_POST['secondary_color'] ?? '', '#16697A');
|
||||||
|
|
||||||
@ -1060,12 +1144,7 @@ function qh_admin_handle_request(): void
|
|||||||
if ($website !== '' && filter_var($website, FILTER_VALIDATE_URL) === false) {
|
if ($website !== '' && filter_var($website, FILTER_VALIDATE_URL) === false) {
|
||||||
throw new InvalidArgumentException(qh_t('Please enter a valid website URL.', 'يرجى إدخال رابط موقع صالح.'));
|
throw new InvalidArgumentException(qh_t('Please enter a valid website URL.', 'يرجى إدخال رابط موقع صالح.'));
|
||||||
}
|
}
|
||||||
if ($logoUrl !== '' && filter_var($logoUrl, FILTER_VALIDATE_URL) === false) {
|
|
||||||
throw new InvalidArgumentException(qh_t('Please enter a valid logo URL.', 'يرجى إدخال رابط صالح للشعار.'));
|
|
||||||
}
|
|
||||||
if ($faviconUrl !== '' && filter_var($faviconUrl, FILTER_VALIDATE_URL) === false) {
|
|
||||||
throw new InvalidArgumentException(qh_t('Please enter a valid favicon URL.', 'يرجى إدخال رابط صالح للأيقونة.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare(
|
$stmt = $pdo->prepare(
|
||||||
"UPDATE hospital_profile_settings
|
"UPDATE hospital_profile_settings
|
||||||
@ -1084,7 +1163,9 @@ function qh_admin_handle_request(): void
|
|||||||
logo_url = :logo_url,
|
logo_url = :logo_url,
|
||||||
favicon_url = :favicon_url,
|
favicon_url = :favicon_url,
|
||||||
primary_color = :primary_color,
|
primary_color = :primary_color,
|
||||||
secondary_color = :secondary_color
|
secondary_color = :secondary_color,
|
||||||
|
news_ticker_en = :news_ticker_en,
|
||||||
|
news_ticker_ar = :news_ticker_ar
|
||||||
WHERE id = 1"
|
WHERE id = 1"
|
||||||
);
|
);
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
@ -1104,6 +1185,8 @@ function qh_admin_handle_request(): void
|
|||||||
'favicon_url' => $faviconUrl,
|
'favicon_url' => $faviconUrl,
|
||||||
'primary_color' => $primaryColor,
|
'primary_color' => $primaryColor,
|
||||||
'secondary_color' => $secondaryColor,
|
'secondary_color' => $secondaryColor,
|
||||||
|
'news_ticker_en' => $newsTickerEn,
|
||||||
|
'news_ticker_ar' => $newsTickerAr,
|
||||||
]);
|
]);
|
||||||
qh_set_flash('success', qh_t('Hospital profile updated successfully.', 'تم تحديث ملف المستشفى بنجاح.'));
|
qh_set_flash('success', qh_t('Hospital profile updated successfully.', 'تم تحديث ملف المستشفى بنجاح.'));
|
||||||
}
|
}
|
||||||
|
|||||||
221
reception.php
221
reception.php
@ -11,112 +11,157 @@ $todayTickets = qh_fetch_tickets(['waiting_vitals', 'ready_for_doctor', 'called'
|
|||||||
|
|
||||||
qh_page_start(
|
qh_page_start(
|
||||||
'reception',
|
'reception',
|
||||||
qh_t('Reception ticket issuance', 'إصدار التذاكر في الاستقبال'),
|
qh_t('Reception', 'الاستقبال'),
|
||||||
qh_t('Reception page with separated language views.', 'صفحة الاستقبال مع فصل واضح بين اللغتين.')
|
qh_t('Issue new tickets and manage patient flow.', 'إصدار التذاكر الجديدة وإدارة حركة المرضى.')
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
<div class="container-xxl px-3 px-lg-4">
|
<div class="container-fluid container-xxl px-3 px-lg-4">
|
||||||
<section class="page-header-panel mb-4">
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<span class="section-kicker"><?= qh_h(qh_t('Reception', 'الاستقبال')) ?></span>
|
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Reception', 'الاستقبال')) ?></h1>
|
||||||
<div class="locale-chip mt-3"><?= qh_h(qh_current_language_badge()) ?></div>
|
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('Issue new tickets and manage patient flow.', 'إصدار التذاكر الجديدة وإدارة حركة المرضى.')) ?></p>
|
||||||
<h1 class="section-title-xl mt-2"><?= qh_h(qh_t('Issue one ticket for the full visit.', 'أصدر تذكرة واحدة لكامل الزيارة.')) ?></h1>
|
|
||||||
<p class="section-copy mb-0"><?= qh_h(qh_t('Reception selects the clinic and doctor once. The system routes to nursing only when the clinic requires vitals.', 'يقوم الاستقبال باختيار العيادة والطبيب مرة واحدة. ويوجه النظام إلى التمريض فقط عندما تتطلب العيادة العلامات الحيوية.')) ?></p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-xl-5">
|
<div class="col-xl-4">
|
||||||
<div class="panel-card h-100">
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<h2 class="section-title mb-3"><?= qh_h(qh_t('New patient ticket', 'تذكرة مريض جديدة')) ?></h2>
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
<form method="post" class="vstack gap-3" novalidate>
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Issue New Ticket', 'إصدار تذكرة جديدة')) ?></h5>
|
||||||
<div>
|
</div>
|
||||||
<label class="form-label"><?= qh_h(qh_t('Patient name', 'اسم المريض')) ?></label>
|
<div class="card-body">
|
||||||
<input class="form-control" type="text" name="patient_name" placeholder="Maha Ali" required>
|
<form method="post" class="vstack gap-3" novalidate>
|
||||||
</div>
|
<div>
|
||||||
<div>
|
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Patient name', 'اسم المريض')) ?></label>
|
||||||
<label class="form-label"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></label>
|
<input class="form-control" type="text" name="patient_name" placeholder="Maha Ali" required>
|
||||||
<select class="form-select js-clinic-select" name="clinic_id" required>
|
</div>
|
||||||
<option value=""><?= qh_h(qh_t('Choose clinic', 'اختر العيادة')) ?></option>
|
<div>
|
||||||
<?php foreach ($clinics as $clinic): ?>
|
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></label>
|
||||||
<option value="<?= qh_h((string) $clinic['id']) ?>"><?= qh_h(qh_name($clinic)) ?><?php if ((int) $clinic['requires_vitals'] === 1): ?> · <?= qh_h(qh_t('Vitals first', 'العلامات أولاً')) ?><?php endif; ?></option>
|
<select class="form-select js-clinic-select" name="clinic_id" required>
|
||||||
<?php endforeach; ?>
|
<option value=""><?= qh_h(qh_t('Choose clinic', 'اختر العيادة')) ?></option>
|
||||||
</select>
|
<?php foreach ($clinics as $clinic): ?>
|
||||||
</div>
|
<option value="<?= qh_h((string) $clinic['id']) ?>"><?= qh_h(qh_name($clinic)) ?><?php if ((int) $clinic['requires_vitals'] === 1): ?> · <?= qh_h(qh_t('Vitals first', 'العلامات أولاً')) ?><?php endif; ?></option>
|
||||||
<div>
|
<?php endforeach; ?>
|
||||||
<label class="form-label"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></label>
|
</select>
|
||||||
<select class="form-select js-doctor-select" name="doctor_id" required>
|
</div>
|
||||||
<option value=""><?= qh_h(qh_t('Choose doctor', 'اختر الطبيب')) ?></option>
|
<div>
|
||||||
<?php foreach ($doctors as $doctor): ?>
|
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></label>
|
||||||
<option value="<?= qh_h((string) $doctor['id']) ?>" data-clinic-id="<?= qh_h((string) $doctor['clinic_id']) ?>"><?= qh_h(qh_name($doctor)) ?> · <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($doctor['room_number']) ?></option>
|
<select class="form-select js-doctor-select" name="doctor_id" required>
|
||||||
<?php endforeach; ?>
|
<option value=""><?= qh_h(qh_t('Choose doctor', 'اختر الطبيب')) ?></option>
|
||||||
</select>
|
<?php foreach ($doctors as $doctor): ?>
|
||||||
</div>
|
<option value="<?= qh_h((string) $doctor['id']) ?>" data-clinic-id="<?= qh_h((string) $doctor['clinic_id']) ?>"><?= qh_h(qh_name($doctor)) ?> · <?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h($doctor['room_number']) ?></option>
|
||||||
<div>
|
<?php endforeach; ?>
|
||||||
<label class="form-label"><?= qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?></label>
|
</select>
|
||||||
<select class="form-select" name="language_pref">
|
</div>
|
||||||
<option value="en">English</option>
|
<div>
|
||||||
<option value="ar">العربية</option>
|
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?></label>
|
||||||
</select>
|
<select class="form-select" name="language_pref">
|
||||||
</div>
|
<option value="en">English</option>
|
||||||
<button class="btn btn-dark" type="submit"><?= qh_h(qh_t('Issue ticket', 'إصدار التذكرة')) ?></button>
|
<option value="ar">العربية</option>
|
||||||
</form>
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary mt-2 shadow-sm" type="submit"><?= qh_h(qh_t('Issue ticket', 'إصدار التذكرة')) ?></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xl-7">
|
<div class="col-xl-8">
|
||||||
<?php if ($currentTicket): ?>
|
<?php if ($currentTicket): ?>
|
||||||
<div class="panel-card ticket-card mb-4">
|
<div class="card shadow-sm border-0 mb-4 bg-light border-start border-primary border-4">
|
||||||
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap">
|
<div class="card-body">
|
||||||
<div>
|
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap">
|
||||||
<div class="section-kicker"><?= qh_h(qh_t('Issued ticket', 'التذكرة الصادرة')) ?></div>
|
<div>
|
||||||
<div class="ticket-number mt-2"><?= qh_h($currentTicket['ticket_number']) ?></div>
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1 small"><?= qh_h(qh_t('Ticket Issued Successfully', 'تم إصدار التذكرة بنجاح')) ?></div>
|
||||||
<div class="mt-2 fw-semibold"><?= qh_h($currentTicket['patient_name']) ?></div>
|
<div class="display-6 fw-bold mt-2 text-dark"><?= qh_h($currentTicket['ticket_number']) ?></div>
|
||||||
<div class="text-secondary"><?= qh_h(qh_name($currentTicket, 'clinic_name')) ?> · <?= qh_h(qh_name($currentTicket, 'doctor_name')) ?> · <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($currentTicket['doctor_room'] ?? '--') ?></div>
|
<div class="mt-2 fw-semibold fs-5 text-dark"><?= qh_h($currentTicket['patient_name']) ?></div>
|
||||||
|
<div class="text-muted mt-1"><?= qh_h(qh_name($currentTicket, 'clinic_name')) ?> • <?= qh_h(qh_name($currentTicket, 'doctor_name')) ?> • <?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h($currentTicket['doctor_room'] ?? '--') ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-column align-items-lg-end gap-2">
|
||||||
|
<?= qh_status_badge($currentTicket['status']) ?>
|
||||||
|
<button class="btn btn-outline-secondary bg-white btn-sm js-print-ticket shadow-sm mt-2" type="button"><i class="bi bi-printer me-1"></i><?= qh_h(qh_t('Print', 'طباعة')) ?></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column align-items-lg-end gap-2">
|
<hr class="my-3">
|
||||||
<?= qh_status_badge($currentTicket['status']) ?>
|
<div class="row g-3 small text-muted">
|
||||||
<button class="btn btn-outline-dark btn-sm js-print-ticket" type="button"><?= qh_h(qh_t('Print ticket', 'طباعة التذكرة')) ?></button>
|
<div class="col-md-4"><strong><?= qh_h(qh_t('Issued', 'وقت الإصدار')) ?>:</strong><br><?= qh_format_datetime($currentTicket['created_at']) ?></div>
|
||||||
|
<div class="col-md-4"><strong><?= qh_h(qh_t('Language', 'اللغة')) ?>:</strong><br><?= qh_h(qh_locale_label($currentTicket['language_pref'] ?? 'en')) ?></div>
|
||||||
|
<div class="col-md-4 text-primary"><strong><?= qh_h(qh_t('Next stop', 'المحطة التالية')) ?>:</strong><br><?= qh_h(qh_ticket_next_stop($currentTicket)) ?></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
|
||||||
<div class="row g-3 small">
|
|
||||||
<div class="col-md-4"><strong><?= qh_h(qh_t('Issued', 'وقت الإصدار')) ?>:</strong><br><?= qh_format_datetime($currentTicket['created_at']) ?></div>
|
|
||||||
<div class="col-md-4"><strong><?= qh_h(qh_t('Language', 'اللغة')) ?>:</strong><br><?= qh_h(qh_locale_label($currentTicket['language_pref'] ?? 'en')) ?></div>
|
|
||||||
<div class="col-md-4"><strong><?= qh_h(qh_t('Next stop', 'المحطة التالية')) ?>:</strong><br><?= qh_h(qh_ticket_next_stop($currentTicket)) ?></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="panel-card h-100">
|
<div class="card shadow-sm border-0 h-100">
|
||||||
<h2 class="section-title mb-1"><?= qh_h(qh_t('Today’s tickets', 'تذاكر اليوم')) ?></h2>
|
<div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
|
||||||
<p class="section-copy mb-3"><?= qh_h(qh_t('Latest tickets and where they currently sit in the visit flow.', 'أحدث التذاكر وموقعها الحالي في مسار الزيارة.')) ?></p>
|
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Today\'s Tickets', 'تذاكر اليوم')) ?></h5>
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table align-middle mb-0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Patient', 'المريض')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
|
||||||
<th><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($todayTickets as $ticket): ?>
|
|
||||||
<tr>
|
|
||||||
<td class="fw-semibold"><?= qh_h($ticket['ticket_number']) ?></td>
|
|
||||||
<td><?= qh_h($ticket['patient_name']) ?></td>
|
|
||||||
<td><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td>
|
|
||||||
<td><?= qh_status_badge($ticket['status']) ?></td>
|
|
||||||
<td class="text-end"><a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('ticket.php', ['id' => (int) $ticket['id']])) ?>"><?= qh_h(qh_t('View', 'عرض')) ?></a></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<?php if (empty($todayTickets)):
|
||||||
|
?><div class="text-center py-5 text-muted">
|
||||||
|
<p class="mb-0"><?= qh_h(qh_t('No tickets issued today.', 'لا توجد تذاكر صادرة اليوم.')) ?></p>
|
||||||
|
</div>
|
||||||
|
<?php else:
|
||||||
|
?><div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th class="border-0 px-4 py-3"><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Patient', 'المريض')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
|
||||||
|
<th class="border-0 py-3"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
|
||||||
|
<th class="border-0 px-4 py-3 text-end"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($todayTickets as $ticket):
|
||||||
|
?><tr >
|
||||||
|
<td class="fw-bold px-4 py-3 text-dark"><?= qh_h($ticket['ticket_number']) ?></td>
|
||||||
|
<td class="py-3"><?= qh_h($ticket['patient_name']) ?></td>
|
||||||
|
<td class="py-3 text-muted"><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td>
|
||||||
|
<td class="py-3"><?= qh_status_badge($ticket['status']) ?></td>
|
||||||
|
<td class="text-end px-4 py-3"><a class="btn btn-sm btn-light border shadow-sm" href="<?= qh_h(qh_url('ticket.php', ['id' => (int) $ticket['id']])) ?>"><?= qh_h(qh_t('View', 'عرض')) ?></a></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach;
|
||||||
|
?></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif;
|
||||||
|
?></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php qh_page_end(); ?>
|
<?php if ($currentTicket): ?>
|
||||||
|
<div id="print-ticket" class="d-none d-print-block">
|
||||||
|
<div class="print-ticket-inner">
|
||||||
|
<div class="pt-header">
|
||||||
|
<?= qh_h(qh_hospital_name()) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-token-title">
|
||||||
|
<?= qh_h(qh_t("Token No.", "رقم التذكرة")) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-token-number">
|
||||||
|
<?= qh_h($currentTicket["ticket_number"]) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-patient-name">
|
||||||
|
<?= qh_h($currentTicket["patient_name"]) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-divider"></div>
|
||||||
|
<div class="pt-detail">
|
||||||
|
<span class="pt-label"><?= qh_h(qh_t("Doctor", "الطبيب")) ?>:</span> <?= qh_h(qh_name($currentTicket, "doctor_name")) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-detail">
|
||||||
|
<span class="pt-label"><?= qh_h(qh_t("Room", "الغرفة")) ?>:</span> <?= qh_h($currentTicket["doctor_room"] ?? "--") ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-detail pt-datetime">
|
||||||
|
<span class="pt-label"><?= qh_h(qh_t("Time", "الوقت")) ?>:</span> <span><?= qh_format_datetime($currentTicket["created_at"]) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="pt-divider"></div>
|
||||||
|
<div class="pt-footer">
|
||||||
|
<?= qh_h(qh_t("Please wait for your turn.", "يرجى الانتظار حتى يحين دورك.")) ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php qh_page_end(); ?>
|
||||||
33
ticket.php
33
ticket.php
@ -39,6 +39,7 @@ qh_page_start(
|
|||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-column gap-2 align-items-lg-end">
|
<div class="d-flex flex-column gap-2 align-items-lg-end">
|
||||||
<?= qh_status_badge($ticket['status']) ?>
|
<?= qh_status_badge($ticket['status']) ?>
|
||||||
|
<button class="btn btn-outline-secondary bg-white btn-sm js-print-ticket shadow-sm mt-2 mb-2 w-100" type="button"><i class="bi bi-printer me-1"></i><?= qh_h(qh_t("Print Ticket", "طباعة التذكرة")) ?></button>
|
||||||
<span class="small text-secondary"><?= qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?>: <?= qh_h(qh_locale_label($ticket['language_pref'] ?? 'en')) ?></span>
|
<span class="small text-secondary"><?= qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?>: <?= qh_h(qh_locale_label($ticket['language_pref'] ?? 'en')) ?></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -79,4 +80,36 @@ qh_page_start(
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if ($ticket): ?>
|
||||||
|
<div id="print-ticket" class="d-none d-print-block">
|
||||||
|
<div class="print-ticket-inner">
|
||||||
|
<div class="pt-header">
|
||||||
|
<?= qh_h(qh_hospital_name()) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-token-title">
|
||||||
|
<?= qh_h(qh_t("Token No.", "رقم التذكرة")) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-token-number">
|
||||||
|
<?= qh_h($ticket["ticket_number"]) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-patient-name">
|
||||||
|
<?= qh_h($ticket["patient_name"]) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-divider"></div>
|
||||||
|
<div class="pt-detail">
|
||||||
|
<span class="pt-label"><?= qh_h(qh_t("Doctor", "الطبيب")) ?>:</span> <?= qh_h(qh_name($ticket, "doctor_name")) ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-detail">
|
||||||
|
<span class="pt-label"><?= qh_h(qh_t("Room", "الغرفة")) ?>:</span> <?= qh_h($ticket["doctor_room"] ?? "--") ?>
|
||||||
|
</div>
|
||||||
|
<div class="pt-detail pt-datetime">
|
||||||
|
<span class="pt-label"><?= qh_h(qh_t("Time", "الوقت")) ?>:</span> <span><?= qh_format_datetime($ticket["created_at"]) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="pt-divider"></div>
|
||||||
|
<div class="pt-footer">
|
||||||
|
<?= qh_h(qh_t("Please wait for your turn.", "يرجى الانتظار حتى يحين دورك.")) ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<?php qh_page_end(); ?>
|
<?php qh_page_end(); ?>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user