Autosave: 20260331-163419

This commit is contained in:
Flatlogic Bot 2026-03-31 16:34:19 +00:00
parent 0c7ba978a8
commit 9ef45e15f2
18 changed files with 1314 additions and 766 deletions

200
admin.php
View File

@ -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>
</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> </div>
<div class="admin-overview-grid mt-4"> <div class="row g-4 mb-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> <div class="col-md-3 col-sm-6">
<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> <div class="card shadow-sm border-0 h-100">
<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> <div class="card-body">
<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 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>
</section>
<section class="admin-card-grid admin-card-grid-triple"> <div class="row g-4 mb-4">
<article class="panel-card admin-link-card"> <div class="col-lg-4">
<span class="section-kicker"><?= qh_h(qh_t('Branding', 'الهوية')) ?></span> <div class="card shadow-sm border-0 h-100">
<h2 class="section-title mt-2 mb-2"><?= qh_h(qh_t('Hospital profile', 'ملف المستشفى')) ?></h2> <div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
<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> <h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Hospital Profile', 'ملف المستشفى')) ?></h5>
<a class="btn btn-dark" href="<?= qh_h(qh_url('admin_hospital.php')) ?>"><?= qh_h(qh_t('Open profile page', 'فتح صفحة الملف')) ?></a> <a class="btn btn-sm btn-outline-secondary shadow-sm" href="<?= qh_h(qh_url('admin_hospital.php')) ?>"><?= qh_h(qh_t('Edit', 'تعديل')) ?></a>
</article> </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>
<article class="panel-card admin-link-card"> <div class="col-lg-4">
<span class="section-kicker"><?= qh_h(qh_t('Directory', 'الدليل')) ?></span> <div class="card shadow-sm border-0 h-100">
<h2 class="section-title mt-2 mb-2"><?= qh_h(qh_t('Manage clinics', 'إدارة العيادات')) ?></h2> <div class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
<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> <h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Recent Clinics', 'أحدث العيادات')) ?></h5>
<a class="btn btn-dark" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Open clinics page', 'فتح صفحة العيادات')) ?></a> <a class="btn btn-sm btn-outline-secondary shadow-sm" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Manage', 'إدارة')) ?></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> </div>
<div class="card-body p-0">
<?php if ($recentClinics === []): ?> <?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> <div class="text-center py-4 text-muted"><p class="mb-0 small"><?= qh_h(qh_t('No clinics yet.', 'لا توجد عيادات.')) ?></p></div>
<?php else: ?> <?php else: ?>
<div class="vstack gap-3 mt-4"> <div class="list-group list-group-flush">
<?php foreach ($recentClinics as $clinic): ?> <?php foreach ($recentClinics as $clinic): ?>
<div class="admin-summary-row"> <div class="list-group-item d-flex justify-content-between align-items-center py-3 border-0 border-bottom">
<div> <div>
<div class="fw-semibold"><?= qh_h(qh_name($clinic)) ?></div> <div class="fw-semibold text-dark mb-1"><?= qh_h(qh_name($clinic)) ?></div>
<div class="small text-secondary"><?= qh_h(qh_t('Code', 'الرمز')) ?>: <?= qh_h((string) $clinic['code']) ?></div> <div class="small text-muted"><?= qh_h(qh_t('Code', 'الرمز')) ?>: <?= qh_h((string) $clinic['code']) ?></div>
</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> <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> </div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
</section> </div>
</div>
</div>
<section class="panel-card admin-section-card"> <div class="col-lg-4">
<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('Doctor preview', 'معاينة الأطباء')) ?></span> <h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Recent Doctors', 'أحدث الأطباء')) ?></h5>
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_t('Latest doctor assignments', 'أحدث تعيينات الأطباء')) ?></h2> <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>
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h(qh_url('admin_doctors.php')) ?>"><?= qh_h(qh_t('View all', 'عرض الكل')) ?></a>
</div> </div>
<div class="card-body p-0">
<?php if ($recentDoctors === []): ?> <?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> <div class="text-center py-4 text-muted"><p class="mb-0 small"><?= qh_h(qh_t('No doctors yet.', 'لا يوجد أطباء.')) ?></p></div>
<?php else: ?> <?php else: ?>
<div class="vstack gap-3 mt-4"> <div class="list-group list-group-flush">
<?php foreach ($recentDoctors as $doctor): ?> <?php foreach ($recentDoctors as $doctor): ?>
<div class="admin-summary-row"> <div class="list-group-item d-flex justify-content-between align-items-center py-3 border-0 border-bottom">
<div> <div>
<div class="fw-semibold"><?= qh_h(qh_name($doctor)) ?></div> <div class="fw-semibold text-dark mb-1"><?= qh_h(qh_name($doctor)) ?></div>
<div class="small text-secondary"><?= qh_h(qh_name($doctor, 'clinic_name', qh_t('Unassigned clinic', 'عيادة غير محددة'))) ?></div> <div class="small text-muted"><?= qh_h(qh_name($doctor, 'clinic_name', qh_t('Unassigned', 'غير محدد'))) ?></div>
</div> </div>
<span class="room-badge"><?= qh_h(qh_t('Room', 'الغرفة')) ?> <?= qh_h((string) $doctor['room_number']) ?></span> <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> </div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
</section>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>
</div> </div>
<?php qh_page_end(); ?> <?php qh_page_end(); ?>

197
admin_ads.php Normal file
View 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(); ?>

View File

