392 lines
13 KiB
PHP
392 lines
13 KiB
PHP
<?php
|
|
require('config.php');
|
|
require('EpiApi.php');
|
|
require('vendor/autoload.php');
|
|
|
|
$Epi = new Epirent();
|
|
?>
|
|
|
|
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Packmonitor</title>
|
|
|
|
<!-- Bootstrap -->
|
|
<link rel="stylesheet" href="vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
|
|
<script src="scripts/jquery-3.5.1.min.js"></script>
|
|
<link href="css/sticky-footer.css" rel="stylesheet">
|
|
<script src="https://kit.fontawesome.com/93d71de8bc.js" crossorigin="anonymous"></script>
|
|
|
|
<script type="text/javascript">
|
|
/* ===========================
|
|
Feature-Toggles aus PHP
|
|
=========================== */
|
|
const SCROLL_FLAGS = {
|
|
checkout: <?php echo (defined('EnableScrollingCheckOut') && EnableScrollingCheckOut) ? 'true' : 'false'; ?>,
|
|
checkin: <?php echo (defined('EnableScrollingCheckIn') && EnableScrollingCheckIn) ? 'true' : 'false'; ?>
|
|
};
|
|
|
|
const SCROLLER_KEYS = new Map([
|
|
['checkout-scroll', 'checkout'],
|
|
['checkin-scroll', 'checkin']
|
|
]);
|
|
|
|
function keyForEl(el) { return el && el.id ? SCROLLER_KEYS.get(el.id) : null; }
|
|
function isEnabledByEl(el) { const k = keyForEl(el); return !!(k && SCROLL_FLAGS[k]); }
|
|
function isEnabledByKey(key) { return !!SCROLL_FLAGS[key]; }
|
|
|
|
/* ===========================
|
|
Höhe der Scroll-Container
|
|
- 2 nebeneinander: volle Höhe bis Viewport-Ende
|
|
- gestapelt: je 1/2 Fensterhöhe
|
|
=========================== */
|
|
function sizeScrollContainers() {
|
|
const nodes = [
|
|
document.querySelector('#checkout-scroll'),
|
|
document.querySelector('#checkin-scroll')
|
|
].filter(Boolean);
|
|
|
|
if (!nodes.length) return;
|
|
|
|
const tops = nodes.map(n => n.getBoundingClientRect().top);
|
|
const ROW_TOL = 8;
|
|
const firstTop = tops[0];
|
|
const isOneRow = tops.every(t => Math.abs(t - firstTop) <= ROW_TOL);
|
|
|
|
const bottomMargin = 16;
|
|
const minH = 120;
|
|
|
|
nodes.forEach(node => {
|
|
const rect = node.getBoundingClientRect();
|
|
if (isOneRow) {
|
|
const available = window.innerHeight - rect.top - bottomMargin;
|
|
node.style.maxHeight = Math.max(minH, available) + 'px';
|
|
} else {
|
|
const half = Math.floor(window.innerHeight / 2) - bottomMargin;
|
|
node.style.maxHeight = Math.max(minH, half) + 'px';
|
|
}
|
|
});
|
|
}
|
|
|
|
/* ===========================
|
|
Scroll-/Interaktionsstatus
|
|
=========================== */
|
|
const scrollState = new WeakMap(); // { userActive:boolean, autoTimer:number|null, loopHeight:number }
|
|
|
|
function ensureState(el) {
|
|
if (!scrollState.get(el)) scrollState.set(el, { userActive: false, autoTimer: null, loopHeight: 0 });
|
|
return scrollState.get(el);
|
|
}
|
|
|
|
function attachScrollGuards($scroller) {
|
|
const el = $scroller.get(0);
|
|
if (!el || el.__guardsBound) return;
|
|
if (!isEnabledByEl(el)) return;
|
|
const state = ensureState(el);
|
|
|
|
const markActive = () => {
|
|
state.userActive = true;
|
|
clearTimeout(state._quietT);
|
|
state._quietT = setTimeout(() => { state.userActive = false; }, 800);
|
|
stopAutoScroll($scroller);
|
|
};
|
|
|
|
$scroller.on('wheel touchstart touchmove keydown mousedown mouseenter', markActive);
|
|
$scroller.on('mouseleave', () => setTimeout(() => maybeStartAutoScroll($scroller), 600));
|
|
el.__guardsBound = true;
|
|
}
|
|
|
|
/* ===========================
|
|
Nahtloser Loop (Clone)
|
|
=========================== */
|
|
function buildSeamlessLoop($table, $scroller) {
|
|
$table.find('tbody.__loopClone').remove();
|
|
|
|
const scEl = $scroller.get(0);
|
|
const enabled = isEnabledByEl(scEl);
|
|
const st = ensureState(scEl);
|
|
st.loopHeight = 0;
|
|
|
|
if (!enabled) return;
|
|
|
|
const $main = $table.find('tbody').first();
|
|
if ($main.length === 0 || $main.children().length === 0) return;
|
|
|
|
const needScroll = $main.get(0).offsetHeight > scEl.clientHeight + 1;
|
|
if (!needScroll) return;
|
|
|
|
const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
|
|
$table.append($clone);
|
|
st.loopHeight = $main.get(0).offsetHeight;
|
|
}
|
|
|
|
/* ===========================
|
|
Auto-Scroll
|
|
=========================== */
|
|
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
|
|
const el = $scroller.get(0);
|
|
if (!isEnabledByEl(el)) return;
|
|
|
|
const st = ensureState(el);
|
|
if (st.autoTimer) return;
|
|
if (st.loopHeight <= 0) return;
|
|
|
|
const tick = () => {
|
|
if (st.userActive) { stopAutoScroll($scroller); return; }
|
|
el.scrollTop += speedPx;
|
|
if (el.scrollTop >= st.loopHeight) el.scrollTop -= st.loopHeight;
|
|
};
|
|
|
|
st.autoTimer = setInterval(tick, stepMs);
|
|
}
|
|
|
|
function stopAutoScroll($scroller) {
|
|
const el = $scroller.get(0);
|
|
const st = ensureState(el);
|
|
if (st.autoTimer) {
|
|
clearInterval(st.autoTimer);
|
|
st.autoTimer = null;
|
|
}
|
|
}
|
|
|
|
function maybeStartAutoScroll($scroller) {
|
|
const el = $scroller.get(0);
|
|
if (!isEnabledByEl(el)) { stopAutoScroll($scroller); return; }
|
|
const st = ensureState(el);
|
|
if (st.userActive) return;
|
|
if (st.loopHeight > 0) startAutoScroll($scroller, 1, 40);
|
|
else stopAutoScroll($scroller);
|
|
}
|
|
|
|
/* ===========================
|
|
Smart AJAX Reload
|
|
=========================== */
|
|
function smartLoad($scroller, $table, $target, url, intervalMs) {
|
|
const scEl = $scroller.get(0);
|
|
const st = ensureState(scEl);
|
|
const enabled = isEnabledByEl(scEl);
|
|
|
|
const oldLoop = st.loopHeight > 0 ? st.loopHeight : Math.max(1, scEl.scrollHeight - scEl.clientHeight);
|
|
const posInLoop = st.loopHeight > 0 ? (scEl.scrollTop % oldLoop) : scEl.scrollTop;
|
|
const posRatio = Math.min(1, posInLoop / oldLoop);
|
|
|
|
$target.load(url, function () {
|
|
if (enabled) {
|
|
buildSeamlessLoop($table, $scroller);
|
|
if (st.loopHeight > 0) {
|
|
const newPos = Math.floor(posRatio * st.loopHeight);
|
|
if (Math.abs(scEl.scrollTop - newPos) > 1) {
|
|
requestAnimationFrame(() => { scEl.scrollTop = newPos; });
|
|
}
|
|
stopAutoScroll($scroller);
|
|
startAutoScroll($scroller, 1, 40);
|
|
} else {
|
|
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
|
|
stopAutoScroll($scroller);
|
|
}
|
|
} else {
|
|
$table.find('tbody.__loopClone').remove();
|
|
stopAutoScroll($scroller);
|
|
if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
|
|
}
|
|
|
|
setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
|
|
});
|
|
}
|
|
|
|
/* ===========================
|
|
Initialisierung
|
|
=========================== */
|
|
$(document).ready(function () {
|
|
sizeScrollContainers();
|
|
|
|
$(window).on('resize', function () {
|
|
sizeScrollContainers();
|
|
['#checkout', '#checkin'].forEach(prefix => {
|
|
const $scroller = $(`${prefix}-scroll`);
|
|
const $table = $(`${prefix}-table`);
|
|
buildSeamlessLoop($table, $scroller);
|
|
maybeStartAutoScroll($scroller);
|
|
});
|
|
});
|
|
|
|
// Guards nur für aktivierte Scroller
|
|
[['#checkout-scroll','checkout'], ['#checkin-scroll','checkin']].forEach(([sel,key]) => {
|
|
if (isEnabledByKey(key)) attachScrollGuards($(sel));
|
|
});
|
|
|
|
// Erstladen je Tabelle
|
|
function initOne(scrollerSel, tableSel, tbodySel, url, ms, key) {
|
|
const $scroller = $(scrollerSel);
|
|
const $table = $(tableSel);
|
|
const $target = $(tbodySel);
|
|
const enabled = isEnabledByKey(key);
|
|
|
|
$target.load(url, function () {
|
|
if (enabled) {
|
|
buildSeamlessLoop($table, $scroller);
|
|
maybeStartAutoScroll($scroller);
|
|
} else {
|
|
$table.find('tbody.__loopClone').remove();
|
|
stopAutoScroll($scroller);
|
|
}
|
|
setTimeout(() => smartLoad($scroller, $table, $target, url, ms), ms);
|
|
});
|
|
}
|
|
|
|
initOne('#checkout-scroll', '#checkout-table', '#getCheckOutTableHolder', 'sources/getCheckOutTable.php', 5000, 'checkout');
|
|
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 5000, 'checkin');
|
|
});
|
|
</script>
|
|
|
|
|
|
<style>
|
|
/* Scroll-Wrapper je Tabelle */
|
|
.tableFixHead {
|
|
overflow-y: auto;
|
|
/* Höhe wird per JS dynamisch gesetzt, damit genau bis zum Viewport-Ende gescrollt wird */
|
|
max-height: 60vh; /* Fallback */
|
|
border: 1px solid rgba(255,255,255,.1);
|
|
border-radius: .25rem;
|
|
}
|
|
|
|
/* Sticky Header */
|
|
.tableFixHead thead th {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 2; /* über Body-Zellen */
|
|
}
|
|
|
|
/* Saubere Hintergrundfarbe für sticky Header (Bootstrap .table-dark) */
|
|
.table-dark thead th {
|
|
background-color: #212529; /* gleiche Farbe wie .table-dark header */
|
|
}
|
|
|
|
/* Optional: dünne Trennlinien */
|
|
.table-dark tbody tr + tr td {
|
|
border-top: 1px solid rgba(255,255,255,.08);
|
|
}
|
|
|
|
body { background-color: black; margin-bottom: 0; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<!-- Check-Out -->
|
|
<div class="col-sm">
|
|
<h2 class="text-light">Check-Out</h2>
|
|
<div class="tableFixHead" id="checkout-scroll">
|
|
<table class="table table-dark mb-0" id="checkout-table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">#</th>
|
|
<th scope="col">Kunde</th>
|
|
<th scope="col">Event</th>
|
|
<?php
|
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
|
echo '<th scope="col">';
|
|
}
|
|
|
|
if(ShowCheckoutTimeOnCheckout){
|
|
echo "Dispo-Start";
|
|
}
|
|
if(ShowVorbereitungTimeOnCheckout){
|
|
if(ShowCheckoutTimeOnCheckout){echo "<br>";}
|
|
echo "VB-Start";
|
|
}
|
|
if(ShowPackagingTimeOnCheckout){
|
|
if(ShowCheckoutTimeOnCheckout||ShowVorbereitungTimeOnCheckout){echo "<br>";}
|
|
echo "Packen";
|
|
}
|
|
if(ShowDeliveryTimeOnCheckout){
|
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout){echo "<br>";}
|
|
echo "Liefern";
|
|
}
|
|
if(ShowCheckoutTimeOnCheckout || ShowVorbereitungTimeOnCheckout||ShowPackagingTimeOnCheckout || ShowDeliveryTimeOnCheckout){
|
|
echo '</th>';
|
|
}
|
|
|
|
?>
|
|
<th scope="col">Status</th>
|
|
<?php
|
|
if(ShowShippingIcons){
|
|
echo "<th scope='col'>Shipping</th>";
|
|
}
|
|
?>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="getCheckOutTableHolder"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Check-In -->
|
|
<div class="col-sm">
|
|
<h2 class="text-light">Check-In</h2>
|
|
<div class="tableFixHead" id="checkin-scroll">
|
|
<table class="table table-dark mb-0" id="checkin-table">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">#</th>
|
|
<th scope="col">Kunde</th>
|
|
<th scope="col">Event</th>
|
|
<?php
|
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin ||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
|
echo '<th scope="col">';
|
|
}
|
|
|
|
if(ShowCheckInTimeOnCheckin){
|
|
echo "Dispo-Ende";
|
|
}
|
|
if(ShowNachbereitungTimeOnCheckin){
|
|
if(ShowCheckInTimeOnCheckin){echo "<br>";}
|
|
echo "Nachbereitung";
|
|
}
|
|
if(ShowRePackagingTimeOnCheckin){
|
|
if(ShowCheckInTimeOnCheckin||ShowNachbereitungTimeOnCheckin){echo "<br>";}
|
|
echo "Zurückpacken";
|
|
}
|
|
if(ShowReDeliveryTimeOnCheckin){
|
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin){echo "<br>";}
|
|
echo "Rückliefern";
|
|
}
|
|
if(ShowCheckInTimeOnCheckin || ShowNachbereitungTimeOnCheckin||ShowRePackagingTimeOnCheckin || ShowReDeliveryTimeOnCheckin){
|
|
echo '</th>';
|
|
}
|
|
|
|
?>
|
|
<th scope="col">Status</th>
|
|
<?php
|
|
if(ShowShippingIcons){
|
|
echo "<th scope='col'>Shipping</th>";
|
|
}
|
|
?>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="getCheckInTableHolder"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>
|
|
</body>
|
|
</html>
|
|
|
|
|
|
<?php
|
|
function getTimeFromSeconds(string $timestring) {
|
|
$hours = floor($timestring / 3600);
|
|
$mins = floor($timestring / 60 % 60);
|
|
return sprintf('%02d:%02d', $hours, $mins);
|
|
}
|
|
?>
|