Autosave: 20260315-175807

This commit is contained in:
Flatlogic Bot 2026-03-15 17:58:07 +00:00
parent 5420bde76a
commit 4ea57c7524
7 changed files with 507 additions and 25 deletions

View File

@ -29,7 +29,7 @@ if ($method === 'GET') {
// Fetch Appointments // Fetch Appointments
$query = " $query = "
SELECT SELECT
a.id, a.start_time as start, a.end_time as end, a.reason as title, a.status, a.id, a.start_time as start, a.end_time as end, a.reason, a.status,
a.patient_id, a.doctor_id, a.patient_id, a.doctor_id,
p.name as patient_name, p.name as patient_name,
d.name_$lang as doctor_name d.name_$lang as doctor_name
@ -70,7 +70,7 @@ if ($method === 'GET') {
]; ];
} }
// Fetch Holidays // Fetch Public Holidays
$holidayQuery = "SELECT holiday_date as start, name_$lang as title FROM holidays WHERE 1=1"; $holidayQuery = "SELECT holiday_date as start, name_$lang as title FROM holidays WHERE 1=1";
$holidayParams = []; $holidayParams = [];
if ($startStr) { $holidayQuery .= " AND holiday_date >= ?"; $holidayParams[] = date('Y-m-d', strtotime($startStr)); } if ($startStr) { $holidayQuery .= " AND holiday_date >= ?"; $holidayParams[] = date('Y-m-d', strtotime($startStr)); }
@ -83,25 +83,70 @@ if ($method === 'GET') {
foreach ($holidays as $h) { foreach ($holidays as $h) {
$events[] = [ $events[] = [
'id' => 'hol_' . $h['start'], 'id' => 'hol_' . $h['start'],
'title' => 'Holiday: ' . $h['title'], 'title' => __('holiday') . ': ' . $h['title'],
'start' => $h['start'], 'start' => $h['start'] . 'T00:00:00',
'allDay' => true, 'end' => $h['start'] . 'T23:59:59',
'color' => '#ffc107', // yellow 'allDay' => false, // Set to false to cover vertical time slots
'textColor' => '#000',
'display' => 'background', 'display' => 'background',
'extendedProps' => ['type' => 'holiday'] 'backgroundColor' => '#ffc107', // Explicitly set background color
];
$events[] = [
'title' => $h['title'],
'start' => $h['start'],
'allDay' => true,
'color' => '#ffc107', 'color' => '#ffc107',
'textColor' => '#000', 'overlap' => false,
'extendedProps' => ['type' => 'holiday'] 'extendedProps' => ['type' => 'public_holiday']
]; ];
} }
// Fetch Doctor Holidays
$docHolidayQuery = "SELECT dh.*, d.name_$lang as doctor_name FROM doctor_holidays dh JOIN doctors d ON dh.doctor_id = d.id WHERE 1=1";
$docHolidayParams = [];
// Date filtering for doctor holidays (ranges)
if ($startStr && $endStr) {
$docHolidayQuery .= " AND dh.start_date <= ? AND dh.end_date >= ?";
$docHolidayParams[] = date('Y-m-d', strtotime($endStr));
$docHolidayParams[] = date('Y-m-d', strtotime($startStr));
}
if ($doctor_id) {
$docHolidayQuery .= " AND dh.doctor_id = ?";
$docHolidayParams[] = $doctor_id;
}
$stmt = $db->prepare($docHolidayQuery);
$stmt->execute($docHolidayParams);
$docHolidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($docHolidays as $dh) {
$title = $dh['doctor_name'] . ' - ' . ($dh['note'] ?: 'Holiday');
// If filtering by specific doctor, show as background to block the day clearly
// If showing all doctors, show as a block event so we know WHO is on holiday
if ($doctor_id) {
$events[] = [
'id' => 'doc_hol_' . $dh['id'],
'title' => 'Holiday',
'start' => $dh['start_date'] . 'T00:00:00',
'end' => date('Y-m-d', strtotime($dh['end_date'] . ' +1 day')) . 'T00:00:00',
'allDay' => false, // Set to false to cover vertical time slots
'display' => 'background',
'backgroundColor' => '#ffc107', // Explicitly set background color
'color' => '#ffc107',
'overlap' => false, // Prevent booking on holiday
'extendedProps' => ['type' => 'doctor_holiday']
];
} else {
$events[] = [
'id' => 'doc_hol_' . $dh['id'],
'title' => $title,
'start' => $dh['start_date'],
'end' => date('Y-m-d', strtotime($dh['end_date'] . ' +1 day')),
'allDay' => true,
'color' => '#fd7e14', // Orange
'textColor' => '#fff',
'extendedProps' => ['type' => 'doctor_holiday', 'doctor_id' => $dh['doctor_id']]
];
}
}
// Fetch Doctor Business Hours // Fetch Doctor Business Hours
if ($doctor_id) { if ($doctor_id) {
$scheduleStmt = $db->prepare("SELECT day_of_week as day, start_time as start, end_time as end FROM doctor_schedules WHERE doctor_id = ?"); $scheduleStmt = $db->prepare("SELECT day_of_week as day, start_time as start, end_time as end FROM doctor_schedules WHERE doctor_id = ?");

53
api/doctor_holidays.php Normal file
View File

@ -0,0 +1,53 @@
<?php
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../helpers.php';
header('Content-Type: application/json');
$db = db();
$method = $_SERVER['REQUEST_METHOD'];
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
if ($method === 'GET') {
$doctor_id = $_GET['doctor_id'] ?? null;
if (!$doctor_id) {
echo json_encode(['success' => false, 'error' => 'Missing doctor_id']);
exit;
}
$stmt = $db->prepare("SELECT * FROM doctor_holidays WHERE doctor_id = ? ORDER BY start_date DESC");
$stmt->execute([$doctor_id]);
$holidays = $stmt->fetchAll();
echo json_encode(['success' => true, 'holidays' => $holidays]);
exit;
}
if ($method === 'POST') {
$action = $input['action'] ?? '';
if ($action === 'create') {
$doctor_id = $input['doctor_id'] ?? null;
$start_date = $input['start_date'] ?? null;
$end_date = $input['end_date'] ?? null;
$note = $input['note'] ?? '';
if ($doctor_id && $start_date && $end_date) {
$stmt = $db->prepare("INSERT INTO doctor_holidays (doctor_id, start_date, end_date, note) VALUES (?, ?, ?, ?)");
$stmt->execute([$doctor_id, $start_date, $end_date, $note]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'error' => 'Missing fields']);
}
} elseif ($action === 'delete') {
$id = $input['id'] ?? null;
if ($id) {
$stmt = $db->prepare("DELETE FROM doctor_holidays WHERE id = ?");
$stmt->execute([$id]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'error' => 'Missing ID']);
}
}
exit;
}

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS doctor_holidays (
id INT AUTO_INCREMENT PRIMARY KEY,
doctor_id INT NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
note VARCHAR(255) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (doctor_id) REFERENCES doctors(id) ON DELETE CASCADE
);

View File

@ -7,9 +7,14 @@
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h3 class="fw-bold text-secondary"><?php echo __('appointments'); ?></h3> <h3 class="fw-bold text-secondary"><?php echo __('appointments'); ?></h3>
<button class="btn btn-primary shadow-sm" onclick="showCreateModal()"> <div>
<i class="bi bi-calendar-plus me-1"></i> <?php echo __('book_appointment'); ?> <button class="btn btn-outline-secondary shadow-sm me-2" onclick="printAppointments()">
</button> <i class="bi bi-printer me-1"></i> Print Daily List
</button>
<button class="btn btn-primary shadow-sm" onclick="showCreateModal()">
<i class="bi bi-calendar-plus me-1"></i> <?php echo __('book_appointment'); ?>
</button>
</div>
</div> </div>
<!-- Filters --> <!-- Filters -->
@ -151,7 +156,9 @@ function showCreateModal(startTime = null) {
$('#apt_doctor_id').val(document.getElementById('doctorFilter').value).trigger('change'); $('#apt_doctor_id').val(document.getElementById('doctorFilter').value).trigger('change');
} }
var modal = new bootstrap.Modal(document.getElementById('appointmentDetailsModal')); // Initialize/reset modal
var modalEl = document.getElementById('appointmentDetailsModal');
var modal = bootstrap.Modal.getOrCreateInstance(modalEl);
modal.show(); modal.show();
} }
@ -175,7 +182,9 @@ function saveAppointment() {
.then(response => response.json()) .then(response => response.json())
.then(result => { .then(result => {
if (result.success) { if (result.success) {
bootstrap.Modal.getInstance(document.getElementById('appointmentDetailsModal')).hide(); var modalEl = document.getElementById('appointmentDetailsModal');
var modal = bootstrap.Modal.getInstance(modalEl);
modal.hide();
calendar.refetchEvents(); calendar.refetchEvents();
} else { } else {
alert('Error: ' + (result.error || 'Unknown error')); alert('Error: ' + (result.error || 'Unknown error'));
@ -195,7 +204,9 @@ function deleteAppointment() {
.then(response => response.json()) .then(response => response.json())
.then(result => { .then(result => {
if (result.success) { if (result.success) {
bootstrap.Modal.getInstance(document.getElementById('appointmentDetailsModal')).hide(); var modalEl = document.getElementById('appointmentDetailsModal');
var modal = bootstrap.Modal.getInstance(modalEl);
modal.hide();
calendar.refetchEvents(); calendar.refetchEvents();
} else { } else {
alert('Error: ' + (result.error || 'Unknown error')); alert('Error: ' + (result.error || 'Unknown error'));
@ -203,6 +214,17 @@ function deleteAppointment() {
}); });
} }
function printAppointments() {
var doctorId = document.getElementById('doctorFilter').value;
// Get the date that is currently focused in the calendar
var date = calendar.getDate();
// Format as YYYY-MM-DD using local time to avoid timezone shifts
var offset = date.getTimezoneOffset() * 60000;
var dateStr = (new Date(date.getTime() - offset)).toISOString().slice(0, 10);
window.open('print_appointments.php?date=' + dateStr + '&doctor_id=' + doctorId, '_blank');
}
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar'); var calendarEl = document.getElementById('calendar');
var doctorFilter = document.getElementById('doctorFilter'); var doctorFilter = document.getElementById('doctorFilter');
@ -254,7 +276,8 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('apt_reason').value = props.reason || ''; document.getElementById('apt_reason').value = props.reason || '';
document.getElementById('btnDeleteApt').style.display = 'block'; document.getElementById('btnDeleteApt').style.display = 'block';
var modal = new bootstrap.Modal(document.getElementById('appointmentDetailsModal')); var modalEl = document.getElementById('appointmentDetailsModal');
var modal = bootstrap.Modal.getOrCreateInstance(modalEl);
modal.show(); modal.show();
} }
} }

View File

@ -73,6 +73,11 @@ if (isset($_GET['ajax_search'])) {
data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>"> data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
</button> </button>
<button class="btn btn-link text-warning py-1 px-2 border-end"
onclick="showDoctorHolidays(<?php echo $d['id']; ?>, '<?php echo htmlspecialchars($d['name_'.$lang]); ?>')"
data-bs-toggle="tooltip" title="Holidays">
<i class="bi bi-calendar-x"></i>
</button>
<button class="btn btn-link text-danger py-1 px-2" <button class="btn btn-link text-danger py-1 px-2"
onclick="showDeleteDoctorModal(<?php echo $d['id']; ?>)" onclick="showDeleteDoctorModal(<?php echo $d['id']; ?>)"
data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>"> data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>">
@ -219,6 +224,11 @@ if (isset($_GET['ajax_search'])) {
data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>"> data-bs-toggle="tooltip" title="<?php echo __('edit'); ?>">
<i class="bi bi-pencil-square"></i> <i class="bi bi-pencil-square"></i>
</button> </button>
<button class="btn btn-link text-warning py-1 px-2 border-end"
onclick="showDoctorHolidays(<?php echo $d['id']; ?>, '<?php echo htmlspecialchars($d['name_'.$lang]); ?>')"
data-bs-toggle="tooltip" title="Holidays">
<i class="bi bi-calendar-x"></i>
</button>
<button class="btn btn-link text-danger py-1 px-2" <button class="btn btn-link text-danger py-1 px-2"
onclick="showDeleteDoctorModal(<?php echo $d['id']; ?>)" onclick="showDeleteDoctorModal(<?php echo $d['id']; ?>)"
data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>"> data-bs-toggle="tooltip" title="<?php echo __('delete'); ?>">
@ -285,6 +295,56 @@ if (isset($_GET['ajax_search'])) {
</div> </div>
</div> </div>
<!-- Doctor Holidays Modal -->
<div class="modal fade" id="doctorHolidaysModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0 shadow">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold text-secondary" id="doctorHolidaysTitle">Doctor Holidays</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body pt-3">
<input type="hidden" id="holidayDoctorId">
<form id="addHolidayForm" onsubmit="addDoctorHoliday(); return false;">
<div class="row g-2 mb-3">
<div class="col-md-5">
<label class="small text-muted">Start Date</label>
<input type="date" id="holidayStartDate" class="form-control form-control-sm" required>
</div>
<div class="col-md-5">
<label class="small text-muted">End Date</label>
<input type="date" id="holidayEndDate" class="form-control form-control-sm" required>
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary btn-sm w-100">Add</button>
</div>
<div class="col-12 mt-1">
<input type="text" id="holidayNote" class="form-control form-control-sm" placeholder="Note (optional)">
</div>
</div>
</form>
<h6 class="small text-muted text-uppercase fw-bold mb-2">Current Holidays</h6>
<div class="table-responsive" style="max-height: 200px; overflow-y: auto;">
<table class="table table-sm table-bordered mb-0">
<thead class="table-light">
<tr>
<th>Start</th>
<th>End</th>
<th>Note</th>
<th style="width: 50px;"></th>
</tr>
</thead>
<tbody id="holidaysListBody">
<tr><td colspan="4" class="text-center text-muted">Loading...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const searchName = document.getElementById('doctorSearchName'); const searchName = document.getElementById('doctorSearchName');
@ -356,4 +416,106 @@ function fetchDoctors(page) {
if (tableBody) tableBody.style.opacity = '1'; if (tableBody) tableBody.style.opacity = '1';
}); });
} }
</script>
function showDoctorHolidays(doctorId, doctorName) {
document.getElementById('holidayDoctorId').value = doctorId;
document.getElementById('doctorHolidaysTitle').innerText = 'Holidays - ' + doctorName;
// Clear form
document.getElementById('holidayStartDate').value = '';
document.getElementById('holidayEndDate').value = '';
document.getElementById('holidayNote').value = '';
// Fetch holidays
fetchHolidays(doctorId);
var modal = new bootstrap.Modal(document.getElementById('doctorHolidaysModal'));
modal.show();
}
function fetchHolidays(doctorId) {
fetch('api/doctor_holidays.php?doctor_id=' + doctorId)
.then(response => response.json())
.then(data => {
const tbody = document.getElementById('holidaysListBody');
tbody.innerHTML = '';
if (data.success && data.holidays.length > 0) {
data.holidays.forEach(h => {
tbody.innerHTML += `
<tr>
<td>${h.start_date}</td>
<td>${h.end_date}</td>
<td>${h.note || '-'}</td>
<td class="text-center">
<button class="btn btn-link text-danger p-0" onclick="deleteDoctorHoliday(${h.id})">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
`;
});
} else {
tbody.innerHTML = '<tr><td colspan="4" class="text-center text-muted">No holidays found.</td></tr>';
}
})
.catch(err => console.error(err));
}
function addDoctorHoliday() {
const doctorId = document.getElementById('holidayDoctorId').value;
const start = document.getElementById('holidayStartDate').value;
const end = document.getElementById('holidayEndDate').value;
const note = document.getElementById('holidayNote').value;
if (!start || !end) return;
fetch('api/doctor_holidays.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
action: 'create',
doctor_id: doctorId,
start_date: start,
end_date: end,
note: note
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
fetchHolidays(doctorId); // Refresh list
document.getElementById('holidayStartDate').value = '';
document.getElementById('holidayEndDate').value = '';
document.getElementById('holidayNote').value = '';
} else {
alert('Error adding holiday: ' + (data.error || 'Unknown error'));
}
})
.catch(err => console.error(err));
}
function deleteDoctorHoliday(id) {
if (!confirm('Delete this holiday?')) return;
const doctorId = document.getElementById('holidayDoctorId').value;
fetch('api/doctor_holidays.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
action: 'delete',
id: id
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
fetchHolidays(doctorId); // Refresh list
} else {
alert('Error deleting holiday: ' + (data.error || 'Unknown error'));
}
})
.catch(err => console.error(err));
}
</script>

