257 lines
11 KiB
PHP
257 lines
11 KiB
PHP
<?php
|
|
// includes/pages/queue_management.php
|
|
|
|
// Fetch Departments
|
|
$departments = $db->query("SELECT * FROM departments")->fetchAll(PDO::FETCH_ASSOC);
|
|
// Fetch Doctors
|
|
$doctors = $db->query("SELECT * FROM doctors")->fetchAll(PDO::FETCH_ASSOC);
|
|
// Fetch Patients (Limit 50 for initial load, preferably use AJAX for real search)
|
|
$patients = $db->query("SELECT * FROM patients ORDER BY id DESC LIMIT 50")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
?>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h3 class="fw-bold text-secondary"><?php echo __('queue_management'); ?></h3>
|
|
<div class="d-flex gap-2">
|
|
<a href="queue_display.php" target="_blank" class="btn btn-outline-primary">
|
|
<i class="bi bi-tv"></i> <?php echo __('open_tv_display'); ?>
|
|
</a>
|
|
<button class="btn btn-success shadow-sm text-white" data-bs-toggle="modal" data-bs-target="#addTokenModal">
|
|
<i class="bi bi-plus-lg me-1"></i> <?php echo __('issue_token'); ?>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="card shadow-sm border-0 mb-4">
|
|
<div class="card-body">
|
|
<form id="queueFilterForm" class="row g-3">
|
|
<div class="col-md-4">
|
|
<select id="filterDepartment" class="form-select">
|
|
<option value=""><?php echo __('all_departments'); ?></option>
|
|
<?php foreach ($departments as $dept): ?>
|
|
<option value="<?php echo $dept['id']; ?>"><?php echo $dept['name_' . $lang]; ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select id="filterStatus" class="form-select">
|
|
<option value=""><?php echo __('all_statuses'); ?></option>
|
|
<option value="waiting"><?php echo __('waiting'); ?></option>
|
|
<option value="serving"><?php echo __('serving'); ?></option>
|
|
<option value="completed"><?php echo __('completed'); ?></option>
|
|
<option value="cancelled"><?php echo __('cancelled'); ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<button type="button" class="btn btn-secondary w-100" onclick="fetchQueue()"><?php echo __('refresh'); ?></button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Queue List -->
|
|
<div class="row" id="queueContainer">
|
|
<!-- Queue cards/table will be injected here -->
|
|
</div>
|
|
|
|
<!-- Add Token Modal -->
|
|
<div class="modal fade" id="addTokenModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><?php echo __('issue_new_token'); ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="addTokenForm">
|
|
<div class="mb-3">
|
|
<label for="tokenPatient" class="form-label"><?php echo __('patient'); ?></label>
|
|
<select class="form-select" id="tokenPatient" required>
|
|
<option value=""><?php echo __('select_patient'); ?></option>
|
|
<?php foreach ($patients as $p): ?>
|
|
<option value="<?php echo $p['id']; ?>"><?php echo $p['name']; ?> (<?php echo $p['phone']; ?>)</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<div class="form-text text-muted"><?php echo __('showing_last_50_patients'); ?></div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="tokenDepartment" class="form-label"><?php echo __('department'); ?></label>
|
|
<select class="form-select" id="tokenDepartment" required>
|
|
<option value=""><?php echo __('select_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="tokenDoctor" class="form-label"><?php echo __('doctor_optional'); ?></label>
|
|
<select class="form-select" id="tokenDoctor">
|
|
<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-primary" onclick="issueToken()"><?php echo __('issue_token'); ?></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
fetchQueue();
|
|
|
|
// Doctor filter logic based on department
|
|
document.getElementById('tokenDepartment').addEventListener('change', function() {
|
|
const deptId = this.value;
|
|
const doctorSelect = document.getElementById('tokenDoctor');
|
|
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);
|
|
});
|
|
|
|
function fetchQueue() {
|
|
const deptId = document.getElementById('filterDepartment').value;
|
|
const status = document.getElementById('filterStatus').value;
|
|
|
|
let url = 'api/queue.php?action=list&lang=<?php echo $lang; ?>';
|
|
if (deptId) url += '&department_id=' + deptId;
|
|
if (status) url += '&status=' + status;
|
|
|
|
fetch(url)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
renderQueue(data.data);
|
|
}
|
|
});
|
|
}
|
|
|
|
function renderQueue(queue) {
|
|
const container = document.getElementById('queueContainer');
|
|
if (!queue.length) {
|
|
container.innerHTML = '<div class="col-12 text-center text-muted py-5"><?php echo __('no_tokens_found'); ?></div>';
|
|
return;
|
|
}
|
|
|
|
let html = '<div class="col-12"><div class="table-responsive"><table class="table table-hover align-middle">';
|
|
html += '<thead class="table-light"><tr><th><?php echo __('token'); ?></th><th><?php echo __('patient'); ?></th><th><?php echo __('department'); ?></th><th><?php echo __('doctor'); ?></th><th><?php echo __('status'); ?></th><th><?php echo __('wait_time'); ?></th><th class="text-end"><?php echo __('actions'); ?></th></tr></thead><tbody>';
|
|
|
|
queue.forEach(q => {
|
|
let statusBadge = '';
|
|
switch(q.status) {
|
|
case 'waiting': statusBadge = '<span class="badge bg-warning text-dark"><?php echo __('waiting'); ?></span>'; break;
|
|
case 'serving': statusBadge = '<span class="badge bg-success"><?php echo __('serving'); ?></span>'; break;
|
|
case 'completed': statusBadge = '<span class="badge bg-secondary"><?php echo __('completed'); ?></span>'; break;
|
|
case 'cancelled': statusBadge = '<span class="badge bg-danger"><?php echo __('cancelled'); ?></span>'; break;
|
|
}
|
|
|
|
// Calculate wait time
|
|
const created = new Date(q.created_at);
|
|
const now = new Date();
|
|
const diffMs = now - created;
|
|
const diffMins = Math.floor(diffMs / 60000);
|
|
const waitTime = diffMins + ' min';
|
|
|
|
html += `<tr>
|
|
<td class="fw-bold fs-5">#${q.token_number}</td>
|
|
<td>${q.patient_name}</td>
|
|
<td>${q.department_name}</td>
|
|
<td>${q.doctor_name || '-'}</td>
|
|
<td>${statusBadge}</td>
|
|
<td><small class="text-muted">${waitTime}</small></td>
|
|
<td class="text-end">
|
|
${getActionButtons(q)}
|
|
</td>
|
|
</tr>`;
|
|
});
|
|
|
|
html += '</tbody></table></div></div>';
|
|
container.innerHTML = html;
|
|
}
|
|
|
|
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-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>`;
|
|
}
|
|
return btns;
|
|
}
|
|
|
|
function updateStatus(id, status) {
|
|
const formData = new FormData();
|
|
formData.append('queue_id', id);
|
|
formData.append('status', status);
|
|
|
|
// Pass current doctor ID if logged in (assuming stored in session/global JS var, but for now we won't strictly enforce it on frontend, API handles logic)
|
|
// Ideally we should pass 'doctor_id' if the user is a doctor claiming the token.
|
|
|
|
fetch('api/queue.php?action=update_status', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
fetchQueue();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function issueToken() {
|
|
const patientId = document.getElementById('tokenPatient').value;
|
|
const deptId = document.getElementById('tokenDepartment').value;
|
|
const docId = document.getElementById('tokenDoctor').value;
|
|
|
|
if (!patientId || !deptId) {
|
|
alert('Please select patient and department');
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData();
|
|
formData.append('patient_id', patientId);
|
|
formData.append('department_id', deptId);
|
|
if (docId) formData.append('doctor_id', docId);
|
|
|
|
fetch('api/queue.php?action=add', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('addTokenModal'));
|
|
modal.hide();
|
|
fetchQueue();
|
|
// Show print dialog or small notification
|
|
alert('Token Generated: #' + data.token_number);
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
</script>
|