Autosave: 20260401-005145

This commit is contained in:
Flatlogic Bot 2026-04-01 00:51:46 +00:00
parent 9ef45e15f2
commit 1fb5dec73f
5 changed files with 252 additions and 35 deletions

View File

@ -15,6 +15,14 @@ $pdo->exec("CREATE TABLE IF NOT EXISTS hospital_ads (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
$pdo->exec("CREATE TABLE IF NOT EXISTS hospital_news (
id INT AUTO_INCREMENT PRIMARY KEY,
phrase VARCHAR(1000) 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') {
@ -74,6 +82,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt = $pdo->prepare("UPDATE hospital_ads SET is_active = NOT is_active WHERE id = ?");
$stmt->execute([$id]);
$message = qh_t('Video status updated.', 'تم تحديث حالة الفيديو.');
} elseif (isset($_POST['action']) && $_POST['action'] === 'add_news' && !empty($_POST['phrase'])) {
$phrase = trim($_POST['phrase']);
$stmt = $pdo->prepare("INSERT INTO hospital_news (phrase, is_active) VALUES (?, 1)");
$stmt->execute([$phrase]);
$message = qh_t('Phrase added successfully.', 'تمت إضافة العبارة بنجاح.');
} elseif (isset($_POST['action']) && $_POST['action'] === 'delete_news' && !empty($_POST['news_id'])) {
$id = (int) $_POST['news_id'];
$pdo->prepare("DELETE FROM hospital_news WHERE id = ?")->execute([$id]);
$message = qh_t('Phrase deleted successfully.', 'تم حذف العبارة بنجاح.');
} elseif (isset($_POST['action']) && $_POST['action'] === 'toggle_news_status' && !empty($_POST['news_id'])) {
$id = (int) $_POST['news_id'];
$pdo->prepare("UPDATE hospital_news SET is_active = NOT is_active WHERE id = ?")->execute([$id]);
$message = qh_t('Phrase status updated.', 'تم تحديث حالة العبارة.');
}
}
@ -81,6 +102,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt = $pdo->query("SELECT * FROM hospital_ads ORDER BY sort_order ASC, id DESC");
$ads = $stmt->fetchAll();
$stmt2 = $pdo->query("SELECT * FROM hospital_news ORDER BY sort_order ASC, id DESC");
$newsPhrases = $stmt2->fetchAll();
qh_page_start(
'admin_ads',
qh_t('Manage Advertisements', 'إدارة الإعلانات'),
@ -191,6 +215,88 @@ qh_page_start(
</div>
</div>
<!-- News Ticker Section -->
<div class="d-flex justify-content-between align-items-center mb-4 mt-5">
<div>
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('News Ticker Phrases', 'عبارات شريط الأخبار')) ?></h1>
<p class="text-muted mb-0 mt-1"><?= qh_h(qh_t('These phrases will scroll at the bottom of the display screen.', 'ستتحرك هذه العبارات في أسفل شاشة العرض.')) ?></p>
</div>
</div>
<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('Add New Phrase', 'إضافة عبارة جديدة')) ?></h5>
</div>
<div class="card-body">
<form method="post" class="row g-3">
<input type="hidden" name="action" value="add_news">
<div class="col-md-10">
<label class="form-label fw-semibold text-dark"><?= qh_h(qh_t('Phrase Text', 'نص العبارة')) ?></label>
<input type="text" name="phrase" class="form-control" placeholder="<?= qh_h(qh_t('e.g. Welcome to our hospital. Please wait for your turn.', 'مثال: أهلاً بكم في مستشفانا. يرجى انتظار دوركم.')) ?>" required>
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100"><?= qh_h(qh_t('Add', 'إضافة')) ?></button>
</div>
</form>
</div>
</div>
<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('Ticker Phrases', 'عبارات الشريط')) ?></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('Phrase', 'العبارة')) ?></th>
<th class="border-0 py-3 text-center" style="width:120px;"><?= qh_h(qh_t('Status', 'الحالة')) ?></th>
<th class="border-0 px-4 py-3 text-end" style="width:200px;"><?= qh_h(qh_t('Actions', 'الإجراءات')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($newsPhrases)): ?>
<tr>
<td colspan="3" class="text-center py-4 text-muted"><?= qh_h(qh_t('No phrases added yet.', 'لم يتم إضافة عبارات بعد.')) ?></td>
</tr>
<?php else: ?>
<?php foreach ($newsPhrases as $n): ?>
<tr>
<td class="px-4 py-3 fw-semibold text-dark">
<?= qh_h($n['phrase']) ?>
</td>
<td class="py-3 text-center">
<?php if ($n['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_news_status">
<input type="hidden" name="news_id" value="<?= (int) $n['id'] ?>">
<button type="submit" class="btn btn-sm <?= $n['is_active'] ? 'btn-outline-secondary' : 'btn-outline-success' ?>">
<?= qh_h($n['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 phrase?', 'هل أنت متأكد أنك تريد حذف هذه العبارة؟')) ?>');">
<input type="hidden" name="action" value="delete_news">
<input type="hidden" name="news_id" value="<?= (int) $n['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>

View File

@ -1259,3 +1259,55 @@ html[dir="rtl"] .hospital-detail-list dd {
size: 80mm auto;
}
}
/* News Ticker */
.news-ticker-container {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background-color: var(--accent-strong);
color: #fff;
padding: 0.75rem 0;
z-index: 1030;
overflow: hidden;
white-space: nowrap;
box-shadow: 0 -4px 12px rgba(0,0,0,0.1);
}
.news-ticker-content {
display: inline-block;
white-space: nowrap;
animation: ticker-ltr 30s linear infinite;
}
@keyframes ticker-ltr {
0% { transform: translateX(-100%); }
100% { transform: translateX(100vw); }
}
.news-ticker-item {
display: inline-block;
padding: 0 1.5rem;
font-weight: 600;
font-size: 1.25rem;
}
.news-ticker-item::after {
content: "✦";
margin-left: 3rem;
opacity: 0.6;
}
html[dir="rtl"] .news-ticker-item::after {
margin-left: 0;
margin-right: 3rem;
}
.news-ticker-item:last-child::after {
display: none;
}
body {
padding-bottom: 3.5rem;
}

View File

@ -16,6 +16,16 @@ try {
// Table might not exist yet, safe to ignore
}
$activeNews = [];
try {
$stmt = db()->query("SELECT phrase FROM hospital_news WHERE is_active = 1 ORDER BY sort_order ASC, id DESC");
if ($stmt) {
$activeNews = $stmt->fetchAll(PDO::FETCH_COLUMN);
}
} catch (Throwable $e) {
// Table might not exist yet
}
qh_page_start(
'display',
qh_t('General display board', 'لوحة العرض العامة'),
@ -23,16 +33,35 @@ qh_page_start(
);
?>
<div class="container-fluid px-3 px-lg-4 py-3" data-auto-refresh="20">
<!-- Top Header for Display Board -->
<header class="d-flex justify-content-between align-items-center mb-4 bg-white p-3 rounded shadow-sm border-0">
<div class="d-flex align-items-center gap-3">
<?php if ($logoUrl = qh_hospital_logo_url()): ?>
<img src="<?= qh_h($logoUrl) ?>" alt="<?= qh_h(qh_hospital_name()) ?>" style="height: 50px;">
<?php else: ?>
<div class="bg-primary text-white rounded d-flex align-items-center justify-content-center fs-4 fw-bold" style="width: 50px; height: 50px;">
<?= qh_h(qh_hospital_brand_initials()) ?>
</div>
<?php endif; ?>
<div>
<h1 class="h3 mb-0 fw-bold text-primary"><?= qh_h(qh_hospital_name()) ?></h1>
<?php if ($tagline = qh_hospital_tagline()): ?>
<div class="text-muted fw-semibold"><?= qh_h($tagline) ?></div>
<?php endif; ?>
</div>
</div>
<div class="d-flex align-items-center gap-3">
<div class="fs-4 fw-bold text-dark js-live-clock"><?= qh_h(date('H:i')) ?></div>
<button type="button" class="btn btn-outline-secondary btn-sm shadow-sm js-fullscreen-toggle" aria-pressed="false" data-label-enter="<?= qh_h(qh_t('Fullscreen', 'ملء الشاشة')) ?>" data-label-exit="<?= qh_h(qh_t('Exit fullscreen', 'إنهاء ملء الشاشة')) ?>"><i class="bi bi-arrows-fullscreen"></i></button>
</div>
</header>
<div class="row g-4 h-100">
<div class="col-xl-8 col-lg-7 d-flex flex-column gap-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>
<h1 class="h3 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Now Serving', 'يتم الآن النداء')) ?></h1>
</div>
<div class="d-flex align-items-center gap-3">
<div class="fs-4 fw-bold text-dark js-live-clock"><?= qh_h(date('H:i')) ?></div>
<button type="button" class="btn btn-outline-secondary btn-sm shadow-sm js-fullscreen-toggle" aria-pressed="false" data-label-enter="<?= qh_h(qh_t('Fullscreen', 'ملء الشاشة')) ?>" data-label-exit="<?= qh_h(qh_t('Exit fullscreen', 'إنهاء ملء الشاشة')) ?>"><i class="bi bi-arrows-fullscreen"></i></button>
<h2 class="h4 mb-0 text-gray-800 fw-bold"><?= qh_h(qh_t('Now Serving', 'يتم الآن النداء')) ?></h2>
</div>
</div>
@ -70,28 +99,27 @@ qh_page_start(
<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('Queue by Clinic', 'الطابور حسب العيادة')) ?></h5>
<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): ?>
<tr>
<td class="fw-semibold px-4 py-3 text-dark"><?= qh_h(qh_name($row)) ?></td>
<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>
<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>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="card-body p-4 bg-light">
<div class="row g-3">
<?php foreach ($queueOverview as $row): ?>
<div class="col-6 col-lg-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-3 d-flex flex-column justify-content-center">
<h6 class="fw-bold text-primary text-center mb-3 text-truncate" title="<?= qh_h(qh_name($row)) ?>"><?= qh_h(qh_name($row)) ?></h6>
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="small text-muted fw-semibold"><?= qh_h(qh_t('Vitals Wait', 'غرفة المعاينة الأولية')) ?></span>
<span class="badge bg-warning text-dark rounded-pill px-2 fs-6"><?= qh_h((string) $row['vitals_waiting']) ?></span>
</div>
<div class="d-flex justify-content-between align-items-center">
<span class="small text-muted fw-semibold"><?= qh_h(qh_t('Doctor Wait', 'انتظار الطبيب')) ?></span>
<span class="badge bg-info text-dark rounded-pill px-2 fs-6"><?= qh_h((string) $row['doctor_waiting']) ?></span>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
@ -107,9 +135,23 @@ qh_page_start(
<script>
document.addEventListener('DOMContentLoaded', function() {
const videos = <?= json_encode($activeVideos, JSON_UNESCAPED_SLASHES) ?>;
let currentIdx = 0;
const player = document.getElementById('adsVideoPlayer');
if (videos.length === 0) return;
let currentIdx = parseInt(sessionStorage.getItem('adsVideoIndex') || '0', 10);
let currentTime = parseFloat(sessionStorage.getItem('adsVideoTime') || '0');
if (isNaN(currentIdx) || currentIdx < 0 || currentIdx >= videos.length) {
currentIdx = 0;
currentTime = 0;
}
player.src = videos[currentIdx];
player.currentTime = isNaN(currentTime) ? 0 : currentTime;
player.play().catch(function(e) { console.error("Error playing video:", e); });
if (videos.length > 1) {
player.addEventListener('ended', function() {
currentIdx = (currentIdx + 1) % videos.length;
@ -117,9 +159,13 @@ qh_page_start(
player.play().catch(function(e) { console.error("Error playing video:", e); });
});
} else {
// If only one video, loop it
player.loop = true;
}
window.addEventListener('beforeunload', function() {
sessionStorage.setItem('adsVideoIndex', currentIdx);
sessionStorage.setItem('adsVideoTime', player.currentTime);
});
});
</script>
</div>
@ -154,4 +200,15 @@ qh_page_start(
</div>
</div>
</div>
<?php if (!empty($activeNews)): ?>
<div class="news-ticker-container">
<div class="news-ticker-content">
<?php foreach ($activeNews as $news): ?>
<span class="news-ticker-item"><?= qh_h($news) ?></span>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php qh_page_end(); ?>

View File

@ -85,16 +85,18 @@ qh_page_start(
<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">
<form method="post" class="d-inline-flex gap-2 flex-wrap justify-content-end">
<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 else: ?>
<button class="btn btn-sm btn-warning shadow-sm" type="submit" name="action" value="call_ticket"><?= qh_h(qh_t('Recall', 'إعادة النداء')) ?></button>
<?php endif; ?>
<button class="btn btn-sm btn-outline-danger shadow-sm bg-white" type="submit" name="action" value="mark_no_show"><?= qh_h(qh_t('Not Show', 'غائب')) ?></button>
<button class="btn btn-sm btn-success shadow-sm" type="submit" name="action" value="complete_ticket"><?= qh_h(qh_t('Served', 'تمت الخدمة')) ?></button>
</form>
</td>
</tr>

View File

@ -968,7 +968,7 @@ function qh_admin_handle_request(): void
}
$action = trim((string) ($_POST['action'] ?? ''));
if ($action === '' || in_array($action, ['add_video', 'delete_video', 'toggle_status'])) {
if ($action === '' || in_array($action, ['add_video', 'delete_video', 'toggle_status', 'add_news', 'delete_news', 'toggle_news_status'])) {
return;
}