View File

@ -270,6 +270,11 @@ $translations = [
'active' => 'Active', 'active' => 'Active',
'inactive' => 'Inactive', 'inactive' => 'Inactive',
'are_you_sure_delete_service' => 'Are you sure you want to delete this service?', 'are_you_sure_delete_service' => 'Are you sure you want to delete this service?',
'appointments_list' => 'Appointments List',
'doctor_holidays' => 'Doctor Holidays',
'printed_on' => 'Printed on',
'no_appointments' => 'No appointments found for this date',
'back' => 'Back',
], ],
'ar' => [ 'ar' => [
'attachment' => 'المرفق', 'attachment' => 'المرفق',
@ -543,5 +548,10 @@ $translations = [
'active' => 'نشط', 'active' => 'نشط',
'inactive' => 'غير نشط', 'inactive' => 'غير نشط',
'are_you_sure_delete_service' => 'هل أنت متأكد أنك تريد حذف هذه الخدمة؟', 'are_you_sure_delete_service' => 'هل أنت متأكد أنك تريد حذف هذه الخدمة؟',
'appointments_list' => 'قائمة المواعيد',
'doctor_holidays' => 'إجازات الأطباء',
'printed_on' => 'طبع في',
'no_appointments' => 'لا توجد مواعيد لهذا التاريخ',
'back' => 'رجوع',
] ]
]; ];

