Autosave: 20260401-005145
This commit is contained in:
parent
9ef45e15f2
commit
1fb5dec73f
106
admin_ads.php
106
admin_ads.php
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
113
display.php
113
display.php
@ -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(); ?>
|
||||
14
doctor.php
14
doctor.php
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user