Files
wms-app/app/include_ending.php
T
2026-08-17 13:12:07 +07:00

126 lines
4.8 KiB
PHP

<div class="row">
<div class="col-12">
<footer class="text-center py-2 mt-6 text-secondary ">
<p class="mb-0">Copyright © 2026 BRN WMS. All rights reserved.</p>
</footer>
</div>
</div>
<!-- ── Real-time WebSocket connection ──────────────────────────────────────── -->
<!-- Socket.io client is served by the Node.js server itself -->
<script src="<?php echo NODE_PUBLIC_URL; ?>/socket.io/socket.io.js"></script>
<script>
(function () {
// company_id is set in include_topbar.php as a JS global
if (typeof company_id === 'undefined' || !company_id) return;
if (typeof io === 'undefined') return;
window._socket = io('<?php echo NODE_PUBLIC_URL; ?>', {
query: { company_id: company_id, user_id: user_id, role: user_role },
reconnection: true,
reconnectionDelay: 2000,
});
window._socket.on('connect', function () {
console.log('[socket] connected', window._socket.id);
});
window._socket.on('disconnect', function () {
console.log('[socket] disconnected');
});
// ── Real-time notifications ───────────────────────────────────────────────
// PHP emits { title, message, type } where type is 'success'|'info'|'warning'|'danger'
window._socket.on('notification', function (data) {
var title = data.title || '';
var message = data.message || '';
var type = data.type || 'info';
show_toast(title, message, type);
if (typeof add_notification === 'function') {
add_notification(title, message, type);
}
});
})();
// ── show_toast(title, message, type) ─────────────────────────────────────────
// Renders a Bootstrap 5 toast in the top-right corner.
// type: 'success' | 'info' | 'warning' | 'danger'
function show_toast(title, message, type) {
type = type || 'info';
var icon_map = {
success: 'ti-circle-check text-success',
info: 'ti-info-circle text-info',
warning: 'ti-alert-triangle text-warning',
danger: 'ti-circle-x text-danger',
};
var icon = icon_map[type] || icon_map.info;
var container = document.getElementById('_toast_container');
if (!container) {
container = document.createElement('div');
container.id = '_toast_container';
container.className = 'toast-container position-fixed top-0 end-0 p-3';
container.style.zIndex = 9999;
document.body.appendChild(container);
}
var id = '_toast_' + Date.now();
var el = document.createElement('div');
el.id = id;
el.className = 'toast align-items-center border-0 shadow-sm';
el.setAttribute('role', 'alert');
el.setAttribute('aria-live', 'assertive');
el.innerHTML = [
'<div class="d-flex">',
' <div class="toast-body d-flex align-items-start gap-2">',
' <i class="ti ' + icon + ' fs-5 mt-1 flex-shrink-0"></i>',
' <div>',
(title ? '<div class="fw-semibold lh-sm">' + title + '</div>' : ''),
(message ? '<div class="small text-muted">' + message + '</div>' : ''),
' </div>',
' </div>',
' <button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast"></button>',
'</div>',
].join('');
container.appendChild(el);
var toast = new bootstrap.Toast(el, { delay: 5000 });
toast.show();
el.addEventListener('hidden.bs.toast', function () { el.remove(); });
}
</script>
<!-- Stock rows modal — used by: order/order.php, order/return.php, po/po.php, po/supplier_returns.php (via show_stock_rows() in custom.js) -->
<div class="modal fade" id="stock_rows_modal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="stock_rows_modal_title">Stock Transactions</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr>
<th class="ps-4">Type</th>
<th>Product</th>
<th>Warehouse</th>
<th>Location</th>
<th>Lot</th>
<th class="text-end">Qty</th>
<th>Status</th>
<th>Date</th>
</tr>
</thead>
<tbody id="stock_rows_tbody"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>