180
print_appointments.php Normal file
View File

@ -0,0 +1,180 @@
<?php
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/helpers.php';
$lang = $_SESSION['lang'] ?? 'en';
$date = $_GET['date'] ?? date('Y-m-d');
$doctor_id = $_GET['doctor_id'] ?? '';
$db = db();
// Fetch Doctor Name if filtered
$doctor_name = '';
if ($doctor_id) {
$stmt = $db->prepare("SELECT name_$lang FROM doctors WHERE id = ?");
$stmt->execute([$doctor_id]);
$doctor_name = $stmt->fetchColumn();
}
// Fetch Holidays for the date
$holidays_on_date = [];
if ($doctor_id) {
// Specific doctor holiday check
$stmt = $db->prepare("SELECT note FROM doctor_holidays WHERE doctor_id = ? AND ? BETWEEN start_date AND end_date");
$stmt->execute([$doctor_id, $date]);
$holiday_note = $stmt->fetchColumn();
if ($holiday_note !== false) {
$holidays_on_date[] = [
'doctor_name' => $doctor_name,
'note' => $holiday_note
];
}
} else {
// All doctors holiday check
$stmt = $db->prepare("
SELECT d.name_$lang as doctor_name, h.note
FROM doctor_holidays h
JOIN doctors d ON h.doctor_id = d.id
WHERE ? BETWEEN h.start_date AND h.end_date
");
$stmt->execute([$date]);
$holidays_on_date = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// Fetch Appointments
$query = "
SELECT
a.start_time, a.end_time, a.status, a.reason,
p.name as patient_name, p.tel as patient_tel,
d.name_$lang as doctor_name
FROM appointments a
JOIN patients p ON a.patient_id = p.id
JOIN doctors d ON a.doctor_id = d.id
WHERE DATE(a.start_time) = ?
";
$params = [$date];
if ($doctor_id) {
$query .= " AND a.doctor_id = ?";
$params[] = $doctor_id;
}
$query .= " ORDER BY a.start_time ASC";
$stmt = $db->prepare($query);
$stmt->execute($params);
$appointments = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="<?php echo $lang; ?>" dir="<?php echo $lang == 'ar' ? 'rtl' : 'ltr'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo __('appointments'); ?> - <?php echo $date; ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #fff; color: #000; }
@media print {
.no-print { display: none !important; }
body { padding: 0; margin: 0; }
.container { max-width: 100% !important; }
a[href]:after { content: none !important; }
}
.header-box { border-bottom: 2px solid #000; padding-bottom: 15px; margin-bottom: 20px; }
.table thead th { background-color: #f0f0f0 !important; border-bottom: 2px solid #000; }
.table td, .table th { border: 1px solid #ddd; padding: 8px 12px; vertical-align: middle; }
.holiday-banner { background-color: #fff3cd; border: 1px solid #ffeeba; color: #856404; padding: 10px; margin-bottom: 15px; border-radius: 4px; }
@media print {
.holiday-banner { border: 1px solid #000; background-color: #eee; color: #000; }
}
</style>
</head>
<body class="p-4">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4 no-print">
<a href="javascript:window.close()" class="btn btn-outline-secondary">&larr; <?php echo __('back'); ?></a>
<button onclick="window.print()" class="btn btn-primary"><?php echo __('print'); ?></button>
</div>
<div class="header-box text-center">
<h3><?php echo __('appointments_list'); ?></h3>
<p class="mb-1"><strong><?php echo __('date'); ?>:</strong> <?php echo date('l, d F Y', strtotime($date)); ?></p>
<?php if ($doctor_name): ?>
<p class="mb-0"><strong><?php echo __('doctor'); ?>:</strong> <?php echo htmlspecialchars($doctor_name); ?></p>
<?php endif; ?>
</div>
<?php if (!empty($holidays_on_date)): ?>
<div class="holiday-banner">
<h5 class="mb-2 fw-bold">⚠️ <?php echo __('doctor_holidays'); ?></h5>
<ul class="mb-0 ps-3">
<?php foreach ($holidays_on_date as $h): ?>
<li>
<strong><?php echo htmlspecialchars($h['doctor_name']); ?>:</strong>
<?php echo htmlspecialchars($h['note'] ?: 'On Holiday'); ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if (empty($appointments)): ?>
<div class="alert alert-light text-center border p-5">
<h4><?php echo __('no_appointments'); ?></h4>
</div>
<?php else: ?>
<table class="table table-striped w-100">
<thead>
<tr>
<th width="15%"><?php echo __('time'); ?></th>
<th width="25%"><?php echo __('patient'); ?></th>
<th width="15%"><?php echo __('phone'); ?></th>
<?php if (!$doctor_id): ?>
<th width="20%"><?php echo __('doctor'); ?></th>
<?php endif; ?>
<th><?php echo __('reason'); ?> / <?php echo __('notes'); ?></th>
<th width="10%"><?php echo __('status'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($appointments as $app): ?>
<tr>
<td class="fw-bold"><?php echo date('h:i A', strtotime($app['start_time'])); ?></td>
<td><?php echo htmlspecialchars($app['patient_name']); ?></td>
<td><?php echo htmlspecialchars($app['patient_tel']); ?></td>
<?php if (!$doctor_id): ?>
<td><?php echo htmlspecialchars($app['doctor_name']); ?></td>
<?php endif; ?>
<td><?php echo htmlspecialchars($app['reason'] ?: '-'); ?></td>
<td>
<span class="badge bg-<?php
echo match($app['status']) {
'Completed' => 'success',
'Cancelled' => 'danger',
default => 'primary'
};
?> text-dark bg-opacity-10 border border-<?php
echo match($app['status']) {
'Completed' => 'success',
'Cancelled' => 'danger',
default => 'primary'
};
?>">
<?php echo $app['status']; ?>
</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<div class="mt-5 pt-3 border-top text-center text-muted small">
<p><?php echo __('printed_on'); ?> <?php echo date('d/m/Y H:i'); ?></p>
</div>
</div>
</body>
</html>