exec($sql); } function qh_seed_demo_data(): void { $pdo = db(); $clinicCount = (int) $pdo->query("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'clinic'")->fetchColumn(); if ($clinicCount === 0) { $insertClinic = $pdo->prepare( 'INSERT INTO hospital_queue_records (item_type, code, name_en, name_ar, requires_vitals, sort_order, status) VALUES (\'clinic\', :code, :name_en, :name_ar, :requires_vitals, :sort_order, \'active\')' ); $clinics = [ ['code' => 'GEN', 'name_en' => 'General Medicine', 'name_ar' => 'الطب العام', 'requires_vitals' => 1, 'sort_order' => 10], ['code' => 'PED', 'name_en' => 'Pediatrics', 'name_ar' => 'طب الأطفال', 'requires_vitals' => 0, 'sort_order' => 20], ['code' => 'CAR', 'name_en' => 'Cardiology', 'name_ar' => 'أمراض القلب', 'requires_vitals' => 1, 'sort_order' => 30], ]; foreach ($clinics as $clinic) { $insertClinic->execute($clinic); } } $clinicMap = []; foreach (qh_fetch_clinics() as $clinic) { $clinicMap[$clinic['code']] = $clinic['id']; } $doctorCount = (int) $pdo->query("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'doctor'")->fetchColumn(); if ($doctorCount === 0 && $clinicMap !== []) { $insertDoctor = $pdo->prepare( 'INSERT INTO hospital_queue_records (item_type, name_en, name_ar, clinic_id, room_number, sort_order, status) VALUES (\'doctor\', :name_en, :name_ar, :clinic_id, :room_number, :sort_order, \'active\')' ); $doctors = [ ['name_en' => 'Dr. Sarah Malik', 'name_ar' => 'د. سارة مالك', 'clinic_id' => $clinicMap['GEN'] ?? null, 'room_number' => '201', 'sort_order' => 10], ['name_en' => 'Dr. Omar Nasser', 'name_ar' => 'د. عمر ناصر', 'clinic_id' => $clinicMap['GEN'] ?? null, 'room_number' => '202', 'sort_order' => 20], ['name_en' => 'Dr. Lina Haddad', 'name_ar' => 'د. لينا حداد', 'clinic_id' => $clinicMap['PED'] ?? null, 'room_number' => '103', 'sort_order' => 30], ['name_en' => 'Dr. Ahmad Kareem', 'name_ar' => 'د. أحمد كريم', 'clinic_id' => $clinicMap['CAR'] ?? null, 'room_number' => '305', 'sort_order' => 40], ]; foreach ($doctors as $doctor) { if ($doctor['clinic_id']) { $insertDoctor->execute($doctor); } } } $ticketCount = (int) $pdo->query("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket'")->fetchColumn(); if ($ticketCount === 0) { $doctors = qh_fetch_doctors(); $doctorByClinic = []; foreach ($doctors as $doctor) { $doctorByClinic[$doctor['clinic_id']][] = $doctor; } foreach (qh_fetch_clinics() as $clinic) { if (empty($doctorByClinic[$clinic['id']])) { continue; } $assignedDoctor = $doctorByClinic[$clinic['id']][0]; qh_create_ticket( $clinic['name_en'] === 'General Medicine' ? 'Maha Ali' : 'Yousef Karim', (int) $clinic['id'], (int) $assignedDoctor['id'], $clinic['name_en'] === 'General Medicine' ? 'ar' : 'en', $clinic['requires_vitals'] ? 'waiting_vitals' : 'ready_for_doctor' ); if ($clinic['name_en'] !== 'General Medicine') { break; } } } } function qh_h(?string $value): string { return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8'); } function qh_project_name(string $fallback = 'Hospital Queue'): string { $candidate = trim((string) ($_SERVER['PROJECT_NAME'] ?? $_SERVER['PROJECT_TITLE'] ?? '')); return $candidate !== '' ? $candidate : $fallback; } function qh_project_description(string $fallback = 'Bilingual hospital queue workflow for reception, nursing, doctors, and the public display.'): string { $candidate = trim((string) ($_SERVER['PROJECT_DESCRIPTION'] ?? '')); return $candidate !== '' ? $candidate : $fallback; } function qh_asset_version(string $relativePath): int { $fullPath = __DIR__ . '/' . ltrim($relativePath, '/'); return is_file($fullPath) ? (int) filemtime($fullPath) : time(); } 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', $tag, qh_h($en), qh_h($ar) ); } function qh_page_start(string $activePage, string $pageTitle, string $metaDescription = ''): void { $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? qh_project_description(); $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; $fullTitle = $pageTitle . ' · ' . qh_project_name(); $description = $metaDescription !== '' ? $metaDescription : qh_project_description(); $bodyClass = 'page-' . preg_replace('/[^a-z0-9\-]/i', '-', $activePage); $assetVersionCss = qh_asset_version('assets/css/custom.css'); $assetVersionJs = qh_asset_version('assets/js/main.js'); echo ''; echo ''; echo ''; echo ' '; echo ' '; echo ' ' . qh_h($fullTitle) . ''; echo ' '; if ($projectDescription) { echo ' '; echo ' '; } if ($projectImageUrl) { echo ' '; echo ' '; } echo ' '; echo ' '; echo ' '; echo ''; echo ''; if ($activePage !== 'display') { qh_render_nav($activePage); } echo '
'; qh_render_flash(); } function qh_page_end(): void { $assetVersionJs = qh_asset_version('assets/js/main.js'); echo '
'; echo ''; echo ''; echo ''; } 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', 'الشاشة العامة')], ]; echo '
'; echo ' '; echo '
'; } function qh_set_flash(string $type, string $message): void { $_SESSION['flash'] = ['type' => $type, 'message' => $message]; } function qh_render_flash(): void { if (empty($_SESSION['flash']) || !is_array($_SESSION['flash'])) { return; } $flash = $_SESSION['flash']; unset($_SESSION['flash']); $typeMap = [ 'success' => 'success', 'danger' => 'danger', 'warning' => 'warning', 'info' => 'primary', ]; $toastType = $typeMap[$flash['type']] ?? 'primary'; echo '
'; echo '
'; echo '
'; echo '
' . qh_h((string) $flash['message']) . '
'; echo ' '; echo '
'; echo '
'; echo '
'; } function qh_redirect(string $location): void { 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"); return $stmt->fetchAll(); } function qh_fetch_doctors(?int $clinicId = null): array { if ($clinicId) { $stmt = db()->prepare( "SELECT d.*, c.name_en AS clinic_name_en, c.name_ar AS clinic_name_ar, c.code AS clinic_code FROM hospital_queue_records d LEFT JOIN hospital_queue_records c ON c.id = d.clinic_id AND c.item_type = 'clinic' WHERE d.item_type = 'doctor' AND d.clinic_id = :clinic_id ORDER BY d.sort_order ASC, d.name_en ASC" ); $stmt->execute(['clinic_id' => $clinicId]); return $stmt->fetchAll(); } $stmt = db()->query( "SELECT d.*, c.name_en AS clinic_name_en, c.name_ar AS clinic_name_ar, c.code AS clinic_code FROM hospital_queue_records d LEFT JOIN hospital_queue_records c ON c.id = d.clinic_id AND c.item_type = 'clinic' WHERE d.item_type = 'doctor' ORDER BY c.sort_order ASC, d.sort_order ASC, d.name_en ASC" ); return $stmt->fetchAll(); } function qh_fetch_ticket(int $ticketId): ?array { $stmt = db()->prepare( "SELECT t.*, c.name_en AS clinic_name_en, c.name_ar AS clinic_name_ar, c.code AS clinic_code, c.requires_vitals AS clinic_requires_vitals, d.name_en AS doctor_name_en, d.name_ar AS doctor_name_ar, d.room_number AS doctor_room FROM hospital_queue_records t LEFT JOIN hospital_queue_records c ON c.id = t.clinic_id AND c.item_type = 'clinic' LEFT JOIN hospital_queue_records d ON d.id = t.doctor_id AND d.item_type = 'doctor' WHERE t.item_type = 'ticket' AND t.id = :ticket_id LIMIT 1" ); $stmt->execute(['ticket_id' => $ticketId]); $ticket = $stmt->fetch(); return $ticket ?: null; } function qh_fetch_tickets(array $statuses = [], ?int $doctorId = null, ?int $limit = null): array { $sql = "SELECT t.*, c.name_en AS clinic_name_en, c.name_ar AS clinic_name_ar, c.code AS clinic_code, c.requires_vitals AS clinic_requires_vitals, d.name_en AS doctor_name_en, d.name_ar AS doctor_name_ar, d.room_number AS doctor_room FROM hospital_queue_records t LEFT JOIN hospital_queue_records c ON c.id = t.clinic_id AND c.item_type = 'clinic' LEFT JOIN hospital_queue_records d ON d.id = t.doctor_id AND d.item_type = 'doctor' WHERE t.item_type = 'ticket'"; $params = []; if ($statuses !== []) { $statusPlaceholders = []; foreach ($statuses as $index => $status) { $key = 'status_' . $index; $statusPlaceholders[] = ':' . $key; $params[$key] = $status; } $sql .= ' AND t.status IN (' . implode(', ', $statusPlaceholders) . ')'; } if ($doctorId) { $sql .= ' AND t.doctor_id = :doctor_id'; $params['doctor_id'] = $doctorId; } $sql .= ' ORDER BY COALESCE(t.called_at, t.created_at) DESC, t.id DESC'; if ($limit) { $sql .= ' LIMIT ' . (int) $limit; } $stmt = db()->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(); } function qh_queue_overview(): array { $sql = "SELECT c.id, c.name_en, c.name_ar, c.code, SUM(CASE WHEN t.status = 'waiting_vitals' THEN 1 ELSE 0 END) AS vitals_waiting, SUM(CASE WHEN t.status = 'ready_for_doctor' THEN 1 ELSE 0 END) AS doctor_waiting, SUM(CASE WHEN t.status IN ('called', 'in_progress') THEN 1 ELSE 0 END) AS active_calls, COUNT(t.id) AS total_today FROM hospital_queue_records c LEFT JOIN hospital_queue_records t ON t.clinic_id = c.id AND t.item_type = 'ticket' AND DATE(t.created_at) = CURDATE() WHERE c.item_type = 'clinic' GROUP BY c.id, c.name_en, c.name_ar, c.code ORDER BY c.sort_order ASC, c.name_en ASC"; return db()->query($sql)->fetchAll(); } function qh_dashboard_stats(): array { $pdo = db(); return [ 'issued_today' => (int) $pdo->query("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket' AND DATE(created_at) = CURDATE()")->fetchColumn(), 'waiting_vitals' => (int) $pdo->query("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket' AND status = 'waiting_vitals'")->fetchColumn(), 'ready_for_doctor' => (int) $pdo->query("SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket' AND status = 'ready_for_doctor'")->fetchColumn(), 'active_rooms' => (int) $pdo->query("SELECT COUNT(DISTINCT doctor_id) FROM hospital_queue_records WHERE item_type = 'ticket' AND status IN ('called', 'in_progress') AND doctor_id IS NOT NULL")->fetchColumn(), ]; } function qh_create_ticket(string $patientName, int $clinicId, int $doctorId, string $languagePref = 'en', ?string $forcedStatus = null): int { $pdo = db(); $clinic = qh_fetch_clinic($clinicId); if (!$clinic) { throw new RuntimeException('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.'); } $ticketNumber = qh_generate_ticket_number($clinic['code']); $status = $forcedStatus ?: ((int) $clinic['requires_vitals'] === 1 ? 'waiting_vitals' : 'ready_for_doctor'); $stmt = $pdo->prepare( "INSERT INTO hospital_queue_records (item_type, clinic_id, doctor_id, patient_name, language_pref, ticket_number, status, display_note) VALUES ('ticket', :clinic_id, :doctor_id, :patient_name, :language_pref, :ticket_number, :status, :display_note)" ); $stmt->execute([ 'clinic_id' => $clinicId, 'doctor_id' => $doctorId, 'patient_name' => $patientName, 'language_pref' => in_array($languagePref, ['en', 'ar'], true) ? $languagePref : 'en', 'ticket_number' => $ticketNumber, 'status' => $status, 'display_note' => $status === 'waiting_vitals' ? 'Proceed to nursing vitals first.' : 'Wait for your doctor call on the public screen.', ]); return (int) $pdo->lastInsertId(); } function qh_generate_ticket_number(string $clinicCode): string { $prefix = strtoupper(substr(preg_replace('/[^A-Z0-9]/i', '', $clinicCode), 0, 3)); $stmt = db()->prepare( "SELECT COUNT(*) FROM hospital_queue_records WHERE item_type = 'ticket' AND ticket_number LIKE :prefix" ); $stmt->execute(['prefix' => $prefix . '-%']); $next = ((int) $stmt->fetchColumn()) + 1; return $prefix . '-' . str_pad((string) $next, 3, '0', STR_PAD_LEFT); } function qh_fetch_clinic(int $clinicId): ?array { $stmt = db()->prepare("SELECT * FROM hospital_queue_records WHERE item_type = 'clinic' AND id = :id LIMIT 1"); $stmt->execute(['id' => $clinicId]); $row = $stmt->fetch(); return $row ?: null; } function qh_fetch_doctor(int $doctorId): ?array { $stmt = db()->prepare("SELECT * FROM hospital_queue_records WHERE item_type = 'doctor' AND id = :id LIMIT 1"); $stmt->execute(['id' => $doctorId]); $row = $stmt->fetch(); return $row ?: null; } function qh_status_meta(string $status): array { $map = [ 'waiting_vitals' => ['class' => 'warning', 'en' => 'Waiting for vitals', 'ar' => 'بانتظار العلامات الحيوية'], 'ready_for_doctor' => ['class' => 'info', 'en' => 'Ready for doctor', 'ar' => 'جاهز للطبيب'], 'called' => ['class' => 'primary', 'en' => 'Called', 'ar' => 'تم النداء'], 'in_progress' => ['class' => 'secondary', 'en' => 'In consultation', 'ar' => 'داخل العيادة'], 'done' => ['class' => 'success', 'en' => 'Completed', 'ar' => 'مكتمل'], 'no_show' => ['class' => 'danger', 'en' => 'No-show', 'ar' => 'لم يحضر'], ]; return $map[$status] ?? ['class' => 'light', 'en' => ucfirst(str_replace('_', ' ', $status)), 'ar' => $status]; } function qh_status_badge(string $status): string { $meta = qh_status_meta($status); return '' . qh_label($meta['en'], $meta['ar']) . ''; } function qh_call_message(array $ticket): array { $ticketNumber = $ticket['ticket_number'] ?? '---'; $doctorNameEn = $ticket['doctor_name_en'] ?? 'Doctor'; $doctorNameAr = $ticket['doctor_name_ar'] ?? 'الطبيب'; $room = $ticket['doctor_room'] ?? '--'; return [ 'en' => sprintf('Ticket %s, please proceed to room %s for %s.', $ticketNumber, $room, $doctorNameEn), 'ar' => sprintf('رقم التذكرة %s، يرجى التوجه إلى الغرفة %s إلى %s.', $ticketNumber, $room, $doctorNameAr), ]; } function qh_format_datetime(?string $value): string { if (!$value) { return '—'; } $timestamp = strtotime($value); return $timestamp ? date('M d, Y H:i', $timestamp) : '—'; } function qh_require_post(): void { if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { qh_set_flash('danger', 'Invalid request method.'); qh_redirect('index.php'); } } function qh_admin_handle_request(): void { if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { return; } $action = $_POST['action'] ?? ''; $pdo = db(); try { if ($action === 'add_clinic') { $code = strtoupper(trim((string) ($_POST['code'] ?? ''))); $nameEn = trim((string) ($_POST['name_en'] ?? '')); $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.'); } $stmt = $pdo->prepare( "INSERT INTO hospital_queue_records (item_type, code, name_en, name_ar, requires_vitals, sort_order, status) VALUES ('clinic', :code, :name_en, :name_ar, :requires_vitals, :sort_order, 'active')" ); $stmt->execute([ 'code' => substr($code, 0, 10), 'name_en' => $nameEn, 'name_ar' => $nameAr, 'requires_vitals' => $requiresVitals, 'sort_order' => (int) ($_POST['sort_order'] ?? 50), ]); qh_set_flash('success', 'Clinic saved successfully.'); } elseif ($action === 'update_clinic') { $clinicId = (int) ($_POST['clinic_id'] ?? 0); $stmt = $pdo->prepare( "UPDATE hospital_queue_records SET requires_vitals = :requires_vitals, sort_order = :sort_order WHERE item_type = 'clinic' AND id = :clinic_id" ); $stmt->execute([ 'requires_vitals' => isset($_POST['requires_vitals']) ? 1 : 0, 'sort_order' => (int) ($_POST['sort_order'] ?? 50), 'clinic_id' => $clinicId, ]); qh_set_flash('success', 'Clinic settings updated.'); } 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.'); } $stmt = $pdo->prepare( "INSERT INTO hospital_queue_records (item_type, name_en, name_ar, clinic_id, room_number, sort_order, status) VALUES ('doctor', :name_en, :name_ar, :clinic_id, :room_number, :sort_order, 'active')" ); $stmt->execute([ 'name_en' => $nameEn, 'name_ar' => $nameAr, 'clinic_id' => $clinicId, 'room_number' => $roomNumber, 'sort_order' => (int) ($_POST['sort_order'] ?? 50), ]); qh_set_flash('success', 'Doctor profile saved.'); } elseif ($action === 'update_doctor') { $doctorId = (int) ($_POST['doctor_id'] ?? 0); $clinicId = (int) ($_POST['clinic_id'] ?? 0); $roomNumber = trim((string) ($_POST['room_number'] ?? '')); $stmt = $pdo->prepare( "UPDATE hospital_queue_records SET clinic_id = :clinic_id, room_number = :room_number, sort_order = :sort_order WHERE item_type = 'doctor' AND id = :doctor_id" ); $stmt->execute([ 'clinic_id' => $clinicId, 'room_number' => $roomNumber, 'sort_order' => (int) ($_POST['sort_order'] ?? 50), 'doctor_id' => $doctorId, ]); qh_set_flash('success', 'Doctor assignment updated.'); } } catch (Throwable $exception) { qh_set_flash('danger', $exception->getMessage()); } qh_redirect('admin.php'); } function qh_reception_handle_request(): void { if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { return; } try { $patientName = trim((string) ($_POST['patient_name'] ?? '')); $clinicId = (int) ($_POST['clinic_id'] ?? 0); $doctorId = (int) ($_POST['doctor_id'] ?? 0); $languagePref = trim((string) ($_POST['language_pref'] ?? 'en')); if ($patientName === '' || $clinicId <= 0 || $doctorId <= 0) { throw new InvalidArgumentException('Please complete patient name, clinic, and doctor.'); } $ticketId = qh_create_ticket($patientName, $clinicId, $doctorId, $languagePref); qh_set_flash('success', 'Ticket issued successfully.'); qh_redirect('reception.php?ticket_id=' . $ticketId); } catch (Throwable $exception) { qh_set_flash('danger', $exception->getMessage()); qh_redirect('reception.php'); } } function qh_nursing_handle_request(): void { if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { return; } try { $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.'); } $stmt = db()->prepare( "UPDATE hospital_queue_records SET vitals_notes = :vitals_notes, status = 'ready_for_doctor', display_note = 'Vitals completed. Wait for doctor call.' WHERE item_type = 'ticket' AND id = :ticket_id AND status = 'waiting_vitals'" ); $stmt->execute([ 'vitals_notes' => $vitalsNotes, 'ticket_id' => $ticketId, ]); qh_set_flash('success', 'Vitals captured and patient moved to doctor queue.'); } catch (Throwable $exception) { qh_set_flash('danger', $exception->getMessage()); } qh_redirect('nursing.php'); } function qh_doctor_handle_request(): void { if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { return; } try { $ticketId = (int) ($_POST['ticket_id'] ?? 0); $doctorId = (int) ($_POST['doctor_id'] ?? 0); $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.'); } if ($action === 'call_ticket') { $message = qh_call_message($ticket); $stmt = db()->prepare( "UPDATE hospital_queue_records SET status = 'called', called_at = NOW(), display_note = :display_note WHERE item_type = 'ticket' AND id = :ticket_id" ); $stmt->execute([ 'display_note' => $message['en'], 'ticket_id' => $ticketId, ]); qh_set_flash('success', 'Patient call pushed to the public display.'); } elseif ($action === 'start_visit') { $stmt = db()->prepare( "UPDATE hospital_queue_records SET status = 'in_progress', display_note = 'Patient is now in consultation.' WHERE item_type = 'ticket' AND id = :ticket_id" ); $stmt->execute(['ticket_id' => $ticketId]); qh_set_flash('success', 'Consultation started.'); } elseif ($action === 'complete_ticket') { $stmt = db()->prepare( "UPDATE hospital_queue_records SET status = 'done', served_at = NOW(), display_note = 'Visit completed.' WHERE item_type = 'ticket' AND id = :ticket_id" ); $stmt->execute(['ticket_id' => $ticketId]); qh_set_flash('success', 'Visit marked as completed.'); } elseif ($action === 'mark_no_show') { $stmt = db()->prepare( "UPDATE hospital_queue_records SET status = 'no_show', served_at = NOW(), display_note = 'Marked as no-show.' WHERE item_type = 'ticket' AND id = :ticket_id" ); $stmt->execute(['ticket_id' => $ticketId]); qh_set_flash('warning', 'Patient marked as no-show.'); } qh_redirect('doctor.php?doctor_id=' . $doctorId); } catch (Throwable $exception) { qh_set_flash('danger', $exception->getMessage()); qh_redirect('doctor.php'); } }