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%1$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');
}
}