@ -38,85 +38,81 @@ 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> </div>
<p class="section-copy mb-0"><?= qh_h(qh_t('Search clinics, adjust codes and routing, and keep the front desk workflow organized.', 'ابحث في العيادات وعدّل الرموز ومسار العمل وحافظ على تنظيم سير العمل في الاستقبال.')) ?></p> <?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> </div>
</section>
<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 class="admin-directory-layout">
<section class="panel-card admin-table-card">
<div class="admin-section-head">
<div>
<span class="section-kicker"><?= qh_h(qh_t('Clinic list', 'قائمة العيادات')) ?></span>
<h2 class="section-title mt-2 mb-1"><?= qh_h(qh_t('Clinics and routing rules', 'العيادات وقواعد مسار العمل')) ?></h2>
<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> </div>
<div class="row g-4">
<div class="col-xl-7 col-lg-6">
<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('Clinics', 'العيادات')) ?></h5>
<span class="badge bg-light text-dark border px-2 py-1"><?= qh_h((string) count($clinics)) ?> <?= qh_h(qh_t('records', 'سجلات')) ?></span>
</div>
<div class="card-body p-0">
<?php if ($clinics === []): ?> <?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> <div class="text-center py-5 text-muted">
<p class="mb-0"><?= qh_h(qh_t('No clinics found.', 'لم يتم العثور على عيادات.')) ?></p>
</div>
<?php else: ?> <?php else: ?>
<div class="table-responsive mt-4"> <div class="table-responsive">
<table class="table align-middle admin-table"> <table class="table table-hover align-middle mb-0">
<thead> <thead class="table-light">
<tr> <tr>
<th><?= qh_h(qh_t('Code', 'الرمز')) ?></th> <th class="border-0 px-4 py-3"><?= qh_h(qh_t('Code', 'الرمز')) ?></th>
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
<th><?= qh_h(qh_t('Routing', 'المسار')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Routing', 'المسار')) ?></th>
<th><?= qh_h(qh_t('Order', 'الترتيب')) ?></th> <th class="border-0 px-4 py-3 text-end"><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
<th><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($clinics as $clinic): ?> <?php foreach ($clinics as $clinic): ?>
<?php $editUrl = qh_url('admin_clinics.php', ['edit' => (int) $clinic['id'], 'q' => $search]); ?> <?php $editUrl = qh_url('admin_clinics.php', ['edit' => (int) $clinic['id'], 'q' => $search]); ?>
<tr> <tr>
<td><span class="room-badge"><?= qh_h((string) $clinic['code']) ?></span></td> <td class="px-4 py-3"><span class="badge bg-light text-dark border"><?= qh_h((string) $clinic['code']) ?></span></td>
<td> <td class="py-3">
<div class="fw-semibold"><?= qh_h(qh_name($clinic)) ?></div> <div class="fw-semibold text-dark"><?= 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> <div class="small text-muted"><?= qh_h(qh_is_ar() ? (string) ($clinic['name_en'] ?? '') : (string) ($clinic['name_ar'] ?? '')) ?></div>
</td> </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 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><?= qh_h((string) $clinic['sort_order']) ?></td> <td class="text-end px-4 py-3">
<td> <div class="d-inline-flex gap-2">
<div class="admin-table-actions"> <a class="btn btn-sm btn-light border shadow-sm" href="<?= qh_h($editUrl) ?>"><i class="bi bi-pencil"></i></a>
<a class="btn btn-sm btn-outline-dark" href="<?= qh_h($editUrl) ?>"><?= qh_h(qh_t('Edit', 'تعديل')) ?></a> <form method="post" class="m-0" onsubmit="return confirm('<?= qh_h(qh_t('Delete this clinic record?', 'هل تريد حذف سجل هذه العيادة؟')) ?>');">
<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="action" value="delete_clinic">
<input type="hidden" name="clinic_id" value="<?= qh_h((string) $clinic['id']) ?>"> <input type="hidden" name="clinic_id" value="<?= qh_h((string) $clinic['id']) ?>">
<input type="hidden" name="return_to" value="admin_clinics.php"> <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> <button class="btn btn-sm btn-outline-danger shadow-sm bg-white" type="submit"><i class="bi bi-trash"></i></button>
</form> </form>
</div> </div>
</td> </td>
@ -126,18 +122,18 @@ qh_page_start(
</table> </table>
</div> </div>
<?php endif; ?> <?php endif; ?>
</section> </div>
<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>
</div> </div>
<form method="post" class="vstack gap-3 mt-4"> <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="action" value="<?= qh_h($editClinic ? 'update_clinic' : 'add_clinic') ?>">
<input type="hidden" name="return_to" value="admin_clinics.php"> <input type="hidden" name="return_to" value="admin_clinics.php">
<?php if ($editClinic): ?> <?php if ($editClinic): ?>
@ -145,42 +141,43 @@ qh_page_start(
<?php endif; ?> <?php endif; ?>
<div class="row g-3"> <div class="row g-3">
<div class="col-sm-4"> <div class="col-sm-6">
<label class="form-label" for="clinicCode"><?= qh_h(qh_t('Clinic code', 'رمز العيادة')) ?></label> <label class="form-label text-dark fw-semibold small" for="clinicCode"><?= qh_h(qh_t('Code', 'الرمز')) ?></label>
<input id="clinicCode" class="form-control" type="text" maxlength="10" name="code" value="<?= qh_h((string) ($editClinic['code'] ?? '')) ?>" required> <input id="clinicCode" class="form-control bg-light" type="text" maxlength="10" name="code" value="<?= qh_h((string) ($editClinic['code'] ?? '')) ?>" required>
</div> </div>
<div class="col-sm-8"> <div class="col-sm-6">
<label class="form-label" for="clinicOrder"><?= qh_h(qh_t('Display order', 'ترتيب العرض')) ?></label> <label class="form-label text-dark fw-semibold small" for="clinicOrder"><?= qh_h(qh_t('Order', 'الترتيب')) ?></label>
<input id="clinicOrder" class="form-control" type="number" min="1" name="sort_order" value="<?= qh_h((string) ($editClinic['sort_order'] ?? 50)) ?>" required> <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> </div>
<div> <div>
<label class="form-label" for="clinicNameEn"><?= qh_h(qh_t('Clinic name (English)', 'اسم العيادة بالإنجليزية')) ?></label> <label class="form-label text-dark fw-semibold small" for="clinicNameEn"><?= qh_h(qh_t('Name (English)', 'الاسم بالإنجليزية')) ?></label>
<input id="clinicNameEn" class="form-control" type="text" name="name_en" value="<?= qh_h((string) ($editClinic['name_en'] ?? '')) ?>" required> <input id="clinicNameEn" class="form-control bg-light" type="text" name="name_en" value="<?= qh_h((string) ($editClinic['name_en'] ?? '')) ?>" required>
</div> </div>
<div> <div>
<label class="form-label" for="clinicNameAr"><?= qh_h(qh_t('Clinic name (Arabic)', 'اسم العيادة بالعربية')) ?></label> <label class="form-label text-dark fw-semibold small" for="clinicNameAr"><?= qh_h(qh_t('Name (Arabic)', 'الاسم بالعربية')) ?></label>
<input id="clinicNameAr" class="form-control" type="text" name="name_ar" value="<?= qh_h((string) ($editClinic['name_ar'] ?? '')) ?>" required> <input id="clinicNameAr" class="form-control bg-light" type="text" name="name_ar" value="<?= qh_h((string) ($editClinic['name_ar'] ?? '')) ?>" required>
</div> </div>
<div class="admin-switch-card"> <div class="p-3 border rounded bg-light mt-2">
<div class="form-check form-switch mb-0"> <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' : '' ?>> <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" for="clinicVitals"><?= qh_h(qh_t('Require nursing vitals before doctor', 'يتطلب العلامات الحيوية في التمريض قبل الطبيب')) ?></label> <label class="form-check-label ms-2 text-dark" for="clinicVitals"><?= qh_h(qh_t('Require vitals before doctor', 'يتطلب علامات حيوية')) ?></label>
</div> </div>
<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>
</div> </div>
<div class="d-flex flex-wrap gap-2 pt-2"> <div class="d-flex gap-2 pt-3 border-top mt-2">
<button class="btn btn-dark" type="submit"><?= qh_h($editClinic ? qh_t('Save changes', 'حفظ التعديلات') : qh_t('Add clinic', 'إضافة عيادة')) ?></button> <button class="btn btn-primary shadow-sm" type="submit"><?= qh_h($editClinic ? qh_t('Save Changes', 'حفظ التعديلات') : qh_t('Add Clinic', 'إضافة عيادة')) ?></button>
<?php if ($editClinic): ?> <?php if ($editClinic): ?>
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('admin_clinics.php')) ?>"><?= qh_h(qh_t('Cancel edit', 'إلغاء التعديل')) ?></a> <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; ?> <?php endif; ?>
</div> </div>
</form> </form>
</section> </div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -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">
<!-- Basic Info -->
<div>
<div class="admin-section-head mb-4">
<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 class="row g-3"> <div class="row g-3">
<div class="col-lg-6"> <div class="col-lg-6">
<label class="form-label" for="hospitalNameEn"><?= qh_h(qh_t('Hospital name (English)', 'اسم المستشفى بالإنجليزية')) ?></label> <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> <input id="hospitalNameEn" class="form-control" type="text" name="name_en" value="<?= qh_h((string) ($profile['name_en'] ?? '')) ?>" required>
</div> </div>
<div class="col-lg-6"> <div class="col-lg-6">
<label class="form-label" for="hospitalNameAr"><?= qh_h(qh_t('Hospital name (Arabic)', 'اسم المستشفى بالعربية')) ?></label> <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> <input id="hospitalNameAr" class="form-control" type="text" name="name_ar" value="<?= qh_h((string) ($profile['name_ar'] ?? '')) ?>" required>
</div> </div>
</div>
<div class="row g-3">
<div class="col-lg-4"> <div class="col-lg-4">
<label class="form-label" for="hospitalShortName"><?= qh_h(qh_t('Short name', 'الاسم المختصر')) ?></label> <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'] ?? '')) ?>"> <input id="hospitalShortName" class="form-control" type="text" maxlength="40" name="short_name" value="<?= qh_h((string) ($profile['short_name'] ?? '')) ?>">
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
<label class="form-label" for="hospitalPhone"><?= qh_h(qh_t('Phone number', 'رقم الهاتف')) ?></label> <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>
<hr class="m-0 text-muted">
<!-- Contact & Location -->
<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 class="row g-3">
<div class="col-lg-4">
<label class="form-label fw-bold" for="hospitalPhone"><?= qh_h(qh_t('Phone number', 'رقم الهاتف')) ?></label>
<input id="hospitalPhone" class="form-control" type="text" name="phone" value="<?= qh_h((string) ($profile['phone'] ?? '')) ?>"> <input id="hospitalPhone" class="form-control" type="text" name="phone" value="<?= qh_h((string) ($profile['phone'] ?? '')) ?>">
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
<label class="form-label" for="hospitalEmail"><?= qh_h(qh_t('Email address', 'البريد الإلكتروني')) ?></label> <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'] ?? '')) ?>"> <input id="hospitalEmail" class="form-control" type="email" name="email" value="<?= qh_h((string) ($profile['email'] ?? '')) ?>">
</div> </div>
</div> <div class="col-lg-4">
<label class="form-label fw-bold" for="hospitalWebsite"><?= qh_h(qh_t('Website URL', 'رابط الموقع الإلكتروني')) ?></label>
<div class="row g-3">
<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">
<div class="col-lg-6">
<label class="form-label" 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" 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>
<div class="row g-3">
<div class="col-lg-6">
<label class="form-label" 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" 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 class="row g-3">
<div class="col-lg-6">
<label class="form-label" 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'] ?? '')) ?>"> <input id="hospitalWebsite" class="form-control" type="url" name="website" placeholder="https://example.com" value="<?= qh_h((string) ($profile['website'] ?? '')) ?>">
</div> </div>
<div class="col-lg-6"> <div class="col-lg-6">
<label class="form-label" for="hospitalLogoUrl"><?= qh_h(qh_t('Logo image URL', 'رابط صورة الشعار')) ?></label> <label class="form-label fw-bold" for="hospitalAddressEn"><?= qh_h(qh_t('Address (English)', 'العنوان بالإنجليزية')) ?></label>
<input id="hospitalLogoUrl" class="form-control" type="url" name="logo_url" placeholder="https://.../logo.png" value="<?= qh_h((string) ($profile['logo_url'] ?? '')) ?>"> <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">
<!-- Branding -->
<div>
<div class="admin-section-head mb-4">
<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 class="row g-4">
<div class="col-lg-6"> <div class="col-lg-6">
<label class="form-label" for="hospitalFaviconUrl"><?= qh_h(qh_t('Favicon URL', 'رابط الأيقونة')) ?></label> <label class="form-label fw-bold" for="hospitalLogoUpload"><?= qh_h(qh_t('Upload Logo', 'رفع الشعار')) ?></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="hospitalLogoUpload" class="form-control" type="file" name="logo_upload" accept="image/*">
<?php if ($logoUrl !== ''): ?>
<div class="form-check mt-2">
<input class="form-check-input" type="checkbox" name="remove_logo" value="1" id="removeLogo">
<label class="form-check-label text-danger" for="removeLogo">
<?= qh_h(qh_t('Remove current logo', 'إزالة الشعار الحالي')) ?>
</label>
</div>
<?php endif; ?>
</div>
<div class="col-lg-6">
<label class="form-label fw-bold" for="hospitalFaviconUpload"><?= qh_h(qh_t('Upload Favicon', 'رفع الأيقونة')) ?></label>
<input id="hospitalFaviconUpload" class="form-control" type="file" name="favicon_upload" accept="image/*">
<?php if ($faviconUrl !== ''): ?>
<div class="form-check mt-2">
<input class="form-check-input" type="checkbox" name="remove_favicon" value="1" id="removeFavicon">
<label class="form-check-label text-danger" for="removeFavicon">
<?= qh_h(qh_t('Remove current favicon', 'إزالة الأيقونة الحالية')) ?>
</label>
</div>
<?php endif; ?>
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<label class="form-label" for="hospitalPrimaryColor"><?= qh_h(qh_t('Primary color', 'اللون الأساسي')) ?></label> <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()) ?>"> <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>
<div class="col-lg-3"> <div class="col-lg-3">
<label class="form-label" for="hospitalSecondaryColor"><?= qh_h(qh_t('Secondary color', 'اللون الثانوي')) ?></label> <label class="form-label fw-bold" 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()) ?>"> <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>
<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>

