updating queuing system

This commit is contained in:
Flatlogic Bot 2026-03-28 10:57:32 +00:00
parent e150954bb6
commit e09626b316
13 changed files with 358 additions and 100 deletions

View File

@ -9,20 +9,36 @@ try {
switch ($action) {
case 'search':
$q = $_GET['q'] ?? '';
// Allow empty search to return nothing or some default?
// Select2 usually sends a query.
if (empty($q)) {
echo json_encode([]);
echo json_encode(['results' => []]);
exit;
}
// Search by name or phone
$sql = "SELECT id, name_en as name, phone FROM patients WHERE name_en LIKE ? OR name_ar LIKE ? OR phone LIKE ? LIMIT 20";
// Search by name, phone or id (patient number)
$sql = "SELECT id, name, phone, civil_id FROM patients WHERE name LIKE ? OR phone LIKE ? OR civil_id LIKE ? OR id = ? LIMIT 20";
$stmt = $pdo->prepare($sql);
$term = "%$q%";
$stmt->execute([$term, $term, $term]);
$id_term = intval($q); // for exact match on patient number
$stmt->execute([$term, $term, $term, $id_term]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($results);
// Format results for select2
$formatted_results = array_map(function($p) {
$patient_number = sprintf('%06d', $p['id']);
$display_text = $patient_number . ' - ' . $p['name'];
if (!empty($p['phone'])) {
$display_text .= ' - ' . $p['phone'];
}
return [
'id' => $p['id'],
'text' => $display_text,
'name' => $p['name'],
'phone' => $p['phone']
];
}, $results);
echo json_encode(['results' => $formatted_results]);
break;
default:
@ -32,4 +48,4 @@ try {
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
}

View File

@ -74,7 +74,7 @@ try {
}
$sql = "
SELECT q.*,
SELECT q.*, td.name_$lang as target_department_name,
p.name as patient_name,
d.name_$lang as doctor_name,
d.name_en as doctor_name_en,
@ -86,6 +86,7 @@ try {
FROM patient_queue q
JOIN patients p ON q.patient_id = p.id
JOIN departments dept ON q.department_id = dept.id
LEFT JOIN departments td ON q.target_department_id = td.id
LEFT JOIN employees d ON q.doctor_id = d.id
$where
ORDER BY
@ -138,6 +139,57 @@ try {
exit;
}
// --- TRANSFER TOKEN ---
if ($action === 'transfer') {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
throw new Exception('Invalid request method');
}
$queue_id = $_POST['queue_id'] ?? null;
$new_department_id = $_POST['department_id'] ?? null;
$new_doctor_id = $_POST['doctor_id'] ?? null;
if (!$queue_id || !$new_department_id) {
throw new Exception('Queue ID and Target Department are required');
}
// Get current queue token
$stmt = $db->prepare("SELECT patient_id FROM patient_queue WHERE id = ?");
$stmt->execute([$queue_id]);
$current = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$current) {
throw new Exception('Queue token not found');
}
// Complete the old token
$stmt = $db->prepare("UPDATE patient_queue SET status = 'completed', updated_at = NOW() WHERE id = ?");
$stmt->execute([$queue_id]);
// Create new token
$today = date('Y-m-d');
$stmt = $db->prepare("
SELECT MAX(token_number)
FROM patient_queue
WHERE department_id = ?
AND DATE(created_at) = ?
");
$stmt->execute([$new_department_id, $today]);
$max_token = $stmt->fetchColumn();
$next_token = ($max_token) ? $max_token + 1 : 1;
$stmt = $db->prepare("
INSERT INTO patient_queue (patient_id, department_id, doctor_id, token_number, status, created_at)
VALUES (?, ?, ?, ?, 'waiting', NOW())
");
$stmt->execute([$current['patient_id'], $new_department_id, $new_doctor_id ?: null, $next_token]);
$new_queue_id = $db->lastInsertId();
echo json_encode(['success' => true, 'message' => 'Token transferred', 'token_number' => $next_token, 'new_queue_id' => $new_queue_id]);
exit;
}
// --- SUMMARY ---
if ($action === 'summary') {
$today = date('Y-m-d');
@ -160,6 +212,7 @@ try {
SUM(CASE WHEN q.status = 'completed' THEN 1 ELSE 0 END) as completed
FROM patient_queue q
JOIN departments dept ON q.department_id = dept.id
LEFT JOIN departments td ON q.target_department_id = td.id
$where
GROUP BY dept.id
";

View File

@ -0,0 +1,3 @@
ALTER TABLE departments ADD COLUMN requires_vitals TINYINT(1) DEFAULT 0;
ALTER TABLE departments ADD COLUMN is_vitals_room TINYINT(1) DEFAULT 0;
ALTER TABLE patient_queue ADD COLUMN target_department_id INT(11) DEFAULT NULL;

View File

@ -1,77 +0,0 @@
<?php
require_once 'db/config.php';
$db = db();
echo "<h1>Applying Database Fixes...</h1>";
try {
// 1. Add columns if missing
try {
$db->exec("ALTER TABLE appointments ADD COLUMN start_time DATETIME NULL");
echo "Added start_time column.<br>";
} catch (PDOException $e) {
echo "start_time check: " . $e->getMessage() . "<br>";
}
try {
$db->exec("ALTER TABLE appointments ADD COLUMN end_time DATETIME NULL");
echo "Added end_time column.<br>";
} catch (PDOException $e) {
echo "end_time check: " . $e->getMessage() . "<br>";
}
// 2. Migrate data
// Check if appointment_date exists before trying to use it
try {
$stmt = $db->query("SHOW COLUMNS FROM appointments LIKE 'appointment_date'");
if ($stmt->fetch()) {
$db->exec("UPDATE appointments SET start_time = appointment_date WHERE start_time IS NULL");
echo "Migrated data from appointment_date to start_time.<br>";
} else {
echo "Column 'appointment_date' does not exist, skipping data migration.<br>";
}
} catch (PDOException $e) {
echo "Data migration check failed: " . $e->getMessage() . "<br>";
}
// Populate end_time based on start_time
try {
$db->exec("UPDATE appointments SET end_time = DATE_ADD(start_time, INTERVAL 30 MINUTE) WHERE end_time IS NULL AND start_time IS NOT NULL");
echo "Populated end_time based on start_time.<br>";
} catch (PDOException $e) {
echo "Could not populate end_time: " . $e->getMessage() . "<br>";
}
// 3. Set NOT NULL constraint
try {
// Only if start_time has values (or table is empty)
$db->exec("ALTER TABLE appointments MODIFY COLUMN start_time DATETIME NOT NULL");
echo "Set start_time to NOT NULL.<br>";
} catch (PDOException $e) {
echo "Could not set start_time to NOT NULL (maybe some rows are NULL?): " . $e->getMessage() . "<br>";
}
try {
$db->exec("ALTER TABLE appointments MODIFY COLUMN end_time DATETIME NOT NULL");
echo "Set end_time to NOT NULL.<br>";
} catch (PDOException $e) {
echo "Could not set end_time to NOT NULL: " . $e->getMessage() . "<br>";
}
// 4. Ensure other columns exist (like nurse_id, visit_type, etc. from previous migrations)
try {
$db->exec("ALTER TABLE appointments ADD COLUMN nurse_id INT NULL");
} catch (Exception $e) {}
try {
$db->exec("ALTER TABLE appointments ADD COLUMN visit_type ENUM('Clinic', 'Home') DEFAULT 'Clinic'");
} catch (Exception $e) {}
try {
$db->exec("ALTER TABLE appointments ADD COLUMN address TEXT NULL");
} catch (Exception $e) {}
echo "<h2>Fix completed successfully.</h2>";
echo "<p><a href='dashboard.php' class='btn btn-primary'>Go to Dashboard</a></p>";
} catch (PDOException $e) {
echo "<h2>Fatal Error during fix:</h2> " . $e->getMessage();
}

View File

@ -152,9 +152,11 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$name_ar = $_POST['name_ar'] ?? '';
$show_in_queue = isset($_POST['show_in_queue']) ? 1 : 0;
$active = isset($_POST['active']) ? 1 : 0;
$requires_vitals = isset($_POST['requires_vitals']) ? 1 : 0;
$is_vitals_room = isset($_POST['is_vitals_room']) ? 1 : 0;
if ($name_en && $name_ar) {
$stmt = $db->prepare("INSERT INTO departments (name_en, name_ar, show_in_queue, active) VALUES (?, ?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $show_in_queue, $active]);
$stmt = $db->prepare("INSERT INTO departments (name_en, name_ar, show_in_queue, active, requires_vitals, is_vitals_room) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $show_in_queue, $active, $requires_vitals, $is_vitals_room]);
$_SESSION['flash_message'] = __('add_department') . ' ' . __('successfully');
$redirect = true;
}
@ -164,9 +166,11 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$name_ar = $_POST['name_ar'] ?? '';
$show_in_queue = isset($_POST['show_in_queue']) ? 1 : 0;
$active = isset($_POST['active']) ? 1 : 0;
$requires_vitals = isset($_POST['requires_vitals']) ? 1 : 0;
$is_vitals_room = isset($_POST['is_vitals_room']) ? 1 : 0;
if ($id && $name_en && $name_ar) {
$stmt = $db->prepare("UPDATE departments SET name_en = ?, name_ar = ?, show_in_queue = ?, active = ? WHERE id = ?");
$stmt->execute([$name_en, $name_ar, $show_in_queue, $active, $id]);
$stmt = $db->prepare("UPDATE departments SET name_en = ?, name_ar = ?, show_in_queue = ?, active = ?, requires_vitals = ?, is_vitals_room = ? WHERE id = ?");
$stmt->execute([$name_en, $name_ar, $show_in_queue, $active, $requires_vitals, $is_vitals_room, $id]);
$_SESSION['flash_message'] = __('edit_department') . ' ' . __('successfully');
$redirect = true;
}
@ -267,6 +271,8 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$stmtQueue = $db->prepare("INSERT INTO patient_queue (patient_id, department_id, doctor_id, visit_id, token_number, status, created_at) VALUES (?, ?, ?, ?, ?, 'waiting', NOW())");
$stmtQueue->execute([$patient_id, $dept_id, $doctor_id, $visit_id, $next_token]);
$queue_id = $db->lastInsertId();
$_SESSION['print_token_id'] = $queue_id;
$token_message = " (" . __('token') . ": #" . $next_token . ")";
}
@ -1068,6 +1074,8 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$text_en = $_POST['text_en'] ?? '';
$text_ar = $_POST['text_ar'] ?? '';
$active = isset($_POST['active']) ? 1 : 0;
$requires_vitals = isset($_POST['requires_vitals']) ? 1 : 0;
$is_vitals_room = isset($_POST['is_vitals_room']) ? 1 : 0;
if ($text_en && $text_ar) {
$stmt = $db->prepare("INSERT INTO queue_ads (text_en, text_ar, active) VALUES (?, ?, ?)");
@ -1080,6 +1088,8 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$text_en = $_POST['text_en'] ?? '';
$text_ar = $_POST['text_ar'] ?? '';
$active = isset($_POST['active']) ? 1 : 0;
$requires_vitals = isset($_POST['requires_vitals']) ? 1 : 0;
$is_vitals_room = isset($_POST['is_vitals_room']) ? 1 : 0;
if ($id && $text_en && $text_ar) {
$stmt = $db->prepare("UPDATE queue_ads SET text_en = ?, text_ar, active = ? WHERE id = ?");
@ -1175,6 +1185,8 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
$role_id = $_POST['role_id'];
$active = isset($_POST['active']) ? 1 : 0;
$requires_vitals = isset($_POST['requires_vitals']) ? 1 : 0;
$is_vitals_room = isset($_POST['is_vitals_room']) ? 1 : 0;
$stmt = $db->prepare("INSERT INTO users (name, email, password, role_id, active) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$name, $email, $password, $role_id, $active]);
@ -1191,6 +1203,8 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST')
$email = $_POST['email'];
$role_id = $_POST['role_id'];
$active = isset($_POST['active']) ? 1 : 0;
$requires_vitals = isset($_POST['requires_vitals']) ? 1 : 0;
$is_vitals_room = isset($_POST['is_vitals_room']) ? 1 : 0;
$sql = "UPDATE users SET name = ?, email = ?, role_id = ?, active = ? WHERE id = ?";
$params = [$name, $email, $role_id, $active, $id];

View File

@ -20,7 +20,7 @@
<select name="patient_id" class="form-select select2-modal" required>
<option value=""><?php echo __("select"); ?>...</option>
<?php foreach ($all_patients as $p): ?>
<option value="<?php echo $p["id"]; ?>"><?php echo htmlspecialchars($p["name"]); ?></option>
<option value="<?php echo $p["id"]; ?>"><?php echo sprintf('%06d', $p['id']) . ' - ' . htmlspecialchars($p['name']) . (!empty($p['phone']) ? ' - ' . htmlspecialchars($p['phone']) : ''); ?></option>
<?php endforeach; ?>
</select>
</div>
@ -744,7 +744,7 @@
<select name="patient_id" id="visit_patient_id" class="form-select select2-modal" required>
<option value=""><?php echo __('select'); ?>...</option>
<?php foreach ($all_patients as $p): ?>
<option value="<?php echo $p['id']; ?>" data-dob="<?php echo $p['dob']; ?>" data-gender="<?php echo $p['gender']; ?>"><?php echo htmlspecialchars($p['name']); ?></option>
<option value="<?php echo $p['id']; ?>" data-dob="<?php echo $p['dob']; ?>" data-gender="<?php echo $p['gender']; ?>"><?php echo sprintf('%06d', $p['id']) . ' - ' . htmlspecialchars($p['name']) . (!empty($p['phone']) ? ' - ' . htmlspecialchars($p['phone']) : ''); ?></option>
<?php endforeach; ?>
</select>
</div>
@ -1615,4 +1615,10 @@ $(document).ready(function() {
});
});
</script>
<script>
<?php if (isset($_SESSION['print_token_id'])): ?>
window.open('print_token.php?id=<?php echo $_SESSION['print_token_id']; ?>', '_blank', 'width=400,height=600');
<?php unset($_SESSION['print_token_id']); ?>
<?php endif; ?>
</script>
</body>

View File

@ -75,7 +75,7 @@
<label class="form-label small text-muted"><?php echo __('patient'); ?></label>
<select id="apt_patient_id" class="form-select select2-modal-apt">
<?php foreach ($all_patients as $p): ?>
<option value="<?php echo $p['id']; ?>" data-address="<?php echo htmlspecialchars($p['address'] ?? ''); ?>"><?php echo htmlspecialchars($p['name']); ?></option>
<option value="<?php echo $p['id']; ?>" data-address="<?php echo htmlspecialchars($p['address'] ?? ''); ?>"><?php echo sprintf('%06d', $p['id']) . ' - ' . htmlspecialchars($p['name']) . (!empty($p['phone']) ? ' - ' . htmlspecialchars($p['phone']) : ''); ?></option>
<?php endforeach; ?>
</select>
</div>

View File

@ -344,7 +344,7 @@ $appointments = $db->query($appointments_sql)->fetchAll();
<label class="form-label small text-muted"><?php echo __('patient'); ?></label>
<select id="dash_apt_patient_id" class="form-select select2-modal-dash">
<?php foreach ($all_patients as $p): ?>
<option value="<?php echo $p['id']; ?>" data-address="<?php echo htmlspecialchars($p['address'] ?? ''); ?>"><?php echo htmlspecialchars($p['name']); ?></option>
<option value="<?php echo $p['id']; ?>" data-address="<?php echo htmlspecialchars($p['address'] ?? ''); ?>"><?php echo sprintf('%06d', $p['id']) . ' - ' . htmlspecialchars($p['name']) . (!empty($p['phone']) ? ' - ' . htmlspecialchars($p['phone']) : ''); ?></option>
<?php endforeach; ?>
</select>
</div>
@ -444,7 +444,7 @@ $appointments = $db->query($appointments_sql)->fetchAll();
<option value=""><?php echo __('select'); ?>...</option>
<?php foreach ($all_patients as $p): ?>
<option value="<?php echo $p['id']; ?>">
<?php echo htmlspecialchars($p['name']) . ($p['phone'] ? ' - ' . htmlspecialchars($p['phone']) : '') . ($p['civil_id'] ? ' (' . htmlspecialchars($p['civil_id']) . ')' : ''); ?>
<?php echo sprintf('%06d', $p['id']) . ' - ' . htmlspecialchars($p['name']) . (!empty($p['phone']) ? ' - ' . htmlspecialchars($p['phone']) : '') . (!empty($p['civil_id']) ? ' (' . htmlspecialchars($p['civil_id']) . ')' : ''); ?>
</option>
<?php endforeach; ?>
</select>

View File

@ -308,6 +308,14 @@ if (isset($_GET['ajax_search'])) {
<input type="checkbox" class="form-check-input" name="show_in_queue" id="deptShowInQueue" value="1" checked>
<label class="form-check-label" for="deptShowInQueue"><?php echo __('show_in_queue'); ?></label>
</div>
<div class="mb-3 form-check form-switch">
<input type="checkbox" class="form-check-input" name="requires_vitals" id="deptRequiresVitals" value="1">
<label class="form-check-label" for="deptRequiresVitals"><?php echo __('requires_vitals'); ?> <small class="text-muted">(<?php echo __('routes_to_vitals_first'); ?>)</small></label>
</div>
<div class="mb-3 form-check form-switch">
<input type="checkbox" class="form-check-input" name="is_vitals_room" id="deptIsVitalsRoom" value="1">
<label class="form-check-label" for="deptIsVitalsRoom"><?php echo __('is_vitals_room'); ?></label>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('close'); ?></button>
@ -415,6 +423,8 @@ function resetDepartmentModal() {
document.getElementById('deptNameAr').value = '';
document.getElementById('deptActive').checked = true;
document.getElementById('deptShowInQueue').checked = true;
document.getElementById('deptRequiresVitals').checked = false;
document.getElementById('deptIsVitalsRoom').checked = false;
}
function showEditDepartmentModal(dept) {
@ -426,6 +436,8 @@ function showEditDepartmentModal(dept) {
document.getElementById('deptNameAr').value = dept.name_ar;
document.getElementById('deptActive').checked = (dept.active == 1);
document.getElementById('deptShowInQueue').checked = (dept.show_in_queue == 1);
document.getElementById('deptRequiresVitals').checked = (dept.requires_vitals == 1);
document.getElementById('deptIsVitalsRoom').checked = (dept.is_vitals_room == 1);
var modal = new bootstrap.Modal(document.getElementById('addDepartmentModal'));
modal.show();

View File

@ -108,11 +108,52 @@ $patients = $db->query("SELECT * FROM patients ORDER BY id DESC LIMIT 50")->fetc
</div>
</div>
<!-- Transfer Token Modal -->
<div class="modal fade" id="transferTokenModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo __('transfer_token'); ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="transferTokenForm">
<input type="hidden" id="transferQueueId">
<div class="mb-3">
<label for="transferDepartment" class="form-label"><?php echo __('department'); ?></label>
<select class="form-select" id="transferDepartment" required>
<option value=""><?php echo __('select_target_department'); ?></option>
<?php foreach ($departments as $dept):
?><option value="<?php echo $dept['id']; ?>"><?php echo $dept['name_' . $lang]; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label for="transferDoctor" class="form-label"><?php echo __('doctor_optional'); ?></label>
<select class="form-select" id="transferDoctor">
<option value=""><?php echo __('any_doctor'); ?></option>
<?php foreach ($doctors as $doc):
?><option value="<?php echo $doc['id']; ?>" data-dept="<?php echo $doc['department_id']; ?>"><?php echo $doc['name_' . $lang]; ?></option>
<?php endforeach; ?>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo __('cancel'); ?></button>
<button type="button" class="btn btn-info text-white" onclick="transferToken()">
<?php echo __('transfer'); ?>
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
fetchQueue();
// Doctor filter logic based on department
// Doctor filter logic based on department (Add Token)
document.getElementById('tokenDepartment').addEventListener('change', function() {
const deptId = this.value;
const doctorSelect = document.getElementById('tokenDoctor');
@ -129,6 +170,23 @@ document.addEventListener('DOMContentLoaded', function() {
doctorSelect.value = '';
});
// Doctor filter logic based on department (Transfer)
document.getElementById('transferDepartment').addEventListener('change', function() {
const deptId = this.value;
const doctorSelect = document.getElementById('transferDoctor');
const options = doctorSelect.querySelectorAll('option');
options.forEach(opt => {
if (!opt.value) return; // Skip default
if (deptId && opt.getAttribute('data-dept') !== deptId) {
opt.style.display = 'none';
} else {
opt.style.display = 'block';
}
});
doctorSelect.value = '';
});
// Auto refresh every 30 seconds
setInterval(fetchQueue, 30000);
});
@ -179,7 +237,7 @@ function renderQueue(queue) {
html += `<tr>
<td class="fw-bold fs-5">#${q.token_number}</td>
<td>${q.patient_name}</td>
<td>${q.department_name}</td>
<td>${q.department_name} ${q.target_department_name ? '<br><small class="text-info"><i class="bi bi-arrow-right"></i> <?php echo __("target_clinic"); ?>' + q.target_department_name + '</small>' : ''}</td>
<td>${q.doctor_name || '-'}</td>
<td>${statusBadge}</td>
<td><small class="text-muted">${waitTime}</small></td>
@ -197,13 +255,73 @@ function getActionButtons(q) {
let btns = '';
if (q.status === 'waiting') {
btns += `<button class="btn btn-sm btn-success me-1" onclick="updateStatus(${q.id}, 'serving')"><i class="bi bi-megaphone"></i> <?php echo __('call'); ?></button>`;
btns += `<button class="btn btn-sm btn-info me-1" onclick="openTransferModal(${q.id}, ${q.department_id}, ${q.target_department_id || 'null'})"><i class="bi bi-arrow-right-circle"></i> <?php echo __('transfer'); ?></button>`;
btns += `<button class="btn btn-sm btn-danger" onclick="updateStatus(${q.id}, 'cancelled')"><i class="bi bi-x-circle"></i></button>`;
} else if (q.status === 'serving') {
btns += `<button class="btn btn-sm btn-primary me-1" onclick="updateStatus(${q.id}, 'completed')"><i class="bi bi-check-circle"></i> <?php echo __('finish'); ?></button>`;
btns += `<button class="btn btn-sm btn-info me-1" onclick="openTransferModal(${q.id}, ${q.department_id}, ${q.target_department_id || 'null'})"><i class="bi bi-arrow-right-circle"></i> <?php echo __('transfer'); ?></button>`;
}
return btns;
}
function openTransferModal(queueId, currentDeptId, targetDeptId = null) {
if (targetDeptId) {
document.getElementById('transferDepartment').value = targetDeptId;
// trigger change to load doctors
const event = new Event('change');
document.getElementById('transferDepartment').dispatchEvent(event);
} else {
document.getElementById('transferDepartment').value = '';
}
document.getElementById('transferQueueId').value = queueId;
document.getElementById('transferDoctor').value = '';
// Hide current department
const deptSelect = document.getElementById('transferDepartment');
Array.from(deptSelect.options).forEach(opt => {
if (opt.value && opt.value == currentDeptId) {
opt.style.display = 'none';
} else {
opt.style.display = 'block';
}
});
const modal = new bootstrap.Modal(document.getElementById('transferTokenModal'));
modal.show();
}
function transferToken() {
const queueId = document.getElementById('transferQueueId').value;
const deptId = document.getElementById('transferDepartment').value;
const docId = document.getElementById('transferDoctor').value;
if (!deptId) {
alert('Please select target department');
return;
}
const formData = new FormData();
formData.append('queue_id', queueId);
formData.append('department_id', deptId);
if (docId) formData.append('doctor_id', docId);
fetch('api/queue.php?action=transfer', {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(data => {
if (data.success) {
const modal = bootstrap.Modal.getInstance(document.getElementById('transferTokenModal'));
modal.hide();
fetchQueue();
alert('Token Transferred! New Token: #' + data.token_number);
} else {
alert('Error: ' + data.error);
}
});
}
function updateStatus(id, status) {
const formData = new FormData();
formData.append('queue_id', id);
@ -251,8 +369,8 @@ function issueToken() {
const modal = bootstrap.Modal.getInstance(document.getElementById('addTokenModal'));
modal.hide();
fetchQueue();
// Show print dialog or small notification
alert('Token Generated: #' + data.token_number);
// Auto-print token
window.open('print_token.php?id=' + data.queue_id, '_blank', 'width=400,height=600');
} else {
alert('Error: ' + data.error);
}

View File

@ -39,6 +39,10 @@ $translations = array (
'edit_department' => 'Edit Department',
'delete_department' => 'Delete Department',
'show_in_queue' => 'Show in Queue',
'requires_vitals' => 'Requires Vitals Check First',
'routes_to_vitals_first' => 'Will auto-route to Vitals/Nursing first',
'is_vitals_room' => 'Is a Vitals / Nursing Room',
'target_clinic' => 'Target Clinic: ',
'no_departments_found' => 'No departments found',
'department_added_successfully' => 'Department added successfully',
'department_updated_successfully' => 'Department updated successfully',
@ -383,6 +387,8 @@ $translations = array (
'doctor_optional' => 'Doctor (Optional)',
'any_doctor' => 'Any Doctor',
'waiting' => 'Waiting',
'please_wait_for_token' => 'Please wait for your token to be called',
'room_number' => 'Room Number',
'serving' => 'Serving',
'all_statuses' => 'All Statuses',
'refresh' => 'Refresh',
@ -390,6 +396,9 @@ $translations = array (
'token' => 'Token',
'wait_time' => 'Wait Time',
'call' => 'Call',
'transfer' => 'Transfer',
'transfer_token' => 'Transfer Token',
'select_target_department' => 'Select Target Department',
'finish' => 'Finish',
'drug_group' => 'Drug Group',
'select_group' => 'Select Group',
@ -481,6 +490,10 @@ $translations = array (
'edit_department' => 'تعديل قسم',
'delete_department' => 'حذف قسم',
'show_in_queue' => 'إظهار في الطابور',
'requires_vitals' => 'يتطلب فحص العلامات الحيوية أولاً',
'routes_to_vitals_first' => 'سيوجه المريض لغرفة التمريض أولاً',
'is_vitals_room' => 'غرفة تمريض / علامات حيوية',
'target_clinic' => 'العيادة المستهدفة: ',
'no_departments_found' => 'لم يتم العثور على أقسام',
'department_added_successfully' => 'تم إضافة القسم بنجاح',
'department_updated_successfully' => 'تم تحديث القسم بنجاح',
@ -825,6 +838,8 @@ $translations = array (
'doctor_optional' => 'Doctor (Optional)',
'any_doctor' => 'Any Doctor',
'waiting' => 'Waiting',
'please_wait_for_token' => 'Please wait for your token to be called',
'room_number' => 'Room Number',
'serving' => 'Serving',
'all_statuses' => 'All Statuses',
'refresh' => 'تحديث',
@ -832,6 +847,9 @@ $translations = array (
'token' => 'التذكرة',
'wait_time' => 'وقت الانتظار',
'call' => 'نداء',
'transfer' => 'تحويل',
'transfer_token' => 'تحويل الرقم',
'select_target_department' => 'اختر القسم الوجهة',
'finish' => 'إنهاء',
'drug_group' => 'مجموعة الدواء',
'select_group' => 'اختر المجموعة',

View File

@ -1 +0,0 @@
<?php require_once __DIR__ . '/db/config.php'; $db = db(); $s = ['timezone' => 'UTC', 'working_hours_start' => '08:00', 'working_hours_end' => '17:00']; foreach ($s as $k => $v) { try { $db->prepare('INSERT IGNORE INTO settings (setting_key, setting_value) VALUES (?, ?)')->execute([$k, $v]); } catch (Exception $e) {} } echo 'patched';

96
print_token.php Normal file
View File

@ -0,0 +1,96 @@
<?php
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/auth.php'; // ensure logged in
$lang = $_SESSION['lang'] ?? 'en';
require_once __DIR__ . '/lang.php';
function __($key) {
global $lang, $translations;
return $translations[$lang][$key] ?? $key;
}
$id = $_GET['id'] ?? null;
if (!$id) die("No Token ID");
$db = db();
$stmt = $db->prepare("
SELECT q.*,
p.name as patient_name,
p.patient_number,
d.name_$lang as doctor_name,
d.room_number,
dept.name_$lang as department_name,
td.name_$lang as target_department_name
FROM patient_queue q
JOIN patients p ON q.patient_id = p.id
JOIN departments dept ON q.department_id = dept.id
LEFT JOIN departments td ON q.target_department_id = td.id
LEFT JOIN employees d ON q.doctor_id = d.id
WHERE q.id = ?
");
$stmt->execute([$id]);
$token = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$token) die("Token not found");
?>
<!DOCTYPE html>
<html lang="<?php echo $lang; ?>" dir="<?php echo $lang === 'ar' ? 'rtl' : 'ltr'; ?>">
<head>
<meta charset="UTF-8">
<title>Print Token</title>
<style>
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
margin: 0;
padding: 10px;
width: 58mm; /* standard 58mm thermal printer width */
color: #000;
}
.center { text-align: center; }
.clinic-name { font-size: 16px; font-weight: bold; margin-bottom: 5px; }
.token-num { font-size: 42px; font-weight: bold; margin: 10px 0; }
hr { border: none; border-top: 1px dashed #000; margin: 10px 0; }
.details { text-align: left; }
.details div { margin-bottom: 5px; }
.footer { font-size: 10px; text-align: center; margin-top: 15px; }
@media print {
@page { margin: 0; }
body { margin: 1cm; }
}
</style>
</head>
<body onload="window.print(); window.setTimeout(window.close, 500);">
<div class="center">
<div class="clinic-name">HMS Clinic</div>
<hr>
<h2><?php echo __('token'); ?></h2>
<div class="token-num">#<?php echo $token['token_number']; ?></div>
<div style="font-size: 16px; font-weight: bold;"><?php echo htmlspecialchars($token['department_name']); ?></div>
<?php if ($token['target_department_name']): ?>
<div style="font-size: 12px; margin-top: 2px;">
<?php echo __('target_clinic'); ?>: <?php echo htmlspecialchars($token['target_department_name']); ?>
</div>
<?php endif; ?>
</div>
<hr>
<div class="details">
<div><strong><?php echo __('patient'); ?>:</strong> <?php echo htmlspecialchars($token['patient_name']); ?></div>
<?php if ($token['doctor_name']): ?>
<div><strong><?php echo __('doctor'); ?>:</strong> <?php echo htmlspecialchars($token['doctor_name']); ?></div>
<?php endif; ?>
<?php if ($token['room_number']): ?>
<div style="font-size: 16px; margin-top: 8px;"><strong><?php echo __('room_number'); ?>:</strong> <?php echo htmlspecialchars($token['room_number']); ?></div>
<?php endif; ?>
<div style="margin-top: 8px; font-size: 12px;"><strong><?php echo __('date'); ?>:</strong> <?php echo date('Y-m-d H:i', strtotime($token['created_at'])); ?></div>
</div>
<hr>
<div class="footer">
<?php echo __('please_wait_for_token'); ?>
</div>
</body>
</html>