-
Waiting for vitals / بانتظار العلامات الحيوية
-
Add a short clinical note to transfer the patient to the assigned doctor.
+
= qh_h(qh_t('Waiting for vitals', 'بانتظار العلامات الحيوية')) ?>
+
= qh_h(qh_t('Add a short note, then send the patient to the doctor queue.', 'أضف ملاحظة قصيرة ثم أرسل المريض إلى طابور الطبيب.')) ?>
-
= qh_h((string) count($waitingTickets)) ?> patients
+
= qh_h(count($waitingTickets) . ' ' . qh_t('patients', 'مرضى')) ?>
-
-
+
+
= qh_h($ticket['ticket_number']) ?>
-
= qh_h($ticket['patient_name']) ?>
-
= qh_h($ticket['clinic_name_en'] ?? '') ?> → = qh_h($ticket['doctor_name_en'] ?? '') ?> · Room = qh_h($ticket['doctor_room'] ?? '--') ?>
+
= qh_h($ticket['patient_name']) ?>
+
= qh_h(qh_name($ticket, 'clinic_name')) ?> · = qh_h(qh_name($ticket, 'doctor_name')) ?> · = qh_h(qh_t('Room', 'غرفة')) ?> = qh_h($ticket['doctor_room'] ?? '--') ?>
-
-
-
+
- No patients waiting for vitals.
- Tickets from vitals-required clinics will appear here automatically after issue.
+ = qh_h(qh_t('No patients are waiting for vitals.', 'لا يوجد مرضى بانتظار العلامات الحيوية.')) ?>
+ = qh_h(qh_t('New vitals-first tickets from reception will appear here.', 'ستظهر هنا التذاكر الجديدة التي تتطلب العلامات الحيوية أولاً.')) ?>
diff --git a/queue_bootstrap.php b/queue_bootstrap.php
index 9e8988e..3e78a3a 100644
--- a/queue_bootstrap.php
+++ b/queue_bootstrap.php
@@ -16,6 +16,7 @@ function qh_boot(): void
qh_ensure_schema();
qh_seed_demo_data();
+ qh_seed_hospital_profile();
$booted = true;
}
@@ -51,6 +52,31 @@ CREATE TABLE IF NOT EXISTS hospital_queue_records (
SQL;
db()->exec($sql);
+
+ $profileSql = <<
exec($profileSql);
}
function qh_seed_demo_data(): void
@@ -128,6 +154,40 @@ function qh_seed_demo_data(): void
}
}
+function qh_seed_hospital_profile(): void
+{
+ $exists = (int) db()->query('SELECT COUNT(*) FROM hospital_profile_settings')->fetchColumn();
+ if ($exists > 0) {
+ return;
+ }
+
+ $stmt = db()->prepare(
+ '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)
+ 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)'
+ );
+
+ $stmt->execute([
+ 'name_en' => qh_project_name('Hospital Queue Center'),
+ 'name_ar' => 'مركز إدارة الطوابير',
+ 'short_name' => 'HQC',
+ 'tagline_en' => 'Organized patient flow, queue control, and staff coordination.',
+ 'tagline_ar' => 'تنظيم تدفق المرضى وإدارة الطوابير وتنسيق عمل الطاقم.',
+ 'phone' => '',
+ 'email' => '',
+ 'website' => '',
+ 'address_en' => '',
+ 'address_ar' => '',
+ 'working_hours_en' => 'Sun–Thu · 8:00 AM – 8:00 PM',
+ 'working_hours_ar' => 'الأحد – الخميس · 8:00 ص – 8:00 م',
+ 'logo_url' => '',
+ 'favicon_url' => '',
+ 'primary_color' => '#0f8b8d',
+ 'secondary_color' => '#16697a',
+ ]);
+}
+
function qh_h(?string $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
@@ -151,29 +211,313 @@ function qh_asset_version(string $relativePath): int
return is_file($fullPath) ? (int) filemtime($fullPath) : time();
}
+
+function qh_locale(): string
+{
+ static $locale = null;
+ if ($locale !== null) {
+ return $locale;
+ }
+
+ $requested = strtolower(trim((string) ($_GET['lang'] ?? '')));
+ if (in_array($requested, ['en', 'ar'], true)) {
+ $_SESSION['qh_lang'] = $requested;
+ }
+
+ $sessionLocale = strtolower(trim((string) ($_SESSION['qh_lang'] ?? 'en')));
+ $locale = in_array($sessionLocale, ['en', 'ar'], true) ? $sessionLocale : 'en';
+
+ return $locale;
+}
+
+function qh_is_ar(): bool
+{
+ return qh_locale() === 'ar';
+}
+
+function qh_t(string $en, string $ar): string
+{
+ return qh_is_ar() ? $ar : $en;
+}
+
+function qh_locale_label(?string $locale = null): string
+{
+ $locale = $locale ?: qh_locale();
+ return $locale === 'ar' ? 'العربية' : 'English';
+}
+
+function qh_other_locale(): string
+{
+ return qh_is_ar() ? 'en' : 'ar';
+}
+
+function qh_url(string $path, array $params = []): string
+{
+ $target = basename($path);
+ $params['lang'] = $params['lang'] ?? qh_locale();
+ $query = http_build_query($params);
+ return $query !== '' ? $target . '?' . $query : $target;
+}
+
+function qh_switch_lang_url(string $locale): string
+{
+ $params = $_GET;
+ $params['lang'] = in_array($locale, ['en', 'ar'], true) ? $locale : qh_other_locale();
+ $target = basename((string) ($_SERVER['PHP_SELF'] ?? 'index.php'));
+ $query = http_build_query($params);
+ return $query !== '' ? $target . '?' . $query : $target;
+}
+
+function qh_name(array $row, string $base = 'name', string $fallback = '—'): string
+{
+ $primaryKey = $base . '_' . (qh_is_ar() ? 'ar' : 'en');
+ $secondaryKey = $base . '_' . (qh_is_ar() ? 'en' : 'ar');
+
+ $primary = trim((string) ($row[$primaryKey] ?? ''));
+ if ($primary !== '') {
+ return $primary;
+ }
+
+ $secondary = trim((string) ($row[$secondaryKey] ?? ''));
+ return $secondary !== '' ? $secondary : $fallback;
+}
+
+function qh_fetch_hospital_profile(): array
+{
+ static $profile = null;
+ if ($profile !== null) {
+ return $profile;
+ }
+
+ $row = db()->query('SELECT * FROM hospital_profile_settings WHERE id = 1 LIMIT 1')->fetch();
+ $profile = is_array($row) ? $row : [];
+
+ return $profile;
+}
+
+function qh_hospital_name(): string
+{
+ $profile = qh_fetch_hospital_profile();
+ $name = qh_name($profile, 'name', '');
+ if ($name !== '') {
+ return $name;
+ }
+
+ $shortName = trim((string) ($profile['short_name'] ?? ''));
+ return $shortName !== '' ? $shortName : qh_project_name();
+}
+
+function qh_hospital_tagline(): string
+{
+ $profile = qh_fetch_hospital_profile();
+ return qh_name($profile, 'tagline', '');
+}
+
+function qh_hospital_logo_url(): string
+{
+ $profile = qh_fetch_hospital_profile();
+ return trim((string) ($profile['logo_url'] ?? ''));
+}
+
+function qh_hospital_favicon_url(): string
+{
+ $profile = qh_fetch_hospital_profile();
+ $favicon = trim((string) ($profile['favicon_url'] ?? ''));
+ if ($favicon !== '') {
+ return $favicon;
+ }
+
+ return qh_hospital_logo_url();
+}
+
+function qh_hospital_contact_value(string $field): string
+{
+ $profile = qh_fetch_hospital_profile();
+ return trim((string) ($profile[$field] ?? ''));
+}
+
+function qh_hospital_brand_initials(): string
+{
+ $profile = qh_fetch_hospital_profile();
+ $source = trim((string) ($profile['short_name'] ?? ''));
+ if ($source === '') {
+ $source = trim((string) ($profile['name_en'] ?? qh_project_name('Hospital Queue')));
+ }
+
+ $parts = preg_split('/[^\p{L}\p{N}]+/u', $source, -1, PREG_SPLIT_NO_EMPTY);
+ $initials = [];
+ foreach ($parts as $part) {
+ if (preg_match('/^[\p{L}\p{N}]/u', $part, $matches)) {
+ $initials[] = strtoupper($matches[0]);
+ }
+ if (count($initials) >= 2) {
+ break;
+ }
+ }
+
+ return $initials !== [] ? implode('', array_slice($initials, 0, 2)) : 'HQ';
+}
+
+function qh_sanitize_hex_color(?string $value, string $fallback): string
+{
+ $candidate = strtoupper(trim((string) $value));
+ return preg_match('/^#[0-9A-F]{6}$/', $candidate) ? $candidate : $fallback;
+}
+
+function qh_hospital_primary_color(): string
+{
+ $profile = qh_fetch_hospital_profile();
+ return qh_sanitize_hex_color($profile['primary_color'] ?? null, '#0F8B8D');
+}
+
+function qh_hospital_secondary_color(): string
+{
+ $profile = qh_fetch_hospital_profile();
+ return qh_sanitize_hex_color($profile['secondary_color'] ?? null, '#16697A');
+}
+
+function qh_current_language_badge(): string
+{
+ return qh_t('English workspace', 'واجهة عربية');
+}
+
+function qh_admin_sections(): array
+{
+ return [
+ 'admin.php' => [
+ 'label' => qh_t('Overview', 'نظرة عامة'),
+ 'description' => qh_t('Admin home and setup summary.', 'الصفحة الرئيسية وملخص الإعدادات.'),
+ 'icon' => 'overview',
+ ],
+ 'admin_hospital.php' => [
+ 'label' => qh_t('Hospital profile', 'ملف المستشفى'),
+ 'description' => qh_t('Manage logo, favicon, contact details, and brand colors.', 'إدارة الشعار والأيقونة وبيانات التواصل وألوان الهوية.'),
+ 'icon' => 'hospital',
+ ],
+ 'admin_clinics.php' => [
+ 'label' => qh_t('Clinics', 'العيادات'),
+ 'description' => qh_t('Manage clinic codes, routing, and order.', 'إدارة رموز العيادات والمسار والترتيب.'),
+ 'icon' => 'clinic',
+ ],
+ 'admin_doctors.php' => [
+ 'label' => qh_t('Doctors', 'الأطباء'),
+ 'description' => qh_t('Manage doctors, rooms, and assignments.', 'إدارة الأطباء والغرف والتعيينات.'),
+ 'icon' => 'doctor',
+ ],
+ ];
+}
+
+function qh_admin_allowed_pages(): array
+{
+ return array_keys(qh_admin_sections());
+}
+
+function qh_admin_return_to(?string $candidate = null): string
+{
+ $page = basename((string) ($candidate ?? 'admin.php'));
+ return in_array($page, qh_admin_allowed_pages(), true) ? $page : 'admin.php';
+}
+
+function qh_admin_sidebar_icon(string $icon): string
+{
+ return match ($icon) {
+ 'hospital' => ' ',
+ 'clinic' => ' ',
+ 'doctor' => ' ',
+ default => ' ',
+ };
+}
+
+function qh_admin_stats(): array
+{
+ $clinics = qh_fetch_clinics();
+ $doctors = qh_fetch_doctors();
+ $vitalsClinics = count(array_filter($clinics, static fn(array $clinic): bool => (int) ($clinic['requires_vitals'] ?? 0) === 1));
+
+ return [
+ 'clinics' => count($clinics),
+ 'doctors' => count($doctors),
+ 'vitals_clinics' => $vitalsClinics,
+ 'direct_clinics' => max(count($clinics) - $vitalsClinics, 0),
+ ];
+}
+
+function qh_render_admin_sidebar(string $activePage, array $stats = []): void
+{
+ $sections = qh_admin_sections();
+ $activePage = qh_admin_return_to($activePage);
+ if ($stats === []) {
+ $stats = qh_admin_stats();
+ }
+
+ echo '';
+}
+
+function qh_ticket_next_stop(array $ticket): string
+{
+ return (int) ($ticket['clinic_requires_vitals'] ?? 0) === 1
+ ? qh_t('Nursing vitals', 'العلامات الحيوية في التمريض')
+ : qh_t('Doctor waiting area', 'منطقة انتظار الطبيب');
+}
+
+function qh_ticket_last_note(array $ticket): string
+{
+ return match ((string) ($ticket['status'] ?? '')) {
+ 'waiting_vitals' => qh_t('Proceed to nursing vitals first.', 'يرجى التوجه أولاً إلى العلامات الحيوية في التمريض.'),
+ 'ready_for_doctor' => qh_t('Wait for the doctor call on the public display.', 'انتظر نداء الطبيب على الشاشة العامة.'),
+ 'called' => qh_t('The patient has been called to the doctor room.', 'تم نداء المريض إلى غرفة الطبيب.'),
+ 'in_progress' => qh_t('The consultation is currently in progress.', 'الاستشارة جارية حالياً.'),
+ 'done' => qh_t('The visit has been completed.', 'تم إكمال الزيارة.'),
+ 'no_show' => qh_t('The patient was marked as no-show.', 'تم تسجيل المريض كحالة عدم حضور.'),
+ default => trim((string) ($ticket['display_note'] ?? '')) !== '' ? (string) $ticket['display_note'] : '—',
+ };
+}
+
function qh_label(string $en, string $ar, string $wrapper = 'span'): string
{
$tag = preg_replace('/[^a-z0-9]/i', '', $wrapper) ?: 'span';
- return sprintf(
- '<%1$s class="bi-label">%2$s / %3$s %1$s>',
- $tag,
- qh_h($en),
- qh_h($ar)
- );
+ return sprintf('<%1$s>%2$s%1$s>', $tag, qh_h(qh_t($en, $ar)));
}
+
+
function qh_page_start(string $activePage, string $pageTitle, string $metaDescription = ''): void
{
+ $locale = qh_locale();
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? qh_project_description();
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
- $fullTitle = $pageTitle . ' · ' . qh_project_name();
+ $brandName = qh_hospital_name();
+ $faviconUrl = qh_hospital_favicon_url();
+ $fullTitle = $pageTitle . ' · ' . $brandName;
$description = $metaDescription !== '' ? $metaDescription : qh_project_description();
- $bodyClass = 'page-' . preg_replace('/[^a-z0-9\-]/i', '-', $activePage);
+ $bodyClass = 'page-' . preg_replace('/[^a-z0-9\-]/i', '-', $activePage) . ' lang-' . $locale;
$assetVersionCss = qh_asset_version('assets/css/custom.css');
- $assetVersionJs = qh_asset_version('assets/js/main.js');
+ $primaryColor = qh_hospital_primary_color();
+ $secondaryColor = qh_hospital_secondary_color();
echo '';
- echo '';
+ echo '';
echo '';
echo ' ';
echo ' ';
@@ -187,11 +531,16 @@ function qh_page_start(string $activePage, string $pageTitle, string $metaDescri
echo ' ';
echo ' ';
}
- echo ' ';
+ if ($faviconUrl !== '') {
+ echo ' ';
+ echo ' ';
+ }
+ echo ' ';
echo ' ';
echo ' ';
+ echo ' ';
echo '';
- echo '';
+ echo '';
if ($activePage !== 'display') {
qh_render_nav($activePage);
}
@@ -199,6 +548,8 @@ function qh_page_start(string $activePage, string $pageTitle, string $metaDescri
qh_render_flash();
}
+
+
function qh_page_end(): void
{
$assetVersionJs = qh_asset_version('assets/js/main.js');
@@ -211,37 +562,57 @@ function qh_page_end(): void
function qh_render_nav(string $activePage): void
{
$links = [
- 'home' => ['href' => 'index.php', 'label' => qh_label('Operations', 'العمليات')],
- 'admin' => ['href' => 'admin.php', 'label' => qh_label('Admin', 'الإدارة')],
- 'reception' => ['href' => 'reception.php', 'label' => qh_label('Reception', 'الاستقبال')],
- 'nursing' => ['href' => 'nursing.php', 'label' => qh_label('Nursing', 'التمريض')],
- 'doctor' => ['href' => 'doctor.php', 'label' => qh_label('Doctor', 'الطبيب')],
- 'display' => ['href' => 'display.php', 'label' => qh_label('Display', 'الشاشة العامة')],
+ 'home' => ['href' => qh_url('index.php'), 'label' => qh_t('Operations', 'العمليات')],
+ 'admin' => ['href' => qh_url('admin.php'), 'label' => qh_t('Admin', 'الإدارة')],
+ 'reception' => ['href' => qh_url('reception.php'), 'label' => qh_t('Reception', 'الاستقبال')],
+ 'nursing' => ['href' => qh_url('nursing.php'), 'label' => qh_t('Nursing', 'التمريض')],
+ 'doctor' => ['href' => qh_url('doctor.php'), 'label' => qh_t('Doctor', 'الطبيب')],
+ 'display' => ['href' => qh_url('display.php'), 'label' => qh_t('Display', 'الشاشة')],
];
- echo '';
+ $logoUrl = qh_hospital_logo_url();
+ $tagline = qh_hospital_tagline();
+
+ echo '';
echo ' ';
echo ' ';
echo ' ';
echo ' ';
}
+
+
function qh_set_flash(string $type, string $message): void
{
$_SESSION['flash'] = ['type' => $type, 'message' => $message];
@@ -268,18 +639,27 @@ function qh_render_flash(): void
echo ' ';
echo '
';
echo '
' . qh_h((string) $flash['message']) . '
';
- echo '
';
+ echo '
';
echo '
';
echo '
';
echo ' ';
}
+
+
function qh_redirect(string $location): void
{
+ if (strpos($location, 'lang=') === false) {
+ $separator = str_contains($location, '?') ? '&' : '?';
+ $location .= $separator . 'lang=' . qh_locale();
+ }
+
header('Location: ' . $location);
exit;
}
+
+
function qh_fetch_clinics(): array
{
$stmt = db()->query("SELECT * FROM hospital_queue_records WHERE item_type = 'clinic' ORDER BY sort_order ASC, name_en ASC");
@@ -398,12 +778,12 @@ function qh_create_ticket(string $patientName, int $clinicId, int $doctorId, str
$pdo = db();
$clinic = qh_fetch_clinic($clinicId);
if (!$clinic) {
- throw new RuntimeException('Clinic not found.');
+ throw new RuntimeException(qh_t('Clinic not found.', 'لم يتم العثور على العيادة.'));
}
$doctor = qh_fetch_doctor($doctorId);
if (!$doctor || (int) $doctor['clinic_id'] !== $clinicId) {
- throw new RuntimeException('Doctor does not belong to the selected clinic.');
+ throw new RuntimeException(qh_t('The doctor does not belong to the selected clinic.', 'الطبيب لا يتبع العيادة المحددة.'));
}
$ticketNumber = qh_generate_ticket_number($clinic['code']);
@@ -477,9 +857,11 @@ function qh_status_meta(string $status): array
function qh_status_badge(string $status): string
{
$meta = qh_status_meta($status);
- return '
' . qh_label($meta['en'], $meta['ar']) . ' ';
+ return '
' . qh_h(qh_t($meta['en'], $meta['ar'])) . ' ';
}
+
+
function qh_call_message(array $ticket): array
{
$ticketNumber = $ticket['ticket_number'] ?? '---';
@@ -505,18 +887,25 @@ function qh_format_datetime(?string $value): string
function qh_require_post(): void
{
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
- qh_set_flash('danger', 'Invalid request method.');
+ qh_set_flash('danger', qh_t('Invalid request method.', 'طريقة الطلب غير صالحة.'));
qh_redirect('index.php');
}
}
+
+
function qh_admin_handle_request(): void
{
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
return;
}
- $action = $_POST['action'] ?? '';
+ $action = trim((string) ($_POST['action'] ?? ''));
+ if ($action === '') {
+ return;
+ }
+
+ $returnTo = qh_admin_return_to($_POST['return_to'] ?? 'admin.php');
$pdo = db();
try {
@@ -526,7 +915,7 @@ function qh_admin_handle_request(): void
$nameAr = trim((string) ($_POST['name_ar'] ?? ''));
$requiresVitals = isset($_POST['requires_vitals']) ? 1 : 0;
if ($code === '' || $nameEn === '' || $nameAr === '') {
- throw new InvalidArgumentException('Please complete the clinic code and bilingual names.');
+ throw new InvalidArgumentException(qh_t('Please complete the clinic code and both clinic names.', 'يرجى إدخال رمز العيادة والاسمين الإنجليزي والعربي.'));
}
$stmt = $pdo->prepare(
"INSERT INTO hospital_queue_records (item_type, code, name_en, name_ar, requires_vitals, sort_order, status)
@@ -537,29 +926,59 @@ function qh_admin_handle_request(): void
'name_en' => $nameEn,
'name_ar' => $nameAr,
'requires_vitals' => $requiresVitals,
- 'sort_order' => (int) ($_POST['sort_order'] ?? 50),
+ 'sort_order' => max((int) ($_POST['sort_order'] ?? 50), 1),
]);
- qh_set_flash('success', 'Clinic saved successfully.');
+ qh_set_flash('success', qh_t('Clinic saved successfully.', 'تم حفظ العيادة بنجاح.'));
} elseif ($action === 'update_clinic') {
$clinicId = (int) ($_POST['clinic_id'] ?? 0);
+ $code = strtoupper(trim((string) ($_POST['code'] ?? '')));
+ $nameEn = trim((string) ($_POST['name_en'] ?? ''));
+ $nameAr = trim((string) ($_POST['name_ar'] ?? ''));
+ if ($clinicId <= 0 || $code === '' || $nameEn === '' || $nameAr === '') {
+ throw new InvalidArgumentException(qh_t('Please complete the clinic details before updating.', 'يرجى إكمال بيانات العيادة قبل التحديث.'));
+ }
$stmt = $pdo->prepare(
"UPDATE hospital_queue_records
- SET requires_vitals = :requires_vitals, sort_order = :sort_order
+ SET code = :code,
+ name_en = :name_en,
+ name_ar = :name_ar,
+ requires_vitals = :requires_vitals,
+ sort_order = :sort_order
WHERE item_type = 'clinic' AND id = :clinic_id"
);
$stmt->execute([
+ 'code' => substr($code, 0, 10),
+ 'name_en' => $nameEn,
+ 'name_ar' => $nameAr,
'requires_vitals' => isset($_POST['requires_vitals']) ? 1 : 0,
- 'sort_order' => (int) ($_POST['sort_order'] ?? 50),
+ 'sort_order' => max((int) ($_POST['sort_order'] ?? 50), 1),
'clinic_id' => $clinicId,
]);
- qh_set_flash('success', 'Clinic settings updated.');
+ qh_set_flash('success', qh_t('Clinic updated successfully.', 'تم تحديث العيادة بنجاح.'));
+ } elseif ($action === 'delete_clinic') {
+ $clinicId = (int) ($_POST['clinic_id'] ?? 0);
+ if ($clinicId <= 0) {
+ throw new InvalidArgumentException(qh_t('Invalid clinic selected.', 'تم اختيار عيادة غير صالحة.'));
+ }
+ $doctorCountStmt = $pdo->prepare("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'doctor' AND clinic_id = :clinic_id");
+ $doctorCountStmt->execute(['clinic_id' => $clinicId]);
+ $doctorCount = (int) $doctorCountStmt->fetchColumn();
+ $ticketCountStmt = $pdo->prepare("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket' AND clinic_id = :clinic_id");
+ $ticketCountStmt->execute(['clinic_id' => $clinicId]);
+ $ticketCount = (int) $ticketCountStmt->fetchColumn();
+ if ($doctorCount > 0 || $ticketCount > 0) {
+ throw new InvalidArgumentException(qh_t('This clinic cannot be deleted because it is linked to doctors or patient tickets.', 'لا يمكن حذف هذه العيادة لأنها مرتبطة بأطباء أو تذاكر مرضى.'));
+ }
+ $stmt = $pdo->prepare("DELETE FROM hospital_queue_records WHERE item_type = 'clinic' AND id = :clinic_id");
+ $stmt->execute(['clinic_id' => $clinicId]);
+ qh_set_flash('success', qh_t('Clinic deleted successfully.', 'تم حذف العيادة بنجاح.'));
} elseif ($action === 'add_doctor') {
$nameEn = trim((string) ($_POST['name_en'] ?? ''));
$nameAr = trim((string) ($_POST['name_ar'] ?? ''));
$clinicId = (int) ($_POST['clinic_id'] ?? 0);
$roomNumber = trim((string) ($_POST['room_number'] ?? ''));
if ($nameEn === '' || $nameAr === '' || $clinicId <= 0 || $roomNumber === '') {
- throw new InvalidArgumentException('Please complete the doctor form before saving.');
+ throw new InvalidArgumentException(qh_t('Please complete the doctor form before saving.', 'يرجى إكمال بيانات الطبيب قبل الحفظ.'));
}
$stmt = $pdo->prepare(
"INSERT INTO hospital_queue_records (item_type, name_en, name_ar, clinic_id, room_number, sort_order, status)
@@ -570,31 +989,129 @@ function qh_admin_handle_request(): void
'name_ar' => $nameAr,
'clinic_id' => $clinicId,
'room_number' => $roomNumber,
- 'sort_order' => (int) ($_POST['sort_order'] ?? 50),
+ 'sort_order' => max((int) ($_POST['sort_order'] ?? 50), 1),
]);
- qh_set_flash('success', 'Doctor profile saved.');
+ qh_set_flash('success', qh_t('Doctor profile saved.', 'تم حفظ ملف الطبيب.'));
} elseif ($action === 'update_doctor') {
$doctorId = (int) ($_POST['doctor_id'] ?? 0);
+ $nameEn = trim((string) ($_POST['name_en'] ?? ''));
+ $nameAr = trim((string) ($_POST['name_ar'] ?? ''));
$clinicId = (int) ($_POST['clinic_id'] ?? 0);
$roomNumber = trim((string) ($_POST['room_number'] ?? ''));
+ if ($doctorId <= 0 || $nameEn === '' || $nameAr === '' || $clinicId <= 0 || $roomNumber === '') {
+ throw new InvalidArgumentException(qh_t('Please complete the doctor details before updating.', 'يرجى إكمال بيانات الطبيب قبل التحديث.'));
+ }
$stmt = $pdo->prepare(
"UPDATE hospital_queue_records
- SET clinic_id = :clinic_id, room_number = :room_number, sort_order = :sort_order
+ SET name_en = :name_en,
+ name_ar = :name_ar,
+ clinic_id = :clinic_id,
+ room_number = :room_number,
+ sort_order = :sort_order
WHERE item_type = 'doctor' AND id = :doctor_id"
);
$stmt->execute([
+ 'name_en' => $nameEn,
+ 'name_ar' => $nameAr,
'clinic_id' => $clinicId,
'room_number' => $roomNumber,
- 'sort_order' => (int) ($_POST['sort_order'] ?? 50),
+ 'sort_order' => max((int) ($_POST['sort_order'] ?? 50), 1),
'doctor_id' => $doctorId,
]);
- qh_set_flash('success', 'Doctor assignment updated.');
+ qh_set_flash('success', qh_t('Doctor assignment updated.', 'تم تحديث تعيين الطبيب.'));
+ } elseif ($action === 'delete_doctor') {
+ $doctorId = (int) ($_POST['doctor_id'] ?? 0);
+ if ($doctorId <= 0) {
+ throw new InvalidArgumentException(qh_t('Invalid doctor selected.', 'تم اختيار طبيب غير صالح.'));
+ }
+ $ticketCountStmt = $pdo->prepare("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket' AND doctor_id = :doctor_id");
+ $ticketCountStmt->execute(['doctor_id' => $doctorId]);
+ $ticketCount = (int) $ticketCountStmt->fetchColumn();
+ if ($ticketCount > 0) {
+ throw new InvalidArgumentException(qh_t('This doctor cannot be deleted because patient tickets are linked to the profile.', 'لا يمكن حذف هذا الطبيب لأن هناك تذاكر مرضى مرتبطة بالملف.'));
+ }
+ $stmt = $pdo->prepare("DELETE FROM hospital_queue_records WHERE item_type = 'doctor' AND id = :doctor_id");
+ $stmt->execute(['doctor_id' => $doctorId]);
+ qh_set_flash('success', qh_t('Doctor deleted successfully.', 'تم حذف الطبيب بنجاح.'));
+ } elseif ($action === 'save_hospital_profile') {
+ $nameEn = trim((string) ($_POST['name_en'] ?? ''));
+ $nameAr = trim((string) ($_POST['name_ar'] ?? ''));
+ $shortName = trim((string) ($_POST['short_name'] ?? ''));
+ $taglineEn = trim((string) ($_POST['tagline_en'] ?? ''));
+ $taglineAr = trim((string) ($_POST['tagline_ar'] ?? ''));
+ $phone = trim((string) ($_POST['phone'] ?? ''));
+ $email = trim((string) ($_POST['email'] ?? ''));
+ $website = trim((string) ($_POST['website'] ?? ''));
+ $addressEn = trim((string) ($_POST['address_en'] ?? ''));
+ $addressAr = trim((string) ($_POST['address_ar'] ?? ''));
+ $workingHoursEn = trim((string) ($_POST['working_hours_en'] ?? ''));
+ $workingHoursAr = trim((string) ($_POST['working_hours_ar'] ?? ''));
+ $logoUrl = trim((string) ($_POST['logo_url'] ?? ''));
+ $faviconUrl = trim((string) ($_POST['favicon_url'] ?? ''));
+ $primaryColor = qh_sanitize_hex_color($_POST['primary_color'] ?? '', '#0F8B8D');
+ $secondaryColor = qh_sanitize_hex_color($_POST['secondary_color'] ?? '', '#16697A');
+
+ if ($nameEn === '' || $nameAr === '') {
+ throw new InvalidArgumentException(qh_t('Please enter both the English and Arabic hospital names.', 'يرجى إدخال اسم المستشفى بالإنجليزية والعربية.'));
+ }
+ if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
+ throw new InvalidArgumentException(qh_t('Please enter a valid hospital email address.', 'يرجى إدخال بريد إلكتروني صالح للمستشفى.'));
+ }
+ if ($website !== '' && filter_var($website, FILTER_VALIDATE_URL) === false) {
+ 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(
+ "UPDATE hospital_profile_settings
+ SET name_en = :name_en,
+ name_ar = :name_ar,
+ short_name = :short_name,
+ tagline_en = :tagline_en,
+ tagline_ar = :tagline_ar,
+ phone = :phone,
+ email = :email,
+ website = :website,
+ address_en = :address_en,
+ address_ar = :address_ar,
+ working_hours_en = :working_hours_en,
+ working_hours_ar = :working_hours_ar,
+ logo_url = :logo_url,
+ favicon_url = :favicon_url,
+ primary_color = :primary_color,
+ secondary_color = :secondary_color
+ WHERE id = 1"
+ );
+ $stmt->execute([
+ 'name_en' => $nameEn,
+ 'name_ar' => $nameAr,
+ 'short_name' => substr($shortName, 0, 40),
+ 'tagline_en' => $taglineEn,
+ 'tagline_ar' => $taglineAr,
+ 'phone' => $phone,
+ 'email' => $email,
+ 'website' => $website,
+ 'address_en' => $addressEn,
+ 'address_ar' => $addressAr,
+ 'working_hours_en' => $workingHoursEn,
+ 'working_hours_ar' => $workingHoursAr,
+ 'logo_url' => $logoUrl,
+ 'favicon_url' => $faviconUrl,
+ 'primary_color' => $primaryColor,
+ 'secondary_color' => $secondaryColor,
+ ]);
+ qh_set_flash('success', qh_t('Hospital profile updated successfully.', 'تم تحديث ملف المستشفى بنجاح.'));
}
} catch (Throwable $exception) {
qh_set_flash('danger', $exception->getMessage());
}
- qh_redirect('admin.php');
+ qh_redirect($returnTo);
}
function qh_reception_handle_request(): void
@@ -610,11 +1127,11 @@ function qh_reception_handle_request(): void
$languagePref = trim((string) ($_POST['language_pref'] ?? 'en'));
if ($patientName === '' || $clinicId <= 0 || $doctorId <= 0) {
- throw new InvalidArgumentException('Please complete patient name, clinic, and doctor.');
+ throw new InvalidArgumentException(qh_t('Please complete the patient name, clinic, and doctor.', 'يرجى إدخال اسم المريض والعيادة والطبيب.'));
}
$ticketId = qh_create_ticket($patientName, $clinicId, $doctorId, $languagePref);
- qh_set_flash('success', 'Ticket issued successfully.');
+ qh_set_flash('success', qh_t('Ticket issued successfully.', 'تم إصدار التذكرة بنجاح.'));
qh_redirect('reception.php?ticket_id=' . $ticketId);
} catch (Throwable $exception) {
qh_set_flash('danger', $exception->getMessage());
@@ -632,7 +1149,7 @@ function qh_nursing_handle_request(): void
$ticketId = (int) ($_POST['ticket_id'] ?? 0);
$vitalsNotes = trim((string) ($_POST['vitals_notes'] ?? ''));
if ($ticketId <= 0 || $vitalsNotes === '') {
- throw new InvalidArgumentException('Please add a short vitals note before sending the patient forward.');
+ throw new InvalidArgumentException(qh_t('Please add a short vitals note before sending the patient forward.', 'يرجى إضافة ملاحظة قصيرة للعلامات الحيوية قبل إرسال المريض.'));
}
$stmt = db()->prepare(
@@ -646,7 +1163,7 @@ function qh_nursing_handle_request(): void
'vitals_notes' => $vitalsNotes,
'ticket_id' => $ticketId,
]);
- qh_set_flash('success', 'Vitals captured and patient moved to doctor queue.');
+ qh_set_flash('success', qh_t('Vitals captured and patient moved to the doctor queue.', 'تم حفظ العلامات الحيوية ونقل المريض إلى طابور الطبيب.'));
} catch (Throwable $exception) {
qh_set_flash('danger', $exception->getMessage());
}
@@ -666,7 +1183,7 @@ function qh_doctor_handle_request(): void
$action = trim((string) ($_POST['action'] ?? ''));
$ticket = qh_fetch_ticket($ticketId);
if (!$ticket || $doctorId <= 0 || (int) $ticket['doctor_id'] !== $doctorId) {
- throw new InvalidArgumentException('That ticket is not available for the selected doctor.');
+ throw new InvalidArgumentException(qh_t('That ticket is not available for the selected doctor.', 'هذه التذكرة غير متاحة للطبيب المحدد.'));
}
if ($action === 'call_ticket') {
@@ -680,7 +1197,7 @@ function qh_doctor_handle_request(): void
'display_note' => $message['en'],
'ticket_id' => $ticketId,
]);
- qh_set_flash('success', 'Patient call pushed to the public display.');
+ qh_set_flash('success', qh_t('Patient call was sent to the public display.', 'تم إرسال نداء المريض إلى الشاشة العامة.'));
} elseif ($action === 'start_visit') {
$stmt = db()->prepare(
"UPDATE hospital_queue_records
@@ -688,7 +1205,7 @@ function qh_doctor_handle_request(): void
WHERE item_type = 'ticket' AND id = :ticket_id"
);
$stmt->execute(['ticket_id' => $ticketId]);
- qh_set_flash('success', 'Consultation started.');
+ qh_set_flash('success', qh_t('Consultation started.', 'بدأت الاستشارة.'));
} elseif ($action === 'complete_ticket') {
$stmt = db()->prepare(
"UPDATE hospital_queue_records
@@ -696,7 +1213,7 @@ function qh_doctor_handle_request(): void
WHERE item_type = 'ticket' AND id = :ticket_id"
);
$stmt->execute(['ticket_id' => $ticketId]);
- qh_set_flash('success', 'Visit marked as completed.');
+ qh_set_flash('success', qh_t('Visit marked as completed.', 'تم إنهاء الزيارة.'));
} elseif ($action === 'mark_no_show') {
$stmt = db()->prepare(
"UPDATE hospital_queue_records
@@ -704,7 +1221,7 @@ function qh_doctor_handle_request(): void
WHERE item_type = 'ticket' AND id = :ticket_id"
);
$stmt->execute(['ticket_id' => $ticketId]);
- qh_set_flash('warning', 'Patient marked as no-show.');
+ qh_set_flash('warning', qh_t('Patient marked as no-show.', 'تم تسجيل المريض كحالة عدم حضور.'));
}
qh_redirect('doctor.php?doctor_id=' . $doctorId);
diff --git a/reception.php b/reception.php
index 66e699d..ceb4ea3 100644
--- a/reception.php
+++ b/reception.php
@@ -9,54 +9,57 @@ $doctors = qh_fetch_doctors();
$currentTicket = isset($_GET['ticket_id']) ? qh_fetch_ticket((int) $_GET['ticket_id']) : null;
$todayTickets = qh_fetch_tickets(['waiting_vitals', 'ready_for_doctor', 'called', 'in_progress', 'done', 'no_show'], null, 12);
-qh_page_start('reception', 'Reception ticket issuance', 'Reception desk ticket issue flow with one ticket that follows the patient through vitals and doctor visit.');
+qh_page_start(
+ 'reception',
+ qh_t('Reception ticket issuance', 'إصدار التذاكر في الاستقبال'),
+ qh_t('Reception page with separated language views.', 'صفحة الاستقبال مع فصل واضح بين اللغتين.')
+);
?>
-
New patient ticket / تذكرة مريض جديدة
+
= qh_h(qh_t('New patient ticket', 'تذكرة مريض جديدة')) ?>
- Patient name / اسم المريض
+ = qh_h(qh_t('Patient name', 'اسم المريض')) ?>
- Clinic / العيادة
+ = qh_h(qh_t('Clinic', 'العيادة')) ?>
- Choose clinic
+ = qh_h(qh_t('Choose clinic', 'اختر العيادة')) ?>
- = qh_h($clinic['name_en']) ?> / = qh_h($clinic['name_ar']) ?>= (int) $clinic['requires_vitals'] === 1 ? ' · Vitals first' : '' ?>
+ = qh_h(qh_name($clinic)) ?> · = qh_h(qh_t('Vitals first', 'العلامات أولاً')) ?>
- Doctor / الطبيب
+ = qh_h(qh_t('Doctor', 'الطبيب')) ?>
- Choose doctor
+ = qh_h(qh_t('Choose doctor', 'اختر الطبيب')) ?>
-
- = qh_h($doctor['name_en']) ?> / = qh_h($doctor['name_ar']) ?> · Room = qh_h($doctor['room_number']) ?>
-
+ = qh_h(qh_name($doctor)) ?> · = qh_h(qh_t('Room', 'غرفة')) ?> = qh_h($doctor['room_number']) ?>
- Preferred language / اللغة المفضلة
+ = qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?>
English
العربية
- Issue ticket / إصدار التذكرة
+ = qh_h(qh_t('Issue ticket', 'إصدار التذكرة')) ?>
@@ -66,40 +69,36 @@ qh_page_start('reception', 'Reception ticket issuance', 'Reception desk ticket i
-
Issued ticket / التذكرة الصادرة
+
= qh_h(qh_t('Issued ticket', 'التذكرة الصادرة')) ?>
= qh_h($currentTicket['ticket_number']) ?>
= qh_h($currentTicket['patient_name']) ?>
-
= qh_h($currentTicket['clinic_name_en'] ?? '') ?> · = qh_h($currentTicket['doctor_name_en'] ?? '') ?> · Room = qh_h($currentTicket['doctor_room'] ?? '--') ?>
+
= qh_h(qh_name($currentTicket, 'clinic_name')) ?> · = qh_h(qh_name($currentTicket, 'doctor_name')) ?> · = qh_h(qh_t('Room', 'غرفة')) ?> = qh_h($currentTicket['doctor_room'] ?? '--') ?>
= qh_status_badge($currentTicket['status']) ?>
- Print ticket
+ = qh_h(qh_t('Print ticket', 'طباعة التذكرة')) ?>
-
Issued: = qh_format_datetime($currentTicket['created_at']) ?>
-
Language: = strtoupper(qh_h($currentTicket['language_pref'])) ?>
-
Next stop: = (int) $currentTicket['clinic_requires_vitals'] === 1 ? 'Nursing vitals / التمريض' : 'Doctor waiting / انتظار الطبيب' ?>
+
= qh_h(qh_t('Issued', 'وقت الإصدار')) ?>: = qh_format_datetime($currentTicket['created_at']) ?>
+
= qh_h(qh_t('Language', 'اللغة')) ?>: = qh_h(qh_locale_label($currentTicket['language_pref'] ?? 'en')) ?>
+
= qh_h(qh_t('Next stop', 'المحطة التالية')) ?>: = qh_h(qh_ticket_next_stop($currentTicket)) ?>
-
-
-
Today’s tickets / تذاكر اليوم
-
The latest issued tickets and where they currently are in the visit flow.
-
-
+
= qh_h(qh_t('Today’s tickets', 'تذاكر اليوم')) ?>
+
= qh_h(qh_t('Latest tickets and where they currently sit in the visit flow.', 'أحدث التذاكر وموقعها الحالي في مسار الزيارة.')) ?>
- Ticket
- Patient
- Clinic
- Status
+ = qh_h(qh_t('Ticket', 'التذكرة')) ?>
+ = qh_h(qh_t('Patient', 'المريض')) ?>
+ = qh_h(qh_t('Clinic', 'العيادة')) ?>
+ = qh_h(qh_t('Status', 'الحالة')) ?>
@@ -108,9 +107,9 @@ qh_page_start('reception', 'Reception ticket issuance', 'Reception desk ticket i
= qh_h($ticket['ticket_number']) ?>
= qh_h($ticket['patient_name']) ?>
- = qh_h($ticket['clinic_name_en'] ?? '—') ?>
+ = qh_h(qh_name($ticket, 'clinic_name')) ?>
= qh_status_badge($ticket['status']) ?>
- View
+ = qh_h(qh_t('View', 'عرض')) ?>
diff --git a/ticket.php b/ticket.php
index 55724b6..2695ecb 100644
--- a/ticket.php
+++ b/ticket.php
@@ -6,22 +6,27 @@ qh_boot();
$ticketId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$ticket = $ticketId > 0 ? qh_fetch_ticket($ticketId) : null;
-qh_page_start('home', 'Ticket detail', 'Detailed patient ticket timeline for the hospital queue workflow.');
+qh_page_start(
+ 'home',
+ qh_t('Ticket detail', 'تفاصيل التذكرة'),
+ qh_t('Detailed patient ticket timeline for the hospital queue workflow.', 'عرض تفصيلي لخط سير التذكرة في مسار طابور المستشفى.')
+);
?>
- Ticket not found.
- Return to reception and choose a valid ticket.
+ = qh_h(qh_t('Ticket not found.', 'لم يتم العثور على التذكرة.')) ?>
+ = qh_h(qh_t('Return to reception and choose a valid ticket.', 'عد إلى الاستقبال واختر تذكرة صالحة.')) ?>
@@ -30,11 +35,11 @@ qh_page_start('home', 'Ticket detail', 'Detailed patient ticket timeline for the
= qh_h($ticket['ticket_number']) ?>
= qh_h($ticket['patient_name']) ?>
-
= qh_h($ticket['clinic_name_en'] ?? '') ?> · = qh_h($ticket['doctor_name_en'] ?? '') ?> · Room = qh_h($ticket['doctor_room'] ?? '--') ?>
+
= qh_h(qh_name($ticket, 'clinic_name')) ?> · = qh_h(qh_name($ticket, 'doctor_name')) ?> · = qh_h(qh_t('Room', 'غرفة')) ?> = qh_h($ticket['doctor_room'] ?? '--') ?>
= qh_status_badge($ticket['status']) ?>
- Preferred language: = strtoupper(qh_h($ticket['language_pref'])) ?>
+ = qh_h(qh_t('Preferred language', 'اللغة المفضلة')) ?>: = qh_h(qh_locale_label($ticket['language_pref'] ?? 'en')) ?>
@@ -42,62 +47,32 @@ qh_page_start('home', 'Ticket detail', 'Detailed patient ticket timeline for the
-
Visit timeline / خط سير الزيارة
+
= qh_h(qh_t('Visit timeline', 'خط سير الزيارة')) ?>
-
-
-
-
Ticket issued / تم إصدار التذكرة
-
= qh_format_datetime($ticket['created_at']) ?>
-
-
+
= qh_h(qh_t('Ticket issued', 'تم إصدار التذكرة')) ?>
= qh_format_datetime($ticket['created_at']) ?>
-
-
-
-
Nursing vitals / العلامات الحيوية
-
= qh_h($ticket['vitals_notes'] ?: 'Waiting for nursing input.') ?>
-
-
+
= qh_h(qh_t('Nursing vitals', 'العلامات الحيوية')) ?>
= qh_h($ticket['vitals_notes'] ?: qh_t('Waiting for nursing input.', 'بانتظار إدخال التمريض.')) ?>
-
-
-
-
Ready for doctor / جاهز للطبيب
-
Assigned to = qh_h($ticket['doctor_name_en'] ?? 'Doctor') ?>, room = qh_h($ticket['doctor_room'] ?? '--') ?>
-
-
-
-
-
-
Called to room / تم النداء للغرفة
-
= qh_format_datetime($ticket['called_at']) ?>
-
-
-
-
-
-
Visit closed / إغلاق الزيارة
-
= $ticket['status'] === 'done' ? 'Completed successfully.' : ($ticket['status'] === 'no_show' ? 'Marked as no-show.' : 'Still active.') ?>
-
-
+
= qh_h(qh_t('Ready for doctor', 'جاهز للطبيب')) ?>
= qh_h(qh_t('Assigned to', 'مخصص إلى')) ?> = qh_h(qh_name($ticket, 'doctor_name', qh_t('Doctor', 'الطبيب'))) ?>، = qh_h(qh_t('room', 'الغرفة')) ?> = qh_h($ticket['doctor_room'] ?? '--') ?>
+
= qh_h(qh_t('Called to room', 'تم النداء للغرفة')) ?>
= qh_format_datetime($ticket['called_at']) ?>
+
= qh_h(qh_t('Visit closed', 'إغلاق الزيارة')) ?>
= qh_h($ticket['status'] === 'done' ? qh_t('Completed successfully.', 'تمت الزيارة بنجاح.') : ($ticket['status'] === 'no_show' ? qh_t('Marked as no-show.', 'تم تسجيل عدم الحضور.') : qh_t('Still active.', 'لا تزال نشطة.'))) ?>
-
Details / التفاصيل
+
= qh_h(qh_t('Details', 'التفاصيل')) ?>
-
Clinic = qh_h($ticket['clinic_name_en'] ?? '—') ?>
-
Doctor = qh_h($ticket['doctor_name_en'] ?? '—') ?>
-
Room = qh_h($ticket['doctor_room'] ?? '--') ?>
-
Vitals note = qh_h($ticket['vitals_notes'] ?: 'Not captured yet.') ?>
-
Last note = qh_h($ticket['display_note'] ?: '—') ?>
+
= qh_h(qh_t('Clinic', 'العيادة')) ?> = qh_h(qh_name($ticket, 'clinic_name')) ?>
+
= qh_h(qh_t('Doctor', 'الطبيب')) ?> = qh_h(qh_name($ticket, 'doctor_name')) ?>
+
= qh_h(qh_t('Room', 'الغرفة')) ?> = qh_h($ticket['doctor_room'] ?? '--') ?>
+
= qh_h(qh_t('Vitals note', 'ملاحظة العلامات')) ?> = qh_h($ticket['vitals_notes'] ?: qh_t('Not captured yet.', 'لم تُسجل بعد.')) ?>
+
= qh_h(qh_t('Last note', 'آخر ملاحظة')) ?> = qh_h(qh_ticket_last_note($ticket)) ?>