View File

@ -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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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 class="card-header bg-white border-bottom py-3 d-flex justify-content-between align-items-center">
<div> <div>
<div class="section-kicker"><?= qh_h(qh_t('General display', 'الشاشة العامة')) ?></div> <h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Now Serving', 'يتم الآن النداء')) ?></h1>
<h1 class="section-title-xl mt-2 mb-1"><?= 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>
<div class="d-flex align-items-center gap-2 flex-wrap justify-content-end"> <div class="d-flex align-items-center gap-3">
<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="fs-4 fw-bold text-dark js-live-clock"><?= qh_h(date('H:i')) ?></div>
<div class="live-clock 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>
<div class="card-body p-4 bg-light">
<?php if ($activeCalls): ?> <?php if ($activeCalls): ?>
<div class="vstack gap-3"> <div class="row g-3">
<?php foreach ($activeCalls as $ticket): $speech = qh_call_message($ticket); ?> <?php foreach ($activeCalls as $ticket): $speech = qh_call_message($ticket); ?>
<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 class="col-6 col-lg-3">
<div> <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="ticket-number large"><?= qh_h($ticket['ticket_number']) ?></div> <div class="card-body text-center p-3">
<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 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>
<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> </div>
</article>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<div class="empty-state display-empty"> <div class="d-flex align-items-center justify-content-center h-100 min-vh-50 text-muted">
<strong><?= qh_h(qh_t('No live calls right now.', 'لا توجد نداءات مباشرة حالياً.')) ?></strong> <div class="text-center">
<span><?= qh_h(qh_t('When a doctor presses “Call patient”, the ticket will appear here.', 'عندما يضغط الطبيب على زر نداء المريض ستظهر التذكرة هنا.')) ?></span> <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> </div>
<?php endif; ?> <?php endif; ?>
</div>
</div>
<div class="panel-subsection mt-4"> <div class="card shadow-sm border-0">
<h2 class="section-title mb-3"><?= qh_h(qh_t('Queue by clinic', 'الطابور حسب العيادة')) ?></h2> <div class="card-header bg-white border-bottom py-3">
<div class="row g-3"> <h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Queue by Clinic', 'الطابور حسب العيادة')) ?></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 text-center"><?= qh_h(qh_t('Vitals Wait', 'انتظار العلامات')) ?></th>
<th class="border-0 px-4 py-3 text-center"><?= qh_h(qh_t('Doctor Wait', 'انتظار الطبيب')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($queueOverview as $row): ?> <?php foreach ($queueOverview as $row): ?>
<div class="col-md-6 col-xl-4"> <tr>
<div class="mini-overview-card"> <td class="fw-semibold px-4 py-3 text-dark"><?= qh_h(qh_name($row)) ?></td>
<div class="fw-semibold"><?= qh_h(qh_name($row)) ?></div> <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>
<div class="d-flex gap-2 mt-3 flex-wrap"> <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 warning"><?= qh_h(qh_t('Vitals', 'العلامات')) ?> <?= qh_h((string) $row['vitals_waiting']) ?></span> </tr>
<span class="table-pill info"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?> <?= qh_h((string) $row['doctor_waiting']) ?></span>
</div>
</div>
</div>
<?php endforeach; ?> <?php endforeach; ?>
</tbody>
</table>
</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 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>
<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('Reminder', 'تذكير')) ?></div>
<h3><?= qh_h(qh_t('Lab packages and wellness checks', 'باقات المختبر والفحوصات الوقائية')) ?></h3> <h4 class="text-white fw-bold"><?= qh_h(qh_t('Keep your ticket visible', 'احتفظ بتذكرتك ظاهرة')) ?></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('We announce ticket numbers on this screen and by voice. Stay near your department area.', 'نعلن أرقام التذاكر على هذه الشاشة وبالصوت. يرجى البقاء قرب منطقة القسم الخاص بك.')) ?></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('Wayfinding', 'الإرشاد')) ?></div>
<h4 class="text-white fw-bold"><?= qh_h(qh_t('Pharmacy & billing', 'الصيدلية والمحاسبة')) ?></h4>
<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 class="ad-tag"><?= qh_h(qh_t('Reminder', 'تذكير')) ?></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>
<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> </div>
</aside>
</section>
</div> </div>
<?php qh_page_end(); ?> <?php qh_page_end(); ?>

