Files
EpiWebview/PackAufgabenMonitor.php
2025-10-10 12:29:25 +02:00

440 lines
16 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">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<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">
// === PHP-Flags in JS bringen ===
const SCROLL_FLAGS = {
checkout: <?php echo (defined('EnableScrollingCheckOut') && EnableScrollingCheckOut) ? 'true' : 'false'; ?>,
checkin: <?php echo (defined('EnableScrollingCheckIn') && EnableScrollingCheckIn) ? 'true' : 'false'; ?>,
aufgaben: <?php echo (defined('EnableScrollingAufgaben') && EnableScrollingAufgaben) ? 'true' : 'false'; ?>
};
// Mapping: DOM-ID -> Flag-Key
const SCROLLER_KEYS = new Map([
['checkout-scroll', 'checkout'],
['checkin-scroll', 'checkin'],
['aufgaben-scroll', 'aufgaben']
]);
const TABLE_KEYS = new Map([
['checkout-table', 'checkout'],
['checkin-table', 'checkin'],
['aufgaben-table', 'aufgaben']
]);
function keyForEl(el, map) {
if (!el || !el.id) return null;
return map.get(el.id) || null;
}
function isEnabledByEl(el) {
const k = keyForEl(el, SCROLLER_KEYS);
return !!(k && SCROLL_FLAGS[k]);
}
function isEnabledByKey(key) {
return !!SCROLL_FLAGS[key];
}
// === Höhe der Scroll-Container: nebeneinander = volle Höhe; gestapelt = Drittel ===
function sizeScrollContainers() {
const nodes = [
document.querySelector('#checkout-scroll'),
document.querySelector('#checkin-scroll'),
document.querySelector('#aufgaben-scroll')
].filter(Boolean);
if (nodes.length === 0) return;
// Ermitteln, ob alles in einer Zeile (nebeneinander) oder gestapelt
const tops = nodes.map(n => n.getBoundingClientRect().top);
const ROW_TOL = 8; // px
const uniqueRows = [];
tops.forEach(t => {
const exists = uniqueRows.some(rt => Math.abs(rt - t) <= ROW_TOL);
if (!exists) uniqueRows.push(t);
});
const isOneRow = uniqueRows.length === 1;
const bottomMargin = 16;
const minH = 120;
nodes.forEach(node => {
// Wenn Scrolling für diesen Bereich deaktiviert ist, Höhe trotzdem setzen (wie gefordert)
const rect = node.getBoundingClientRect();
if (isOneRow) {
const available = window.innerHeight - rect.top - bottomMargin;
node.style.maxHeight = Math.max(minH, available) + 'px';
} else {
const third = Math.floor(window.innerHeight / 3) - bottomMargin;
node.style.maxHeight = Math.max(minH, third) + 'px';
}
});
}
// === Scroll-/Interaktionsstatus je Scroller ===
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; // nur wenn Feature aktiv
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;
}
// === Loop aufbauen: nahtloses Doppel nur bei Bedarf & nur wenn aktiv ===
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; // deaktiviert -> keine Loops
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; // Höhe des Original-Inhalts als Loop-Länge
}
// === Auto-Scroll (nahtlos) ===
function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
const el = $scroller.get(0);
if (!isEnabledByEl(el)) return;
const st = ensureState(el);
if (st.autoTimer) return; // schon aktiv
if (st.loopHeight <= 0) return; // kein Overflow => nicht scrollen
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, 80);
else stopAutoScroll($scroller);
}
// === AJAX-Reload: relative Position innerhalb der Loop erhalten ===
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, 80);
} 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', '#aufgaben'].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'], ['#aufgaben-scroll','aufgaben']].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', 10000, 'checkout');
initOne('#checkin-scroll', '#checkin-table', '#getCheckInTableHolder', 'sources/getCheckInTable.php', 10000, 'checkin');
initOne('#aufgaben-scroll', '#aufgaben-table', '#AufgabenTableHolder', 'sources/getAufgabenTable.php', 60000, 'aufgaben');
});
</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);
}
</style>
</head>
<body style="margin-bottom:0px; background-color: black;">
<div class="container-fluid">
<div class="row">
<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>
<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 class="col-sm">
<h2 class="text-light">Aufgaben</h2>
<div class="tableFixHead" id="aufgaben-scroll">
<table class="table table-dark mb-0" id="aufgaben-table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Bearbeiter</th>
<th scope="col">Aufgabe</th>
<th scope="col">Zieldatum</th>
<th scope="col">Priorität</th>
</tr>
</thead>
<tbody id="AufgabenTableHolder"></tbody>
</table>
</div>
</div>
</div>
</header>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="vendor/twbs/bootstrap/dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</body>
</html>
<?php
function getTimeFromSeconds(string $timestring) {
$hours = floor($timestring / 3600);
$mins = floor($timestring / 60 % 60);
$secs = floor($timestring % 60);
$timeFormat = sprintf('%02d:%02d', $hours, $mins);
return $timeFormat;
}
?>