updating queuing system
This commit is contained in:
parent
e150954bb6
commit
e09626b316
@ -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:
|
||||
|
||||
@ -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
|
||||
";
|
||||
|
||||
3
db/migrations/20260328_add_smart_queue_routing.sql
Normal file
3
db/migrations/20260328_add_smart_queue_routing.sql
Normal 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;
|
||||
77
fix_db.php
77
fix_db.php
@ -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();
|
||||
}
|
||||
@ -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];
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
18
lang.php
18
lang.php
@ -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' => 'اختر المجموعة',
|
||||
|
||||
@ -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
96
print_token.php
Normal 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>
|
||||
Loading…
x
Reference in New Issue
Block a user