View File

@ -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> </div>
<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 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">
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Select Room', 'اختيار الغرفة')) ?></h5>
</div>
<div class="card-body">
<form method="get" class="vstack gap-3"> <form method="get" class="vstack gap-3">
<input type="hidden" name="lang" value="<?= qh_h(qh_locale()) ?>"> <input type="hidden" name="lang" value="<?= qh_h(qh_locale()) ?>">
<select class="form-select" name="doctor_id" onchange="this.form.submit()"> <div>
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Doctor room', 'غرفة الطبيب')) ?></label>
<select class="form-select bg-light" name="doctor_id" onchange="this.form.submit()">
<?php foreach ($doctors as $doctor): ?> <?php foreach ($doctors as $doctor): ?>
<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> <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>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div>
<?php if ($selectedDoctor): ?> <?php if ($selectedDoctor): ?>
<div class="mini-overview-card"> <div class="p-3 bg-light rounded border mt-2">
<div class="fw-semibold"><?= qh_h(qh_name($selectedDoctor)) ?></div> <div class="text-xs font-weight-bold text-primary text-uppercase mb-1 small"><?= qh_h(qh_t('Active Doctor', 'الطبيب النشط')) ?></div>
<div class="small text-secondary mt-1"><?= qh_h(qh_t('Room', 'الغرفة')) ?> <?= qh_h($selectedDoctor['room_number'] ?? '--') ?></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> </div>
<?php endif; ?> <?php endif; ?>
<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> <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> </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>
<p class="section-copy mb-0"><?= qh_h(qh_t('Patients currently assigned to this room.', 'المرضى المخصصون حالياً لهذه الغرفة.')) ?></p>
</div> </div>
<?php if ($selectedDoctor): ?> <div class="card-body p-0">
<span class="room-badge"><?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($selectedDoctor['room_number'] ?? '--') ?></span> <?php if (!$selectedDoctor): ?>
<?php endif; ?> <div class="text-center py-5 text-muted">
<p class="mb-0"><?= qh_h(qh_t('Please select a doctor to view the queue.', 'يرجى اختيار طبيب لعرض الطابور.')) ?></p>
</div> </div>
<?php elseif (empty($doctorQueue)): ?>
<?php if ($doctorQueue): ?> <div class="text-center py-5 text-muted">
<div class="vstack gap-3"> <p class="mb-0"><?= qh_h(qh_t('No patients waiting for this room.', 'لا يوجد مرضى في انتظار هذه الغرفة.')) ?></p>
<?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> </div>
<?php else: ?> <?php else: ?>
<div class="empty-state"> <div class="table-responsive">
<strong><?= qh_h(qh_t('No patients are in this doctor queue.', 'لا يوجد مرضى في طابور هذا الطبيب.')) ?></strong> <table class="table table-hover align-middle mb-0">
<span><?= qh_h(qh_t('Issue a ticket at reception or complete vitals in nursing to fill this room.', 'أصدر تذكرة من الاستقبال أو أكمل العلامات الحيوية في التمريض لإضافة مرضى لهذه الغرفة.')) ?></span> <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> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
<?php qh_page_end(); ?> <?php qh_page_end(); ?>

