308 lines
13 KiB
PHP
308 lines
13 KiB
PHP
<div class="container-fluid">
|
|
<h2 class="mt-4 mb-4"><?= __('pharmacy_alerts') ?></h2>
|
|
|
|
<ul class="nav nav-tabs" id="pharmacyAlertsTab" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="low-stock-tab" data-bs-toggle="tab" data-bs-target="#low-stock" type="button" role="tab" aria-controls="low-stock" aria-selected="true"><?= __('low_stock') ?></button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="expired-tab" data-bs-toggle="tab" data-bs-target="#expired" type="button" role="tab" aria-controls="expired" aria-selected="false"><?= __('expired_batches') ?></button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="near-expiry-tab" data-bs-toggle="tab" data-bs-target="#near-expiry" type="button" role="tab" aria-controls="near-expiry" aria-selected="false"><?= __('near_expiry') ?></button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content" id="pharmacyAlertsTabContent">
|
|
|
|
<!-- Low Stock Tab -->
|
|
<div class="tab-pane fade show active" id="low-stock" role="tabpanel" aria-labelledby="low-stock-tab">
|
|
<div class="card mt-3 border-0 shadow-sm">
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover" id="lowStockTable">
|
|
<thead>
|
|
<tr>
|
|
<th><?= __('drug_name') ?></th>
|
|
<th><?= __('stock') ?></th>
|
|
<th><?= __('reorder_level') ?></th>
|
|
<th><?= __('min_stock') ?></th>
|
|
<th><?= __('actions') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td colspan="5" class="text-center"><?= __('loading') ?></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<!-- Pagination Container -->
|
|
<div id="lowStockPagination" class="mt-3"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Expired Tab -->
|
|
<div class="tab-pane fade" id="expired" role="tabpanel" aria-labelledby="expired-tab">
|
|
<div class="card mt-3 border-0 shadow-sm">
|
|
<div class="card-body">
|
|
<div class="alert alert-danger">
|
|
<i class="bi bi-exclamation-triangle-fill"></i> <?= __('these_batches_expired') ?>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover" id="expiredTable">
|
|
<thead>
|
|
<tr>
|
|
<th><?= __('drug_name') ?></th>
|
|
<th><?= __('batch_number') ?></th>
|
|
<th><?= __('expiry_date') ?></th>
|
|
<th><?= __('quantity') ?></th>
|
|
<th><?= __('supplier') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td colspan="5" class="text-center"><?= __('loading') ?></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<!-- Pagination Container -->
|
|
<div id="expiredPagination" class="mt-3"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Near Expiry Tab -->
|
|
<div class="tab-pane fade" id="near-expiry" role="tabpanel" aria-labelledby="near-expiry-tab">
|
|
<div class="card mt-3 border-0 shadow-sm">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<div>
|
|
<label for="daysFilter" class="form-label me-2"><?= __('show_expiring_within') ?></label>
|
|
<select id="daysFilter" class="form-select d-inline-block w-auto">
|
|
<option value="30">30 <?= __('days') ?></option>
|
|
<option value="60">60 <?= __('days') ?></option>
|
|
<option value="90" selected>90 <?= __('days') ?></option>
|
|
<option value="180">180 <?= __('days') ?></option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover" id="nearExpiryTable">
|
|
<thead>
|
|
<tr>
|
|
<th><?= __('drug_name') ?></th>
|
|
<th><?= __('batch_number') ?></th>
|
|
<th><?= __('expiry_date') ?></th>
|
|
<th><?= __('days_remaining') ?></th>
|
|
<th><?= __('quantity') ?></th>
|
|
<th><?= __('supplier') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr><td colspan="6" class="text-center"><?= __('loading') ?></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<!-- Pagination Container -->
|
|
<div id="nearExpiryPagination" class="mt-3"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadLowStock();
|
|
|
|
// Load other tabs when clicked
|
|
document.getElementById('expired-tab').addEventListener('shown.bs.tab', function (event) {
|
|
loadExpired();
|
|
});
|
|
|
|
document.getElementById('near-expiry-tab').addEventListener('shown.bs.tab', function (event) {
|
|
loadNearExpiry();
|
|
});
|
|
|
|
document.getElementById('daysFilter').addEventListener('change', function() {
|
|
loadNearExpiry();
|
|
});
|
|
});
|
|
|
|
function loadLowStock(page = 1) {
|
|
fetch(`api/pharmacy.php?action=get_low_stock&page=${page}&limit=10`)
|
|
.then(response => response.json())
|
|
.then(response => {
|
|
const tbody = document.querySelector('#lowStockTable tbody');
|
|
tbody.innerHTML = '';
|
|
|
|
const data = response.data || []; // Handle case where API might not return data key if error or old format (safety)
|
|
|
|
if (data.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" class="text-center"><?= __('no_low_stock_found') ?></td></tr>';
|
|
document.getElementById('lowStockPagination').innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
data.forEach(item => {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${item.name_en} <br> <small class="text-muted">${item.name_ar || ''}</small></td>
|
|
<td><span class="badge bg-danger">${item.total_stock} ${item.unit || ''}</span></td>
|
|
<td>${item.reorder_level}</td>
|
|
<td>${item.min_stock_level}</td>
|
|
<td>
|
|
<a href="pharmacy_lpos.php" class="btn btn-sm btn-primary">
|
|
<i class="bi bi-cart-plus"></i> <?= __('order_stock') ?>
|
|
</a>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
|
|
renderPagination(response.total, response.page, response.limit, 'lowStockPagination', loadLowStock);
|
|
})
|
|
.catch(error => console.error('Error loading low stock:', error));
|
|
}
|
|
|
|
function loadExpired(page = 1) {
|
|
fetch(`api/pharmacy.php?action=get_expired&page=${page}&limit=10`)
|
|
.then(response => response.json())
|
|
.then(response => {
|
|
const tbody = document.querySelector('#expiredTable tbody');
|
|
tbody.innerHTML = '';
|
|
|
|
const data = response.data || [];
|
|
|
|
if (data.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" class="text-center"><?= __('no_expired_batches_found') ?></td></tr>';
|
|
document.getElementById('expiredPagination').innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
data.forEach(item => {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${item.drug_name} <br> <small class="text-muted">${item.drug_name_ar || ''}</small></td>
|
|
<td>${item.batch_number}</td>
|
|
<td class="text-danger fw-bold">${item.expiry_date}</td>
|
|
<td>${item.quantity}</td>
|
|
<td>${item.supplier_name || '-'}</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
|
|
renderPagination(response.total, response.page, response.limit, 'expiredPagination', loadExpired);
|
|
})
|
|
.catch(error => console.error('Error loading expired:', error));
|
|
}
|
|
|
|
function loadNearExpiry(page = 1) {
|
|
const days = document.getElementById('daysFilter').value;
|
|
fetch(`api/pharmacy.php?action=get_near_expiry&days=${days}&page=${page}&limit=10`)
|
|
.then(response => response.json())
|
|
.then(response => {
|
|
const tbody = document.querySelector('#nearExpiryTable tbody');
|
|
tbody.innerHTML = '';
|
|
|
|
const data = response.data || [];
|
|
|
|
if (data.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="6" class="text-center"><?= __('no_batches_expiring_soon') ?></td></tr>';
|
|
document.getElementById('nearExpiryPagination').innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
data.forEach(item => {
|
|
const tr = document.createElement('tr');
|
|
let badgeClass = 'bg-warning text-dark';
|
|
if (item.days_remaining < 30) badgeClass = 'bg-danger';
|
|
|
|
tr.innerHTML = `
|
|
<td>${item.drug_name} <br> <small class="text-muted">${item.drug_name_ar || ''}</small></td>
|
|
<td>${item.batch_number}</td>
|
|
<td>${item.expiry_date}</td>
|
|
<td><span class="badge ${badgeClass}">${item.days_remaining} <?= __('days') ?></span></td>
|
|
<td>${item.quantity}</td>
|
|
<td>${item.supplier_name || '-'}</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
|
|
renderPagination(response.total, response.page, response.limit, 'nearExpiryPagination', loadNearExpiry);
|
|
})
|
|
.catch(error => console.error('Error loading near expiry:', error));
|
|
}
|
|
|
|
function renderPagination(total, page, limit, containerId, callback) {
|
|
const totalPages = Math.ceil(total / limit);
|
|
const container = document.getElementById(containerId);
|
|
container.innerHTML = '';
|
|
|
|
if (totalPages <= 1) return;
|
|
|
|
const ul = document.createElement('ul');
|
|
ul.className = 'pagination justify-content-center';
|
|
|
|
// Previous
|
|
const prevLi = document.createElement('li');
|
|
prevLi.className = `page-item ${page === 1 ? 'disabled' : ''}`;
|
|
prevLi.innerHTML = `<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">«</span></a>`;
|
|
prevLi.onclick = (e) => {
|
|
e.preventDefault();
|
|
if (page > 1) callback(page - 1);
|
|
};
|
|
ul.appendChild(prevLi);
|
|
|
|
// Page Numbers logic
|
|
let startPage = Math.max(1, page - 2);
|
|
let endPage = Math.min(totalPages, page + 2);
|
|
|
|
if (startPage > 1) {
|
|
ul.appendChild(createPageItem(1, page, callback));
|
|
if (startPage > 2) {
|
|
const ellipsis = document.createElement('li');
|
|
ellipsis.className = 'page-item disabled';
|
|
ellipsis.innerHTML = '<span class="page-link">...</span>';
|
|
ul.appendChild(ellipsis);
|
|
}
|
|
}
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
ul.appendChild(createPageItem(i, page, callback));
|
|
}
|
|
|
|
if (endPage < totalPages) {
|
|
if (endPage < totalPages - 1) {
|
|
const ellipsis = document.createElement('li');
|
|
ellipsis.className = 'page-item disabled';
|
|
ellipsis.innerHTML = '<span class="page-link">...</span>';
|
|
ul.appendChild(ellipsis);
|
|
}
|
|
ul.appendChild(createPageItem(totalPages, page, callback));
|
|
}
|
|
|
|
// Next
|
|
const nextLi = document.createElement('li');
|
|
nextLi.className = `page-item ${page === totalPages ? 'disabled' : ''}`;
|
|
nextLi.innerHTML = `<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">»</span></a>`;
|
|
nextLi.onclick = (e) => {
|
|
e.preventDefault();
|
|
if (page < totalPages) callback(page + 1);
|
|
};
|
|
ul.appendChild(nextLi);
|
|
|
|
container.appendChild(ul);
|
|
}
|
|
|
|
function createPageItem(pageNum, currentPage, callback) {
|
|
const li = document.createElement('li');
|
|
li.className = `page-item ${pageNum === currentPage ? 'active' : ''}`;
|
|
li.innerHTML = `<a class="page-link" href="#">${pageNum}</a>`;
|
|
li.onclick = (e) => {
|
|
e.preventDefault();
|
|
callback(pageNum);
|
|
};
|
|
return li;
|
|
}
|
|
</script>
|