199
index.php
View File

@ -14,62 +14,79 @@ 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 class="col-lg-7">
<span class="section-kicker"><?= qh_h(qh_t('Hospital queue system', 'نظام طوابير المستشفى')) ?></span>
<div class="locale-chip mt-3"><?= qh_h(qh_current_language_badge()) ?></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>
<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>
<div class="d-flex flex-wrap gap-2">
<a class="btn btn-dark" href="<?= qh_h(qh_url('reception.php')) ?>"><?= qh_h(qh_t('Issue ticket', 'إصدار تذكرة')) ?></a>
<a class="btn btn-outline-dark" href="<?= qh_h(qh_url('display.php')) ?>"><?= qh_h(qh_t('Open public display', 'فتح الشاشة العامة')) ?></a>
</div>
</div>
<div class="col-lg-5">
<div class="hero-card stack-card h-100">
<div class="small text-uppercase text-secondary fw-semibold mb-2"><?= qh_h(qh_t('Today', 'اليوم')) ?></div>
<div class="row g-3">
<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="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>
</section>
<section class="row g-4 mb-4 mb-lg-5">
<div class="col-xl-8">
<div class="panel-card h-100">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
<div> <div>
<h2 class="section-title mb-1"><?= qh_h(qh_t('Live queue overview', 'نظرة مباشرة على الطابور')) ?></h2> <h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Operations Dashboard', 'لوحة عمليات المستشفى')) ?></h1>
<p class="section-copy mb-0"><?= qh_h(qh_t('See clinic demand, waiting load, and active calls at a glance.', 'اطلع بسرعة على ضغط العيادات والانتظار والنداءات النشطة.')) ?></p> <p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('Real-time hospital queue system metrics.', 'إحصائيات لحظية لنظام طوابير المستشفى.')) ?></p>
</div> </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 class="d-flex gap-2">
<a class="btn btn-primary shadow-sm" href="<?= qh_h(qh_url('reception.php')) ?>"><?= qh_h(qh_t('Issue ticket', 'إصدار تذكرة')) ?></a>
<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>
</div> </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('Issued today', 'التذاكر الصادرة اليوم')) ?></div>
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['issued_today']) ?></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('Waiting vitals', 'بانتظار العلامات')) ?></div>
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['waiting_vitals']) ?></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('Ready for doctor', 'جاهز للطبيب')) ?></div>
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['ready_for_doctor']) ?></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('Active rooms', 'الغرف النشطة')) ?></div>
<div class="h3 mb-0 font-weight-bold text-gray-800"><?= qh_h((string) $stats['active_rooms']) ?></div>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-xl-8">
<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"> <div class="table-responsive">
<table class="table align-middle mb-0"> <table class="table table-hover align-middle mb-0">
<thead> <thead class="table-light">
<tr> <tr>
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th> <th class="border-0 px-4 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
<th><?= qh_h(qh_t('Vitals wait', 'انتظار العلامات')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Vitals wait', 'انتظار العلامات')) ?></th>
<th><?= qh_h(qh_t('Doctor wait', 'انتظار الطبيب')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Doctor wait', 'انتظار الطبيب')) ?></th>
<th><?= qh_h(qh_t('Active calls', 'النداءات النشطة')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Active calls', 'النداءات النشطة')) ?></th>
<th><?= qh_h(qh_t('Today total', 'إجمالي اليوم')) ?></th> <th class="border-0 text-end px-4 py-3"><?= qh_h(qh_t('Today total', 'إجمالي اليوم')) ?></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($overview as $row): ?> <?php foreach ($overview as $row): ?>
<tr> <tr>
<td class="fw-semibold"><?= qh_h(qh_name($row)) ?></td> <td class="fw-semibold px-4 py-3 text-dark"><?= qh_h(qh_name($row)) ?></td>
<td><span class="table-pill warning"><?= qh_h((string) $row['vitals_waiting']) ?></span></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><span class="table-pill info"><?= qh_h((string) $row['doctor_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><span class="table-pill dark"><?= qh_h((string) $row['active_calls']) ?></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><?= qh_h((string) $row['total_today']) ?></td> <td class="text-end px-4 py-3 text-muted"><?= qh_h((string) $row['total_today']) ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
@ -77,93 +94,73 @@ qh_page_start(
</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>
</div>
<div class="card-body">
<?php if ($calledTickets): ?> <?php if ($calledTickets): ?>
<div class="vstack gap-3"> <div class="vstack gap-3">
<?php foreach ($calledTickets as $ticket): ?> <?php foreach ($calledTickets as $ticket): ?>
<div class="call-strip"> <div class="d-flex justify-content-between align-items-center p-3 border rounded bg-light">
<div> <div>
<div class="ticket-code"><?= qh_h($ticket['ticket_number']) ?></div> <div class="fw-bold h5 mb-1 text-dark"><?= qh_h($ticket['ticket_number']) ?></div>
<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 class="small text-muted"><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Unassigned', 'غير محدد'))) ?> &bull; <?= qh_h(qh_t('Room', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?></div>
</div> </div>
<?= qh_status_badge($ticket['status']) ?> <?= qh_status_badge($ticket['status']) ?>
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<div class="empty-state compact"> <div class="text-center py-4 text-muted">
<strong><?= qh_h(qh_t('No active calls yet.', 'لا توجد نداءات نشطة حالياً.')) ?></strong> <p class="mb-0"><?= qh_h(qh_t('No active calls yet.', 'لا توجد نداءات نشطة حالياً.')) ?></p>
<span><?= qh_h(qh_t('Use the doctor page to call the next patient.', 'استخدم صفحة الطبيب لنداء المريض التالي.')) ?></span>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
</section> </div>
</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>
<div class="card-body p-0">
<?php if ($recentTickets): ?> <?php if ($recentTickets): ?>
<div class="table-responsive"> <div class="table-responsive">
<table class="table align-middle mb-0"> <table class="table table-hover align-middle mb-0">
<thead> <thead class="table-light">
<tr> <tr>
<th><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th> <th class="border-0 px-4 py-3"><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th>
<th><?= qh_h(qh_t('Patient', 'المريض')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Patient', 'المريض')) ?></th>
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
<th><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></th>
<th><?= qh_h(qh_t('Status', 'الحالة')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
<th></th> <th class="border-0 px-4 py-3 text-end"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($recentTickets as $ticket): ?> <?php foreach ($recentTickets as $ticket): ?>
<tr> <tr>
<td class="fw-semibold"><?= qh_h($ticket['ticket_number']) ?></td> <td class="fw-bold px-4 py-3 text-dark"><?= qh_h($ticket['ticket_number']) ?></td>
<td><?= qh_h($ticket['patient_name']) ?></td> <td class="py-3"><?= qh_h($ticket['patient_name']) ?></td>
<td><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td> <td class="py-3 text-muted"><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td>
<td><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Unassigned', 'غير محدد'))) ?></td> <td class="py-3 text-muted"><?= qh_h(qh_name($ticket, 'doctor_name', qh_t('Unassigned', 'غير محدد'))) ?></td>
<td><?= qh_status_badge($ticket['status']) ?></td> <td class="py-3"><?= 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 detail', 'عرض التفاصيل')) ?></a></td> <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>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>
</table> </table>
</div> </div>
<?php else: ?> <?php else: ?>
<div class="empty-state compact"> <div class="text-center py-5 text-muted">
<strong><?= qh_h(qh_t('No recent tickets yet.', 'لا توجد تذاكر حديثة حتى الآن.')) ?></strong> <p class="mb-0"><?= qh_h(qh_t('No recent tickets yet.', 'لا توجد تذاكر حديثة حتى الآن.')) ?></p>
<span><?= qh_h(qh_t('Start from reception to populate the workflow.', 'ابدأ من صفحة الاستقبال لبدء تعبئة مسار العمل.')) ?></span>
</div> </div>
<?php endif; ?> <?php endif; ?>
</section> </div>
</div>
</div> </div>
<?php qh_page_end(); ?> <?php qh_page_end(); ?>

View File

@ -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 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('Waiting for vitals', 'بانتظار العلامات الحيوية')) ?></h2>
<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>
<div class="card shadow-sm border-0 mb-4">
<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('Waiting for Vitals', 'بانتظار العلامات الحيوية')) ?></h5>
<span class="badge bg-secondary rounded-pill"><?= qh_h(count($waitingTickets) . ' ' . qh_t('patients', 'مرضى')) ?></span>
</div>
<div class="card-body">
<?php if ($waitingTickets): ?> <?php if ($waitingTickets): ?>
<div class="vstack gap-3"> <div class="vstack gap-3">
<?php foreach ($waitingTickets as $ticket): ?> <?php foreach ($waitingTickets as $ticket): ?>
<article class="list-row-form"> <div class="border rounded p-3 bg-light">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3"> <div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-3">
<div> <div>
<div class="ticket-code"><?= qh_h($ticket['ticket_number']) ?></div> <div class="fw-bold fs-5 text-dark"><?= qh_h($ticket['ticket_number']) ?></div>
<div class="fw-semibold mt-1"><?= qh_h($ticket['patient_name']) ?></div> <div class="fw-semibold mt-1 text-dark"><?= qh_h($ticket['patient_name']) ?></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="small text-muted mt-1"><?= qh_h(qh_name($ticket, 'clinic_name')) ?> &bull; <?= qh_h(qh_name($ticket, 'doctor_name')) ?> &bull; <?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h($ticket['doctor_room'] ?? '--') ?></div>
</div> </div>
<div class="d-flex gap-2 flex-wrap align-items-center"> <div class="d-flex gap-2 flex-wrap align-items-center">
<?= qh_status_badge($ticket['status']) ?> <?= 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> <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> </div>
<form method="post" class="row g-3 align-items-end mt-1"> <form method="post" class="row g-3 align-items-end">
<input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>"> <input type="hidden" name="ticket_id" value="<?= qh_h((string) $ticket['id']) ?>">
<div class="col-lg-9"> <div class="col-lg-9">
<label class="form-label"><?= qh_h(qh_t('Vitals note', 'ملاحظة العلامات الحيوية')) ?></label> <label class="form-label text-dark fw-semibold small"><?= qh_h(qh_t('Vitals note', 'ملاحظة العلامات الحيوية')) ?></label>
<textarea class="form-control" name="vitals_notes" rows="2" placeholder="<?= qh_h(qh_t('Blood pressure, pulse, temperature...', 'الضغط والنبض والحرارة...')) ?>" required></textarea> <textarea class="form-control bg-white" name="vitals_notes" rows="2" placeholder="<?= qh_h(qh_t('Blood pressure, pulse, temperature...', 'الضغط والنبض والحرارة...')) ?>" required></textarea>
</div> </div>
<div class="col-lg-3 d-grid"> <div class="col-lg-3 d-grid">
<button class="btn btn-dark" type="submit"><?= qh_h(qh_t('Send to doctor', 'إرسال إلى الطبيب')) ?></button> <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> </div>
</form> </form>
</article> </div>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<div class="empty-state"> <div class="text-center py-5 text-muted">
<strong><?= qh_h(qh_t('No patients are waiting for vitals.', 'لا يوجد مرضى بانتظار العلامات الحيوية.')) ?></strong> <p class="mb-0"><?= qh_h(qh_t('No patients are waiting for vitals.', 'لا يوجد مرضى بانتظار العلامات الحيوية.')) ?></p>
<span><?= qh_h(qh_t('New vitals-first tickets from reception will appear here.', 'ستظهر هنا التذاكر الجديدة التي تتطلب العلامات الحيوية أولاً.')) ?></span>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div>
</div> </div>
<?php qh_page_end(); ?> <?php qh_page_end(); ?>

0
output.html Normal file
View File

View 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.', 'تم تحديث ملف المستشفى بنجاح.'));
} }

View File

@ -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> </div>
<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 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">
<h5 class="mb-0 font-weight-bold text-dark"><?= qh_h(qh_t('Issue New Ticket', 'إصدار تذكرة جديدة')) ?></h5>
</div>
<div class="card-body">
<form method="post" class="vstack gap-3" novalidate> <form method="post" class="vstack gap-3" novalidate>
<div> <div>
<label class="form-label"><?= qh_h(qh_t('Patient name', 'اسم المريض')) ?></label> <label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Patient name', 'اسم المريض')) ?></label>
<input class="form-control" type="text" name="patient_name" placeholder="Maha Ali" required> <input class="form-control" type="text" name="patient_name" placeholder="Maha Ali" required>
</div> </div>
<div> <div>
<label class="form-label"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></label> <label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></label>
<select class="form-select js-clinic-select" name="clinic_id" required> <select class="form-select js-clinic-select" name="clinic_id" required>
<option value=""><?= qh_h(qh_t('Choose clinic', 'اختر العيادة')) ?></option> <option value=""><?= qh_h(qh_t('Choose clinic', 'اختر العيادة')) ?></option>
<?php foreach ($clinics as $clinic): ?> <?php foreach ($clinics as $clinic): ?>
<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> <option value="<?= qh_h((string) $clinic['id']) ?>"><?= qh_h(qh_name($clinic)) ?><?php if ((int) $clinic['requires_vitals'] === 1): ?> &middot; <?= qh_h(qh_t('Vitals first', 'العلامات أولاً')) ?><?php endif; ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div> <div>
<label class="form-label"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></label> <label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Doctor', 'الطبيب')) ?></label>
<select class="form-select js-doctor-select" name="doctor_id" required> <select class="form-select js-doctor-select" name="doctor_id" required>
<option value=""><?= qh_h(qh_t('Choose doctor', 'اختر الطبيب')) ?></option> <option value=""><?= qh_h(qh_t('Choose doctor', 'اختر الطبيب')) ?></option>
<?php foreach ($doctors as $doctor): ?> <?php foreach ($doctors as $doctor): ?>
<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> <option value="<?= qh_h((string) $doctor['id']) ?>" data-clinic-id="<?= qh_h((string) $doctor['clinic_id']) ?>"><?= qh_h(qh_name($doctor)) ?> &middot; <?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h($doctor['room_number']) ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div> <div>
<label class="form-label"><?= qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?></label> <label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?></label>
<select class="form-select" name="language_pref"> <select class="form-select" name="language_pref">
<option value="en">English</option> <option value="en">English</option>
<option value="ar">العربية</option> <option value="ar">العربية</option>
</select> </select>
</div> </div>
<button class="btn btn-dark" type="submit"><?= qh_h(qh_t('Issue ticket', 'إصدار التذكرة')) ?></button> <button class="btn btn-primary mt-2 shadow-sm" type="submit"><?= qh_h(qh_t('Issue ticket', 'إصدار التذكرة')) ?></button>
</form> </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="card-body">
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap"> <div class="d-flex justify-content-between align-items-start gap-3 flex-wrap">
<div> <div>
<div class="section-kicker"><?= qh_h(qh_t('Issued ticket', 'التذكرة الصادرة')) ?></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="ticket-number mt-2"><?= qh_h($currentTicket['ticket_number']) ?></div> <div class="display-6 fw-bold mt-2 text-dark"><?= qh_h($currentTicket['ticket_number']) ?></div>
<div class="mt-2 fw-semibold"><?= qh_h($currentTicket['patient_name']) ?></div> <div class="mt-2 fw-semibold fs-5 text-dark"><?= qh_h($currentTicket['patient_name']) ?></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="text-muted mt-1"><?= qh_h(qh_name($currentTicket, 'clinic_name')) ?> &bull; <?= qh_h(qh_name($currentTicket, 'doctor_name')) ?> &bull; <?= qh_h(qh_t('Rm', 'غرفة')) ?> <?= qh_h($currentTicket['doctor_room'] ?? '--') ?></div>
</div> </div>
<div class="d-flex flex-column align-items-lg-end gap-2"> <div class="d-flex flex-column align-items-lg-end gap-2">
<?= qh_status_badge($currentTicket['status']) ?> <?= qh_status_badge($currentTicket['status']) ?>
<button class="btn btn-outline-dark btn-sm js-print-ticket" type="button"><?= qh_h(qh_t('Print ticket', 'طباعة التذكرة')) ?></button> <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>
<hr> <hr class="my-3">
<div class="row g-3 small"> <div class="row g-3 small text-muted">
<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('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('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 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> </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('Todays 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"> </div>
<table class="table align-middle mb-0"> <div class="card-body p-0">
<thead> <?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> <tr>
<th><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th> <th class="border-0 px-4 py-3"><?= qh_h(qh_t('Ticket', 'التذكرة')) ?></th>
<th><?= qh_h(qh_t('Patient', 'المريض')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Patient', 'المريض')) ?></th>
<th><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Clinic', 'العيادة')) ?></th>
<th><?= qh_h(qh_t('Status', 'الحالة')) ?></th> <th class="border-0 py-3"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
<th></th> <th class="border-0 px-4 py-3 text-end"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($todayTickets as $ticket): ?> <?php foreach ($todayTickets as $ticket):
<tr> ?><tr >
<td class="fw-semibold"><?= qh_h($ticket['ticket_number']) ?></td> <td class="fw-bold px-4 py-3 text-dark"><?= qh_h($ticket['ticket_number']) ?></td>
<td><?= qh_h($ticket['patient_name']) ?></td> <td class="py-3"><?= qh_h($ticket['patient_name']) ?></td>
<td><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td> <td class="py-3 text-muted"><?= qh_h(qh_name($ticket, 'clinic_name')) ?></td>
<td><?= qh_status_badge($ticket['status']) ?></td> <td class="py-3"><?= 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> <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> </tr>
<?php endforeach; ?> <?php endforeach;
</tbody> ?></tbody>
</table> </table>
</div> </div>
<?php endif;
?></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<?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(); ?> <?php qh_page_end(); ?>

View File

@ -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(); ?>